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 click(element, click_state) {}

View File

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

View File

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

View File

@ -1,5 +1,7 @@
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 std::rc::Rc;
use wgpu::BufferAddress;
@ -152,7 +154,6 @@ impl RenderState {
self.vertex_buffers.object_counter as u32
}
/*
pub fn push_radialbar_buffer(&mut self, instance: RadialBarInstance) {
// Enforce buffer 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;
}
*/
pub fn get_radialbar_counter(&self) -> 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 sprite;
mod spritebuilder;
@ -11,6 +13,8 @@ pub fn register_into_engine(engine: &mut Engine) {
engine
.build_type::<State>()
.build_type::<Rect>()
.build_type::<Color>()
.build_type::<RadialBuilder>()
.build_type::<SpriteElement>()
.build_type::<SpriteBuilder>()
.build_type::<TextBoxBuilder>()
@ -30,6 +34,8 @@ pub fn register_into_engine(engine: &mut Engine) {
.register_static_module("SceneAction", exported_module!(sceneaction_mod).into());
}
pub use color::*;
pub use radialbuilder::*;
pub use rect::*;
pub use sprite::*;
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;
#[derive(Debug, Clone)]
pub struct SpriteBuilder {
pub name: String,
pub name: ImmutableString,
pub rect: Rect,
pub sprite: String,
pub mask: Option<String>,
pub sprite: ImmutableString,
pub mask: Option<ImmutableString>,
}
impl SpriteBuilder {
pub fn new(name: String, sprite: String, rect: Rect) -> Self {
pub fn new(name: ImmutableString, sprite: ImmutableString, rect: Rect) -> Self {
Self {
name,
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);
}
}

View File

@ -1,21 +1,21 @@
use rhai::{CustomType, TypeBuilder};
use rhai::{CustomType, ImmutableString, TypeBuilder};
use super::{Rect, TextBoxFont, TextBoxJustify};
#[derive(Debug, Clone)]
pub struct TextBoxBuilder {
pub name: String,
pub name: ImmutableString,
pub font_size: f32,
pub line_height: f32,
pub font: TextBoxFont,
pub justify: TextBoxJustify,
pub rect: Rect,
pub text: String,
pub text: ImmutableString,
}
impl TextBoxBuilder {
pub fn new(
name: String,
name: ImmutableString,
font_size: f32,
line_height: f32,
font: TextBoxFont,
@ -29,11 +29,11 @@ impl TextBoxBuilder {
font,
justify,
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
}
}

View File

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

View File

@ -1,5 +1,7 @@
mod radialbar;
mod sprite;
mod textbox;
pub use radialbar::*;
pub use sprite::*;
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) {}
}