diff --git a/examples/mario/main.rs b/examples/mario/main.rs index 9f37632..67e96c9 100644 --- a/examples/mario/main.rs +++ b/examples/mario/main.rs @@ -26,6 +26,7 @@ use scion::{ }; use crate::{character_control_system::move_char_system, collisions_system::collider_system}; +use scion::core::components::maths::hierarchy::Parent; pub const MAX_VELOCITY: i32 = 100; @@ -43,28 +44,26 @@ pub struct Mario { } impl SimpleGameLayer for Mario { - fn on_start(&mut self, world: &mut World, resources: &mut Resources) { + fn on_start(&mut self, world: &mut World, _resources: &mut Resources) { add_background(world); self.hero = Some(add_character(world)); self.map = add_level_data(world); - resources.insert(Camera::new_with_position( + let mut camera_transform = Transform::new(Coordinates::new(-202., -320.), 1.0, 0.); + camera_transform.set_global_translation_bounds(Some(0.),Some(2060.),Some(0.),Some(0.)); + world.push((Camera::new( 500., 640., - 10., - Coordinates::new(50., 0.), - )); + 10.), + camera_transform + , Parent(self.hero.expect("Hero is mandatory")))); } - fn late_update(&mut self, world: &mut World, resources: &mut Resources) { + fn late_update(&mut self, world: &mut World, _resources: &mut Resources) { let hero = <(&mut Hero, &mut Transform)>::query() .get_mut(world, self.hero.unwrap()) .unwrap(); if hero.0.velocity != 0 { hero.1 .append_x(hero.0.velocity as f32 / MAX_VELOCITY as f32 * 2.5); - resources - .get_mut::() - .unwrap() - .append_position(hero.0.velocity as f32 / MAX_VELOCITY as f32 * 2.5, 0.); } if hero.0.gravity != 0 { @@ -103,7 +102,7 @@ fn add_level_data(world: &mut World) -> Vec> { .join("examples/mario/assets/level.csv") .get(), )) - .unwrap_or(vec![]); + .unwrap_or(vec![]); let csv = from_utf8(file.as_slice()).expect("no"); let data: Vec> = csv .split("\r\n") @@ -192,8 +191,8 @@ fn main() { ) .get(), ) - .with_game_layer(GameLayer::weak::("Mario")) - .with_system(move_char_system()) - .with_system(collider_system()) - .run(); + .with_game_layer(GameLayer::weak::("Mario")) + .with_system(move_char_system()) + .with_system(collider_system()) + .run(); } diff --git a/examples/taquin/main.rs b/examples/taquin/main.rs index d09d6e1..6baa73d 100644 --- a/examples/taquin/main.rs +++ b/examples/taquin/main.rs @@ -128,7 +128,7 @@ impl SimpleGameLayer for Layer { } } } - resources.insert(Camera::new(768., 768., 10.)); + world.push((Camera::new(768., 768., 10.), Transform::default())); resources.insert(Taquin::new()); } } @@ -144,7 +144,7 @@ fn main() { ) .get(), ) - .with_system(taquin_system()) - .with_game_layer(GameLayer::weak::("Taquin")) - .run(); + .with_system(taquin_system()) + .with_game_layer(GameLayer::weak::("Taquin")) + .run(); } diff --git a/examples/tetris/layer.rs b/examples/tetris/layer.rs index e139f5e..f9641ab 100644 --- a/examples/tetris/layer.rs +++ b/examples/tetris/layer.rs @@ -12,7 +12,6 @@ use scion::{ }, }, legion::{Entity, Resources, World}, - utils::file::app_base_path, }; use crate::{asset_path, resources::TetrisResource}; @@ -27,7 +26,7 @@ impl SimpleGameLayer for TetrisLayer { add_main_ui_mask(world); add_ui_top_overflow(world); self.score = Some(add_score_ui(world)); - resources.insert(Camera::new(544., 704., 10.)); + world.push((Camera::new(544., 704., 10.), Transform::default())); let _r = resources .get_mut::() .unwrap() diff --git a/src/core/components/maths/camera.rs b/src/core/components/maths/camera.rs index 1aabbff..67cc2e2 100644 --- a/src/core/components/maths/camera.rs +++ b/src/core/components/maths/camera.rs @@ -1,6 +1,6 @@ -use crate::core::components::maths::transform::Coordinates; -/// Mandatory resource to add to the Resources to have anything rendered. + +/// Mandatory component to add to the World to have anything rendered. pub struct Camera { pub(crate) left: f32, pub(crate) right: f32, @@ -8,7 +8,6 @@ pub struct Camera { pub(crate) bottom: f32, pub(crate) near: f32, pub(crate) far: f32, - pub(crate) position: Coordinates, } impl Camera { @@ -21,33 +20,6 @@ impl Camera { bottom: -1. * height, near: 0.0, far: depth, - position: Coordinates::default(), - } - } - - /// Creates a camera with a viewport of size (width;height;depth). In general the same width and height of the window. - pub fn new_with_position(width: f32, height: f32, depth: f32, position: Coordinates) -> Self { - Self { - left: 0., - right: width, - top: 0., - bottom: -1. * height, - near: 0.0, - far: depth, - position, } } - - pub fn position(&self) -> &Coordinates { - &self.position - } - - pub fn set_position(&mut self, new_position: Coordinates) { - self.position = new_position; - } - - pub fn append_position(&mut self, x: f32, y: f32) { - self.position.set_x(self.position.x() + x); - self.position.set_y(self.position.y() + y); - } } diff --git a/src/core/components/maths/transform.rs b/src/core/components/maths/transform.rs index 8d97799..b94efb4 100644 --- a/src/core/components/maths/transform.rs +++ b/src/core/components/maths/transform.rs @@ -50,6 +50,10 @@ pub struct Transform { pub(crate) angle: f32, pub(crate) dirty: bool, pub(crate) dirty_child: bool, + pub(crate) min_x: Option, + pub(crate) max_x: Option, + pub(crate) min_y: Option, + pub(crate) max_y: Option, } impl Default for Transform { @@ -61,6 +65,10 @@ impl Default for Transform { angle: 0.0, dirty: false, dirty_child: true, + min_x: None, + max_x: None, + min_y: None, + max_y: None, } } } @@ -75,6 +83,10 @@ impl Transform { angle, dirty: false, dirty_child: true, + min_x: None, + max_x: None, + min_y: None, + max_y: None, } } @@ -85,6 +97,7 @@ impl Transform { self.global_translation.x += x; self.global_translation.y += y; self.dirty = true; + self.handle_bounds(); } /// Appends the x val to the translation's x value @@ -92,6 +105,7 @@ impl Transform { self.local_translation.x += x; self.global_translation.x += x; self.dirty = true; + self.handle_bounds(); } /// Appends the y val to the translation's y value @@ -99,6 +113,7 @@ impl Transform { self.local_translation.y += y; self.global_translation.y += y; self.dirty = true; + self.handle_bounds(); } /// Move this transform down @@ -106,6 +121,7 @@ impl Transform { self.local_translation.y += y; self.global_translation.y += y; self.dirty = true; + self.handle_bounds(); } /// Append an angle to this transform's angle @@ -133,6 +149,27 @@ impl Transform { self.local_translation.layer = layer } + /// Configure the minimum global x position for this transform to be min_x + pub fn set_min_x(&mut self, min_x: Option) { self.min_x = min_x; self.handle_bounds();} + + /// Configure the maximum global x position for this transform to be max_x + pub fn set_max_x(&mut self, max_x: Option) { self.max_x = max_x; self.handle_bounds();} + + /// Configure the minimum global y position for this transform to be min_x + pub fn set_min_y(&mut self, min_y: Option) { self.min_y = min_y; self.handle_bounds();} + + /// Configure the maximum global y position for this transform to be max_x + pub fn set_max_y(&mut self, max_y: Option) { self.max_y = max_y; self.handle_bounds();} + + /// Configure the minimum and maximum global values of x and y + pub fn set_global_translation_bounds(&mut self, min_x: Option, max_x: Option, min_y: Option, max_y: Option){ + self.min_x = min_x; + self.max_x = max_x; + self.min_y = min_y; + self.max_y = max_y; + self.handle_bounds(); + } + /// Computes the global_translation using the parent as origin pub(crate) fn compute_global_from_parent(&mut self, parent_translation: &Coordinates) { let mut new_global = parent_translation.clone(); @@ -141,6 +178,33 @@ impl Transform { new_global.layer = self.local_translation.layer; self.global_translation = new_global; self.dirty = true; + self.handle_bounds(); + } + + fn handle_bounds(&mut self) { + if let Some(min_x) = self.min_x { + if self.global_translation.x < min_x { + self.global_translation.set_x(min_x); + } + } + + if let Some(max_x) = self.max_x { + if self.global_translation.x > max_x { + self.global_translation.set_x(max_x); + } + } + + if let Some(min_y) = self.min_y { + if self.global_translation.y < min_y { + self.global_translation.set_y(min_y); + } + } + + if let Some(max_y) = self.max_y { + if self.global_translation.y > max_y { + self.global_translation.set_y(max_y); + } + } } } @@ -190,4 +254,13 @@ mod tests { transform.compute_global_from_parent(&Coordinates::new(1., 2.)); assert_eq!(true, transform.dirty); } + + #[test] + fn handle_bounds_test() { + let mut t = Transform::default(); + t.set_min_x(Some(1.)); + assert_eq!(1., t.global_translation.x); + t.append_x(-6.); + assert_eq!(1., t.global_translation.x); + } } diff --git a/src/core/event_handler.rs b/src/core/event_handler.rs index e9fc76f..e650462 100644 --- a/src/core/event_handler.rs +++ b/src/core/event_handler.rs @@ -64,8 +64,8 @@ pub(crate) fn handle_event( window.request_redraw(); } Event::RedrawRequested(_) => { - renderer.update(world, resources); - match renderer.render(world, resources) { + renderer.update(world); + match renderer.render(world) { Ok(_) => {} Err(e) => log::error!("{:?}", e), } diff --git a/src/core/systems/hierarchy_system.rs b/src/core/systems/hierarchy_system.rs index 6fadc94..a54be89 100644 --- a/src/core/systems/hierarchy_system.rs +++ b/src/core/systems/hierarchy_system.rs @@ -1,7 +1,6 @@ use legion::{system, systems::CommandBuffer, world::{EntityAccessError, SubWorld}, Entity, Query, EntityStore}; use crate::core::components::maths::hierarchy::{Children, Parent}; -use futures::StreamExt; /// System responsible to add/modify Children components to the entities referenced by a Parent component /// If the parent component referenced by the Children one is not found, then it deletes the entity @@ -38,8 +37,8 @@ pub(crate) fn children_manager( } }); - query_children.for_each_mut(&mut w2, |(e, p)|{ - if let Some(mut p) = p { + query_children.for_each_mut(&mut w2, |(_e, p)|{ + if let Some(p) = p { let mut to_delete = Vec::new(); for (index, child) in p.0.iter().enumerate(){ if w1.entry_ref(*child).is_err(){ diff --git a/src/core/systems/ui_text_system.rs b/src/core/systems/ui_text_system.rs index 49df6ca..4216a78 100644 --- a/src/core/systems/ui_text_system.rs +++ b/src/core/systems/ui_text_system.rs @@ -67,7 +67,7 @@ pub(crate) fn ui_text_bitmap_update( let mut char_transform = Transform::new( Coordinates::new( - (index as f32 * (width + 1.)),0., + index as f32 * (width + 1.),0., ), 1.0, 0., diff --git a/src/rendering/bidimensional/gl_representations.rs b/src/rendering/bidimensional/gl_representations.rs index 4683377..2c41b2c 100644 --- a/src/rendering/bidimensional/gl_representations.rs +++ b/src/rendering/bidimensional/gl_representations.rs @@ -162,7 +162,7 @@ pub(crate) fn create_glmat(t: &Vec4) -> [f32; 4] { pub(crate) struct UniformData<'a> { pub transform: &'a Transform, - pub camera: &'a Camera, + pub camera: (&'a Camera, &'a Transform), pub is_ui_component: bool, } @@ -177,8 +177,8 @@ impl From> for GlUniform { }); if !uniform_data.is_ui_component { model_trans.append_translation(Vec3 { - x: -1. * uniform_data.camera.position.x(), - y: -1. * uniform_data.camera.position.y(), + x: -1. * uniform_data.camera.1.global_translation().x(), + y: -1. * uniform_data.camera.1.global_translation().y(), z: 0.0, }); } @@ -186,12 +186,12 @@ impl From> for GlUniform { .prepend_rotation(Rotor3::from_rotation_xy(uniform_data.transform.angle).normalized()); let mut model_trans = model_trans.into_homogeneous_matrix(); let mut camera_view = ultraviolet::projection::lh_ydown::orthographic_wgpu_dx( - uniform_data.camera.left, - uniform_data.camera.right, - uniform_data.camera.bottom, - uniform_data.camera.top, - uniform_data.camera.near, - uniform_data.camera.far, + uniform_data.camera.0.left, + uniform_data.camera.0.right, + uniform_data.camera.0.bottom, + uniform_data.camera.0.top, + uniform_data.camera.0.near, + uniform_data.camera.0.far, ); GlUniform { model_trans: create_glmat4(&mut model_trans), diff --git a/src/rendering/bidimensional/scion2d.rs b/src/rendering/bidimensional/scion2d.rs index 86b9b68..be2c806 100644 --- a/src/rendering/bidimensional/scion2d.rs +++ b/src/rendering/bidimensional/scion2d.rs @@ -1,6 +1,6 @@ use std::{collections::HashMap, ops::Range, path::Path}; -use legion::{storage::Component, Entity, IntoQuery, Resources, World}; +use legion::{storage::Component, Entity, IntoQuery, World}; use wgpu::{ util::{BufferInitDescriptor, DeviceExt}, BindGroup, BindGroupLayout, Buffer, CommandEncoder, Device, Queue, RenderPassColorAttachment, @@ -55,14 +55,13 @@ impl ScionRenderer for Scion2D { fn update( &mut self, world: &mut World, - resources: &mut Resources, device: &Device, sc_desc: &SwapChainDescriptor, queue: &mut Queue, ) { - if resources.contains::() { + if world_contains_camera(world) { self.update_diffuse_bind_groups(world, device, queue); - self.update_transforms(world, resources, &device, queue); + self.update_transforms(world, &device, queue); self.upsert_component_pipeline::(world, &device, &sc_desc); self.upsert_component_pipeline::(world, &device, &sc_desc); self.upsert_component_pipeline::(world, &device, &sc_desc); @@ -77,7 +76,6 @@ impl ScionRenderer for Scion2D { fn render( &mut self, world: &mut World, - resource: &mut Resources, frame: &SwapChainTexture, encoder: &mut CommandEncoder, ) { @@ -89,7 +87,7 @@ impl ScionRenderer for Scion2D { }); } - if resource.contains::() { + if world_contains_camera(world) { let mut rendering_infos = Vec::new(); rendering_infos.append(&mut self.pre_render_component::(world)); rendering_infos.append(&mut self.pre_render_component::(world)); @@ -106,6 +104,7 @@ impl ScionRenderer for Scion2D { } } + fn load_texture_to_queue( texture: &Texture, queue: &mut Queue, @@ -196,7 +195,7 @@ fn load_texture_to_queue( fn create_transform_uniform_bind_group( device: &Device, transform: &Transform, - camera: &Camera, + camera: (&Camera, &Transform), is_ui_component: bool, ) -> (GlUniform, Buffer, BindGroupLayout, BindGroup) { let uniform = GlUniform::from(UniformData { @@ -447,22 +446,22 @@ impl Scion2D { fn update_transforms( &mut self, - world: &mut World, - resources: &mut Resources, + main_world: &mut World, device: &&Device, queue: &mut Queue, ) { - let camera = resources - .get::() - .expect("Missing Camera2D component, can't update transform without the camera view"); + let mut camera_query = <(&Camera, &Transform)>::query(); + let (camera_world, mut world) = main_world.split_for_query(&camera_query); + let camera = camera_query.iter(&camera_world) + .next().expect("No camera has been found in the world after the security check"); for (entity, transform, optional_ui_component) in - <(Entity, &Transform, Option<&UiComponent>)>::query().iter_mut(world) + <(Entity, &Transform, Option<&UiComponent>)>::query().iter_mut(&mut world) { if !self.transform_uniform_bind_groups.contains_key(entity) { let (uniform, uniform_buffer, glayout, group) = create_transform_uniform_bind_group( &device, transform, - &*camera, + camera, optional_ui_component.is_some(), ); queue.write_buffer(&uniform_buffer, 0, bytemuck::cast_slice(&[uniform])); @@ -475,7 +474,7 @@ impl Scion2D { .expect("Fatal error, a transform has been marked as found but doesn't exist"); uniform.replace_with(GlUniform::from(UniformData { transform, - camera: &camera, + camera, is_ui_component: optional_ui_component.is_some(), })); queue.write_buffer(uniform_buffer, 0, bytemuck::cast_slice(&[*uniform])); @@ -525,3 +524,7 @@ fn get_path_from_color(color: &Color) -> String { color.alpha() ) } + +fn world_contains_camera(world: &mut World) -> bool { + <&Camera>::query().iter(world).count() > 0 +} diff --git a/src/rendering/mod.rs b/src/rendering/mod.rs index 1fc14a5..358efe5 100644 --- a/src/rendering/mod.rs +++ b/src/rendering/mod.rs @@ -1,5 +1,5 @@ //! Everything that is relative to rendering to the window (Like renderable components, camera, transforms..) -use legion::{Resources, World}; +use legion::{World}; use wgpu::{CommandEncoder, Device, Queue, SwapChainDescriptor, SwapChainTexture}; use crate::rendering::bidimensional::scion2d::Scion2D; @@ -14,7 +14,6 @@ pub trait ScionRenderer { fn update( &mut self, world: &mut World, - resource: &mut Resources, device: &Device, sc_desc: &SwapChainDescriptor, queue: &mut Queue, @@ -24,7 +23,6 @@ pub trait ScionRenderer { fn render( &mut self, world: &mut World, - resources: &mut Resources, frame: &SwapChainTexture, encoder: &mut CommandEncoder, ); diff --git a/src/rendering/renderer_state.rs b/src/rendering/renderer_state.rs index d50e586..cd0bbe0 100644 --- a/src/rendering/renderer_state.rs +++ b/src/rendering/renderer_state.rs @@ -1,4 +1,4 @@ -use legion::{Resources, World}; +use legion::{World}; use winit::{event::WindowEvent, window::Window}; use crate::rendering::ScionRenderer; @@ -70,10 +70,9 @@ impl RendererState { false } - pub(crate) fn update(&mut self, world: &mut World, resources: &mut Resources) { + pub(crate) fn update(&mut self, world: &mut World) { self.scion_renderer.update( world, - resources, &self.device, &self.sc_desc, &mut self.queue, @@ -83,7 +82,6 @@ impl RendererState { pub(crate) fn render( &mut self, world: &mut World, - resources: &mut Resources, ) -> Result<(), wgpu::SwapChainError> { let frame = self.swap_chain.get_current_frame()?.output; let mut encoder = self @@ -93,7 +91,7 @@ impl RendererState { }); self.scion_renderer - .render(world, resources, &frame, &mut encoder); + .render(world, &frame, &mut encoder); self.queue.submit(std::iter::once(encoder.finish())); Ok(())