Added radialbar builder

master
Mark 2024-02-03 07:33:10 -08:00
parent 093a7f271c
commit 3c582006ef
Signed by: Mark
GPG Key ID: C6D63995FE72FD80
12 changed files with 262 additions and 61 deletions

View File

@ -1,3 +1,44 @@
fn init(state) { return []; } fn init(state) {
let ring = SpriteBuilder(
"ring",
"ui::status",
Rect(
-5.0, -5.0, 100.0, 100.0,
SpriteAnchor::NorthEast,
SpriteAnchor::NorthEast
)
);
let shield = RadialBuilder(
"shield", 2.5,
Color(0.3, 0.6, 0.8, 1.0),
Rect(
-9.5, -9.5, 91.0, 91.0,
SpriteAnchor::NorthEast,
SpriteAnchor::NorthEast
)
);
shield.set_progress(0.2);
let hull = RadialBuilder(
"hull", 2.5,
Color(0.8, 0.7, 0.5, 1.0),
Rect(
-13.5, -13.5, 83.0, 83.0,
SpriteAnchor::NorthEast,
SpriteAnchor::NorthEast
)
);
hull.set_progress(0.4);
return [
ring,
shield,
hull
];
}
fn hover(element, hover_state) {} fn hover(element, hover_state) {}
fn click(element, click_state) {} fn click(element, click_state) {}

View File

@ -121,11 +121,8 @@ fn try_main() -> Result<()> {
let event_loop = EventLoop::new(); let event_loop = EventLoop::new();
let window = WindowBuilder::new().build(&event_loop).unwrap(); let window = WindowBuilder::new().build(&event_loop).unwrap();
let mut gpu = pollster::block_on(galactica_render::GPUState::new( let mut gpu = pollster::block_on(galactica_render::GPUState::new(window, content.clone()))?;
window, gpu.set_scene(RenderScenes::System);
content.clone(),
RenderScenes::System,
))?;
gpu.init(&content); gpu.init(&content);
// TODO: don't clone content // TODO: don't clone content

View File

@ -37,11 +37,7 @@ pub struct GPUState {
impl GPUState { impl GPUState {
/// Make a new GPUState that draws on `window` /// Make a new GPUState that draws on `window`
pub async fn new( pub async fn new(window: winit::window::Window, ct: Rc<Content>) -> Result<Self> {
window: winit::window::Window,
ct: Rc<Content>,
scene: RenderScenes,
) -> Result<Self> {
let window_size = window.inner_size(); let window_size = window.inner_size();
let window_aspect = window_size.width as f32 / window_size.height as f32; let window_aspect = window_size.width as f32 / window_size.height as f32;
@ -229,7 +225,7 @@ impl GPUState {
starfield_pipeline, starfield_pipeline,
ui_pipeline, ui_pipeline,
radialbar_pipeline, radialbar_pipeline,
scene, scene: RenderScenes::Landed,
state: RenderState { state: RenderState {
queue, queue,

View File

@ -1,5 +1,7 @@
use galactica_content::Content; use galactica_content::Content;
use galactica_util::constants::{OBJECT_SPRITE_INSTANCE_LIMIT, UI_SPRITE_INSTANCE_LIMIT}; use galactica_util::constants::{
OBJECT_SPRITE_INSTANCE_LIMIT, RADIALBAR_SPRITE_INSTANCE_LIMIT, UI_SPRITE_INSTANCE_LIMIT,
};
use glyphon::{FontSystem, SwashCache, TextAtlas, TextRenderer}; use glyphon::{FontSystem, SwashCache, TextAtlas, TextRenderer};
use std::rc::Rc; use std::rc::Rc;
use wgpu::BufferAddress; use wgpu::BufferAddress;
@ -152,7 +154,6 @@ impl RenderState {
self.vertex_buffers.object_counter as u32 self.vertex_buffers.object_counter as u32
} }
/*
pub fn push_radialbar_buffer(&mut self, instance: RadialBarInstance) { pub fn push_radialbar_buffer(&mut self, instance: RadialBarInstance) {
// Enforce buffer limit // Enforce buffer limit
if self.vertex_buffers.radialbar_counter as u64 > RADIALBAR_SPRITE_INSTANCE_LIMIT { if self.vertex_buffers.radialbar_counter as u64 > RADIALBAR_SPRITE_INSTANCE_LIMIT {
@ -167,7 +168,6 @@ impl RenderState {
); );
self.vertex_buffers.radialbar_counter += 1; self.vertex_buffers.radialbar_counter += 1;
} }
*/
pub fn get_radialbar_counter(&self) -> u32 { pub fn get_radialbar_counter(&self) -> u32 {
self.vertex_buffers.radialbar_counter as u32 self.vertex_buffers.radialbar_counter as u32

View File

@ -0,0 +1,25 @@
use nalgebra::Vector4;
use rhai::{CustomType, TypeBuilder};
#[derive(Debug, Clone)]
pub struct Color {
pub val: Vector4<f32>,
}
impl Color {
pub fn new(r: f32, g: f32, b: f32, a: f32) -> Self {
Self {
val: Vector4::new(r, g, b, a),
}
}
pub fn as_array(&self) -> [f32; 4] {
[self.val.x, self.val.y, self.val.z, self.val.w]
}
}
impl CustomType for Color {
fn build(mut builder: TypeBuilder<Self>) {
builder.with_name("Color").with_fn("Color", Self::new);
}
}

View File

@ -1,3 +1,5 @@
mod color;
mod radialbuilder;
mod rect; mod rect;
mod sprite; mod sprite;
mod spritebuilder; mod spritebuilder;
@ -11,6 +13,8 @@ pub fn register_into_engine(engine: &mut Engine) {
engine engine
.build_type::<State>() .build_type::<State>()
.build_type::<Rect>() .build_type::<Rect>()
.build_type::<Color>()
.build_type::<RadialBuilder>()
.build_type::<SpriteElement>() .build_type::<SpriteElement>()
.build_type::<SpriteBuilder>() .build_type::<SpriteBuilder>()
.build_type::<TextBoxBuilder>() .build_type::<TextBoxBuilder>()
@ -30,6 +34,8 @@ pub fn register_into_engine(engine: &mut Engine) {
.register_static_module("SceneAction", exported_module!(sceneaction_mod).into()); .register_static_module("SceneAction", exported_module!(sceneaction_mod).into());
} }
pub use color::*;
pub use radialbuilder::*;
pub use rect::*; pub use rect::*;
pub use sprite::*; pub use sprite::*;
pub use spritebuilder::*; pub use spritebuilder::*;

View File

@ -0,0 +1,39 @@
use nalgebra::clamp;
use rhai::{CustomType, ImmutableString, TypeBuilder};
use super::{color::Color, Rect};
#[derive(Debug, Clone)]
pub struct RadialBuilder {
pub name: ImmutableString,
pub rect: Rect,
pub stroke: f32,
pub color: Color,
pub progress: f32,
}
impl RadialBuilder {
pub fn new(name: ImmutableString, stroke: f32, color: Color, rect: Rect) -> Self {
Self {
name,
rect,
stroke,
color,
progress: 0.0,
}
}
pub fn set_progress(&mut self, progress: f32) {
self.progress = clamp(progress, 0.0, 1.0);
}
}
impl CustomType for RadialBuilder {
fn build(mut builder: TypeBuilder<Self>) {
builder
.with_name("RadialBuilder")
.with_fn("RadialBuilder", Self::new)
.with_fn("set_progress", Self::set_progress);
}
}

View File

@ -1,17 +1,17 @@
use rhai::{CustomType, TypeBuilder}; use rhai::{CustomType, ImmutableString, TypeBuilder};
use super::Rect; use super::Rect;
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct SpriteBuilder { pub struct SpriteBuilder {
pub name: String, pub name: ImmutableString,
pub rect: Rect, pub rect: Rect,
pub sprite: String, pub sprite: ImmutableString,
pub mask: Option<String>, pub mask: Option<ImmutableString>,
} }
impl SpriteBuilder { impl SpriteBuilder {
pub fn new(name: String, sprite: String, rect: Rect) -> Self { pub fn new(name: ImmutableString, sprite: ImmutableString, rect: Rect) -> Self {
Self { Self {
name, name,
rect, rect,
@ -20,7 +20,7 @@ impl SpriteBuilder {
} }
} }
pub fn set_mask(&mut self, mask: String) { pub fn set_mask(&mut self, mask: ImmutableString) {
self.mask = Some(mask); self.mask = Some(mask);
} }
} }

View File

@ -1,21 +1,21 @@
use rhai::{CustomType, TypeBuilder}; use rhai::{CustomType, ImmutableString, TypeBuilder};
use super::{Rect, TextBoxFont, TextBoxJustify}; use super::{Rect, TextBoxFont, TextBoxJustify};
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct TextBoxBuilder { pub struct TextBoxBuilder {
pub name: String, pub name: ImmutableString,
pub font_size: f32, pub font_size: f32,
pub line_height: f32, pub line_height: f32,
pub font: TextBoxFont, pub font: TextBoxFont,
pub justify: TextBoxJustify, pub justify: TextBoxJustify,
pub rect: Rect, pub rect: Rect,
pub text: String, pub text: ImmutableString,
} }
impl TextBoxBuilder { impl TextBoxBuilder {
pub fn new( pub fn new(
name: String, name: ImmutableString,
font_size: f32, font_size: f32,
line_height: f32, line_height: f32,
font: TextBoxFont, font: TextBoxFont,
@ -29,11 +29,11 @@ impl TextBoxBuilder {
font, font,
justify, justify,
rect, rect,
text: String::new(), text: ImmutableString::new(),
} }
} }
pub fn set_text(&mut self, text: String) { pub fn set_text(&mut self, text: ImmutableString) {
self.text = text self.text = text
} }
} }

View File

@ -7,10 +7,13 @@ use std::{cell::RefCell, collections::HashSet, fmt::Debug, rc::Rc};
use super::{ use super::{
api::{self, SceneAction, SpriteElement, TextBoxBuilder}, api::{self, SceneAction, SpriteElement, TextBoxBuilder},
util::{Sprite, TextBox}, util::{RadialBar, TextBox},
}; };
use crate::{ use crate::{
ui::api::{SpriteBuilder, State}, ui::{
api::{RadialBuilder, SpriteBuilder, State},
util::Sprite,
},
RenderInput, RenderState, RenderInput, RenderState,
}; };
@ -58,6 +61,7 @@ impl ToString for UiScene {
enum UiElement { enum UiElement {
Sprite(Rc<RefCell<Sprite>>), Sprite(Rc<RefCell<Sprite>>),
RadialBar(Rc<RefCell<RadialBar>>),
Text(TextBox), Text(TextBox),
} }
@ -66,6 +70,10 @@ impl UiElement {
Self::Sprite(Rc::new(RefCell::new(sprite))) Self::Sprite(Rc::new(RefCell::new(sprite)))
} }
pub fn new_radialbar(bar: RadialBar) -> Self {
Self::RadialBar(Rc::new(RefCell::new(bar)))
}
pub fn new_text(text: TextBox) -> Self { pub fn new_text(text: TextBox) -> Self {
Self::Text(text) Self::Text(text)
} }
@ -83,6 +91,13 @@ impl UiElement {
_ => None, _ => None,
} }
} }
pub fn radialbar(&self) -> Option<Rc<RefCell<RadialBar>>> {
match self {
Self::RadialBar(r) => Some(r.clone()),
_ => None,
}
}
} }
pub(crate) struct UiManager { pub(crate) struct UiManager {
@ -143,32 +158,55 @@ impl UiManager {
for v in builders { for v in builders {
if v.is::<SpriteBuilder>() { if v.is::<SpriteBuilder>() {
let s = v.cast::<SpriteBuilder>(); let s = v.cast::<SpriteBuilder>();
if used_names.contains(&s.name) { if used_names.contains(s.name.as_str()) {
error!( error!(
"UI scene `{}` re-uses element name `{}`", "UI scene `{}` re-uses element name `{}`",
self.current_scene.to_string(), self.current_scene.to_string(),
s.name s.name
); );
} else { } else {
used_names.insert(s.name.clone()); used_names.insert(s.name.to_string());
} }
self.elements.push(UiElement::new_sprite(Sprite::new( self.elements.push(UiElement::new_sprite(Sprite::new(
&self.ct, s.name, s.sprite, s.mask, s.rect, &self.ct,
s.name.to_string(),
s.sprite.to_string(),
s.mask.map(|x| x.to_string()),
s.rect,
)));
} else if v.is::<RadialBuilder>() {
let r = v.cast::<RadialBuilder>();
if used_names.contains(r.name.as_str()) {
error!(
"UI scene `{}` re-uses element name `{}`",
self.current_scene.to_string(),
r.name
);
} else {
used_names.insert(r.name.to_string());
}
self.elements.push(UiElement::new_radialbar(RadialBar::new(
&self.ct,
r.name.to_string(),
r.stroke,
r.color,
r.rect,
r.progress,
))); )));
} else if v.is::<TextBoxBuilder>() { } else if v.is::<TextBoxBuilder>() {
let t = v.cast::<TextBoxBuilder>(); let t = v.cast::<TextBoxBuilder>();
if used_names.contains(&t.name) { if used_names.contains(t.name.as_str()) {
error!( error!(
"UI scene `{}` re-uses element name `{}`", "UI scene `{}` re-uses element name `{}`",
self.current_scene.to_string(), self.current_scene.to_string(),
t.name t.name
); );
} else { } else {
used_names.insert(t.name.clone()); used_names.insert(t.name.to_string());
} }
let mut b = TextBox::new( let mut b = TextBox::new(
state, state,
t.name, t.name.to_string(),
t.font_size, t.font_size,
t.line_height, t.line_height,
t.font, t.font,
@ -187,12 +225,7 @@ impl UiManager {
/// Draw all ui elements /// Draw all ui elements
pub fn draw(&mut self, input: &RenderInput, state: &mut RenderState) -> Result<()> { pub fn draw(&mut self, input: &RenderInput, state: &mut RenderState) -> Result<()> {
let mut iter = self let mut iter = self.elements.iter();
.elements
.iter()
.map(|x| x.sprite())
.filter(|x| x.is_some())
.map(|x| x.unwrap());
let action: SceneAction = loop { let action: SceneAction = loop {
let e = match iter.next() { let e = match iter.next() {
@ -200,6 +233,8 @@ impl UiManager {
None => break SceneAction::None, None => break SceneAction::None,
}; };
let action: Dynamic = {
if let Some(e) = e.sprite() {
let mut x = (*e).borrow_mut(); let mut x = (*e).borrow_mut();
let m = x.check_mouse(input, state); let m = x.check_mouse(input, state);
x.step(input, state); x.step(input, state);
@ -207,7 +242,7 @@ impl UiManager {
drop(x); drop(x);
// we MUST drop here, since script calls mutate the sprite RefCell // we MUST drop here, since script calls mutate the sprite RefCell
let action: Dynamic = match m { match m {
MouseEvent::None => Dynamic::from(SceneAction::None), MouseEvent::None => Dynamic::from(SceneAction::None),
MouseEvent::Release | MouseEvent::Click => self.engine.call_fn( MouseEvent::Release | MouseEvent::Click => self.engine.call_fn(
@ -223,6 +258,15 @@ impl UiManager {
"hover", "hover",
(SpriteElement::new(self.ct.clone(), e.clone()), m.is_enter()), (SpriteElement::new(self.ct.clone(), e.clone()), m.is_enter()),
)?, )?,
}
} else if let Some(e) = e.radialbar() {
let mut x = (*e).borrow_mut();
x.step(input, state);
x.push_to_buffer(input, state);
Dynamic::from(SceneAction::None)
} else {
Dynamic::from(SceneAction::None)
}
}; };
if let Some(action) = action.try_cast::<SceneAction>() { if let Some(action) = action.try_cast::<SceneAction>() {

View File

@ -1,5 +1,7 @@
mod radialbar;
mod sprite; mod sprite;
mod textbox; mod textbox;
pub use radialbar::*;
pub use sprite::*; pub use sprite::*;
pub use textbox::*; pub use textbox::*;

View File

@ -0,0 +1,51 @@
use galactica_content::Content;
use std::f32::consts::TAU;
use super::super::api::Rect;
use crate::{
ui::api::Color, vertexbuffer::types::RadialBarInstance, PositionAnchor, RenderInput,
RenderState,
};
#[derive(Debug, Clone)]
pub struct RadialBar {
pub name: String,
pub rect: Rect,
pub stroke: f32,
pub color: Color,
pub progress: f32,
}
impl RadialBar {
pub fn new(
_ct: &Content,
name: String,
stroke: f32,
color: Color,
rect: Rect,
progress: f32,
) -> Self {
Self {
name,
rect,
stroke,
color,
progress,
}
}
pub fn push_to_buffer(&self, input: &RenderInput, state: &mut RenderState) {
let rect = self.rect.to_centered(state, input.ct.get_config().ui_scale);
state.push_radialbar_buffer(RadialBarInstance {
position: [rect.pos.x, rect.pos.y],
anchor: PositionAnchor::CC.to_int(),
diameter: rect.dim.x.min(rect.dim.y),
stroke: self.stroke * input.ct.get_config().ui_scale,
color: self.color.as_array(),
angle: self.progress * TAU,
});
}
pub fn step(&mut self, _input: &RenderInput, _state: &mut RenderState) {}
}