feat: added a camera with orthographic projection and did some work restructuring the comet_app to make the setup system optional. Input handling is moved to the app

This commit is contained in:
lisk77 2024-11-13 03:33:02 +01:00
parent 780365aeb8
commit 5a9f632e3a
22 changed files with 1173 additions and 349 deletions

View file

@ -26,7 +26,8 @@ instant = "0.1"
image = { version = "0.24", default_features = false, features = ["png", "jpeg", "hdr"] }
chrono = "0.4.38"
colored = "2.1.0"
winit_input_helper = "0.16.0"
spin_sleep = "1.2.1"
[build-dependencies]
anyhow = "1.0"
@ -43,7 +44,7 @@ members = [
"./crates/comet_ecs",
"./crates/comet_input",
"./crates/comet_log"
]
, "crates/comet_ui"]
[workspace.dependencies]
comet_app = { path = "./crates/comet_app", workspace = true }
@ -53,4 +54,4 @@ comet_renderer = { path = "./crates/comet_renderer", workspace = true }
comet_resources = { path = "./crates/comet_resources", workspace = true }
comet_ecs = { path = "./crates/comet_ecs", workspace = true }
comet_input = { path = "./crates/comet_input", workspace = true }
comet_log = { path = "./crates/comet_log", workspace = true }
comet_log = { path = "./crates/comet_log", workspace = true }

View file

@ -1,6 +1,6 @@
[package]
name = "comet_app"
version = "0.1.0"
version = "0.2.0"
edition = "2021"
[dependencies]
@ -9,6 +9,7 @@ comet_renderer = { path = "../comet_renderer" }
comet_resources = { path = "../comet_resources" }
comet_colors = { path = "../comet_colors" }
comet_log = { path = "../comet_log" }
comet_input = { path = "../comet_input" }
winit = { version = "0.29", features = ["rwh_05"] }
env_logger = "0.10"
@ -18,6 +19,8 @@ log = "0.4.22"
anyhow = "1.0.89"
bytemuck = "1.18.0"
chrono = "0.4.0"
winit_input_helper = "0.16.0"
spin_sleep = "1.2.1"
[dependencies.image]
version = "0.24"

View file

@ -1,4 +1,5 @@
use std::sync::Arc;
use std::time::{Duration, Instant};
use comet_ecs::{Component, ComponentSet, Render, Renderer2D, Transform2D, World};
use comet_resources::{ResourceManager, Vertex};
use comet_renderer::{Renderer};
@ -13,6 +14,11 @@ use comet_colors::LinearRgba;
use comet_ecs::math::Point3;
use comet_log::*;
use winit::dpi::{LogicalSize, PhysicalSize};
use winit::event_loop::ControlFlow;
use winit::platform::windows::WindowBuilderExtWindows;
use winit_input_helper::WinitInputHelper;
use comet_input::input_handler::InputHandler;
use comet_input::keyboard::Key;
pub enum ApplicationType {
App2D,
@ -24,6 +30,10 @@ pub struct App<'a> {
icon: Option<Icon>,
size: Option<LogicalSize<u32>>,
clear_color: Option<LinearRgba>,
setup: Option<fn(&mut World)>,
input_manager: WinitInputHelper,
delta_time: f32,
update_timer: f32,
world: World,
fullscreen: bool,
should_quit: bool
@ -41,6 +51,10 @@ impl<'a> App<'a> {
icon: None,
size: None,
clear_color: None,
setup: None,
input_manager: WinitInputHelper::new(),
delta_time: 0.0,
update_timer: 0.0166667,
world,
fullscreen: false,
should_quit: false
@ -67,6 +81,11 @@ impl<'a> App<'a> {
self
}
pub fn with_setup(mut self, setup: fn(&mut World)) -> Self {
self.setup = Some(setup);
self
}
fn load_icon(path: &std::path::Path) -> Option<Icon> {
let image = image::open(path).expect("Failed to open icon image");
let rgba_image = image.to_rgba8();
@ -74,51 +93,6 @@ impl<'a> App<'a> {
Some(Icon::from_rgba(rgba_image.into_raw(), width, height).unwrap())
}
pub fn render_scene_2d(&self, renderer: &mut Renderer) {
let entities = self.world.get_entities_with(ComponentSet::from_ids(vec![Renderer2D::type_id()]));
let mut vertex_buffer: Vec<Vertex> = Vec::new();
let mut index_buffer: Vec<u16> = Vec::new();
for entity in entities {
let renderer_component = self.world().get_component::<Renderer2D>(entity as usize);
let transform_component = self.world().get_component::<Transform2D>(entity as usize);
if renderer_component.is_visible() {
//renderer.draw_texture_at(renderer_component.get_texture(), Point3::new(transform_component.position().x(), transform_component.position().y(), 0.0));
let position = transform_component.position();
let region = renderer.get_texture(renderer_component.get_texture().to_string());
let (dim_x, dim_y) = region.dimensions();
let (bound_x, bound_y) =
((dim_x as f32/ renderer.config().width as f32) * 0.5, (dim_y as f32/ renderer.config().height as f32) * 0.5);
let buffer_size = vertex_buffer.len() as u16;
vertex_buffer.append(&mut vec![
Vertex :: new ( [-bound_x + position.x(), bound_y + position.y(), 0.0], [region.x0(), region.y0()] ),
Vertex :: new ( [-bound_x + position.x(), -bound_y + position.y(), 0.0], [region.x0(), region.y1()] ),
Vertex :: new ( [ bound_x + position.x(), -bound_y + position.y(), 0.0], [region.x1(), region.y1()] ) ,
Vertex :: new ( [ bound_x + position.x(), bound_y + position.y(), 0.0], [region.x1(), region.y0()] )
]);
index_buffer.append(&mut vec![
0 + buffer_size, 1 + buffer_size, 3 + buffer_size,
1 + buffer_size, 2 + buffer_size, 3 + buffer_size
]);
}
}
renderer.set_buffers(vertex_buffer, index_buffer);
/*for entity in entities {
let renderer_component = self.world.get_component::<Renderer2D>(entity as usize);
let position_component = self.world.get_component::<Transform2D>(entity as usize);
if renderer_component.is_visible() {
renderer.draw_texture_at(renderer_component.get_texture(), Point3::new(position_component.position().x(), position_component.position().y(), 0.0));
}
}*/
}
pub fn world(&self) -> &World {
&self.world
}
@ -127,10 +101,37 @@ impl<'a> App<'a> {
&mut self.world
}
pub fn input_manager(&self) -> &WinitInputHelper {
&self.input_manager
}
pub fn key_pressed(&self, key: Key) -> bool {
self.input_manager.key_pressed(key)
}
pub fn key_held(&self, key: Key) -> bool {
self.input_manager.key_held(key)
}
pub fn key_released(&self, key: Key) -> bool {
self.input_manager.key_released(key)
}
pub fn dt(&self) -> f32 {
self.delta_time
}
pub fn quit(&mut self) {
self.should_quit = true;
}
pub fn get_time_step(&self) -> f32 {
self.update_timer
}
pub fn set_time_step(&mut self, time_step: f32) {
self.update_timer = time_step;
}
fn create_window(app_title: &str, app_icon: &Option<Icon>, window_size: &Option<LogicalSize<u32>>, event_loop: &EventLoop<()>) -> winit::window::Window {
let winit_window = winit::window::WindowBuilder::new()
.with_title(app_title);
@ -141,6 +142,13 @@ impl<'a> App<'a> {
else {
winit_window
};
let winit_window = if let Some(icon) = app_icon.clone() {
winit_window.with_taskbar_icon(Some(icon))
}
else {
winit_window
};
let winit_window = if let Some(size) = window_size.clone() {
winit_window.with_inner_size(size)
}
@ -151,66 +159,64 @@ impl<'a> App<'a> {
winit_window.build(event_loop).unwrap()
}
async fn run_app<F: Fn(&WindowEvent, &mut App, &mut Renderer), G: Fn(&mut World, &mut Renderer)>(mut self, input_manager: F, game_manager: G) {
env_logger::init();
async fn run_app<U: Fn(&mut App, &mut Renderer, f32)>(mut self, update: U) {
let event_loop = EventLoop::new().unwrap();
let window = Self::create_window(self.title, &self.icon, &self.size ,&event_loop);
let mut renderer = Renderer::new(&window, self.clear_color.clone()).await.unwrap();
let mut surface_configured = false;
window.set_maximized(true);
if let Some(setup) = self.setup {
setup(&mut self.world);
}
renderer.initialize_atlas();
event_loop.run(|event, control_flow| {
let mut time_stack = 0.0;
event_loop.run(|event, elwt| {
self.delta_time = renderer.update();
if self.should_quit {
control_flow.exit()
elwt.exit()
}
game_manager(&mut self.world, &mut renderer);
self.render_scene_2d(&mut renderer);
self.input_manager.update(&event);
time_stack += self.delta_time;
while time_stack > self.update_timer {
let time = self.get_time_step();
update(&mut self, &mut renderer, time);
time_stack -= self.update_timer;
}
renderer.render_scene_2d(self.world());
match event {
Event::WindowEvent {
ref event,
window_id,
} if window_id == renderer.window().id() => {
Event::WindowEvent { ref event, window_id, }
if window_id == renderer.window().id() => {
match event {
WindowEvent::CloseRequested {} => control_flow.exit(),
WindowEvent::CloseRequested {} => elwt.exit(),
WindowEvent::Resized(physical_size) => {
surface_configured = true;
renderer.resize(*physical_size);
}
WindowEvent::RedrawRequested => {
renderer.window().request_redraw();
if !surface_configured {
return;
}
/*if self.fullscreen && !renderer.window().fullscreen().is_some() {
renderer.resize(renderer.window().inner_size().into());
}*/
renderer.update();
//println!("{}", 1.0/dt);
match renderer.render() {
Ok(_) => {}
Err(
wgpu::SurfaceError::Lost | wgpu::SurfaceError::Outdated,
) => renderer.resize(renderer.size()),
Err(wgpu::SurfaceError::OutOfMemory) => {
log::error!("OutOfMemory");
control_flow.exit();
error!("OutOfMemory");
elwt.exit();
}
Err(wgpu::SurfaceError::Timeout) => {
warn!("Surface timeout")
}
}
}
_ => {
input_manager(event, &mut self, &mut renderer);
}
_ => {}
}
}
_ => {}
@ -218,7 +224,7 @@ impl<'a> App<'a> {
}).unwrap();
}
pub fn run<F: Fn(&WindowEvent, &mut App, &mut Renderer), G: Fn(&mut World, &mut Renderer)>(mut self, input_manager: F, game_manager: G) {
pollster::block_on(self.run_app(input_manager, game_manager));
pub fn run<U: Fn(&mut App, &mut Renderer, f32)>(mut self, update: U) {
pollster::block_on(self.run_app(update));
}
}

View file

@ -4,9 +4,10 @@ version = "0.2.0"
edition = "2021"
[dependencies]
bit-set = "0.8.0"
component_derive = { path = "./component_derive" }
comet_math = { path = "../comet_math" }
comet_resources = { path = "../comet_resources" }
comet_log = { path = "../comet_log" }
chrono = "0.4"
chrono = "0.4"
bit-set = "0.8.0"

View file

@ -1,22 +1,11 @@
//use comet_resources::Vertex;
use std::cell::RefCell;
use std::rc::Rc;
use crate::math::{
Vec2,
Vec3
};
use component_derive::Component;
pub trait Component: Send + Sync + PartialEq + Default + 'static {
fn new() -> Self where Self: Sized;
fn type_id() -> std::any::TypeId {
std::any::TypeId::of::<Self>()
}
fn type_name() -> String {
std::any::type_name::<Self>().to_string()
}
}
// ##################################################
// # BASIC #
// ##################################################
@ -43,6 +32,12 @@ pub struct Rotation3D {
theta_z: f32
}
#[derive(Component)]
pub struct Rectangle2D{
position: Position2D,
size: Vec2
}
#[derive(Component)]
pub struct Renderer2D {
is_visible: bool,
@ -50,6 +45,53 @@ pub struct Renderer2D {
scale: Vec2
}
// ##################################################
// # BUNDLES #
// ##################################################
#[derive(Component)]
pub struct Transform2D {
position: Position2D,
rotation: Rotation2D
}
#[derive(Component)]
pub struct Transform3D {
position: Position3D,
rotation: Rotation3D
}
// ##################################################
// # TRAITS #
// ##################################################
pub trait Component: Send + Sync + PartialEq + Default + 'static {
fn new() -> Self where Self: Sized;
fn type_id() -> std::any::TypeId {
std::any::TypeId::of::<Self>()
}
fn type_name() -> String {
std::any::type_name::<Self>().to_string()
}
}
pub trait Collider {
fn is_colliding(&self, other: &Self) -> bool;
}
pub trait Render {
fn is_visible(&self) -> bool;
fn set_visibility(&mut self, is_visible: bool);
fn get_texture(&self) -> String;
fn set_texture(&mut self, texture: &'static str);
}
// ##################################################
// # IMPLS #
// ##################################################
impl Position2D {
pub fn from_vec(vec: Vec2) -> Self {
Self {
@ -114,12 +156,44 @@ impl Position3D {
}
}
pub trait Render {
fn is_visible(&self) -> bool;
fn set_visibility(&mut self, is_visible: bool);
fn get_texture(&self) -> String;
fn set_texture(&mut self, texture: &'static str);
//fn get_vertex_data(&self) -> Vec<Vertex>;
impl Rectangle2D {
pub fn new(position: Position2D, size: Vec2) -> Self {
Self {
position,
size
}
}
pub fn position(&self) -> Position2D {
self.position
}
pub fn set_position(&mut self, position: Position2D) {
self.position = position;
}
pub fn size(&self) -> Vec2 {
self.size
}
}
impl Collider for Rectangle2D {
fn is_colliding(&self, other: &Self) -> bool {
let x1 = self.position().x();
let y1 = self.position().y();
let w1 = self.size().x();
let h1 = self.size().y();
let x2 = other.position().x();
let y2 = other.position().y();
let w2 = other.size().x();
let h2 = other.size().y();
x1 < x2 + w2 &&
x1 + w1 > x2 &&
y1 < y2 + h2 &&
y1 + h1 > y2
}
}
impl Render for Renderer2D {
@ -141,31 +215,6 @@ impl Render for Renderer2D {
fn set_texture(&mut self, texture: &'static str) {
self.texture = texture;
}
/*fn get_vertex_data(&self) -> Vec<Vertex> {
vec![
Vertex::new([0.0, 0.0, 0.0], [0.0, 0.0]),
Vertex::new([1.0, 0.0, 0.0], [1.0, 0.0]),
Vertex::new([1.0, 1.0, 0.0], [1.0, 1.0]),
Vertex::new([0.0, 1.0, 0.0], [0.0, 1.0])
]
}*/
}
// ##################################################
// # BUNDLES #
// ##################################################
#[derive(Component)]
pub struct Transform2D {
position: Position2D,
rotation: Rotation2D
}
#[derive(Component)]
pub struct Transform3D {
position: Position3D,
rotation: Rotation3D
}
impl Transform2D {
@ -184,6 +233,13 @@ impl Transform2D {
pub fn rotation_mut(&mut self) -> &mut Rotation2D {
&mut self.rotation
}
pub fn translate(&mut self, displacement: Vec2) {
let x = self.position().x() + displacement.x();
let y = self.position().y() + displacement.y();
self.position_mut().set_x(x);
self.position_mut().set_y(y);
}
}
impl Transform3D {
@ -202,6 +258,4 @@ impl Transform3D {
pub fn rotation_mut(&mut self) -> &mut Rotation3D {
&mut self.rotation
}
}
}

View file

@ -23,16 +23,16 @@ pub struct World {
}
impl World {
pub fn new(dimension: &str) -> Self {
pub fn new(application: &str) -> Self {
let mut component_storage = ComponentStorage::new();
match dimension {
match application {
"2D" => component_storage.register_component::<Transform2D>(0),
"3D" => component_storage.register_component::<Transform3D>(0),
_ => {}
}
Self {
dimension: dimension.to_string(),
dimension: application.to_string(),
id_queue: IdQueue::new(),
next_id: 0,
entities: Vec::new(),
@ -118,7 +118,7 @@ impl World {
self.id_queue.sorted_enqueue(entity_id as u32);
self.get_next_id();
self.remove_entity_from_archetype_subsets(entity_id as u32, self.get_component_set(entity_id));
info!(format!("Deleted entity! ID: {}", entity_id));
info!("Deleted entity! ID: {}", entity_id);
}
fn create_archetype(&mut self, components: ComponentSet) {
@ -179,12 +179,12 @@ impl World {
pub fn register_component<T: Component + 'static>(&mut self) {
self.components.register_component::<T>(self.entities.len());
self.create_archetype(ComponentSet::from_ids(vec![T::type_id()]));
info!(format!("Registered component: {}", T::type_name()));
info!("Registered component: {}", T::type_name());
}
pub fn deregister_component<T: Component + 'static>(&mut self) {
self.components.deregister_component::<T>();
info!(format!("Deregistered component: {}", T::type_name()));
info!("Deregistered component: {}", T::type_name());
}
pub fn add_component<T: Component + 'static>(&mut self, entity_id: usize, component: T) {
@ -201,14 +201,13 @@ impl World {
if self.get_component_set(entity_id) != ComponentSet::from_ids(vec![T::type_id()]) {
self.add_entity_to_archetype(entity_id as u32, self.get_component_set(entity_id));
}
info!(format!("Added component {} to entity {}", T::type_name(), entity_id));
debug!(format!("{:?}", self.archetypes));
info!("Added component {} to entity {}", T::type_name(), entity_id);
}
pub fn remove_component<T: Component + 'static>(&mut self, entity_id: usize) {
self.components.remove_component::<T>(entity_id);
self.remove_entity_from_archetype_subsets(entity_id as u32, self.get_component_set(entity_id));
info!(format!("Removed component {} from entity {}", T::type_name(), entity_id));
info!("Removed component {} from entity {}", T::type_name(), entity_id);
}
pub fn get_component<T: Component + 'static>(&self, entity_id: usize) -> &T {

View file

@ -1,7 +1,8 @@
[package]
name = "comet_input"
version = "0.1.0"
version = "0.1.1"
edition = "2021"
[dependencies]
winit = { version = "0.29", features = ["rwh_05"] }
winit = { version = "0.29", features = ["rwh_05"] }
winit_input_helper = "0.16.0"

View file

@ -0,0 +1,73 @@
use winit::event::{ElementState, WindowEvent, KeyEvent, Event};
use std::collections::HashSet;
use winit::event::WindowEvent::KeyboardInput;
use winit::keyboard::PhysicalKey;
use crate::keyboard::Key;
#[derive(Debug)]
pub struct InputHandler {
keys_pressed: Vec<PhysicalKey>,
keys_held: Vec<PhysicalKey>,
keys_released: Vec<PhysicalKey>
}
impl InputHandler {
pub fn new() -> Self {
Self {
keys_pressed: Vec::new(),
keys_held: Vec::new(),
keys_released: Vec::new()
}
}
pub fn update<T>(&mut self, event: &Event<T>) {
match event {
Event::WindowEvent {
event: WindowEvent::KeyboardInput {
event: KeyEvent {
state,
physical_key: PhysicalKey::Code(keycode),
..
},
..
},
..
} =>
{
match state {
ElementState::Pressed => {
if self.keys_pressed.contains(&PhysicalKey::Code(keycode.clone())) {
self.keys_held.push(PhysicalKey::Code(keycode.clone()));
} else {
self.keys_pressed.push(PhysicalKey::Code(keycode.clone()));
}
self.keys_pressed.push(PhysicalKey::Code(keycode.clone()));
}
ElementState::Released => {
self.keys_released = vec![];
if let Some(index) = self.keys_pressed.iter().position(|&x| x == PhysicalKey::Code(keycode.clone())) {
self.keys_pressed.remove(index);
}
if let Some(index) = self.keys_held.iter().position(|&x| x == PhysicalKey::Code(keycode.clone())) {
self.keys_held.remove(index);
}
self.keys_released.push(PhysicalKey::Code(keycode.clone()));
}
}
}
_ => {}
}
}
pub fn key_pressed(&self, key: Key) -> bool {
self.keys_pressed.contains(&PhysicalKey::Code(key))
}
pub fn key_held(&self, key: Key) -> bool {
self.keys_held.contains(&PhysicalKey::Code(key))
}
pub fn key_released(&self, key: Key) -> bool {
self.keys_released.contains(&PhysicalKey::Code(key))
}
}

View file

@ -1,5 +1,5 @@
use winit::event::{ElementState, KeyEvent, WindowEvent};
use winit::keyboard::{ KeyCode, PhysicalKey};
use winit::keyboard::{ KeyCode, PhysicalKey };
pub type Key = KeyCode;
@ -29,4 +29,18 @@ pub fn key_released(event: &WindowEvent, key_code: Key) -> bool {
} => *code == key_code,
_ => false,
}
}
pub fn key_press(event: &WindowEvent, key_code: Key) -> bool {
match event {
WindowEvent::KeyboardInput {
event: KeyEvent {
state: ElementState::Pressed,
physical_key: PhysicalKey::Code(code),
..
},
..
} => *code == key_code,
_ => false,
}
}

View file

@ -1,2 +1,3 @@
pub mod keyboard;
pub mod mouse;
pub mod input_handler;

View file

@ -1,47 +1,83 @@
#[macro_export]
macro_rules! info {
($msg:expr) => {
println!(
"{} {} : {}",
($fmt:expr $(, $args:expr)*) => {
eprintln!(
"{} [{}::{}] [{}] : {}",
chrono::Local::now().format("%Y-%m-%d %H:%M:%S"),
std::env::var("CARGO_PKG_NAME").unwrap(),
module_path!(),
"\x1b[32m\x1b[1mINFO\x1b[0m",
$msg
format!($fmt $(, $args)*)
);
};
}
#[macro_export]
macro_rules! debug {
($msg:expr) => {
println!(
"{} {} : {}",
($fmt:expr $(, $args:expr)*) => {
eprintln!(
"{} [{}::{}] [{}] : {}",
chrono::Local::now().format("%Y-%m-%d %H:%M:%S"),
std::env::var("CARGO_PKG_NAME").unwrap(),
module_path!(),
"\x1b[34m\x1b[1mDEBUG\x1b[0m",
$msg
format!($fmt $(, $args)*)
);
};
}
#[macro_export]
macro_rules! warn {
($msg:expr) => {
println!(
"{} {} : {}",
($fmt:expr $(, $args:expr)*) => {
eprintln!(
"{} [{}::{}] [{}] : {}",
chrono::Local::now().format("%Y-%m-%d %H:%M:%S"),
std::env::var("CARGO_PKG_NAME").unwrap(),
module_path!(),
"\x1b[33m\x1b[1mWARNING\x1b[0m",
$msg
format!($fmt $(, $args)*)
);
};
}
#[macro_export]
macro_rules! error {
($msg:expr) => {
println!(
"{} {} : {}",
($fmt:expr $(, $args:expr)*) => {
eprintln!(
"{} [{}::{}] [{}] : {}",
chrono::Local::now().format("%Y-%m-%d %H:%M:%S"),
std::env::var("CARGO_PKG_NAME").unwrap(),
module_path!(),
"\x1b[31m\x1b[1mERROR\x1b[0m",
$msg
format!($fmt $(, $args)*)
);
};
}
#[macro_export]
macro_rules! fatal {
($fmt:expr $(, $args:expr)*) => {
eprintln!(
"{} [{}::{}] [{}] : {}",
chrono::Local::now().format("%Y-%m-%d %H:%M:%S"),
std::env::var("CARGO_PKG_NAME").unwrap(),
module_path!(),
"\x1b[41mFATAL\x1b[0m",
format!($fmt $(, $args)*)
);
};
}
#[macro_export]
macro_rules! trace {
($fmt:expr $(, $args:expr)*) => {
eprintln!(
"{} [{}::{}] [{}] : {}",
chrono::Local::now().format("%Y-%m-%d %H:%M:%S"),
std::env::var("CARGO_PKG_NAME").unwrap(),
module_path!(),
"\x1b[35m\x1b[1mTRACE\x1b[0m",
format!($fmt $(, $args)*)
);
};
}

View file

@ -1,7 +1,9 @@
[package]
name = "comet_math"
version = "0.1.0"
version = "0.1.1"
edition = "2021"
[dependencies]
comet_log = { path = "../comet_log" }
num-traits = "0.2.19"
chrono = "0.4.0"

View file

@ -11,7 +11,7 @@ trait LinearTransformation {
// ##################################################
/// Representation of a 2x2 Matrix in row major
/// Representation of a 2x2 Matrix
#[repr(C)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Debug, PartialEq)]
@ -39,13 +39,20 @@ impl Mat2 {
}
}
pub fn from_vec(row1: Vec2, row2: Vec2) -> Self {
pub fn from_rows(row1: Vec2, row2: Vec2) -> Self {
Self {
x00: row1.x(), x01: row1.y(),
x10: row2.x(), x11: row2.y()
}
}
pub fn from_cols(col1: Vec2, col2: Vec2) -> Self {
Self {
x00: col1.x(), x01: col2.x(),
x10: col1.y(), x11: col2.y()
}
}
pub fn get(&self, row: usize, col: usize) -> Option<f32> {
assert!(row <= 1, "This row ({}) is out of bounds! Bounds: 0..1", row);
assert!(col <= 1, "This row ({}) is out of bounds! Bounds: 0..1", col);
@ -68,6 +75,15 @@ impl Mat2 {
}
}
pub fn get_col(&self, col: usize) -> Option<Vec2> {
assert!(col <= 1, "This row ({}) is out of bounds! Bounds: 0..1", col);
match col {
0 => Some(Vec2::new(self.x00, self.x10)),
1 => Some(Vec2::new(self.x01, self.x11)),
_ => None
}
}
pub fn set(&mut self, row: usize, col: usize, element: f32) {
assert!(row <= 1, "This row ({}) is out of bounds! Bounds: 0..1", row);
assert!(col <= 1, "This row ({}) is out of bounds! Bounds: 0..1", col);
@ -91,6 +107,16 @@ impl Mat2 {
}
}
pub fn set_col(&mut self, col: usize, col_content: Vec2) {
assert!(col <= 1, "This row ({}) is out of bounds! Bounds: 0..1", col);
match col {
0 => { self.x00 = col_content.x(); self.x10 = col_content.y(); },
1 => { self.x01 = col_content.x(); self.x11 = col_content.y(); },
_ => {}
}
}
pub fn det(&self) -> f32 {
self.x00 * self.x11
- self.x01 * self.x10
@ -108,6 +134,12 @@ impl Mat2 {
self.set_row(row1, self.get_row(row2).expect(format!("This row ({}) is out of bounds! Bounds: 0..1", row2).as_str()));
self.set_row(row2, tmp);
}
pub fn swap_cols(&mut self, col1: usize, col2: usize) {
let tmp = self.get_col(col1).expect(format!("This row ({}) is out of bounds! Bounds: 0..1", col1).as_str());
self.set_col(col1, self.get_col(col2).expect(format!("This row ({}) is out of bounds! Bounds: 0..1", col2).as_str()));
self.set_col(col2, tmp);
}
}
impl Add<Mat2> for Mat2 {
@ -187,11 +219,12 @@ impl Div<f32> for Mat2 {
}
}
/// [WARN]: This will return a column-major array for wgpu use!
impl Into<[[f32; 2]; 2]> for Mat2 {
fn into(self) -> [[f32; 2]; 2] {
[
[self.x00, self.x01],
[self.x10, self.x11],
[self.x00, self.x10],
[self.x01, self.x11],
]
}
}
@ -235,7 +268,7 @@ impl Mat3 {
}
}
pub fn from_vec(row1: Vec3, row2: Vec3, row3: Vec3) -> Self {
pub fn from_rows(row1: Vec3, row2: Vec3, row3: Vec3) -> Self {
Self {
x00: row1.x(), x01: row1.y(), x02: row1.z(),
x10: row2.x(), x11: row2.y(), x12: row2.z(),
@ -243,6 +276,14 @@ impl Mat3 {
}
}
pub fn from_cols(col1: Vec3, col2: Vec3, col3: Vec3) -> Self {
Self {
x00: col1.x(), x01: col2.x(), x02: col3.x(),
x10: col1.y(), x11: col2.y(), x12: col3.y(),
x20: col1.z(), x21: col2.z(), x22: col3.z()
}
}
pub fn get(&self, row: usize, col: usize) -> Option<f32> {
assert!(row <= 2, "This row ({}) is out of bounds! Bounds: 0..2", row);
assert!(col <= 2, "This row ({}) is out of bounds! Bounds: 0..2", col);
@ -270,6 +311,16 @@ impl Mat3 {
}
}
pub fn get_col(&self, col: usize) -> Option<Vec3> {
assert!(col <= 2, "This row ({}) is out of bounds! Bounds: 0..2", col);
match col {
0 => Some(Vec3::new(self.x00, self.x10, self.x20)),
1 => Some(Vec3::new(self.x01, self.x11, self.x21)),
2 => Some(Vec3::new(self.x02, self.x12, self.x22)),
_ => None
}
}
pub fn set(&mut self, row: usize, col: usize, element: f32) {
assert!(row <= 2, "This row ({}) is out of bounds! Bounds: 0..2", row);
assert!(col <= 2, "This row ({}) is out of bounds! Bounds: 0..2", col);
@ -298,6 +349,16 @@ impl Mat3 {
}
}
pub fn set_col(&mut self, col: usize, col_content: Vec3) {
assert!(col <= 2, "This row ({}) is out of bounds! Bounds: 0..2", col);
match col {
0 => { self.x00 = col_content.x; self.x10 = col_content.y; self.x20 = col_content.z; },
1 => { self.x01 = col_content.x; self.x11 = col_content.y; self.x21 = col_content.z; },
2 => { self.x02 = col_content.x; self.x12 = col_content.y; self.x22 = col_content.z; }
_ => {}
}
}
pub fn det(&self) -> f32 {
self.x00 * self.x11 * self.x22
+ self.x01 * self.x12 * self.x20
@ -320,6 +381,12 @@ impl Mat3 {
self.set_row(row1, self.get_row(row2).expect(format!("This row ({}) is out of bounds! Bounds: 0..2", row2).as_str()));
self.set_row(row2, tmp);
}
pub fn swap_cols(&mut self, col1: usize, col2: usize) {
let tmp = self.get_col(col1).expect(format!("This row ({}) is out of bounds! Bounds: 0..2", col1).as_str());
self.set_col(col1, self.get_col(col2).expect(format!("This row ({}) is out of bounds! Bounds: 0..2", col2).as_str()));
self.set_col(col2, tmp);
}
}
impl Add<Mat3> for Mat3 {
@ -412,9 +479,9 @@ impl Div<f32> for Mat3 {
impl Into<[[f32; 3]; 3]> for Mat3 {
fn into(self) -> [[f32; 3]; 3] {
[
[self.x00, self.x01, self.x02],
[self.x10, self.x11, self.x12],
[self.x20, self.x21, self.x22],
[self.x00, self.x10, self.x20],
[self.x01, self.x11, self.x21],
[self.x02, self.x12, self.x22],
]
}
}
@ -470,7 +537,7 @@ impl Mat4 {
}
}
pub fn from_vec(row1: Vec4, row2: Vec4, row3: Vec4, row4: Vec4) -> Self {
pub fn from_rows(row1: Vec4, row2: Vec4, row3: Vec4, row4: Vec4) -> Self {
Self {
x00: row1.x(), x01: row1.y(), x02: row1.z(), x03: row1.w(),
x10: row2.x(), x11: row2.y(), x12: row2.z(), x13: row2.w(),
@ -479,6 +546,15 @@ impl Mat4 {
}
}
pub fn from_cols(col1: Vec4, col2: Vec4, col3: Vec4, col4: Vec4) -> Self {
Self {
x00: col1.x(), x01: col2.x(), x02: col3.x(), x03: col4.x(),
x10: col1.y(), x11: col2.y(), x12: col3.y(), x13: col4.y(),
x20: col1.z(), x21: col2.z(), x22: col3.z(), x23: col4.z(),
x30: col1.w(), x31: col2.w(), x32: col3.w(), x33: col4.w()
}
}
pub fn rh_look_to(camera: Point3, dir: Vec3, up: Vec3) -> Self {
let f = dir.normalize();
let s = cross(f, up).normalize();
@ -486,19 +562,19 @@ impl Mat4 {
let cam = camera.to_vec();
/*Mat4::new(
Mat4::new(
s.x().clone(), u.x().clone(), -f.x().clone(), 0.0,
s.y().clone(), u.y().clone(), -f.y().clone(), 0.0,
s.z().clone(), u.z().clone(), -f.z().clone(), 0.0,
-dot(&cam, &s), -dot(&cam, &u), dot(&cam, &f),1.0
)*/
)
Mat4::new(
/*Mat4::new(
s.x().clone(), s.y().clone(), s.z().clone(), 0.0,
u.x().clone(), u.y().clone(), u.z().clone(), 0.0,
-f.x().clone(), -f.y().clone(), -f.z().clone(), 0.0,
-dot(&cam, &s), -dot(&cam, &u), dot(&cam, &f), 1.0
)
)*/
}
@ -524,22 +600,31 @@ impl Mat4 {
let bottom = -ymax;
let top = ymax;
/*Mat4::new(
Mat4::new(
(2.0 * near) / (right - left), 0.0, (right + left) / (right - left), 0.0,
0.0, (2.0 * near) / (top - bottom), (top + bottom) / (top - bottom), 0.0,
0.0, 0.0, -(far + near) / (far - near), -(2.0 * far * near) / (far - near),
0.0, 0.0, -1.0, 0.0
)*/
)
Mat4::new(
/*Mat4::new(
(2.0 * near) / (right - left), 0.0, 0.0, 0.0,
0.0, (2.0 * near) / (top - bottom), 0.0, 0.0,
(right + left) / (right - left), (top + bottom) / (top - bottom), -(far + near) / (far - near), -1.0,
0.0, 0.0, -(2.0 * far * near) / (far - near), 0.0
)
)*/
}
pub fn orthographic_matrix(left: f32, right: f32, bottom: f32, top: f32, near: f32, far: f32) -> Self {
Mat4::new(
2.0 / (right - left), 0.0, 0.0, 0.0,
0.0, 2.0 / (top - bottom), 0.0, 0.0,
0.0, 0.0, -2.0 / (far - near), 0.0,
-(right + left) / (right - left), -(top + bottom) / (top - bottom), -(far + near) / (far - near), 1.0
)
}
pub fn get(&self, row: usize, col: usize) -> Option<f32> {
assert!(row <= 3, "This row ({}) is out of bounds! Bounds: 0..3", row);
assert!(col <= 3, "This row ({}) is out of bounds! Bounds: 0..3", col);
@ -575,6 +660,17 @@ impl Mat4 {
}
}
pub fn get_col(&self, col: usize) -> Option<Vec4> {
assert!(col <= 3, "This row ({}) is out of bounds! Bounds: 0..3", col);
match col {
0 => Some(Vec4::new(self.x00, self.x10, self.x20, self.x30)),
1 => Some(Vec4::new(self.x01, self.x11, self.x21, self.x31)),
2 => Some(Vec4::new(self.x02, self.x12, self.x22, self.x32)),
3 => Some(Vec4::new(self.x03, self.x13, self.x23, self.x33)),
_ => None
}
}
pub fn set(&mut self, row: usize, col: usize, element: f32) {
assert!(row <= 3, "The given row ({}) is out of bounds! Bounds: 0..3", row);
assert!(col <= 3, "The given column ({}) is out of bounds! Bounds: 0..3", col);
@ -610,6 +706,17 @@ impl Mat4 {
}
}
pub fn set_col(&mut self, col: usize, col_content: Vec4) {
assert!(col <= 3, "This column ({}) is out of bounds! Bounds: 0..3", col);
match col {
0 => { self.x00 = col_content.x(); self.x10 = col_content.y(); self.x20 = col_content.z(); self.x30 = col_content.w(); },
1 => { self.x01 = col_content.x(); self.x11 = col_content.y(); self.x21 = col_content.z(); self.x31 = col_content.w(); },
2 => { self.x02 = col_content.x(); self.x12 = col_content.y(); self.x22 = col_content.z(); self.x32 = col_content.w(); },
3 => { self.x03 = col_content.x(); self.x13 = col_content.y(); self.x23 = col_content.z(); self.x33 = col_content.w(); }
_ => {}
}
}
pub fn det(&self) -> f32 {
self.x00 * (self.x11 * (self.x22* self.x33 - self.x23 * self.x32)
- self.x21 * (self.x12 * self.x33 - self.x13 * self.x32)
@ -633,6 +740,18 @@ impl Mat4 {
x30: self.x03, x31: self.x13, x32: self.x23, x33: self.x33
}
}
pub fn swap_rows(&mut self, row1: usize, row2: usize) {
let tmp = self.get_row(row1).expect(format!("This row ({}) is out of bounds! Bounds: 0..2", row1).as_str());
self.set_row(row1, self.get_row(row2).expect(format!("This row ({}) is out of bounds! Bounds: 0..2", row2).as_str()));
self.set_row(row2, tmp);
}
pub fn swap_cols(&mut self, col1: usize, col2: usize) {
let tmp = self.get_col(col1).expect(format!("This row ({}) is out of bounds! Bounds: 0..2", col1).as_str());
self.set_col(col1, self.get_col(col2).expect(format!("This row ({}) is out of bounds! Bounds: 0..2", col2).as_str()));
self.set_col(col2, tmp);
}
}
impl Add<Mat4> for Mat4 {
@ -715,10 +834,10 @@ impl Mul<Mat4> for Mat4 {
impl Into<[[f32; 4]; 4]> for Mat4 {
fn into(self) -> [[f32; 4]; 4] {
[
[self.x00, self.x01, self.x02, self.x03],
[self.x10, self.x11, self.x12, self.x13],
[self.x20, self.x21, self.x22, self.x23],
[self.x30, self.x31, self.x32, self.x33],
[self.x00, self.x10, self.x20, self.x30],
[self.x01, self.x11, self.x21, self.x31],
[self.x02, self.x12, self.x22, self.x32],
[self.x03, self.x13, self.x23, self.x33],
]
}
}

View file

@ -1,12 +1,13 @@
use crate::point::{Point2, Point3};
use crate::quaternion::Quat;
use crate::utilities::acos;
use std::ops::{Add, Div, Mul, Sub};
use std::ops::{Add, AddAssign, Div, Mul, Sub, SubAssign};
use comet_log::*;
pub trait InnerSpace {
fn dot(&self, other: &Self) -> f32;
fn dist(&self, other: &Self) -> f32;
fn vAngle(&self, other: &Self) -> f32;
fn v_angle(&self, other: &Self) -> f32;
}
// ##################################################
@ -103,6 +104,13 @@ impl Add<Vec2> for Vec2 {
}
}
impl AddAssign for Vec2 {
fn add_assign(&mut self, other: Vec2) {
self.x += other.x;
self.y += other.y;
}
}
impl Sub<Vec2> for Vec2 {
type Output = Vec2;
@ -114,6 +122,13 @@ impl Sub<Vec2> for Vec2 {
}
}
impl SubAssign for Vec2 {
fn sub_assign(&mut self, other: Vec2) {
self.x -= other.x;
self.y -= other.y;
}
}
impl Mul<f32> for Vec2 {
type Output = Vec2;
@ -125,6 +140,228 @@ impl Mul<f32> for Vec2 {
}
}
impl Into<[f32;2]> for Vec2 {
fn into(self) -> [f32;2] {
[self.x, self.y]
}
}
impl Into<Vec2> for [f32;2] {
fn into(self) -> Vec2 {
Vec2 {
x: self[0],
y: self[1],
}
}
}
/// Representation of a 2D integer Vector
#[repr(C)]
#[derive(Debug, Clone, Copy, PartialEq, Default)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct IVec2 {
x: i64,
y: i64,
}
impl IVec2 {
pub const X: IVec2 = IVec2 { x: 1, y: 0 };
pub const Y: IVec2 = IVec2 { x: 0, y: 1 };
pub const ZERO: IVec2 = IVec2 { x: 0, y: 0 };
pub const fn new(x: i64, y: i64) -> Self {
IVec2 { x, y }
}
pub fn from_point(p: Point2) -> Self {
Self { x: p.x() as i64, y: p.y() as i64 }
}
pub fn as_vec2(&self) -> Vec2 {
Vec2 {
x: self.x as f32,
y: self.y as f32,
}
}
pub fn x(&self) -> i64 {
self.x
}
pub fn y(&self) -> i64 {
self.y
}
pub fn set_x(&mut self, new_x: i64) {
self.x = new_x;
}
pub fn set_y(&mut self, new_y: i64) {
self.y = new_y;
}
pub fn length(&self) -> i64 {
((self.x * self.x + self.y * self.y) as f32).sqrt() as i64
}
pub fn normalize(&self) -> Self {
let factor = 1.0 / self.length() as f32;
IVec2 {
x: (factor * self.x as f32) as i64,
y: (factor * self.y as f32) as i64,
}
}
pub fn xx(&self) -> Self {
Self {
x: self.x,
y: self.x,
}
}
pub fn xy(&self) -> Self {
Self {
x: self.x,
y: self.y,
}
}
pub fn yx(&self) -> Self {
Self {
x: self.y,
y: self.x,
}
}
pub fn yy(&self) -> Self {
Self {
x: self.y,
y: self.y,
}
}
}
impl Add<IVec2> for IVec2 {
type Output = IVec2;
fn add(self, other: IVec2) -> IVec2 {
IVec2 {
x: self.x + other.x,
y: self.y + other.y,
}
}
}
impl Add<IVec2> for Vec2 {
type Output = Vec2;
fn add(self, other: IVec2) -> Vec2 {
Vec2 {
x: self.x + other.x as f32,
y: self.y + other.y as f32,
}
}
}
impl Add<Vec2> for IVec2 {
type Output = Vec2;
fn add(self, other: Vec2) -> Vec2 {
Vec2 {
x: self.x as f32 + other.x,
y: self.y as f32 + other.y,
}
}
}
impl AddAssign for IVec2 {
fn add_assign(&mut self, other: IVec2) {
self.x += other.x;
self.y += other.y;
}
}
impl Sub<IVec2> for IVec2 {
type Output = IVec2;
fn sub(self, other: IVec2) -> IVec2 {
IVec2 {
x: self.x - other.x,
y: self.y - other.y,
}
}
}
impl Sub<IVec2> for Vec2 {
type Output = Vec2;
fn sub(self, other: IVec2) -> Vec2 {
Vec2 {
x: self.x - other.x as f32,
y: self.y - other.y as f32,
}
}
}
impl Sub<Vec2> for IVec2 {
type Output = Vec2;
fn sub(self, other: Vec2) -> Vec2 {
Vec2 {
x: self.x as f32 - other.x,
y: self.y as f32 - other.y,
}
}
}
impl SubAssign for IVec2 {
fn sub_assign(&mut self, other: IVec2) {
self.x -= other.x;
self.y -= other.y;
}
}
impl Mul<f32> for IVec2 {
type Output = IVec2;
fn mul(self, other: f32) -> IVec2 {
IVec2 {
x: self.x * other as i64,
y: self.y * other as i64,
}
}
}
impl From<IVec2> for Vec2 {
fn from(v: IVec2) -> Vec2 {
Vec2 {
x: v.x as f32,
y: v.y as f32,
}
}
}
impl From<Vec2> for IVec2 {
fn from(v: Vec2) -> IVec2 {
IVec2 {
x: v.x as i64,
y: v.y as i64,
}
}
}
impl Into<[i64;2]> for IVec2 {
fn into(self) -> [i64;2] {
[self.x, self.y]
}
}
impl Into<[f32;2]> for IVec2 {
fn into(self) -> [f32;2] {
[self.x as f32, self.y as f32]
}
}
// ##################################################
// # VECTOR 3D #
// ##################################################
@ -181,17 +418,6 @@ impl Vec3 {
self.z = new_z;
}
pub fn into_quaternion(&self) -> Quat {
Quat {
s: 0.0,
v: Vec3 {
x: self.x,
y: self.y,
z: self.z,
}
}
}
pub fn length(&self) -> f32 {
(self.x * self.x + self.y * self.y + self.z * self.z).sqrt()
}
@ -408,6 +634,14 @@ impl Add<Vec3> for Vec3 {
}
}
impl AddAssign for Vec3 {
fn add_assign(&mut self, other: Vec3) {
self.x += other.x;
self.y += other.y;
self.z += other.z;
}
}
impl Sub<Vec3> for Vec3 {
type Output = Vec3;
@ -420,6 +654,14 @@ impl Sub<Vec3> for Vec3 {
}
}
impl SubAssign for Vec3 {
fn sub_assign(&mut self, other: Vec3) {
self.x -= other.x;
self.y -= other.y;
self.z -= other.z;
}
}
impl Mul<f32> for Vec3 {
type Output = Vec3;
@ -432,6 +674,202 @@ impl Mul<f32> for Vec3 {
}
}
impl Into<Quat> for Vec3 {
fn into(self) -> Quat {
Quat::new(0.0, self)
}
}
impl Into<[f32;3]> for Vec3 {
fn into(self) -> [f32;3] {
[self.x, self.y, self.z]
}
}
#[repr(C)]
#[derive(Debug, Clone, Copy, PartialEq, Default)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct IVec3 {
pub x: i64,
pub y: i64,
pub z: i64,
}
impl IVec3 {
pub const X: IVec3 = IVec3 { x: 1, y: 0, z: 0 };
pub const Y: IVec3 = IVec3 { x: 0, y: 1, z: 0 };
pub const Z: IVec3 = IVec3 { x: 0, y: 0, z: 1 };
pub const ZERO: IVec3 = IVec3 { x: 0, y: 0, z: 0 };
pub const fn new(x: i64, y: i64, z: i64) -> Self {
IVec3 { x, y, z }
}
pub fn from_point(p: Point3) -> Self {
Self {
x: p.x() as i64,
y: p.y() as i64,
z: p.z() as i64,
}
}
pub fn x(&self) -> i64 {
self.x
}
pub fn y(&self) -> i64 {
self.y
}
pub fn z(&self) -> i64 {
self.z
}
pub fn set_x(&mut self, new_x: i64) {
self.x = new_x;
}
pub fn set_y(&mut self, new_y: i64) {
self.y = new_y;
}
pub fn set_z(&mut self, new_z: i64) {
self.z = new_z;
}
pub fn length(&self) -> i64 {
((self.x * self.x + self.y * self.y + self.z * self.z) as f32).sqrt() as i64
}
pub fn normalize(&self) -> Self {
let factor = 1 / self.length();
IVec3 {
x: factor * self.x,
y: factor * self.y,
z: factor * self.z,
}
}
}
impl Add<IVec3> for IVec3 {
type Output = IVec3;
fn add(self, other: IVec3) -> IVec3 {
IVec3 {
x: self.x + other.x,
y: self.y + other.y,
z: self.z + other.z,
}
}
}
impl Add<IVec3> for Vec3 {
type Output = Vec3;
fn add(self, other: IVec3) -> Vec3 {
Vec3 {
x: self.x + other.x as f32,
y: self.y + other.y as f32,
z: self.z + other.z as f32,
}
}
}
impl Add<Vec3> for IVec3 {
type Output = Vec3;
fn add(self, other: Vec3) -> Vec3 {
Vec3 {
x: self.x as f32 + other.x,
y: self.y as f32 + other.y,
z: self.z as f32 + other.z,
}
}
}
impl AddAssign for IVec3 {
fn add_assign(&mut self, other: IVec3) {
self.x += other.x;
self.y += other.y;
self.z += other.z;
}
}
impl Sub<IVec3> for IVec3 {
type Output = IVec3;
fn sub(self, other: IVec3) -> IVec3 {
IVec3 {
x: self.x - other.x,
y: self.y - other.y,
z: self.z - other.z,
}
}
}
impl Sub<IVec3> for Vec3 {
type Output = Vec3;
fn sub(self, other: IVec3) -> Vec3 {
Vec3 {
x: self.x - other.x as f32,
y: self.y - other.y as f32,
z: self.z - other.z as f32,
}
}
}
impl Sub<Vec3> for IVec3 {
type Output = Vec3;
fn sub(self, other: Vec3) -> Vec3 {
Vec3 {
x: self.x as f32 - other.x,
y: self.y as f32 - other.y,
z: self.z as f32 - other.z,
}
}
}
impl SubAssign for IVec3 {
fn sub_assign(&mut self, other: IVec3) {
self.x -= other.x;
self.y -= other.y;
}
}
impl Mul<f32> for IVec3 {
type Output = IVec3;
fn mul(self, other: f32) -> IVec3 {
IVec3 {
x: self.x * other as i64,
y: self.y * other as i64,
z: self.z * other as i64,
}
}
}
impl From<IVec3> for Vec3 {
fn from(v: IVec3) -> Vec3 {
Vec3 {
x: v.x as f32,
y: v.y as f32,
z: v.z as f32,
}
}
}
impl From<Vec3> for IVec3 {
fn from(v: Vec3) -> IVec3 {
IVec3 {
x: v.x as i64,
y: v.y as i64,
z: v.z as i64,
}
}
}
// ##################################################
// # VECTOR 4D #
// ##################################################
@ -2568,6 +3006,15 @@ impl Add<Vec4> for Vec4 {
}
}
impl AddAssign for Vec4 {
fn add_assign(&mut self, other: Vec4) {
self.x += other.x;
self.y += other.y;
self.z += other.z;
self.w += other.w;
}
}
impl Sub<Vec4> for Vec4 {
type Output = Vec4;
@ -2581,6 +3028,15 @@ impl Sub<Vec4> for Vec4 {
}
}
impl SubAssign for Vec4 {
fn sub_assign(&mut self, other: Vec4) {
self.x -= other.x;
self.y -= other.y;
self.z -= other.z;
self.w -= other.w;
}
}
impl Mul<f32> for Vec4 {
type Output = Vec4;
@ -2594,6 +3050,12 @@ impl Mul<f32> for Vec4 {
}
}
impl Into<[f32;4]> for Vec4 {
fn into(self) -> [f32;4] {
[self.x, self.y, self.z, self.w]
}
}
impl InnerSpace for Vec2 {
fn dot(&self, other: &Self) -> f32 {
self.x * other.x + self.y * other.y
@ -2607,7 +3069,8 @@ impl InnerSpace for Vec2 {
.length()
}
fn vAngle(&self, other: &Self) -> f32 {
fn v_angle(&self, other: &Self) -> f32 {
//debug!("{:?}", dot(self,other)/(self.length()*other.length()));
acos(dot(self, other) / (self.length() * other.length()))
}
}
@ -2626,7 +3089,7 @@ impl InnerSpace for Vec3 {
.length()
}
fn vAngle(&self, other: &Self) -> f32 {
fn v_angle(&self, other: &Self) -> f32 {
acos(dot(self, other) / (self.length() * other.length()))
}
}
@ -2646,7 +3109,7 @@ impl InnerSpace for Vec4 {
.length()
}
fn vAngle(&self, other: &Self) -> f32 {
fn v_angle(&self, other: &Self) -> f32 {
acos(dot(self, other) / (self.length() * other.length()))
}
}
@ -2672,5 +3135,5 @@ pub fn v_dist<T: InnerSpace>(v1: &T, v2: &T) -> f32 {
}
pub fn v_angle<T: InnerSpace>(v1: &T, v2: &T) -> f32 {
v1.vAngle(v2)
v1.v_angle(v2)
}

View file

@ -1,9 +1,10 @@
[package]
name = "comet_renderer"
version = "0.1.0"
version = "0.2.0"
edition = "2021"
[dependencies]
comet_ecs = { path = "../comet_ecs" }
comet_math = { path = "../comet_math" }
comet_resources = { path = "../comet_resources" }
comet_colors = { path = "../comet_colors" }

View file

@ -1,4 +1,4 @@
use comet_math::Point3;
use comet_math::{Point3, Vec2, Vec3};
#[rustfmt::skip]
pub const OPENGL_TO_WGPU_MATRIX: cgmath::Matrix4<f32> = cgmath::Matrix4::new(
@ -11,44 +11,30 @@ pub const OPENGL_TO_WGPU_MATRIX: cgmath::Matrix4<f32> = cgmath::Matrix4::new(
const SAFE_FRAC_PI_2: f32 = std::f32::consts::FRAC_PI_2 - 0.0001;
pub struct Camera {
eye: cgmath::Point3<f32>,
target: cgmath::Point3<f32>,
up: cgmath::Vector3<f32>,
aspect: f32,
fovy: f32,
znear: f32,
zfar: f32,
zoom: f32,
dimension: Vec2,
position: Vec3
}
impl Camera {
pub fn new(
eye: cgmath::Point3<f32>,
target: cgmath::Point3<f32>,
up: cgmath::Vector3<f32>,
aspect: f32,
fovy: f32,
znear: f32,
zfar: f32,
zoom: f32,
dimension: Vec2,
position: Vec3
) -> Self {
Self {
eye,
target,
up,
aspect,
fovy,
znear,
zfar,
zoom,
dimension,
position
}
}
pub fn build_view_projection_matrix(&self) -> cgmath::Matrix4<f32> {
// 1.
let view = cgmath::Matrix4::look_at_rh(self.eye, self.target, self.up);
// 2.
let proj = cgmath::perspective(cgmath::Deg(self.fovy), self.aspect, self.znear, self.zfar);
let proj = cgmath::ortho(self.position.x() - self.dimension.x() / 2.0, self.position.x() + self.dimension.x() / 2.0, self.position.y() - self.dimension.y() / 2.0, self.position.y() + self.dimension.y() / 2.0, 1.0, 0.0);
// 3.
return OPENGL_TO_WGPU_MATRIX * proj * view;
return OPENGL_TO_WGPU_MATRIX * proj;
}
}

View file

@ -7,7 +7,6 @@ use std::sync::Arc;
use std::time::Instant;
use cgmath::num_traits::FloatConst;
use image::GenericImageView;
use log::info;
use wgpu::Color;
use wgpu::util::DeviceExt;
use winit::{
@ -16,9 +15,10 @@ use winit::{
};
use winit::dpi::Position;
use comet_colors::LinearRgba;
use comet_log::error;
use comet_ecs::{Component, ComponentSet, Render, Renderer2D, Transform2D, World};
use comet_log::*;
use comet_math;
use comet_math::{Mat4, Point3, Vec3};
use comet_math::{Mat4, Point3, Vec2, Vec3};
use comet_resources::{ResourceManager, texture, Vertex, Texture};
use comet_resources::texture_atlas::TextureRegion;
use crate::camera::{Camera, CameraUniform};
@ -67,10 +67,10 @@ pub struct Renderer<'a> {
diffuse_texture: texture::Texture,
diffuse_bind_group: wgpu::BindGroup,
resource_manager: ResourceManager,
/*camera: Camera,
camera: Camera,
camera_uniform: CameraUniform,
camera_buffer: wgpu::Buffer,
camera_bind_group: wgpu::BindGroup,*/
camera_bind_group: wgpu::BindGroup,
}
impl<'a> Renderer<'a> {
@ -199,64 +199,47 @@ impl<'a> Renderer<'a> {
label: Some("diffuse_bind_group"),
});
/*let camera = Camera::new(
// position the camera 1 unit up and 2 units back
// +z is out of the screen
(0.0, 1.0, 2.0).into(),
// have it look at the origin
(0.0, 0.0, 0.0).into(),
// which way is "up"
cgmath::Vector3::unit_y(),
config.width as f32 / config.height as f32,
45.0,
0.1,
100.0,
);
let camera = Camera::new(1.0, Vec2::new(2.0, 2.0), Vec3::new(0.0, 0.0, 0.0));
let mut camera_uniform = CameraUniform::new();
camera_uniform.update_view_proj(&camera);
let mut camera_uniform = CameraUniform::new();
camera_uniform.update_view_proj(&camera);
let camera_buffer = device.create_buffer_init(
&wgpu::util::BufferInitDescriptor {
label: Some("Camera Buffer"),
contents: bytemuck::cast_slice(&[camera_uniform]),
usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
}
);
let camera_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
label: Some("Camera Buffer"),
contents: bytemuck::cast_slice(&[camera_uniform]),
usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
});
let camera_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("camera_bind_group_layout"),
});
let camera_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("camera_bind_group_layout"),
});
let camera_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
layout: &camera_bind_group_layout,
entries: &[
wgpu::BindGroupEntry {
binding: 0,
resource: camera_buffer.as_entire_binding(),
}
],
label: Some("camera_bind_group"),
});*/
let camera_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
layout: &camera_bind_group_layout,
entries: &[wgpu::BindGroupEntry {
binding: 0,
resource: camera_buffer.as_entire_binding(),
}],
label: Some("camera_bind_group"),
});
let render_pipeline_layout =
device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
label: Some("Render Pipeline Layout"),
bind_group_layouts: &[
&texture_bind_group_layout,
//&camera_bind_group_layout,
&camera_bind_group_layout,
],
push_constant_ranges: &[],
});
@ -347,10 +330,10 @@ impl<'a> Renderer<'a> {
diffuse_texture,
diffuse_bind_group,
resource_manager,
/*camera,
camera,
camera_uniform,
camera_buffer,
camera_bind_group,*/
camera_bind_group,
})
}
@ -380,10 +363,10 @@ impl<'a> Renderer<'a> {
((width/ self.config.width as f32) * 0.5, (height/ self.config.height as f32) * 0.5);
vec![
Vertex :: new ( [-bound_x, bound_y, 0.0], [0.0, 0.0] ),
Vertex :: new ( [-bound_x, -bound_y, 0.0], [0.0, 1.0] ),
Vertex :: new ( [ bound_x, -bound_y, 0.0], [1.0, 1.0]) ,
Vertex :: new ( [ bound_x, bound_y, 0.0], [1.0, 0.0] )
Vertex :: new ( [-bound_x, bound_y, 0.0], [0.0, 0.0], [0.0, 0.0, 0.0, 0.0] ),
Vertex :: new ( [-bound_x, -bound_y, 0.0], [0.0, 1.0], [0.0, 0.0, 0.0, 0.0] ),
Vertex :: new ( [ bound_x, -bound_y, 0.0], [1.0, 1.0], [0.0, 0.0, 0.0, 0.0] ),
Vertex :: new ( [ bound_x, bound_y, 0.0], [1.0, 0.0], [0.0, 0.0, 0.0, 0.0] )
]
}
@ -401,10 +384,10 @@ impl<'a> Renderer<'a> {
((self.diffuse_texture.size.width as f32/ self.config.width as f32) * 0.5, (self.diffuse_texture.size.height as f32/ self.config.height as f32) * 0.5);
let vertices: Vec<Vertex> = vec![
Vertex :: new ( [-bound_x, bound_y, 0.0], [0.0, 0.0] ),
Vertex :: new ( [-bound_x, -bound_y, 0.0], [0.0, 1.0] ),
Vertex :: new ( [ bound_x, -bound_y, 0.0], [1.0, 1.0]) ,
Vertex :: new ( [ bound_x, bound_y, 0.0], [1.0, 0.0] )
Vertex :: new ( [-bound_x, bound_y, 0.0], [0.0, 0.0], [0.0, 0.0, 0.0, 0.0] ),
Vertex :: new ( [-bound_x, -bound_y, 0.0], [0.0, 1.0], [0.0, 0.0, 0.0, 0.0] ),
Vertex :: new ( [ bound_x, -bound_y, 0.0], [1.0, 1.0], [0.0, 0.0, 0.0, 0.0] ),
Vertex :: new ( [ bound_x, bound_y, 0.0], [1.0, 0.0], [0.0, 0.0, 0.0, 0.0] )
];
/*let vertices: Vec<Vertex> = vec![
@ -494,8 +477,6 @@ impl<'a> Renderer<'a> {
paths.push(texture_path.clone() + path.unwrap().file_name().to_str().unwrap());
}
error!(format!("{:?}", paths));
self.set_texture_atlas(paths);
}
@ -572,10 +553,10 @@ impl<'a> Renderer<'a> {
((dim_x as f32/ self.config.width as f32) * 0.5, (dim_y as f32/ self.config.height as f32) * 0.5);
let vertices: &mut Vec<Vertex> = &mut vec![
Vertex :: new ( [-bound_x + position.x(), bound_y + position.y(), 0.0 + position.z()], [region.x0(), region.y0()] ),
Vertex :: new ( [-bound_x + position.x(), -bound_y + position.y(), 0.0 + position.z()], [region.x0(), region.y1()] ),
Vertex :: new ( [ bound_x + position.x(), -bound_y + position.y(), 0.0 + position.z()], [region.x1(), region.y1()] ) ,
Vertex :: new ( [ bound_x + position.x(), bound_y + position.y(), 0.0 + position.z()], [region.x1(), region.y0()] )
Vertex :: new ( [-bound_x + position.x(), bound_y + position.y(), 0.0 + position.z()], [region.x0(), region.y0()], [0.0, 0.0, 0.0, 0.0] ),
Vertex :: new ( [-bound_x + position.x(), -bound_y + position.y(), 0.0 + position.z()], [region.x0(), region.y1()], [0.0, 0.0, 0.0, 0.0] ),
Vertex :: new ( [ bound_x + position.x(), -bound_y + position.y(), 0.0 + position.z()], [region.x1(), region.y1()], [0.0, 0.0, 0.0, 0.0] ) ,
Vertex :: new ( [ bound_x + position.x(), bound_y + position.y(), 0.0 + position.z()], [region.x1(), region.y0()], [0.0, 0.0, 0.0, 0.0] )
];
let buffer_size = self.vertex_data.len() as u16;
@ -588,6 +569,45 @@ impl<'a> Renderer<'a> {
self.push_to_buffers(vertices, indices)
}
pub fn render_scene_2d(&mut self, world: &World) {
let entities = world.get_entities_with(ComponentSet::from_ids(vec![Renderer2D::type_id()]));
let mut vertex_buffer: Vec<Vertex> = Vec::new();
let mut index_buffer: Vec<u16> = Vec::new();
for entity in entities {
let renderer_component = world.get_component::<Renderer2D>(entity as usize);
let transform_component = world.get_component::<Transform2D>(entity as usize);
if renderer_component.is_visible() {
//renderer.draw_texture_at(renderer_component.get_texture(), Point3::new(transform_component.position().x(), transform_component.position().y(), 0.0));
let mut position = transform_component.position().clone();
position.set_x(position.x() / self.config().width as f32);
position.set_y(position.y() / self.config().height as f32);
let region = self.get_texture(renderer_component.get_texture().to_string());
let (dim_x, dim_y) = region.dimensions();
let (bound_x, bound_y) =
((dim_x as f32/ self.config().width as f32) * 0.5, (dim_y as f32/ self.config().height as f32) * 0.5);
let buffer_size = vertex_buffer.len() as u16;
vertex_buffer.append(&mut vec![
Vertex :: new ( [-bound_x + position.x(), bound_y + position.y(), 0.0], [region.x0(), region.y0()], [0.0, 0.0, 0.0, 0.0] ),
Vertex :: new ( [-bound_x + position.x(), -bound_y + position.y(), 0.0], [region.x0(), region.y1()], [0.0, 0.0, 0.0, 0.0] ),
Vertex :: new ( [ bound_x + position.x(), -bound_y + position.y(), 0.0], [region.x1(), region.y1()], [0.0, 0.0, 0.0, 0.0] ) ,
Vertex :: new ( [ bound_x + position.x(), bound_y + position.y(), 0.0], [region.x1(), region.y0()], [0.0, 0.0, 0.0, 0.0] )
]);
index_buffer.append(&mut vec![
0 + buffer_size, 1 + buffer_size, 3 + buffer_size,
1 + buffer_size, 2 + buffer_size, 3 + buffer_size
]);
}
}
self.set_buffers(vertex_buffer, index_buffer);
}
pub fn window(&self) -> &Window {
&self.window
}
@ -606,10 +626,11 @@ impl<'a> Renderer<'a> {
}
}
pub fn update(&mut self) {
pub fn update(&mut self) -> f32 {
let now = Instant::now();
self.deltatime = now.duration_since(self.last_frame_time).as_secs_f32(); // Time delta in seconds
self.last_frame_time = now;
self.deltatime
}
pub fn render(&mut self) -> Result<(), wgpu::SurfaceError> {
@ -642,7 +663,7 @@ impl<'a> Renderer<'a> {
render_pass.set_pipeline(&self.render_pipeline);
render_pass.set_bind_group(0, &self.diffuse_bind_group, &[]);
//render_pass.set_bind_group(1, &self.camera_bind_group, &[]);
render_pass.set_bind_group(1, &self.camera_bind_group, &[]);
render_pass.set_vertex_buffer(0, self.vertex_buffer.slice(..));
render_pass.set_index_buffer(self.index_buffer.slice(..), wgpu::IndexFormat::Uint16);
render_pass.draw_indexed(0..self.num_indices, 0, 0..1);

View file

@ -1,18 +1,20 @@
// Vertex shader
/*struct CameraUniform {
struct CameraUniform {
view_proj: mat4x4<f32>,
};
@group(1) @binding(0) // 1.
var<uniform> camera: CameraUniform;*/
var<uniform> camera: CameraUniform;
struct VertexInput {
@location(0) position: vec3<f32>,
@location(1) tex_coords: vec2<f32>,
@location(2) color: vec4<f32>,
}
struct VertexOutput {
@builtin(position) clip_position: vec4<f32>,
@location(0) tex_coords: vec2<f32>,
@location(1) color: vec4<f32>,
}
@vertex
@ -21,7 +23,8 @@ fn vs_main(
) -> VertexOutput {
var out: VertexOutput;
out.tex_coords = model.tex_coords;
out.clip_position = /*camera.view_proj **/ vec4<f32>(model.position, 1.0);
out.color = model.color;
out.clip_position = camera.view_proj * vec4<f32>(model.position, 1.0);
return out;
}

View file

@ -1,6 +1,5 @@
use std::collections::HashMap;
use std::path::Path;
use chrono::Local;
use std::time::Instant;
use image::{DynamicImage, GenericImage, GenericImageView, ImageFormat};
use comet_log::*;
@ -165,7 +164,7 @@ impl TextureAtlas {
//base.save_with_format(output_path, ImageFormat::Png).expect("Failed to save texture atlas");
info!("Texture atlas created!");
debug!(format!("{:?}", regions));
//debug!(format!("{:?}", regions));
/*let t1 = Instant::now();
let delta = t1.duration_since(t0);

View file

@ -5,13 +5,15 @@ use wgpu::Color;
pub struct Vertex {
position: [f32; 3],
tex_coords: [f32; 2],
color: [f32; 4]
}
impl Vertex {
pub fn new(position: [f32; 3], tex_coords: [f32; 2]) -> Self {
pub fn new(position: [f32; 3], tex_coords: [f32; 2], color: [f32; 4]) -> Self {
Self {
position,
tex_coords
tex_coords,
color
}
}
@ -23,6 +25,10 @@ impl Vertex {
self.tex_coords = new_tex_coords
}
pub fn set_color(&mut self, new_color: [f32; 4]) {
self.color = new_color
}
pub fn desc() -> wgpu::VertexBufferLayout<'static> {
wgpu::VertexBufferLayout {
array_stride: std::mem::size_of::<Vertex>() as wgpu::BufferAddress,
@ -37,6 +43,11 @@ impl Vertex {
offset: std::mem::size_of::<[f32; 3]>() as wgpu::BufferAddress,
shader_location: 1,
format: wgpu::VertexFormat::Float32x2,
},
wgpu::VertexAttribute {
offset: std::mem::size_of::<[f32; 5]>() as wgpu::BufferAddress,
shader_location: 2,
format: wgpu::VertexFormat::Float32x4,
}
]
}

View file

@ -6,4 +6,8 @@ pub use comet_ecs as ecs;
pub use comet_app as app;
pub use comet_colors as colors;
pub use comet_input as input;
pub use comet_log as log;
pub use comet_log as log;
pub mod prelude {
pub use comet_app::App;
}

View file

@ -4,64 +4,89 @@ use comet::{
ApplicationType::*
},
renderer::Renderer,
ecs::World,
ecs::*,
math::*,
input::keyboard::*,
log::*
};
use winit::event::{WindowEvent};
use comet_ecs::{Component, ComponentSet, Render, Renderer2D, Transform2D};
use comet_input::mouse::{mouse_entered, mouse_pressed, Button};
fn input(event: &WindowEvent, app: &mut App, renderer: &mut Renderer) {
match event {
_ if key_pressed(event, Key::Escape) => app.quit(),
_ if key_pressed(event, Key::KeyC) => { renderer.clear_buffers() }
_ if key_pressed(event, Key::KeyE) => {
let mut renderer2d = Renderer2D::new();
renderer2d.set_texture(r"resources/textures/comet_icon.png");
renderer2d.set_visibility(true);
use winit_input_helper::WinitInputHelper;
use comet_input::input_handler::InputHandler;
let id = app.world_mut().new_entity();
app.world_mut().add_component(id as usize, renderer2d.clone());
app.world_mut().add_component(0, renderer2d);
fn update_position(input: WinitInputHelper, transform: &mut Transform2D, dt: f32) {
let mut direction = Vec2::ZERO;
let previous = transform.position().clone();
let transform = app.world_mut().get_component_mut::<Transform2D>(id as usize);
transform.position_mut().set_x(0.5);
if input.key_held(Key::KeyW) {
direction += Vec2::Y;
}
if input.key_held(Key::KeyA) {
direction -= Vec2::X;
}
if input.key_held(Key::KeyS) {
direction -= Vec2::Y;
}
if input.key_held(Key::KeyD) {
direction += Vec2::X;
}
debug!(format!("{:?}", app.world().components().get_component::<Renderer2D>(0)));
},
_ if key_pressed(event, Key::KeyW) => {
let transform = app.world_mut().get_component_mut::<Transform2D>(0);
let y = transform.position().y();
transform.position_mut().set_y(y + 0.1);
},
_ if key_pressed(event, Key::KeyA) => {
let transform = app.world_mut().get_component_mut::<Transform2D>(0);
let x = transform.position().x();
transform.position_mut().set_x(x - 0.1);
},
_ if key_pressed(event, Key::KeyS) => {
let transform = app.world_mut().get_component_mut::<Transform2D>(0);
let y = transform.position().y();
transform.position_mut().set_y(y - 0.1);
},
_ if key_pressed(event, Key::KeyD) => {
let transform = app.world_mut().get_component_mut::<Transform2D>(0);
let x = transform.position().x();
transform.position_mut().set_x(x + 0.1);
if direction != Vec2::ZERO {
let normalized_dir = direction.normalize();
if normalized_dir.x().is_nan() || normalized_dir.y().is_nan() {
error!("Direction is NaN! X: {}, Y: {}", normalized_dir.x(), normalized_dir.y());
}
_ => {}
let displacement = normalized_dir * 777.7 * dt;
transform.translate(displacement);
}
if (transform.position().as_vec() - previous.as_vec()).x() > 13.0 {
debug!("Actual Displacement: {:?}", transform.position().as_vec() - previous.as_vec());
}
}
fn update(world: &mut World, renderer: &mut Renderer) {
if !world.components().contains_components(ComponentSet::from_ids(vec![Transform2D::type_id(), Renderer2D::type_id()])) {
world.register_component::<Renderer2D>();
}
if world.entities().len() == 0 {
let id = world.new_entity();
fn setup(world: &mut World) {
world.register_component::<Renderer2D>();
//world.register_component::<Rectangle2D>();
let mut renderer2d = Renderer2D::new();
renderer2d.set_texture(r"resources/textures/comet_icon.png");
renderer2d.set_visibility(true);
let id = world.new_entity();
world.add_component(id as usize, renderer2d.clone());
let transform = world.get_component_mut::<Transform2D>(id as usize);
transform.translate(Vec2::X*5.0);
world.add_component(id as usize, renderer2d);
/*let rectangle2d = Rectangle2D::new(*tranform.position(), Vec2::new(0.1, 0.1));
world.add_component(id as usize, rectangle2d);
let id2 = world.new_entity();
let tranform2 = world.get_component_mut::<Transform2D>(id as usize);
let rectangle = Rectangle2D::new(*tranform2.position(), Vec2::new(0.1, 0.1));
world.add_component(id2 as usize, rectangle);*/
}
fn update(app: &mut App, renderer: &mut Renderer, dt: f32) {
if app.key_pressed(Key::Escape) { app.quit() }
if app.key_pressed(Key::KeyC) { app.set_time_step(0.0016667) }
if app.key_pressed(Key::KeyV) { app.set_time_step(0.0166667) }
if app.key_pressed(Key::KeyE) { app.world_mut().get_component_mut::<Transform2D>(0).translate([0f32,0f32].into()) }
if app.key_held(Key::KeyW)
|| app.key_held(Key::KeyA)
|| app.key_held(Key::KeyS)
|| app.key_held(Key::KeyD)
{
update_position(app.input_manager().clone(), app.world_mut().get_component_mut::<Transform2D>(0), dt);
}
let mut transform = app.world_mut().get_component_mut::<Transform2D>(0);
}
fn main() {
@ -69,6 +94,7 @@ fn main() {
.with_title("Comet App")
.with_icon(r"resources/textures/comet_icon.png")
.with_size(1920, 1080)
.run(input, update)
.with_setup(setup)
.run(update)
;
}