From 7d5b244492f93253e3cd341bdff1acc2b8bd0679 Mon Sep 17 00:00:00 2001 From: Mark Date: Sat, 20 Jan 2024 10:05:39 -0800 Subject: [PATCH] Reworked UI sprites --- crates/render/src/ui/planet.rs | 71 +++++++-------- crates/render/src/ui/util/image.rs | 60 ------------- crates/render/src/ui/util/mod.rs | 16 +++- crates/render/src/ui/util/sprite.rs | 128 ++++++++++++++++++++++++++++ 4 files changed, 179 insertions(+), 96 deletions(-) delete mode 100644 crates/render/src/ui/util/image.rs create mode 100644 crates/render/src/ui/util/sprite.rs diff --git a/crates/render/src/ui/planet.rs b/crates/render/src/ui/planet.rs index 7a79466..ab3777e 100644 --- a/crates/render/src/ui/planet.rs +++ b/crates/render/src/ui/planet.rs @@ -1,42 +1,62 @@ use galactica_content::{Content, SystemObject, SystemObjectHandle}; use galactica_system::{data::ShipState, phys::PhysSimShipHandle}; -use galactica_util::to_radians; use glyphon::{cosmic_text::Align, Attrs, Color, Metrics, TextArea, Weight}; use nalgebra::{Point2, Vector2}; -use super::util::{SpriteRect, UiImage, UiTextArea}; -use crate::{vertexbuffer::types::UiInstance, PositionAnchor, RenderInput, RenderState}; +use super::util::{SpriteRect, UiSprite, UiTextArea}; +use crate::{RenderInput, RenderState}; pub(super) struct Planet { // UI elements planet_desc: UiTextArea, planet_name: UiTextArea, - landscape: UiImage, - - // Height of whole element,in logical pixels - size: f32, + sprite: UiSprite, /// What object we're displaying currently. /// Whenever this changes, we need to reflow text. current_object: Option, } -// TODO: no scroll // TODO: animate in/out impl Planet { pub fn new(ct: &Content, state: &mut RenderState) -> Self { - let size = 800.0; + let mut sprite = UiSprite::new( + ct.get_sprite_handle("ui::planet"), + None, + SpriteRect { + pos: Point2::new(0.0, 0.0), + dim: Vector2::new(800.0, 800.0), + }, + ); + + sprite.add_child_under(Box::new(UiSprite::new( + ct.get_sprite_handle("ui::landscape::test"), + Some(ct.get_sprite_handle("ui::landscapemask")), + SpriteRect { + pos: Point2::new(32.0, 75.0) / 512.0, + dim: Vector2::new(344.0, 173.0) / 512.0, + }, + ))); + + sprite.add_child_under(Box::new(UiSprite::new( + ct.get_sprite_handle("ui::planet::button"), + None, + SpriteRect { + pos: Point2::new(375.0, 90.0) / 512.0, + dim: Vector2::new(113.569, 20.0) / 512.0, + }, + ))); + let s = Self { // height of element in logical pixels - size, current_object: None, planet_desc: UiTextArea::new( state, ct.get_sprite_handle("ui::planet"), Point2::new(0.0, 0.0), - size, + 800.0, SpriteRect { pos: Point2::new(25.831, 284.883) / 512.0, dim: Vector2::new(433.140, 97.220) / 512.0, @@ -50,7 +70,7 @@ impl Planet { state, ct.get_sprite_handle("ui::planet"), Point2::new(0.0, 0.0), - size, + 800.0, SpriteRect { pos: Point2::new(165.506, 82.0) / 512.0, dim: Vector2::new(74.883, 17.0) / 512.0, @@ -60,17 +80,9 @@ impl Planet { Align::Center, ), - landscape: UiImage::new( - ct.get_sprite_handle("ui::planet"), - Point2::new(0.0, 0.0), - size, - ct.get_sprite_handle("ui::landscape::test"), - ct.get_sprite_handle("ui::landscapemask"), - SpriteRect { - pos: Point2::new(32.031, 75.587) / 512.0, - dim: Vector2::new(342.811, 171.402) / 512.0, - }, - ), + // TODO: use both dimensions, + // not just height + sprite, }; return s; @@ -104,7 +116,7 @@ impl Planet { .systemsim .get_ship(&PhysSimShipHandle(ship_handle)) .unwrap(); - let planet_handle = match ship_data.data.get_state() { + let planet_handle = match ship_data.get_data().get_state() { ShipState::Landed { target } => *target, _ => unreachable!("tried to draw planet interface while not landed!"), }; @@ -120,16 +132,7 @@ impl Planet { } // Draw elements - self.landscape.push_to_buffer(state); - state.push_ui_buffer(UiInstance { - anchor: PositionAnchor::CC.to_int(), - position: [0.0, 0.0], - angle: to_radians(90.0), - size: self.size, - color: [1.0, 1.0, 1.0, 1.0], - sprite_index: input.ct.get_sprite_handle("ui::planet").get_index(), - mask_index: [0, 0], - }); + self.sprite.push_to_buffer(input, state); } pub fn get_textarea(&self, _input: &RenderInput, state: &RenderState) -> [TextArea; 2] { diff --git a/crates/render/src/ui/util/image.rs b/crates/render/src/ui/util/image.rs deleted file mode 100644 index 1bf037b..0000000 --- a/crates/render/src/ui/util/image.rs +++ /dev/null @@ -1,60 +0,0 @@ -use galactica_content::SpriteHandle; -use galactica_util::to_radians; -use nalgebra::{Point2, Vector2}; - -use super::SpriteRect; -use crate::{vertexbuffer::types::UiInstance, PositionAnchor, RenderState}; - -pub struct UiImage { - parent: SpriteHandle, - parent_position: Point2, - parent_size: f32, - - inner: SpriteHandle, - mask: SpriteHandle, - rect: SpriteRect, -} - -impl UiImage { - pub fn new( - parent: SpriteHandle, - parent_position: Point2, - parent_size: f32, - inner: SpriteHandle, - mask: SpriteHandle, - rect: SpriteRect, - ) -> Self { - return Self { - parent, - parent_position, - parent_size, - inner, - mask, - rect, - }; - } - - /// Add this image to the gpu sprite buffer - pub fn push_to_buffer(&self, state: &mut RenderState) { - let h = self.parent_size; - let w = self.parent.aspect * h; - - let zero = Point2::new( - self.parent_position.x - (w / 2.0), - self.parent_position.y + (h / 2.0), - ); - - let pos = zero + Vector2::new(self.rect.pos.x * w, -self.rect.pos.y * h); - let dim = Vector2::new(self.rect.dim.x * w, self.rect.dim.y * h); - - state.push_ui_buffer(UiInstance { - anchor: PositionAnchor::CNw.to_int(), - position: pos.into(), - angle: to_radians(90.0), - size: dim.y, - color: [1.0, 1.0, 1.0, 1.0], - sprite_index: self.inner.get_index(), - mask_index: [1, self.mask.get_index()], - }); - } -} diff --git a/crates/render/src/ui/util/mod.rs b/crates/render/src/ui/util/mod.rs index 54a3f5d..b3d8880 100644 --- a/crates/render/src/ui/util/mod.rs +++ b/crates/render/src/ui/util/mod.rs @@ -1,11 +1,13 @@ -mod image; +mod sprite; mod textarea; -pub(super) use image::UiImage; +pub(super) use sprite::UiSprite; pub(super) use textarea::UiTextArea; use nalgebra::{Point2, Vector2}; +use crate::{RenderInput, RenderState}; + /// Represents a rectangular region inside a sprite. pub(crate) struct SpriteRect { /// The position of the top-left corner of this rectangle, in fractional units. @@ -16,3 +18,13 @@ pub(crate) struct SpriteRect { /// 1.0 will be as tall as the sprite, 0.5 will be half as tall pub dim: Vector2, } + +pub(super) trait UiElement { + fn push_to_buffer_child( + &self, + input: &RenderInput, + state: &mut RenderState, + parent_pos: Point2, + parent_size: Vector2, + ); +} diff --git a/crates/render/src/ui/util/sprite.rs b/crates/render/src/ui/util/sprite.rs new file mode 100644 index 0000000..03e880d --- /dev/null +++ b/crates/render/src/ui/util/sprite.rs @@ -0,0 +1,128 @@ +use galactica_content::SpriteHandle; +use galactica_util::to_radians; +use nalgebra::{Point2, Vector2}; + +use super::{SpriteRect, UiElement}; +use crate::{vertexbuffer::types::UiInstance, PositionAnchor, RenderInput, RenderState}; + +pub struct UiSprite { + sprite: SpriteHandle, + mask: Option, + + rect: SpriteRect, + children_under: Vec>, + children_above: Vec>, +} + +impl UiSprite { + pub fn new(sprite: SpriteHandle, mask: Option, rect: SpriteRect) -> Self { + return Self { + sprite, + mask, + rect, + children_under: Vec::new(), + children_above: Vec::new(), + }; + } + + /// Add a child under this sprite + pub fn add_child_under(&mut self, child: Box) { + self.children_under.push(child); + } + + /// Add a child above this sprite + //pub fn add_child_above(&mut self, child: Box) { + // self.children_above.push(child); + //} + + /// Add this image to the gpu sprite buffer + pub fn push_to_buffer(&self, input: &RenderInput, state: &mut RenderState) { + let pos = Point2::new(self.rect.pos.x, self.rect.pos.y); + let dim = Vector2::new(self.rect.dim.y * self.sprite.aspect, self.rect.dim.y); + + for c in &self.children_under { + c.push_to_buffer_child(input, state, pos, dim); + } + + let sprite = input.ct.get_sprite(self.sprite); + let texture_a = sprite.get_section(sprite.default_section).frames[0]; // ANIMATE + + state.push_ui_buffer(UiInstance { + anchor: PositionAnchor::CC.to_int(), + position: pos.into(), + angle: to_radians(90.0), + size: dim.y, + color: [1.0, 1.0, 1.0, 1.0], + texture_index: [texture_a, texture_a], + texture_fade: 1.0, + mask_index: self + .mask + .map(|x| { + let sprite = input.ct.get_sprite(x); + let texture_b = sprite.get_section(sprite.default_section).frames[0]; // ANIMATE + [1, texture_b] + }) + .unwrap_or([0, 0]), + }); + + for c in &self.children_above { + c.push_to_buffer_child(input, state, pos, dim); + } + } +} + +impl UiElement for UiSprite { + /// Add this image to the gpu sprite buffer, + /// as a child of another sprite + fn push_to_buffer_child( + &self, + input: &RenderInput, + state: &mut RenderState, + parent_pos: Point2, + parent_size: Vector2, + ) { + let zero = Point2::new( + parent_pos.x - (parent_size.x / 2.0), + parent_pos.y + (parent_size.y / 2.0), + ); + + let pos = zero + + Vector2::new( + self.rect.pos.x * parent_size.x, + -self.rect.pos.y * parent_size.y, + ); + let dim = Vector2::new( + self.rect.dim.x * parent_size.x, + self.rect.dim.y * parent_size.y, + ); + + for c in &self.children_under { + c.push_to_buffer_child(input, state, pos, dim); + } + + let sprite = input.ct.get_sprite(self.sprite); + let texture_a = sprite.get_section(sprite.default_section).frames[0]; // ANIMATE + + state.push_ui_buffer(UiInstance { + anchor: PositionAnchor::CNw.to_int(), + position: pos.into(), + angle: to_radians(90.0), + size: dim.y, + color: [1.0, 1.0, 1.0, 1.0], + texture_index: [texture_a, texture_a], + texture_fade: 1.0, + mask_index: self + .mask + .map(|x| { + let sprite = input.ct.get_sprite(x); + let texture_b = sprite.get_section(sprite.default_section).frames[0]; // ANIMATE + [1, texture_b] + }) + .unwrap_or([0, 0]), + }); + + for c in &self.children_above { + c.push_to_buffer_child(input, state, pos, dim); + } + } +}