tirbofish/dropbear · commit
e0632fe9cac4c227ac16fb8c8490b830891e46a5
feature: did a little bit here and there
Signature present but could not be verified.
Unverified
@@ -243,6 +243,7 @@ impl KinoState { device, queue, self.renderer.texture_bind_group_layout(), + self.renderer.format, ); return self.assets.add_texture_with_label(label, fallback); } @@ -262,6 +263,7 @@ impl KinoState { raw.as_ref(), width, height, + self.renderer.format, ); self.assets.add_texture_with_label(label, texture) } @@ -271,7 +273,8 @@ impl KinoState { self.assets.get_texture_handle(label) } - pub(crate) fn input(&self) -> &KinoWinitWindowing { + /// Returns a reference to the windowing. Used for input detection. + pub fn input(&self) -> &KinoWinitWindowing { &self.windowing } @@ -348,11 +351,23 @@ impl KinoState { /// The id of the widget, often being a hash. #[derive(Clone, Copy, Hash, Eq, PartialEq, Debug)] -pub struct WidgetId(pub u64); +pub struct WidgetId(u64); impl WidgetId { - pub fn from_str(str: &str) -> Self { - str.into() + /// Creates a new [`WidgetId`] from an object that can be hashed. + pub fn new<H: Hash>(value: H) -> Self { + let mut hasher = DefaultHasher::new(); + value.hash(&mut hasher); + WidgetId(hasher.finish()) + } + + /// Creates a new [`WidgetId`] from a simple u64 value. + pub fn from_raw(value: u64) -> Self { + WidgetId(value) + } + + pub fn get_id(&self) -> u64 { + self.0 } } @@ -364,6 +379,29 @@ impl Into<WidgetId> for &str { } } +impl Into<WidgetId> for String { + fn into(self) -> WidgetId { + let mut hasher = DefaultHasher::new(); + self.hash(&mut hasher); + WidgetId(hasher.finish()) + } +} + +impl Into<WidgetId> for u64 { + fn into(self) -> WidgetId { + WidgetId(self) + } +} + +impl<T: Hash, U: Hash> Into<WidgetId> for (T, U) { + fn into(self) -> WidgetId { + let mut hasher = DefaultHasher::new(); + self.0.hash(&mut hasher); + self.1.hash(&mut hasher); + WidgetId(hasher.finish()) + } +} + pub enum UiInstructionType { Containered(ContaineredWidgetType), Widget(Box<dyn NativeWidget>), @@ -14,6 +14,7 @@ pub mod batching; pub struct KinoWGPURenderer { pipeline: KinoRendererPipeline, default_texture: texture::Texture, + pub format: wgpu::TextureFormat, pub size: Vec2, // pub text: KinoTextRenderer, @@ -53,13 +54,14 @@ impl KinoWGPURenderer { }], }); - let default_texture = texture::Texture::create_default(&device, &queue, &pipeline.texture_bind_group_layout); + let default_texture = texture::Texture::create_default(&device, &queue, &pipeline.texture_bind_group_layout, surface_format); // let text = KinoTextRenderer::new(&device, &queue, surface_format); log::debug!("Created KinoWGPURenderer"); Self { pipeline, default_texture, + format: surface_format, size: Vec2::from_array(size), // text, camera: CameraRendering { @@ -27,6 +27,7 @@ impl Texture { data: &[u8], width: u32, height: u32, + texture_format: TextureFormat, ) -> Self { log::debug!("Creating new texture"); @@ -40,7 +41,7 @@ impl Texture { mip_level_count: 1, sample_count: 1, dimension: TextureDimension::D2, - format: TextureFormat::Rgba8UnormSrgb, + format: texture_format, usage: TextureUsages::TEXTURE_BINDING | TextureUsages::COPY_DST, view_formats: &[], }); @@ -94,9 +95,9 @@ impl Texture { /// Creates a 1×1 white fallback texture /// /// Used when no valid texture is provided for a draw call - pub fn create_default(device: &Device, queue: &Queue, layout: &BindGroupLayout) -> Self { + pub fn create_default(device: &Device, queue: &Queue, layout: &BindGroupLayout, texture_format: TextureFormat) -> Self { log::debug!("Creating standard white texture"); - Self::from_bytes(device, queue, layout, &[255u8, 255, 255, 255], 1, 1) + Self::from_bytes(device, queue, layout, &[255u8, 255, 255, 255], 1, 1, texture_format) } /// Binds this texture at the given index in the render pass @@ -9,7 +9,12 @@ pub enum Anchor { TopLeft, } +/// Defines a widget with no children. pub trait NativeWidget: Send + Sync { + /// Renders the widget. + /// + /// The state is provided for you to manipulate, such as adding a new response based on the + /// [`WidgetId`]. fn render(self: Box<Self>, state: &mut KinoState); fn id(&self) -> WidgetId; fn as_any(&self) -> &dyn Any; @@ -19,4 +24,43 @@ pub trait ContaineredWidget: Send + Sync { fn render(self: Box<Self>, children: Vec<UiNode>, state: &mut KinoState); fn id(&self) -> WidgetId; fn as_any(&self) -> &dyn Any; +} + +/// Describes the colour that the widget will be filled in with. +#[derive(Copy, Clone, Debug, PartialEq)] +pub struct Fill { + /// The colour of the fill described as RGBA between the range `0.0` <-> `1.0`. + /// + /// If a texture is applied to the colour, it will create a tinted texture on the quad. + pub colour: [f32; 4], +} + +impl Fill { + /// Creates a new [`Fill`] + pub fn new(colour: [f32; 4]) -> Self { + Fill { colour } + } +} + +impl Default for Fill { + fn default() -> Self { + Fill { colour: [1.0, 1.0, 1.0, 1.0] } + } +} + +/// Describes the properties of the border/stroke of the widget. +#[derive(Copy, Clone, Debug, PartialEq)] +pub struct Border { + /// The colour of the border described as RGBA between the range `0.0` <-> `1.0`. + pub colour: [f32; 4], + + /// The width of the border. + pub width: f32, +} + +impl Border { + /// Creates a new [`Border`]. + pub fn new(colour: [f32; 4], width: f32) -> Self { + Self { colour, width } + } } @@ -1,3 +1,5 @@ +//! Defines the primitive [`Rectangle`] widget. + use std::any::Any; use glam::{vec2, Mat2, Vec2}; use crate::{KinoState, WidgetId}; @@ -5,50 +7,70 @@ use crate::asset::Handle; use crate::math::Rect; use crate::rendering::texture::Texture; use crate::rendering::vertex::Vertex; -use crate::widgets::{Anchor, NativeWidget}; +use crate::widgets::{Anchor, Border, Fill, NativeWidget}; use crate::resp::WidgetResponse; use winit::event::{ElementState, MouseButton}; +/// A simple and humble rectangle. pub struct Rectangle { /// The identifier of the widget. /// /// To make life easier, a text id works pretty well. pub id: WidgetId, + /// The positioning of the rectangle. + /// + /// Default: [`Anchor::TopLeft`] pub anchor: Anchor, /// The position of the rectangle + /// + /// Default: [`Vec2::ZERO`] pub position: Vec2, /// The size of the rectangle + /// + /// Default: [`vec2(64.0, 64.0)`] pub size: Vec2, /// The texture that this - pub texture: Option<Handle<Texture>>, - - /// Colour described as RGBA. /// - /// If a texture is applied to the colour, it will create a tinted texture on the quad. - pub colour: [f32; 4], // for now until colour is properly implemented + /// Default: [`None`] + pub texture: Option<Handle<Texture>>, /// Rotation of the rectangle in radians + /// + /// Default: `0.0` rad / `0.0` degrees pub rotation: f32, /// The UV of the textures. + /// + /// Default: `[[0.0, 0.0], [1.0, 0.0], [1.0, 1.0], [0.0, 1.0]]` pub uvs: [[f32; 2]; 4], + + /// The fill properties of the rectangle + /// + /// Default: `[1.0, 1.0, 1.0, 1.0]` + pub fill: Fill, + + /// The stroke/border properties of the rectangle + /// + /// Default: [`None`] + pub border: Option<Border>, } impl Rectangle { - pub fn new(id: WidgetId) -> Self { + pub fn new(id: impl Into<WidgetId>) -> Self { Self { - id, + id: id.into(), anchor: Anchor::TopLeft, position: Vec2::ZERO, size: vec2(64.0, 64.0), rotation: 0.0, - colour: [1.0, 1.0, 1.0, 1.0], uvs: [[0.0, 0.0], [1.0, 0.0], [1.0, 1.0], [0.0, 1.0]], + fill: Fill::default(), texture: None, + border: None, } } @@ -58,6 +80,7 @@ impl Rectangle { self.size = rect.size; self } + /// Sets the anchor point of the rectangle /// Defaults to [`Anchor::TopLeft`]. pub fn anchor(mut self, anchor: Anchor) -> Self { @@ -74,22 +97,32 @@ impl Rectangle { self.size = size; self } - /// Sets the colour of the rectangle - pub fn colour(mut self, colour: [f32; 4]) -> Self { - self.colour = colour; + + /// Sets the fill properties of the rectangle + pub fn fill(mut self, fill: Fill) -> Self { + self.fill = fill; self } + + /// Sets the border properties of the rectangle + pub fn border(mut self, border: Border) -> Self { + self.border = Some(border); + self + } + /// Sets rotation (in radians) around the rectangle's center /// 0 radians points up (positive Y), increasing clockwise pub fn rotate(mut self, angle: f32) -> Self { self.rotation = angle + std::f32::consts::FRAC_PI_2; self } + /// Sets the texture ID for the rectangle pub fn texture(mut self, id: Handle<Texture>) -> Self { self.texture = Some(id); self } + /// Custom UV coordinates /// Defaults to covering the full texture ((0,0) - (1,1)) pub fn uv(mut self, coords: [[f32; 2]; 4]) -> Self { @@ -106,6 +139,7 @@ impl NativeWidget for Rectangle { }; let top_left = self.position + offset; let rect = Rect::new(top_left, self.size); + let input = state.input(); let hovering = rect.contains(input.mouse_position); let clicked = hovering @@ -118,18 +152,53 @@ impl NativeWidget for Rectangle { hovering, }, ); + let rot = Mat2::from_angle(self.rotation); - let verts: Vec<_> = rect + + let fill_verts: Vec<_> = rect .corners() .iter() .zip(self.uvs.iter()) .map(|(&corner, &uv)| { let world = rot * (corner - rect.center()) + rect.center(); - Vertex::new(world.to_array(), self.colour, uv) + Vertex::new(world.to_array(), self.fill.colour, uv) }) .collect(); - state.batch.push(&verts, &[0, 1, 2, 2, 3, 0], self.texture); + state.batch.push(&fill_verts, &[0, 1, 2, 2, 3, 0], self.texture); + + if let Some(border) = self.border { + let half_width = border.width / 2.0; + let outer_rect = Rect::new( + top_left - Vec2::splat(half_width), + self.size + Vec2::splat(border.width) + ); + let inner_rect = Rect::new( + top_left + Vec2::splat(half_width), + self.size - Vec2::splat(border.width) + ); + + let outer_corners = outer_rect.corners(); + let inner_corners = inner_rect.corners(); + + let mut border_verts = Vec::with_capacity(8); + for i in 0..4 { + let outer_world = rot * (outer_corners[i] - rect.center()) + rect.center(); + border_verts.push(Vertex::new(outer_world.to_array(), border.colour, [0.0, 0.0])); + + let inner_world = rot * (inner_corners[i] - rect.center()) + rect.center(); + border_verts.push(Vertex::new(inner_world.to_array(), border.colour, [0.0, 0.0])); + } + + let border_indices = [ + 0, 1, 3, 3, 2, 0, + 2, 3, 5, 5, 4, 2, + 4, 5, 7, 7, 6, 4, + 6, 7, 1, 1, 0, 6, + ]; + + state.batch.push(&border_verts, &border_indices, None); + } } fn id(&self) -> WidgetId { @@ -7,13 +7,12 @@ use eucalyptus_core::egui::CentralPanel; use eucalyptus_core::physics::collider::ColliderGroup; use eucalyptus_core::physics::collider::ColliderShapeKey; use eucalyptus_core::physics::collider::shader::ColliderInstanceRaw; -use glam::{DMat4, DQuat, DVec3, Quat, Vec2}; +use glam::{vec2, DMat4, DQuat, DVec3, Quat, Vec2}; use hecs::Entity; -use wgpu::{Color}; +use wgpu::Color; use wgpu::util::DeviceExt; use winit::event_loop::ActiveEventLoop; use winit::event::WindowEvent; -use yakui_wgpu::SurfaceInfo; use dropbear_engine::camera::Camera; use dropbear_engine::buffer::ResizableBuffer; use dropbear_engine::entity::{EntityTransform, MeshRenderer, Transform}; @@ -22,7 +21,6 @@ use dropbear_engine::lighting::{Light, LightComponent}; use dropbear_engine::lighting::MAX_LIGHTS; use dropbear_engine::model::{DrawLight, DrawModel, ModelId, MODEL_CACHE}; use dropbear_engine::scene::{Scene, SceneCommand}; -use dropbear_engine::texture::Texture; use eucalyptus_core::camera::CameraComponent; use eucalyptus_core::command::CommandBufferPoller; use eucalyptus_core::hierarchy::{EntityTransformExt, Parent}; @@ -32,12 +30,10 @@ use eucalyptus_core::rapier3d::geometry::SharedShape; use eucalyptus_core::states::{Label, PROJECT}; use eucalyptus_core::states::SCENES; use eucalyptus_core::scene::loading::{IsSceneLoaded, SceneLoadResult, SCENE_LOADER}; -use crate::{PlayMode}; +use crate::PlayMode; use eucalyptus_core::physics::collider::shader::create_wireframe_geometry; use eucalyptus_core::ui::UI_CONTEXT; -use kino_ui::math::Rect; -use kino_ui::WidgetId; -use kino_ui::widgets::{Anchor, NativeWidget}; +use kino_ui::widgets::{Border, Fill}; use kino_ui::widgets::rect::Rectangle; impl Scene for PlayMode { @@ -534,11 +530,20 @@ impl Scene for PlayMode { }); if let Some(kino) = &mut self.kino { - let peter_griffen = kino.add_texture_from_bytes(&graphics.device, &graphics.queue, "theres nothing", include_bytes!("../../../resources/textures/no-texture.png"), 256, 256); + #[allow(dead_code)] + let no_texture = kino.add_texture_from_bytes( + &graphics.device, &graphics.queue, + "no texture", + include_bytes!("../../../resources/textures/no-texture.png"), + 256, 256 + ); let rect = kino.add_widget(Box::new( - Rectangle::new("rect".into()) - .texture(peter_griffen) + Rectangle::new("rect") + .texture(no_texture) + .size(vec2(128.0, 100.0)) + .border(Border::new([1.0, 1.0, 1.0, 1.0], 3.0)) + .fill(Fill::new([1.0, 1.0, 1.0, 0.5])), )); kino.poll();