diff --git a/Cargo.toml b/Cargo.toml index b889f49b..9ccd7f5a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,7 +15,7 @@ resolver = "2" [workspace.dependencies] async-channel = "1.8" bytemuck = { version = "1.13.0", features = ["derive"] } -crabslab = { version = "0.1.3", default-features = false } +crabslab = { version = "0.2.0", default-features = false } futures-lite = "1.13" gltf = { git = 'https://github.com/gltf-rs/gltf.git', features = ["KHR_lights_punctual", "KHR_materials_unlit", "KHR_materials_emissive_strength", "extras"] } image = "0.24" diff --git a/DEVLOG.md b/DEVLOG.md index 78392845..5feefe16 100644 --- a/DEVLOG.md +++ b/DEVLOG.md @@ -1,5 +1,15 @@ # devlog +## Mon Jan 8, 2024 + +I added another ty var to `crabslab::Offset` to help with pointer math. + +I've also added yet another layer of indirection around rendering. +Now the top level unit of rendering is `Rendering`, which is an enum of +`Id`s that point to different renderable things. There's an uber-vertex-shader +that tracks this and proxies to the correct sub-shader. This is in anticipation +of adding SDF rendering. + ## Fri Jan 5, 2024 The slab implementation in this repo has been spun off into its own thing. diff --git a/crates/renderling-gpui/Cargo.toml b/crates/renderling-gpui/Cargo.toml index b5a11821..201af6e2 100644 --- a/crates/renderling-gpui/Cargo.toml +++ b/crates/renderling-gpui/Cargo.toml @@ -10,14 +10,16 @@ default = ["winit"] winit = ["renderling/winit", "dep:winit"] [dependencies] +ab_glyph = { version = "0.2" } +crabslab = "0.1.3" +glam = { workspace = true, default-features = false, features = ["bytemuck", "libm"] } +glyph_brush = { version = "0.7" } image = { version = "^0.24" } log = "^0.4" -renderling = { path = "../renderling", default-features = false, features = ["text"] } +renderling = { path = "../renderling" } snafu = "^0.7" -winit = { version = "^0.27", optional = true } - -glam = { workspace = true, default-features = false, features = ["bytemuck", "libm"] } wgpu = { workspace = true } +winit = { version = "^0.27", optional = true } [dev-dependencies] env_logger = "^0.10" diff --git a/crates/renderling-gpui/src/elements.rs b/crates/renderling-gpui/src/elements.rs index 2536a1ac..7b3b755b 100644 --- a/crates/renderling-gpui/src/elements.rs +++ b/crates/renderling-gpui/src/elements.rs @@ -1,35 +1,70 @@ //! User interface elements. +use crabslab::{GrowableSlab, Slab}; +use glam::Vec3; +use renderling::shader::{ + gltf::{GltfMesh, GltfNode, GltfPrimitive}, + pbr::Material, + stage::{RenderUnit, Transform, Vertex}, +}; + pub use super::*; -#[derive(Default)] +#[derive(Clone)] pub struct Rectangle { aabb: AABB, - color: Vec4, - draw_object: Option, + primitive: Id, + material: Id, + render_unit: Id, + updates: Stage, } impl Rectangle { // Create a new rectangle. - pub fn new() -> Self { + pub fn new(gpui: &Gpui) -> Self { + let mut stage = gpui.stage.clone(); + let material = Id::NONE; + let primitive = stage.new_primitive(Self::vertices(), None, material); + let primitives = stage.append_array(&[primitive]); + let mesh = stage.append(&GltfMesh { + primitives, + ..Default::default() + }); + let node = stage.append(&GltfNode { + mesh, + ..Default::default() + }); + let node_path = stage.append_array(&[node]); + let render_unit = stage.draw_unit(&RenderUnit { + node_path, + camera: gpui.camera, + vertex_count: 6, + ..Default::default() + }); Self { aabb: AABB { min: Vec2::ZERO, max: Vec2::splat(100.0), }, - color: Vec4::ONE, - draw_object: None, + primitive: primitives.at(0), + material, + render_unit, + stage, } } + /// Set the upper left corner of the rectangle. pub fn set_origin(&mut self, origin: impl Into) { let origin = origin.into(); if self.aabb.min != origin { let delta = origin - self.aabb.min; self.aabb.min += delta; self.aabb.max += delta; - if let Some(obj) = self.draw_object.as_mut() { - obj.set_position(self.aabb.min); - } + self.stage.write( + self.render_unit + + RenderUnit::offset_of_transform() + + Transform::offset_of_translation(), + &Vec3::new(self.aabb.min.x, self.aabb.min.y, 0.0), + ); } } @@ -54,594 +89,564 @@ impl Rectangle { self } - // Get the color of the rectangle. - pub fn get_color(&self) -> Vec4 { - self.color - } - - // Set the color of the rectangle. - pub fn set_color(&mut self, color: impl Into) { - let color = color.into(); - if self.color != color { - self.color = color; - let vertices = self.vertices(); - if let Some(obj) = self.draw_object.as_mut() { - obj.set_vertices(vertices); - } - } - } - - pub fn with_color(mut self, color: impl Into) -> Self { - self.set_color(color); - self - } - - fn vertices(&self) -> [UiVertex; 6] { - let tl = UiVertex::default() - .with_position(Vec2::ZERO) - .with_color(self.color); - let tr = UiVertex::default() - .with_position(Vec2::new(1.0, 0.0)) - .with_color(self.color); - let bl = UiVertex::default() - .with_position(Vec2::new(0.0, 1.0)) - .with_color(self.color); - let br = UiVertex::default() - .with_position(Vec2::ONE) - .with_color(self.color); + fn vertices() -> [Vertex; 6] { + let tl = Vertex::default().with_position(Vec3::ZERO); + let tr = Vertex::default().with_position(Vec3::new(1.0, 0.0, 0.0)); + let bl = Vertex::default().with_position(Vec3::new(0.0, 1.0, 0.0)); + let br = Vertex::default().with_position(Vec3::new(1.0, 1.0, 0.0)); [tl, bl, br, tl, br, tr] } + + //fn allocate(&mut self, gpui: &mut Gpui) -> { + // let origin = self.aabb.min; + // let size = self.aabb.size(); + // if self.draw_object.is_none() { + // let obj = UiDrawObjectBuilder::new(device) + // .with_draw_mode(UiMode::DEFAULT) + // .with_position(origin) + // .with_scale(size) + // .with_vertices(self.vertices()) + // .build(); + // self.draw_object = Some(obj); + // } + // let draw_obj = self.draw_object.as_mut().unwrap(); + + // if origin != draw_obj.get_position() { + // draw_obj.set_position(origin); + // } + // if size != draw_obj.get_scale() { + // draw_obj.set_scale(size); + // } + + // draw_obj.update(device, queue).unwrap(); + // vec![Paint::drawing(|render_pass, default_texture_bindgroup| { + // draw_obj.draw(render_pass, default_texture_bindgroup); + // })] + //} } impl Element for Rectangle { type OutputEvent = (); - fn layout(&mut self, constraint: AABB) -> AABB { - self.aabb = AABB { + fn update(&mut self, gpui: &mut Gpui, constraint: AABB) -> AABB { + let aabb = AABB { min: self.aabb.min.max(constraint.min), max: self.aabb.max.min(constraint.max), }; - - self.aabb - } - - fn paint<'a, 'b: 'a>( - &'b mut self, - device: &wgpu::Device, - queue: &wgpu::Queue, - ) -> Vec> { - let origin = self.aabb.min; - let size = self.aabb.size(); - if self.draw_object.is_none() { - let obj = UiDrawObjectBuilder::new(device) - .with_draw_mode(UiMode::DEFAULT) - .with_position(origin) - .with_scale(size) - .with_vertices(self.vertices()) - .build(); - self.draw_object = Some(obj); - } - let draw_obj = self.draw_object.as_mut().unwrap(); - - if origin != draw_obj.get_position() { - draw_obj.set_position(origin); - } - if size != draw_obj.get_scale() { - draw_obj.set_scale(size); - } - - draw_obj.update(device, queue).unwrap(); - vec![Paint::drawing(|render_pass, default_texture_bindgroup| { - draw_obj.draw(render_pass, default_texture_bindgroup); - })] - } - - fn event(&mut self, _event: Event) {} -} - -pub struct Text { - cache: GlyphCache, - section: OwnedSection, - aabb: AABB, - updated: bool, - draw_object: Option, -} - -impl Text { - pub fn new(font: &FontArc) -> Self { - let cache = GlyphCache::new(vec![font.clone()]); - Text { - section: OwnedSection::default().add_text(OwnedText::new("").with_scale(12.0)), - cache, - aabb: AABB::default(), - updated: true, - draw_object: None, - } - } - - pub fn set_font(&mut self, font: FontArc) { - self.cache = GlyphCache::new(vec![font]); - self.updated = true; - } - - pub fn with_font(mut self, font: FontArc) -> Self { - self.set_font(font); - self - } - - pub fn set_text(&mut self, text: impl Into) { - self.section.text.resize_with(1, Default::default); - self.section.text[0].text = text.into(); - self.updated = true; - } - - pub fn with_text(mut self, text: impl Into) -> Self { - self.set_text(text); - self - } - - pub fn set_scale(&mut self, scale: f32) { - self.section - .text - .iter_mut() - .for_each(|t| t.scale = scale.into()); - self.updated = true; - } - - pub fn with_scale(mut self, scale: f32) -> Self { - self.set_scale(scale); - self - } - - pub fn set_color(&mut self, color: impl Into) { - let color: Vec4 = color.into(); - self.section - .text - .iter_mut() - .for_each(|t| t.extra.color = color.to_array()); - self.updated = true; - } - - pub fn with_color(mut self, color: impl Into) -> Self { - self.set_color(color); - self - } - - /// Return the bounding box for this text. - /// - /// This will return `AABB::default` until [`Rectangle::layout`] is called. - pub fn aabb(&self) -> AABB { - self.aabb - } -} - -impl Element for Text { - type OutputEvent = (); - - fn layout(&mut self, constraint: AABB) -> AABB { - use renderling::GlyphCruncher; - let max_size = constraint.size(); - let max_size: (f32, f32) = max_size.into(); - if self.section.bounds != max_size { - self.section.bounds = max_size; - self.updated = true; - } - - if self.updated { - self.aabb = AABB { - min: constraint.min, - max: constraint.min, - }; - if let Some(rect) = self.cache.brush.glyph_bounds(&self.section) { - self.aabb.max = self.aabb.min + Vec2::new(rect.width(), rect.height()); - } - } - self.aabb - } - - fn paint<'a, 'b: 'a>( - &'b mut self, - device: &wgpu::Device, - queue: &wgpu::Queue, - ) -> Vec> { - let origin = self.aabb.min; - if self.draw_object.is_none() { - self.draw_object = Some( - UiDrawObjectBuilder::new(device) - .with_draw_mode(UiMode::TEXT) - .with_position(origin) - .build(), - ); - } - let draw_obj = self.draw_object.as_mut().unwrap(); - if self.updated { - self.updated = false; - self.cache.queue(&self.section); - } - - let (may_vertices, may_texture) = self.cache.get_updated(device, queue); - if let Some(verts) = may_vertices { - draw_obj.set_vertices(verts); - } - if let Some(texture) = may_texture { - draw_obj.set_texture(&texture); - } - if origin != draw_obj.get_position() { - draw_obj.set_position(origin); - } - - draw_obj.update(device, queue).unwrap(); - vec![Paint::drawing(|render_pass, default_texture_bindgroup| { - draw_obj.draw(render_pass, default_texture_bindgroup); - })] - } - - fn event(&mut self, _: Event) {} -} - -#[derive(Clone, Copy, Debug, Default, PartialEq)] -pub enum ButtonState { - #[default] - Normal, - Over, - Down, -} - -#[derive(Clone, Copy, Debug, PartialEq)] -pub enum ButtonEvent { - Over, - Out, - Down, - Up, -} - -pub struct Button { - foreground: Rectangle, - background: Rectangle, - text: Text, - aabb: AABB, - state: ButtonState, -} - -impl Button { - const TEXT_COLOR_NORMAL: Vec4 = Vec4::new(0.1, 0.1, 0.1, 1.0); - const TEXT_COLOR_OVER: Vec4 = Vec4::new(0.7, 0.13, 0.13, 1.0); - const TEXT_COLOR_DOWN: Vec4 = Vec4::new(1.0, 0.0, 0.0, 1.0); - const PX_OFFSET: f32 = 8.0; - const PX_BORDER: f32 = 4.0; - - pub fn new(font: &FontArc) -> Self { - let mut btn = Button { - foreground: Rectangle::new(), - background: Rectangle::new().with_color(Vec4::new(0.0, 0.0, 0.0, 0.5)), - text: Text::new(font) - .with_text("Button") - .with_scale(16.0) - .with_color(Self::TEXT_COLOR_NORMAL), - aabb: AABB::default(), - state: ButtonState::default(), - }; - btn.set_normal(); - btn - } - - pub fn set_text(&mut self, text: impl Into) { - self.text.set_text(text); - } - - pub fn with_text(mut self, text: impl Into) -> Self { - self.text.set_text(text); - self - } - - pub fn get_text(&self) -> String { - self.text - .section - .text - .iter() - .map(|t| t.text.clone()) - .collect::>() - .concat() - } - - pub fn get_text_field(&self) -> &Text { - &self.text - } - - pub fn get_text_field_mut(&mut self) -> &mut Text { - &mut self.text - } - - pub fn set_scale(&mut self, scale: f32) { - self.text.set_scale(scale) - } - - pub fn with_scale(mut self, scale: f32) -> Self { - self.text.set_scale(scale); - self - } - - fn set_over(&mut self) { - if self.state == ButtonState::Over { - return; - } - self.state = ButtonState::Over; - self.text.section.text.iter_mut().for_each(|text| { - *text = std::mem::take(text).with_color(Self::TEXT_COLOR_OVER); - }); - self.text.updated = true; - } - - fn set_normal(&mut self) { - if self.state == ButtonState::Normal { - return; + if aabb != self.aabb { + self.aabb = aabb; } - self.state = ButtonState::Normal; - self.text.section.text.iter_mut().for_each(|text| { - *text = std::mem::take(text).with_color(Self::TEXT_COLOR_NORMAL); - }); - self.text.updated = true; - } - fn set_down(&mut self) { - if self.state == ButtonState::Down { - return; - } - self.state = ButtonState::Down; - self.text.section.text.iter_mut().for_each(|text| { - *text = std::mem::take(text).with_color(Self::TEXT_COLOR_DOWN); - }); - self.text.updated = true; - } -} - -impl Element for Button { - type OutputEvent = Option; - - fn layout(&mut self, constraint: AABB) -> AABB { - let border = Vec2::splat(Self::PX_BORDER); - let offset = Vec2::splat(Self::PX_OFFSET); - let down_offset = if self.state == ButtonState::Down { - offset * 0.5 - } else { - Vec2::ZERO - }; - let text_aabb = self.text.layout(AABB { - min: constraint.min + border + down_offset, - max: constraint.max, - }); - - let bg_size = text_aabb.size() + border * 2.0; - self.foreground.set_origin(constraint.min + down_offset); - self.foreground.set_size(bg_size); - self.background - .set_origin(self.foreground.aabb.min - down_offset + offset); - self.background.set_size(bg_size); - - let fg_aabb = self.foreground.layout(constraint); - let bg_aabb = self.background.layout(constraint); - self.aabb = text_aabb.union(fg_aabb).union(bg_aabb); self.aabb } - fn paint<'a, 'b: 'a>( - &'b mut self, - device: &wgpu::Device, - queue: &wgpu::Queue, - ) -> Vec> { - let mut ps = vec![]; - ps.extend(self.background.paint(device, queue)); - ps.extend(self.foreground.paint(device, queue)); - ps.extend(self.text.paint(device, queue)); - ps - } - - fn event(&mut self, event: Event) -> Option { - let from_state = self.state; - match event { - Event::MouseMoved { position, is_down } => { - let position = Vec2::new(position.x as f32, position.y as f32); - if self.aabb.contains(position) { - if is_down { - self.set_down(); - } else { - self.set_over(); - } - } else { - self.set_normal(); - } - } - Event::MouseButton { position, is_down } => { - let position = Vec2::new(position.x as f32, position.y as f32); - if self.aabb.contains(position) { - if is_down { - self.set_down(); - } else { - self.set_over(); - } - } else { - self.set_normal(); - } - } - Event::WindowResized { .. } => {} - }; - match (from_state, self.state) { - (ButtonState::Normal, ButtonState::Normal) => None, - (ButtonState::Normal, ButtonState::Over) => Some(ButtonEvent::Over), - (ButtonState::Normal, ButtonState::Down) => None, - (ButtonState::Over, ButtonState::Normal) => Some(ButtonEvent::Out), - (ButtonState::Over, ButtonState::Over) => None, - (ButtonState::Over, ButtonState::Down) => Some(ButtonEvent::Down), - (ButtonState::Down, ButtonState::Normal) => None, - (ButtonState::Down, ButtonState::Over) => Some(ButtonEvent::Up), - (ButtonState::Down, ButtonState::Down) => None, - } - } -} - -pub struct Dropdown { - selections: Vec<(String, T)>, - label_font: FontArc, - scale: f32, - is_open: bool, - text_label: Text, - button_open: Button, - buttons: Vec