Reworked ui positioning
parent
ca15dcae34
commit
85c4265216
2
assets
2
assets
|
@ -1 +1 @@
|
||||||
Subproject commit df96cae3dec9e5dce1ee0f1d59b1dbb90add8db8
|
Subproject commit e5898796144c7590c3cf18e14867a5ec47637fd4
|
|
@ -94,6 +94,9 @@ file = "ui/landscape-mask.png"
|
||||||
[sprite."ui::outfitbg"]
|
[sprite."ui::outfitbg"]
|
||||||
file = "ui/outfit-bg.png"
|
file = "ui/outfit-bg.png"
|
||||||
|
|
||||||
|
[sprite."ui::outfitterbox"]
|
||||||
|
file = "ui/outfitter-box.png"
|
||||||
|
|
||||||
[sprite."ui::shopsepbar"]
|
[sprite."ui::shopsepbar"]
|
||||||
file = "ui/shop-sep-bar.png"
|
file = "ui/shop-sep-bar.png"
|
||||||
|
|
||||||
|
|
|
@ -1,12 +1,10 @@
|
||||||
[ui.status]
|
[ui.status]
|
||||||
|
|
||||||
# TODO: unified color value
|
# TODO: unified color value
|
||||||
# TODO: anchor
|
|
||||||
# TODO: bar type: linear/radial
|
# TODO: bar type: linear/radial
|
||||||
# TODO: bar as ui util struct
|
# TODO: bar as ui util struct
|
||||||
# TODO: mouse collider
|
# TODO: mouse collider
|
||||||
# TODO: modular UI (how?)
|
# TODO: modular UI (how?)
|
||||||
# TODO: textbox configuration
|
|
||||||
|
|
||||||
# shield_bar.pos = [-19, -19]
|
# shield_bar.pos = [-19, -19]
|
||||||
# shield_bar.diameter = 182
|
# shield_bar.diameter = 182
|
||||||
|
@ -24,19 +22,59 @@
|
||||||
|
|
||||||
[ui.landed]
|
[ui.landed]
|
||||||
frame.sprite = "ui::planet"
|
frame.sprite = "ui::planet"
|
||||||
frame.pos = [0.0, 0.0]
|
frame.rect.pos = [0.0, 0.0]
|
||||||
frame.dim = [800.0, 800.0]
|
frame.rect.dim = [800.0, 800.0]
|
||||||
|
frame.rect.anchor_self = "center"
|
||||||
|
frame.rect.anchor_parent = "center"
|
||||||
|
|
||||||
landscape.mask = "ui::landscapemask"
|
landscape.mask = "ui::landscapemask"
|
||||||
landscape.pos = [32.0, 75.0]
|
landscape.rect.pos = [-350.0, 282.8]
|
||||||
landscape.dim = [344.0, 173.0]
|
landscape.rect.dim = [537.5, 270.31]
|
||||||
landscape.loc_div = 512
|
landscape.rect.anchor_self = "northwest"
|
||||||
|
landscape.rect.anchor_parent = "center"
|
||||||
|
|
||||||
|
|
||||||
button.sprite = "ui::planet::button"
|
button.sprite = "ui::planet::button"
|
||||||
button.pos = [356, 90]
|
button.rect.pos = [178.12, 254.6]
|
||||||
button.dim = [113.569, 20]
|
button.rect.dim = [175.16, 38.437]
|
||||||
button.loc_div = 512
|
button.rect.anchor_self = "northwest"
|
||||||
|
button.rect.anchor_parent = "center"
|
||||||
button.on_mouse_enter.edge = "on:top"
|
button.on_mouse_enter.edge = "on:top"
|
||||||
button.on_mouse_enter.duration = 0.1
|
button.on_mouse_enter.duration = 0.1
|
||||||
button.on_mouse_leave.edge = "off:top"
|
button.on_mouse_leave.edge = "off:top"
|
||||||
button.on_mouse_leave.duration = 0.1
|
button.on_mouse_leave.duration = 0.1
|
||||||
|
|
||||||
|
planet_name.rect.pos = [-143.89, 273]
|
||||||
|
planet_name.rect.dim = [121.98, 18.094]
|
||||||
|
planet_name.rect.anchor_self = "northwest"
|
||||||
|
planet_name.rect.anchor_parent = "center"
|
||||||
|
planet_name.font_size = 19
|
||||||
|
planet_name.line_height = 19
|
||||||
|
planet_name.align = "center"
|
||||||
|
|
||||||
|
planet_desc.rect.pos = [-358.43, -32]
|
||||||
|
planet_desc.rect.dim = [673.91, 153.75]
|
||||||
|
planet_desc.rect.anchor_self = "northwest"
|
||||||
|
planet_desc.rect.anchor_parent = "center"
|
||||||
|
planet_desc.font_size = 16
|
||||||
|
planet_desc.line_height = 18
|
||||||
|
planet_desc.align = "left"
|
||||||
|
|
||||||
|
|
||||||
|
[ui.outfitter]
|
||||||
|
|
||||||
|
se_box.sprite = "ui::outfitterbox"
|
||||||
|
se_box.rect.pos = [-10.0, -10.0]
|
||||||
|
se_box.rect.dim = [512.0, 337.0] # todo: auto aspect
|
||||||
|
se_box.rect.anchor_self = "southwest"
|
||||||
|
se_box.rect.anchor_parent = "southwest"
|
||||||
|
|
||||||
|
exit_button.sprite = "ui::button"
|
||||||
|
exit_button.rect.pos = [279.07, 135.38]
|
||||||
|
exit_button.rect.dim = [173.44, 45.01]
|
||||||
|
exit_button.rect.anchor_self = "northwest"
|
||||||
|
exit_button.rect.anchor_parent = "southwest"
|
||||||
|
exit_button.on_mouse_enter.edge = "on:top"
|
||||||
|
exit_button.on_mouse_enter.duration = 0.1
|
||||||
|
exit_button.on_mouse_leave.edge = "off:top"
|
||||||
|
exit_button.on_mouse_leave.duration = 0.1
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
use anyhow::{Context, Result};
|
use anyhow::{Context, Result};
|
||||||
use nalgebra::{Point2, Vector2};
|
use nalgebra::{Point2, Vector2};
|
||||||
|
use serde::Deserialize;
|
||||||
|
|
||||||
use crate::{handle::SpriteHandle, Content, ContentBuildContext, SectionEdge};
|
use crate::{handle::SpriteHandle, Content, ContentBuildContext, SectionEdge};
|
||||||
|
|
||||||
|
@ -14,6 +15,7 @@ pub(crate) mod syntax {
|
||||||
#[derive(Debug, Deserialize)]
|
#[derive(Debug, Deserialize)]
|
||||||
pub struct Ui {
|
pub struct Ui {
|
||||||
pub landed: UiLanded,
|
pub landed: UiLanded,
|
||||||
|
pub outfitter: UiOutfitter,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
#[derive(Debug, Deserialize)]
|
||||||
|
@ -21,17 +23,22 @@ pub(crate) mod syntax {
|
||||||
pub frame: UiSprite,
|
pub frame: UiSprite,
|
||||||
pub landscape: UiSprite,
|
pub landscape: UiSprite,
|
||||||
pub button: UiSprite,
|
pub button: UiSprite,
|
||||||
|
pub planet_name: UiText,
|
||||||
|
pub planet_desc: UiText,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
#[derive(Debug, Deserialize)]
|
||||||
pub struct UiSprite {
|
pub struct UiOutfitter {
|
||||||
pub sprite: Option<String>,
|
pub se_box: UiSprite,
|
||||||
|
pub exit_button: UiSprite,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
pub struct UiRect {
|
||||||
pub pos: [f32; 2],
|
pub pos: [f32; 2],
|
||||||
pub dim: [f32; 2],
|
pub dim: [f32; 2],
|
||||||
pub loc_div: Option<f32>,
|
pub anchor_self: super::UiPositionAnchor,
|
||||||
pub mask: Option<String>,
|
pub anchor_parent: super::UiPositionAnchor,
|
||||||
pub on_mouse_enter: Option<EdgeSpec>,
|
|
||||||
pub on_mouse_leave: Option<EdgeSpec>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
#[derive(Debug, Deserialize)]
|
||||||
|
@ -40,15 +47,53 @@ pub(crate) mod syntax {
|
||||||
pub duration: f32,
|
pub duration: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
pub struct UiText {
|
||||||
|
pub rect: UiRect,
|
||||||
|
pub font_size: f32,
|
||||||
|
pub line_height: f32,
|
||||||
|
pub align: super::UiTextAlign,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl UiText {
|
||||||
|
pub fn build(
|
||||||
|
self,
|
||||||
|
_build_context: &ContentBuildContext,
|
||||||
|
_ct: &Content,
|
||||||
|
) -> Result<super::UiTextConfig> {
|
||||||
|
let rect = {
|
||||||
|
super::UiRect {
|
||||||
|
pos: Point2::new(self.rect.pos[0], self.rect.pos[1]),
|
||||||
|
dim: Vector2::new(self.rect.dim[0], self.rect.dim[1]),
|
||||||
|
anchor_self: self.rect.anchor_self,
|
||||||
|
anchor_parent: self.rect.anchor_parent,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return Ok(super::UiTextConfig {
|
||||||
|
rect,
|
||||||
|
font_size: self.font_size,
|
||||||
|
line_height: self.line_height,
|
||||||
|
align: self.align,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
pub struct UiSprite {
|
||||||
|
pub sprite: Option<String>,
|
||||||
|
pub rect: UiRect,
|
||||||
|
pub mask: Option<String>,
|
||||||
|
pub on_mouse_enter: Option<EdgeSpec>,
|
||||||
|
pub on_mouse_leave: Option<EdgeSpec>,
|
||||||
|
}
|
||||||
|
|
||||||
impl UiSprite {
|
impl UiSprite {
|
||||||
pub fn build(
|
pub fn build(
|
||||||
self,
|
self,
|
||||||
build_context: &ContentBuildContext,
|
build_context: &ContentBuildContext,
|
||||||
ct: &Content,
|
ct: &Content,
|
||||||
|
|
||||||
// If true, this ui sprite will be positioned relative to another
|
|
||||||
is_child: bool,
|
|
||||||
|
|
||||||
// If true, fail if self.sprite is missing.
|
// If true, fail if self.sprite is missing.
|
||||||
// If false, fail if self.sprite exists.
|
// If false, fail if self.sprite exists.
|
||||||
// This is false for sprites that may change---for example, planet landscapes
|
// This is false for sprites that may change---for example, planet landscapes
|
||||||
|
@ -112,19 +157,12 @@ pub(crate) mod syntax {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let d = self.loc_div.unwrap_or(1.0);
|
|
||||||
|
|
||||||
let rect = {
|
let rect = {
|
||||||
if is_child {
|
super::UiRect {
|
||||||
super::UiSpriteRect::Relative {
|
pos: Point2::new(self.rect.pos[0], self.rect.pos[1]),
|
||||||
pos: Point2::new(self.pos[0], self.pos[1]) / d,
|
dim: Vector2::new(self.rect.dim[0], self.rect.dim[1]),
|
||||||
dim: Vector2::new(self.dim[0], self.dim[1]) / d,
|
anchor_self: self.rect.anchor_self,
|
||||||
}
|
anchor_parent: self.rect.anchor_parent,
|
||||||
} else {
|
|
||||||
super::UiSpriteRect::Absolute {
|
|
||||||
pos: Point2::new(self.pos[0], self.pos[1]) / d,
|
|
||||||
dim: Vector2::new(self.dim[0], self.dim[1]) / d,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -139,6 +177,42 @@ pub(crate) mod syntax {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// How to align text in a text box
|
||||||
|
#[derive(Debug, Deserialize, Clone, Copy)]
|
||||||
|
pub enum UiTextAlign {
|
||||||
|
/// Center-align
|
||||||
|
#[serde(rename = "center")]
|
||||||
|
Center,
|
||||||
|
|
||||||
|
/// Left-align
|
||||||
|
#[serde(rename = "left")]
|
||||||
|
Left,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// How to position a UI sprite
|
||||||
|
#[derive(Debug, Deserialize, Clone, Copy)]
|
||||||
|
pub enum UiPositionAnchor {
|
||||||
|
/// Anchored at center
|
||||||
|
#[serde(rename = "center")]
|
||||||
|
Center,
|
||||||
|
|
||||||
|
/// Anchored at top-left
|
||||||
|
#[serde(rename = "northwest")]
|
||||||
|
NorthWest,
|
||||||
|
|
||||||
|
/// Anchored at top-right
|
||||||
|
#[serde(rename = "northeast")]
|
||||||
|
NorthEast,
|
||||||
|
|
||||||
|
/// Anchored at bottom-left
|
||||||
|
#[serde(rename = "southwest")]
|
||||||
|
SouthWest,
|
||||||
|
|
||||||
|
/// Anchored at bottom-right
|
||||||
|
#[serde(rename = "southeast")]
|
||||||
|
SouthEast,
|
||||||
|
}
|
||||||
|
|
||||||
/// UI Configuration
|
/// UI Configuration
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct Ui {
|
pub struct Ui {
|
||||||
|
@ -150,54 +224,35 @@ pub struct Ui {
|
||||||
|
|
||||||
/// Test button
|
/// Test button
|
||||||
pub landed_button: UiSpriteConfig,
|
pub landed_button: UiSpriteConfig,
|
||||||
|
|
||||||
|
/// Landed planet name
|
||||||
|
pub landed_planet_name: UiTextConfig,
|
||||||
|
|
||||||
|
/// Landed planet description
|
||||||
|
pub landed_planet_desc: UiTextConfig,
|
||||||
|
|
||||||
|
/// Outfitter exit button
|
||||||
|
pub outfitter_exit_button: UiSpriteConfig,
|
||||||
|
|
||||||
|
/// Outfitter south-east box
|
||||||
|
pub outfitter_se_box: UiSpriteConfig,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A UI sprite's position
|
/// A UI sprite's position
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum UiSpriteRect {
|
pub struct UiRect {
|
||||||
/// Positioning relative to a parent sprite
|
|
||||||
Relative {
|
|
||||||
// Note that both pos and dim include transparent pixels,
|
|
||||||
// of this sprite AND its parent.
|
|
||||||
|
|
||||||
// We use the top left corner here because that's how inkscape
|
|
||||||
// positions its objects. This makes it very easy to compute position.
|
|
||||||
// TODO: maybe add anchors here too?
|
|
||||||
/// The position of this sprite's northeast corner, relative to its parent's NE corner.
|
|
||||||
/// Positive X is right, positive Y is down.
|
|
||||||
pos: Point2<f32>,
|
|
||||||
|
|
||||||
/// This sprite's w and h, as a fraction of the parent's width and height.
|
|
||||||
dim: Vector2<f32>,
|
|
||||||
},
|
|
||||||
|
|
||||||
/// Absolute positioning
|
|
||||||
Absolute {
|
|
||||||
/// The position of the center of this sprite, in logical pixels,
|
/// The position of the center of this sprite, in logical pixels,
|
||||||
/// with 0, 0 at the center of the screen
|
/// with 0, 0 at the center of the screen
|
||||||
pos: Point2<f32>,
|
pub pos: Point2<f32>,
|
||||||
|
|
||||||
/// This sprite's w and h, in logical pixels.
|
/// This sprite's w and h, in logical pixels.
|
||||||
dim: Vector2<f32>,
|
pub dim: Vector2<f32>,
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
impl UiSpriteRect {
|
/// The point on this sprite that pos is anchored to
|
||||||
/// Get this rectangle's position
|
pub anchor_self: UiPositionAnchor,
|
||||||
pub fn get_pos(&self) -> &Point2<f32> {
|
|
||||||
match self {
|
|
||||||
Self::Relative { pos, .. } => pos,
|
|
||||||
Self::Absolute { pos, .. } => pos,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get this rectangle's dimensions
|
/// The point on the parent that pos is relative to
|
||||||
pub fn get_dim(&self) -> &Vector2<f32> {
|
pub anchor_parent: UiPositionAnchor,
|
||||||
match self {
|
|
||||||
Self::Relative { dim, .. } => dim,
|
|
||||||
Self::Absolute { dim, .. } => dim,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A single UI sprite instance
|
/// A single UI sprite instance
|
||||||
|
@ -210,7 +265,7 @@ pub struct UiSpriteConfig {
|
||||||
pub mask: Option<SpriteHandle>,
|
pub mask: Option<SpriteHandle>,
|
||||||
|
|
||||||
/// This sprite's position and size
|
/// This sprite's position and size
|
||||||
pub rect: UiSpriteRect,
|
pub rect: UiRect,
|
||||||
|
|
||||||
/// Animation edge to take when mouse enters this sprite
|
/// Animation edge to take when mouse enters this sprite
|
||||||
pub on_mouse_enter: Option<SectionEdge>,
|
pub on_mouse_enter: Option<SectionEdge>,
|
||||||
|
@ -219,6 +274,22 @@ pub struct UiSpriteConfig {
|
||||||
pub on_mouse_leave: Option<SectionEdge>,
|
pub on_mouse_leave: Option<SectionEdge>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A UI text box
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct UiTextConfig {
|
||||||
|
/// Text box location and dimensions
|
||||||
|
pub rect: UiRect,
|
||||||
|
|
||||||
|
/// Text box font size
|
||||||
|
pub font_size: f32,
|
||||||
|
|
||||||
|
/// Text box line height
|
||||||
|
pub line_height: f32,
|
||||||
|
|
||||||
|
/// Text box alignment
|
||||||
|
pub align: UiTextAlign,
|
||||||
|
}
|
||||||
|
|
||||||
impl crate::Build for Ui {
|
impl crate::Build for Ui {
|
||||||
type InputSyntaxType = syntax::Ui;
|
type InputSyntaxType = syntax::Ui;
|
||||||
|
|
||||||
|
@ -231,18 +302,39 @@ impl crate::Build for Ui {
|
||||||
landed_frame: ui
|
landed_frame: ui
|
||||||
.landed
|
.landed
|
||||||
.frame
|
.frame
|
||||||
.build(build_context, ct, false, true)
|
.build(build_context, ct, true)
|
||||||
.with_context(|| format!("in ui config (frame)"))?,
|
.with_context(|| format!("in ui config (landed_frame)"))?,
|
||||||
landed_landscape: ui
|
landed_landscape: ui
|
||||||
.landed
|
.landed
|
||||||
.landscape
|
.landscape
|
||||||
.build(build_context, ct, true, false)
|
.build(build_context, ct, false)
|
||||||
.with_context(|| format!("in ui config (image)"))?,
|
.with_context(|| format!("in ui config (landed_landscape)"))?,
|
||||||
landed_button: ui
|
landed_button: ui
|
||||||
.landed
|
.landed
|
||||||
.button
|
.button
|
||||||
.build(build_context, ct, true, true)
|
.build(build_context, ct, true)
|
||||||
.with_context(|| format!("in ui config (button)"))?,
|
.with_context(|| format!("in ui config (landed_button)"))?,
|
||||||
|
landed_planet_name: ui
|
||||||
|
.landed
|
||||||
|
.planet_name
|
||||||
|
.build(build_context, ct)
|
||||||
|
.with_context(|| format!("in ui config (landed_planet_name)"))?,
|
||||||
|
landed_planet_desc: ui
|
||||||
|
.landed
|
||||||
|
.planet_desc
|
||||||
|
.build(build_context, ct)
|
||||||
|
.with_context(|| format!("in ui config (landed_planet_desc)"))?,
|
||||||
|
|
||||||
|
outfitter_exit_button: ui
|
||||||
|
.outfitter
|
||||||
|
.exit_button
|
||||||
|
.build(build_context, ct, true)
|
||||||
|
.with_context(|| format!("in ui config (outfitter_exit_button)"))?,
|
||||||
|
outfitter_se_box: ui
|
||||||
|
.outfitter
|
||||||
|
.se_box
|
||||||
|
.build(build_context, ct, true)
|
||||||
|
.with_context(|| format!("in ui config (outfitter_se_box)"))?,
|
||||||
});
|
});
|
||||||
|
|
||||||
return Ok(());
|
return Ok(());
|
||||||
|
|
|
@ -4,7 +4,7 @@ struct InstanceInput {
|
||||||
@location(2) anchor: u32,
|
@location(2) anchor: u32,
|
||||||
@location(3) position: vec2<f32>,
|
@location(3) position: vec2<f32>,
|
||||||
@location(4) angle: f32,
|
@location(4) angle: f32,
|
||||||
@location(5) size: f32,
|
@location(5) dim: vec2<f32>,
|
||||||
@location(6) color_transform: vec4<f32>,
|
@location(6) color_transform: vec4<f32>,
|
||||||
@location(7) texture_index: vec2<u32>,
|
@location(7) texture_index: vec2<u32>,
|
||||||
@location(8) texture_fade: f32,
|
@location(8) texture_fade: f32,
|
||||||
|
@ -44,30 +44,26 @@ fn transform_vertex(
|
||||||
texture_index: u32,
|
texture_index: u32,
|
||||||
) -> vec4<f32> {
|
) -> vec4<f32> {
|
||||||
|
|
||||||
|
// Window size in logical pixels
|
||||||
let window_dim = (
|
let window_dim = (
|
||||||
vec2(global_data.window_size_w, global_data.window_size_h)
|
vec2(global_data.window_size_w, global_data.window_size_h)
|
||||||
/ global_data.window_scale
|
/ global_data.window_scale
|
||||||
);
|
);
|
||||||
|
|
||||||
let scale = instance.size / window_dim.y;
|
let scale = instance.dim.y / window_dim.y;
|
||||||
let aspect = (
|
|
||||||
global_atlas[instance.texture_index.x].width /
|
|
||||||
global_atlas[instance.texture_index.x].height
|
|
||||||
);
|
|
||||||
|
|
||||||
// Apply scale and sprite aspect
|
|
||||||
// Note that our mesh starts centered at (0, 0). This is important!
|
|
||||||
var pos: vec2<f32> = vec2(
|
var pos: vec2<f32> = vec2(
|
||||||
vertex_position.x * scale * aspect,
|
vertex_position.x * scale * (instance.dim.x / instance.dim.y),
|
||||||
vertex_position.y * scale
|
vertex_position.y * scale
|
||||||
);
|
);
|
||||||
|
|
||||||
// Apply rotation (and adjust sprite angle, since sprites point north)
|
|
||||||
pos = mat2x2(
|
pos = mat2x2(
|
||||||
vec2(cos(instance.angle - 1.5708), sin(instance.angle - 1.5708)),
|
vec2(cos(instance.angle - 1.5708), sin(instance.angle - 1.5708)),
|
||||||
vec2(-sin(instance.angle - 1.5708), cos(instance.angle - 1.5708))
|
vec2(-sin(instance.angle - 1.5708), cos(instance.angle - 1.5708))
|
||||||
) * pos;
|
) * pos;
|
||||||
|
|
||||||
|
// Apply rotation (and adjust sprite angle, since sprites point north)
|
||||||
|
|
||||||
// Correct for screen aspect, preserving height
|
// Correct for screen aspect, preserving height
|
||||||
pos = vec2(
|
pos = vec2(
|
||||||
pos.x / global_data.window_aspect,
|
pos.x / global_data.window_aspect,
|
||||||
|
@ -77,7 +73,7 @@ fn transform_vertex(
|
||||||
pos = pos + anchor(
|
pos = pos + anchor(
|
||||||
instance.anchor,
|
instance.anchor,
|
||||||
instance.position,
|
instance.position,
|
||||||
vec2(instance.size * aspect, instance.size)
|
instance.dim
|
||||||
);
|
);
|
||||||
|
|
||||||
return vec4<f32>(pos, 0.0, 1.0);
|
return vec4<f32>(pos, 0.0, 1.0);
|
||||||
|
@ -90,9 +86,6 @@ fn vertex_main(
|
||||||
) -> VertexOutput {
|
) -> VertexOutput {
|
||||||
var out: VertexOutput;
|
var out: VertexOutput;
|
||||||
|
|
||||||
// TODO: this will break if we try to use texture 0.
|
|
||||||
// implement animations for ui sprites & fix that here.
|
|
||||||
|
|
||||||
// Pick texture size by the size of the visible texture
|
// Pick texture size by the size of the visible texture
|
||||||
// (texture index 0 is special, it's the "hidden" texture)
|
// (texture index 0 is special, it's the "hidden" texture)
|
||||||
if instance.texture_index.x == 0u && instance.texture_index.y == 0u {
|
if instance.texture_index.x == 0u && instance.texture_index.y == 0u {
|
||||||
|
|
|
@ -63,7 +63,7 @@ impl Radar {
|
||||||
anchor: PositionAnchor::NwNw.to_int(),
|
anchor: PositionAnchor::NwNw.to_int(),
|
||||||
position: [10.0, -10.0],
|
position: [10.0, -10.0],
|
||||||
angle: 0.0,
|
angle: 0.0,
|
||||||
size: radar_size,
|
dim: [sprite.aspect * radar_size, radar_size],
|
||||||
color: [1.0, 1.0, 1.0, 1.0],
|
color: [1.0, 1.0, 1.0, 1.0],
|
||||||
texture_index: [texture_a, texture_a],
|
texture_index: [texture_a, texture_a],
|
||||||
texture_fade: 1.0,
|
texture_fade: 1.0,
|
||||||
|
@ -96,7 +96,7 @@ impl Radar {
|
||||||
+ (d * (radar_size / 2.0)))
|
+ (d * (radar_size / 2.0)))
|
||||||
.into(),
|
.into(),
|
||||||
angle: o.angle,
|
angle: o.angle,
|
||||||
size,
|
dim: [sprite.aspect * size, size],
|
||||||
color: [0.5, 0.5, 0.5, 1.0],
|
color: [0.5, 0.5, 0.5, 1.0],
|
||||||
texture_index: [texture_a, texture_a],
|
texture_index: [texture_a, texture_a],
|
||||||
texture_fade: 1.0,
|
texture_fade: 1.0,
|
||||||
|
@ -154,7 +154,7 @@ impl Radar {
|
||||||
anchor: PositionAnchor::NwC.to_int(),
|
anchor: PositionAnchor::NwC.to_int(),
|
||||||
position: position.into(),
|
position: position.into(),
|
||||||
angle: player_ship.rigidbody.rotation().angle(),
|
angle: player_ship.rigidbody.rotation().angle(),
|
||||||
size,
|
dim: [sprite.aspect * size, size],
|
||||||
color,
|
color,
|
||||||
texture_index: [texture_a, texture_a],
|
texture_index: [texture_a, texture_a],
|
||||||
texture_fade: 1.0,
|
texture_fade: 1.0,
|
||||||
|
@ -186,7 +186,7 @@ impl Radar {
|
||||||
)
|
)
|
||||||
.into(),
|
.into(),
|
||||||
angle: to_radians(90.0),
|
angle: to_radians(90.0),
|
||||||
size,
|
dim: [sprite.aspect * size, size],
|
||||||
color,
|
color,
|
||||||
texture_index: [texture_a, texture_a],
|
texture_index: [texture_a, texture_a],
|
||||||
texture_fade: 1.0,
|
texture_fade: 1.0,
|
||||||
|
@ -201,7 +201,7 @@ impl Radar {
|
||||||
)
|
)
|
||||||
.into(),
|
.into(),
|
||||||
angle: to_radians(180.0),
|
angle: to_radians(180.0),
|
||||||
size,
|
dim: [sprite.aspect * size, size],
|
||||||
color,
|
color,
|
||||||
texture_index: [texture_a, texture_a],
|
texture_index: [texture_a, texture_a],
|
||||||
texture_fade: 1.0,
|
texture_fade: 1.0,
|
||||||
|
@ -216,7 +216,7 @@ impl Radar {
|
||||||
)
|
)
|
||||||
.into(),
|
.into(),
|
||||||
angle: to_radians(270.0),
|
angle: to_radians(270.0),
|
||||||
size,
|
dim: [sprite.aspect * size, size],
|
||||||
color,
|
color,
|
||||||
texture_index: [texture_a, texture_a],
|
texture_index: [texture_a, texture_a],
|
||||||
texture_fade: 1.0,
|
texture_fade: 1.0,
|
||||||
|
@ -231,7 +231,7 @@ impl Radar {
|
||||||
)
|
)
|
||||||
.into(),
|
.into(),
|
||||||
angle: to_radians(0.0),
|
angle: to_radians(0.0),
|
||||||
size,
|
dim: [sprite.aspect * size, size],
|
||||||
color,
|
color,
|
||||||
texture_index: [texture_a, texture_a],
|
texture_index: [texture_a, texture_a],
|
||||||
texture_fade: 1.0,
|
texture_fade: 1.0,
|
||||||
|
@ -254,7 +254,7 @@ impl Radar {
|
||||||
anchor: PositionAnchor::NwC.to_int(),
|
anchor: PositionAnchor::NwC.to_int(),
|
||||||
position: position.into(),
|
position: position.into(),
|
||||||
angle,
|
angle,
|
||||||
size: 10.0,
|
dim: [10.0 * sprite.aspect, 10.0],
|
||||||
color: [1.0, 1.0, 1.0, 1f32.min((m - 200.0) / 400.0)],
|
color: [1.0, 1.0, 1.0, 1f32.min((m - 200.0) / 400.0)],
|
||||||
texture_index: [texture_a, texture_a],
|
texture_index: [texture_a, texture_a],
|
||||||
texture_fade: 1.0,
|
texture_fade: 1.0,
|
||||||
|
|
|
@ -68,7 +68,7 @@ impl Status {
|
||||||
anchor: PositionAnchor::NeNe.to_int(),
|
anchor: PositionAnchor::NeNe.to_int(),
|
||||||
position: [-10.0, -10.0],
|
position: [-10.0, -10.0],
|
||||||
angle: 0.0,
|
angle: 0.0,
|
||||||
size: 200.0,
|
dim: [sprite.aspect * 200.0, 200.0],
|
||||||
color: [1.0, 1.0, 1.0, 1.0],
|
color: [1.0, 1.0, 1.0, 1.0],
|
||||||
texture_index: [texture_a, texture_a],
|
texture_index: [texture_a, texture_a],
|
||||||
texture_fade: 1.0,
|
texture_fade: 1.0,
|
||||||
|
|
|
@ -1,12 +1,11 @@
|
||||||
use galactica_content::{Content, SystemObject, SystemObjectHandle};
|
use galactica_content::{Content, SystemObject, SystemObjectHandle};
|
||||||
use galactica_system::{data::ShipState, phys::PhysSimShipHandle};
|
use galactica_system::{data::ShipState, phys::PhysSimShipHandle};
|
||||||
use glyphon::{cosmic_text::Align, Attrs, Color, Metrics, TextArea, Weight};
|
use glyphon::{Attrs, TextArea, Weight};
|
||||||
use nalgebra::{Point2, Vector2};
|
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
ui::{
|
ui::{
|
||||||
manager::{UiScene, UiSceneStepResult, UiScenes},
|
manager::{UiScene, UiSceneStepResult, UiScenes},
|
||||||
util::{SpriteRect, UiSprite, UiTextArea},
|
util::{UiSprite, UiTextArea},
|
||||||
},
|
},
|
||||||
RenderInput, RenderState,
|
RenderInput, RenderState,
|
||||||
};
|
};
|
||||||
|
@ -45,35 +44,8 @@ impl UiLandedScene {
|
||||||
current_object: None,
|
current_object: None,
|
||||||
leftclick_down: false,
|
leftclick_down: false,
|
||||||
|
|
||||||
description: UiTextArea::new(
|
description: UiTextArea::from(ct, state, &ct.get_ui().landed_planet_desc),
|
||||||
ct,
|
title: UiTextArea::from(ct, state, &ct.get_ui().landed_planet_name),
|
||||||
state,
|
|
||||||
frame.get_sprite(),
|
|
||||||
Point2::new(0.0, 0.0),
|
|
||||||
frame.get_height(),
|
|
||||||
SpriteRect {
|
|
||||||
pos: Point2::new(25.831, 284.883) / 512.0,
|
|
||||||
dim: Vector2::new(433.140, 97.220) / 512.0,
|
|
||||||
},
|
|
||||||
Metrics::new(16.0, 18.0),
|
|
||||||
Color::rgb(255, 255, 255),
|
|
||||||
Align::Left,
|
|
||||||
),
|
|
||||||
|
|
||||||
title: UiTextArea::new(
|
|
||||||
ct,
|
|
||||||
state,
|
|
||||||
frame.get_sprite(),
|
|
||||||
Point2::new(0.0, 0.0),
|
|
||||||
frame.get_height(),
|
|
||||||
SpriteRect {
|
|
||||||
pos: Point2::new(165.506, 82.0) / 512.0,
|
|
||||||
dim: Vector2::new(74.883, 17.0) / 512.0,
|
|
||||||
},
|
|
||||||
Metrics::new(19.0, 19.0),
|
|
||||||
Color::rgb(255, 255, 255),
|
|
||||||
Align::Center,
|
|
||||||
),
|
|
||||||
|
|
||||||
frame,
|
frame,
|
||||||
landscape,
|
landscape,
|
||||||
|
@ -105,15 +77,14 @@ impl UiLandedScene {
|
||||||
|
|
||||||
impl<'this> UiScene<'this> for UiLandedScene {
|
impl<'this> UiScene<'this> for UiLandedScene {
|
||||||
fn step(&mut self, input: &RenderInput, state: &mut RenderState) -> UiSceneStepResult {
|
fn step(&mut self, input: &RenderInput, state: &mut RenderState) -> UiSceneStepResult {
|
||||||
let frame_rect = Some(self.frame.get_rect(input));
|
self.button.step(input, state);
|
||||||
self.button.step(input, state, frame_rect);
|
self.landscape.step(input, state);
|
||||||
self.landscape.step(input, state, frame_rect);
|
self.frame.step(input, state);
|
||||||
self.frame.step(input, state, None);
|
|
||||||
|
|
||||||
let mut new_scene = None;
|
let mut new_scene = None;
|
||||||
if input.player.input.pressed_leftclick() && !self.leftclick_down {
|
if input.player.input.pressed_leftclick() && !self.leftclick_down {
|
||||||
self.leftclick_down = true;
|
self.leftclick_down = true;
|
||||||
if self.button.contains_mouse(input, state, frame_rect) {
|
if self.button.contains_mouse(input, state) {
|
||||||
new_scene = Some(UiScenes::Outfitter(UiOutfitterScene::new(input.ct, state)));
|
new_scene = Some(UiScenes::Outfitter(UiOutfitterScene::new(input.ct, state)));
|
||||||
}
|
}
|
||||||
} else if !input.player.input.pressed_leftclick() {
|
} else if !input.player.input.pressed_leftclick() {
|
||||||
|
@ -147,19 +118,18 @@ impl<'this> UiScene<'this> for UiLandedScene {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Draw elements
|
// Draw elements
|
||||||
let frame_rect = Some(self.frame.get_rect(input));
|
self.button.push_to_buffer(input, state);
|
||||||
self.button.push_to_buffer(input, state, frame_rect);
|
self.landscape.push_to_buffer(input, state);
|
||||||
self.landscape.push_to_buffer(input, state, frame_rect);
|
self.frame.push_to_buffer(input, state);
|
||||||
self.frame.push_to_buffer(input, state, None);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_textareas(
|
fn get_textareas(
|
||||||
&'this self,
|
&'this self,
|
||||||
v: &mut Vec<TextArea<'this>>,
|
v: &mut Vec<TextArea<'this>>,
|
||||||
input: &RenderInput,
|
_input: &RenderInput,
|
||||||
state: &RenderState,
|
state: &RenderState,
|
||||||
) {
|
) {
|
||||||
v.push(self.description.get_textarea(input, state));
|
v.push(self.description.get_textarea(state));
|
||||||
v.push(self.title.get_textarea(input, state));
|
v.push(self.title.get_textarea(state));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use galactica_content::{Content, SystemObject, SystemObjectHandle};
|
use galactica_content::{Content, SystemObject, SystemObjectHandle, UiPositionAnchor};
|
||||||
use galactica_system::{data::ShipState, phys::PhysSimShipHandle};
|
use galactica_system::{data::ShipState, phys::PhysSimShipHandle};
|
||||||
use glyphon::{cosmic_text::Align, Attrs, Color, Metrics, TextArea, Weight};
|
use glyphon::{cosmic_text::Align, Attrs, Color, Metrics, TextArea, Weight};
|
||||||
use nalgebra::{Point2, Vector2};
|
use nalgebra::{Point2, Vector2};
|
||||||
|
@ -15,9 +15,9 @@ use super::UiLandedScene;
|
||||||
|
|
||||||
pub struct UiOutfitterScene {
|
pub struct UiOutfitterScene {
|
||||||
// UI elements
|
// UI elements
|
||||||
description: UiTextArea,
|
se_box: UiSprite,
|
||||||
landscape: UiSprite,
|
exit_button: UiSprite,
|
||||||
button: UiSprite,
|
exit_text: UiTextArea,
|
||||||
|
|
||||||
/// What object we're displaying currently.
|
/// What object we're displaying currently.
|
||||||
/// Whenever this changes, we need to reflow text.
|
/// Whenever this changes, we need to reflow text.
|
||||||
|
@ -30,55 +30,39 @@ pub struct UiOutfitterScene {
|
||||||
|
|
||||||
impl UiOutfitterScene {
|
impl UiOutfitterScene {
|
||||||
pub fn new(ct: &Content, state: &mut RenderState) -> Self {
|
pub fn new(ct: &Content, state: &mut RenderState) -> Self {
|
||||||
let button = UiSprite::new(
|
let exit_button = UiSprite::from(ct, &ct.get_ui().outfitter_exit_button);
|
||||||
ct,
|
let se_box = UiSprite::from(ct, &ct.get_ui().outfitter_se_box);
|
||||||
ct.get_sprite_handle("ui::button"),
|
|
||||||
None,
|
|
||||||
SpriteRect {
|
|
||||||
pos: Point2::new(0.0, 0.0),
|
|
||||||
dim: Vector2::new(200.0, 200.0),
|
|
||||||
},
|
|
||||||
None,
|
|
||||||
None,
|
|
||||||
);
|
|
||||||
|
|
||||||
let landscape = UiSprite::from_with_sprite(
|
|
||||||
ct,
|
|
||||||
&ct.get_ui().landed_landscape,
|
|
||||||
ct.get_sprite_handle("ui::landscape::test"),
|
|
||||||
);
|
|
||||||
|
|
||||||
let s = Self {
|
let s = Self {
|
||||||
// height of element in logical pixels
|
// height of element in logical pixels
|
||||||
current_object: None,
|
current_object: None,
|
||||||
leftclick_down: false,
|
leftclick_down: false,
|
||||||
|
|
||||||
description: UiTextArea::new(
|
exit_text: UiTextArea::new(
|
||||||
ct,
|
ct,
|
||||||
state,
|
state,
|
||||||
button.get_sprite(),
|
|
||||||
Point2::new(0.0, 0.0),
|
|
||||||
800.0,
|
|
||||||
SpriteRect {
|
SpriteRect {
|
||||||
pos: Point2::new(25.831, 284.883) / 512.0,
|
pos: Point2::new(0.0, 0.0),
|
||||||
dim: Vector2::new(433.140, 97.220) / 512.0,
|
dim: Vector2::new(200.0, 200.0), // TODO: do this better
|
||||||
|
anchor_self: UiPositionAnchor::Center,
|
||||||
|
anchor_parent: UiPositionAnchor::Center,
|
||||||
},
|
},
|
||||||
Metrics::new(16.0, 18.0),
|
Metrics::new(16.0, 18.0),
|
||||||
Color::rgb(255, 255, 255),
|
Color::rgb(255, 255, 255),
|
||||||
Align::Left,
|
Align::Center,
|
||||||
),
|
),
|
||||||
|
|
||||||
landscape,
|
se_box,
|
||||||
button,
|
exit_button,
|
||||||
};
|
};
|
||||||
|
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn reflow(&mut self, planet: &SystemObject, state: &mut RenderState) {
|
fn reflow(&mut self, planet: &SystemObject, state: &mut RenderState) {
|
||||||
self.description.set_text(
|
self.exit_text.set_text(
|
||||||
state,
|
state,
|
||||||
&planet.desc,
|
"Exit",
|
||||||
Attrs::new()
|
Attrs::new()
|
||||||
.weight(Weight::NORMAL)
|
.weight(Weight::NORMAL)
|
||||||
.family(glyphon::Family::SansSerif),
|
.family(glyphon::Family::SansSerif),
|
||||||
|
@ -90,13 +74,13 @@ impl UiOutfitterScene {
|
||||||
|
|
||||||
impl<'this> UiScene<'this> for UiOutfitterScene {
|
impl<'this> UiScene<'this> for UiOutfitterScene {
|
||||||
fn step(&mut self, input: &RenderInput, state: &mut RenderState) -> UiSceneStepResult {
|
fn step(&mut self, input: &RenderInput, state: &mut RenderState) -> UiSceneStepResult {
|
||||||
self.button.step(input, state, None);
|
self.se_box.step(input, state);
|
||||||
self.landscape.step(input, state, None);
|
self.exit_button.step(input, state);
|
||||||
|
|
||||||
let mut new_scene = None;
|
let mut new_scene = None;
|
||||||
if input.player.input.pressed_leftclick() && !self.leftclick_down {
|
if input.player.input.pressed_leftclick() && !self.leftclick_down {
|
||||||
self.leftclick_down = true;
|
self.leftclick_down = true;
|
||||||
if self.button.contains_mouse(input, state, None) {
|
if self.exit_button.contains_mouse(input, state) {
|
||||||
new_scene = Some(UiScenes::Landed(UiLandedScene::new(input.ct, state)));
|
new_scene = Some(UiScenes::Landed(UiLandedScene::new(input.ct, state)));
|
||||||
}
|
}
|
||||||
} else if !input.player.input.pressed_leftclick() {
|
} else if !input.player.input.pressed_leftclick() {
|
||||||
|
@ -130,16 +114,16 @@ impl<'this> UiScene<'this> for UiOutfitterScene {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Draw elements
|
// Draw elements
|
||||||
self.button.push_to_buffer(input, state, None);
|
self.se_box.push_to_buffer(input, state);
|
||||||
self.landscape.push_to_buffer(input, state, None);
|
self.exit_button.push_to_buffer(input, state);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_textareas(
|
fn get_textareas(
|
||||||
&'this self,
|
&'this self,
|
||||||
v: &mut Vec<TextArea<'this>>,
|
v: &mut Vec<TextArea<'this>>,
|
||||||
input: &RenderInput,
|
_input: &RenderInput,
|
||||||
state: &RenderState,
|
state: &RenderState,
|
||||||
) {
|
) {
|
||||||
v.push(self.description.get_textarea(input, state));
|
v.push(self.exit_text.get_textarea(state));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,14 +1,16 @@
|
||||||
mod sprite;
|
mod sprite;
|
||||||
mod textarea;
|
mod textarea;
|
||||||
|
|
||||||
|
use galactica_content::UiPositionAnchor;
|
||||||
pub(super) use sprite::UiSprite;
|
pub(super) use sprite::UiSprite;
|
||||||
pub(super) use textarea::UiTextArea;
|
pub(super) use textarea::UiTextArea;
|
||||||
|
|
||||||
use nalgebra::{Point2, Vector2};
|
use nalgebra::{Point2, Vector2};
|
||||||
|
use winit::dpi::LogicalSize;
|
||||||
|
|
||||||
use crate::{RenderInput, RenderState};
|
use crate::{RenderInput, RenderState};
|
||||||
|
|
||||||
/// Represents a rectangular region inside a sprite.
|
/// Represents a rectangular region
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
pub(crate) struct SpriteRect {
|
pub(crate) struct SpriteRect {
|
||||||
/// The position of the top-left corner of this rectangle, in fractional units.
|
/// The position of the top-left corner of this rectangle, in fractional units.
|
||||||
|
@ -18,17 +20,143 @@ pub(crate) struct SpriteRect {
|
||||||
/// The width and height of this rectangle, in fractional units.
|
/// The width and height of this rectangle, in fractional units.
|
||||||
/// 1.0 will be as tall as the sprite, 0.5 will be half as tall
|
/// 1.0 will be as tall as the sprite, 0.5 will be half as tall
|
||||||
pub dim: Vector2<f32>,
|
pub dim: Vector2<f32>,
|
||||||
|
|
||||||
|
/// How to compute this rectangle's coordinates relative to its parent
|
||||||
|
pub anchor_self: UiPositionAnchor,
|
||||||
|
|
||||||
|
/// How to compute this rectangle's coordinates relative to its parent
|
||||||
|
pub anchor_parent: UiPositionAnchor,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SpriteRect {
|
impl SpriteRect {
|
||||||
/// Northeast corner of this rect
|
/*
|
||||||
pub fn ne_corner(&self) -> Point2<f32> {
|
pub fn to_centered_relative(&self, parent: SpriteRectCentered) -> SpriteRectCentered {
|
||||||
self.pos + Vector2::new(-self.dim.x, self.dim.y) / 2.0
|
let dim = Vector2::new(self.dim.x * parent.dim.x, self.dim.y * parent.dim.y);
|
||||||
|
|
||||||
|
let pos = Vector2::new(self.pos.x * parent.dim.x, self.pos.y * parent.dim.y);
|
||||||
|
|
||||||
|
let mut zero = match self.anchor_parent {
|
||||||
|
PositionAnchor::Center => parent.pos,
|
||||||
|
|
||||||
|
PositionAnchor::NorthWest => Point2::new(
|
||||||
|
parent.pos.x - (parent.dim.x / 2.0),
|
||||||
|
parent.pos.y + (parent.dim.y / 2.0),
|
||||||
|
),
|
||||||
|
|
||||||
|
PositionAnchor::SouthWest => Point2::new(
|
||||||
|
parent.pos.x - (parent.dim.x / 2.0),
|
||||||
|
parent.pos.y - (parent.dim.y / 2.0),
|
||||||
|
),
|
||||||
|
|
||||||
|
PositionAnchor::NorthEast => Point2::new(
|
||||||
|
parent.pos.x + (parent.dim.x / 2.0),
|
||||||
|
parent.pos.y + (parent.dim.y / 2.0),
|
||||||
|
),
|
||||||
|
|
||||||
|
PositionAnchor::SouthEast => Point2::new(
|
||||||
|
parent.pos.x + (parent.dim.x / 2.0),
|
||||||
|
parent.pos.y - (parent.dim.y / 2.0),
|
||||||
|
),
|
||||||
|
};
|
||||||
|
|
||||||
|
match self.anchor_self {
|
||||||
|
PositionAnchor::Center => {}
|
||||||
|
|
||||||
|
PositionAnchor::NorthWest => {
|
||||||
|
zero += Vector2::new(dim.x, -dim.y) / 2.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Southwest corner of this rect
|
PositionAnchor::NorthEast => {
|
||||||
pub fn sw_corner(&self) -> Point2<f32> {
|
zero += Vector2::new(-dim.x, -dim.y) / 2.0;
|
||||||
self.pos + Vector2::new(self.dim.x, -self.dim.y) / 2.0
|
}
|
||||||
|
|
||||||
|
PositionAnchor::SouthWest => {
|
||||||
|
zero += Vector2::new(dim.x, dim.y) / 2.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
PositionAnchor::SouthEast => {
|
||||||
|
zero += Vector2::new(-dim.x, dim.y) / 2.0;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return SpriteRectCentered {
|
||||||
|
dim,
|
||||||
|
pos: zero + pos,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
/// Convert this rectangle to a centered rectangle.
|
||||||
|
pub fn to_centered(&self, state: &RenderState) -> CenteredSpriteRect {
|
||||||
|
let w: LogicalSize<f32> = state.window_size.to_logical(state.window.scale_factor());
|
||||||
|
let w = Vector2::new(w.width, w.height);
|
||||||
|
|
||||||
|
let mut pos = self.pos;
|
||||||
|
let dim = self.dim;
|
||||||
|
|
||||||
|
// Origin
|
||||||
|
match self.anchor_parent {
|
||||||
|
UiPositionAnchor::Center => {}
|
||||||
|
|
||||||
|
UiPositionAnchor::NorthWest => {
|
||||||
|
pos += Vector2::new(-w.x, w.y) / 2.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
UiPositionAnchor::SouthWest => {
|
||||||
|
pos += Vector2::new(-w.x, -w.y) / 2.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
UiPositionAnchor::NorthEast => {
|
||||||
|
pos += Vector2::new(w.x, w.y) / 2.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
UiPositionAnchor::SouthEast => {
|
||||||
|
pos += Vector2::new(w.x, -w.y) / 2.0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Offset for self dimensions
|
||||||
|
match self.anchor_self {
|
||||||
|
UiPositionAnchor::Center => {}
|
||||||
|
|
||||||
|
UiPositionAnchor::NorthWest => {
|
||||||
|
pos += Vector2::new(dim.x, -dim.y) / 2.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
UiPositionAnchor::NorthEast => {
|
||||||
|
pos += Vector2::new(-dim.x, -dim.y) / 2.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
UiPositionAnchor::SouthWest => {
|
||||||
|
pos += Vector2::new(dim.x, dim.y) / 2.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
UiPositionAnchor::SouthEast => {
|
||||||
|
pos += Vector2::new(dim.x, dim.y) / 2.0;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return CenteredSpriteRect { pos, dim };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Represents a rectangular region, in absolute coordinates relative to the screen center.
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
pub(crate) struct CenteredSpriteRect {
|
||||||
|
/// The position of the top-left corner of this rectangle, in fractional units.
|
||||||
|
/// (0.0 is left edge of sprite, 1.0 is right edge)
|
||||||
|
pub pos: Point2<f32>,
|
||||||
|
|
||||||
|
/// The width and height of this rectangle, in fractional units.
|
||||||
|
/// 1.0 will be as tall as the sprite, 0.5 will be half as tall
|
||||||
|
pub dim: Vector2<f32>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CenteredSpriteRect {
|
||||||
|
pub fn contains_point(&self, pt: Point2<f32>) -> bool {
|
||||||
|
let ne = self.pos + Vector2::new(-self.dim.x, self.dim.y) / 2.0;
|
||||||
|
let sw = self.pos + Vector2::new(self.dim.x, -self.dim.y) / 2.0;
|
||||||
|
return (pt.y < ne.y && pt.y > sw.y) && (pt.x > ne.x && pt.x < sw.x);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,8 +2,8 @@ use galactica_content::{Content, SectionEdge, SpriteAutomaton, SpriteHandle, UiS
|
||||||
use galactica_util::to_radians;
|
use galactica_util::to_radians;
|
||||||
use nalgebra::{Point2, Vector2};
|
use nalgebra::{Point2, Vector2};
|
||||||
|
|
||||||
use super::SpriteRect;
|
use super::{CenteredSpriteRect, SpriteRect};
|
||||||
use crate::{vertexbuffer::types::UiInstance, PositionAnchor, RenderInput, RenderState};
|
use crate::{vertexbuffer::types::UiInstance, RenderInput, RenderState};
|
||||||
|
|
||||||
pub struct UiSprite {
|
pub struct UiSprite {
|
||||||
pub anim: SpriteAutomaton,
|
pub anim: SpriteAutomaton,
|
||||||
|
@ -49,22 +49,21 @@ impl UiSprite {
|
||||||
sprite,
|
sprite,
|
||||||
ui.mask,
|
ui.mask,
|
||||||
SpriteRect {
|
SpriteRect {
|
||||||
pos: *ui.rect.get_pos(),
|
pos: ui.rect.pos,
|
||||||
dim: *ui.rect.get_dim(),
|
dim: ui.rect.dim,
|
||||||
|
anchor_self: ui.rect.anchor_self,
|
||||||
|
anchor_parent: ui.rect.anchor_parent,
|
||||||
},
|
},
|
||||||
ui.on_mouse_enter,
|
ui.on_mouse_enter,
|
||||||
ui.on_mouse_leave,
|
ui.on_mouse_leave,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn step(&mut self, input: &RenderInput, state: &RenderState, parent: Option<SpriteRect>) {
|
pub fn step(&mut self, input: &RenderInput, state: &RenderState) {
|
||||||
if self.contains_mouse(input, state, parent)
|
if self.contains_mouse(input, state) && !self.has_mouse && self.on_mouse_enter.is_some() {
|
||||||
&& !self.has_mouse
|
|
||||||
&& self.on_mouse_enter.is_some()
|
|
||||||
{
|
|
||||||
self.has_mouse = true;
|
self.has_mouse = true;
|
||||||
self.anim.jump_to(input.ct, self.on_mouse_enter.unwrap())
|
self.anim.jump_to(input.ct, self.on_mouse_enter.unwrap())
|
||||||
} else if !self.contains_mouse(input, state, parent)
|
} else if !self.contains_mouse(input, state)
|
||||||
&& self.has_mouse
|
&& self.has_mouse
|
||||||
&& self.on_mouse_leave.is_some()
|
&& self.on_mouse_leave.is_some()
|
||||||
{
|
{
|
||||||
|
@ -75,13 +74,8 @@ impl UiSprite {
|
||||||
self.anim.step(input.ct, input.time_since_last_run);
|
self.anim.step(input.ct, input.time_since_last_run);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn contains_mouse(
|
pub fn contains_mouse(&self, input: &RenderInput, state: &RenderState) -> bool {
|
||||||
&self,
|
let rect = self.get_rect(state);
|
||||||
input: &RenderInput,
|
|
||||||
state: &RenderState,
|
|
||||||
parent: Option<SpriteRect>,
|
|
||||||
) -> bool {
|
|
||||||
let rect = self.get_relative_rect(input, parent);
|
|
||||||
|
|
||||||
let fac = state.window.scale_factor() as f32;
|
let fac = state.window.scale_factor() as f32;
|
||||||
let window_size = Vector2::new(
|
let window_size = Vector2::new(
|
||||||
|
@ -90,69 +84,40 @@ impl UiSprite {
|
||||||
);
|
);
|
||||||
|
|
||||||
let pos = input.player.input.get_mouse_pos();
|
let pos = input.player.input.get_mouse_pos();
|
||||||
let mouse_pos = Vector2::new(
|
let mouse_pos = Point2::new(
|
||||||
pos.x / fac - window_size.x / 2.0,
|
pos.x / fac - window_size.x / 2.0,
|
||||||
window_size.y / 2.0 - pos.y / fac,
|
window_size.y / 2.0 - pos.y / fac,
|
||||||
);
|
);
|
||||||
|
|
||||||
let ne = rect.ne_corner();
|
return rect.contains_point(mouse_pos);
|
||||||
let sw = rect.sw_corner();
|
|
||||||
return (mouse_pos.y < ne.y && mouse_pos.y > sw.y)
|
|
||||||
&& (mouse_pos.x > ne.x && mouse_pos.x < sw.x);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_rect(&self, input: &RenderInput) -> SpriteRect {
|
pub fn get_rect(
|
||||||
let pos = Point2::new(self.rect.pos.x, self.rect.pos.y);
|
&self,
|
||||||
let dim = Vector2::new(
|
state: &RenderState,
|
||||||
self.rect.dim.y * input.ct.get_sprite(self.anim.get_sprite()).aspect,
|
//parent: Option<SpriteRectCentered>,
|
||||||
self.rect.dim.y,
|
) -> CenteredSpriteRect {
|
||||||
);
|
/*
|
||||||
|
|
||||||
return SpriteRect { dim, pos };
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_relative_rect(&self, input: &RenderInput, parent: Option<SpriteRect>) -> SpriteRect {
|
|
||||||
if let Some(parent) = parent {
|
if let Some(parent) = parent {
|
||||||
let zero = Point2::new(
|
return self.rect.to_centered_relative(parent);
|
||||||
parent.pos.x - (parent.dim.x / 2.0),
|
|
||||||
parent.pos.y + (parent.dim.y / 2.0),
|
|
||||||
);
|
|
||||||
|
|
||||||
let mut pos = zero
|
|
||||||
+ Vector2::new(
|
|
||||||
self.rect.pos.x * parent.dim.x,
|
|
||||||
-self.rect.pos.y * parent.dim.y,
|
|
||||||
);
|
|
||||||
let dim = Vector2::new(
|
|
||||||
self.rect.dim.x * parent.dim.x,
|
|
||||||
self.rect.dim.y * parent.dim.y,
|
|
||||||
);
|
|
||||||
|
|
||||||
pos += Vector2::new(dim.x, -dim.y) / 2.0;
|
|
||||||
|
|
||||||
return SpriteRect { dim, pos };
|
|
||||||
} else {
|
} else {
|
||||||
return self.get_rect(input);
|
return self.rect.to_centered(state);
|
||||||
}
|
}*/
|
||||||
|
return self.rect.to_centered(state);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add this image to the gpu sprite buffer
|
/// Add this image to the gpu sprite buffer
|
||||||
pub fn push_to_buffer(
|
pub fn push_to_buffer(&self, input: &RenderInput, state: &mut RenderState) {
|
||||||
&self,
|
let rect = self.get_rect(state);
|
||||||
input: &RenderInput,
|
|
||||||
state: &mut RenderState,
|
|
||||||
parent: Option<SpriteRect>,
|
|
||||||
) {
|
|
||||||
let rect = self.get_relative_rect(input, parent.clone());
|
|
||||||
|
|
||||||
// TODO: use both dimensions,
|
// TODO: use both dimensions,
|
||||||
// not just height
|
// not just height
|
||||||
let anim_state = self.anim.get_texture_idx();
|
let anim_state = self.anim.get_texture_idx();
|
||||||
state.push_ui_buffer(UiInstance {
|
state.push_ui_buffer(UiInstance {
|
||||||
anchor: PositionAnchor::CC.to_int(),
|
anchor: crate::PositionAnchor::CC.to_int(),
|
||||||
position: rect.pos.into(),
|
position: rect.pos.into(),
|
||||||
angle: to_radians(90.0),
|
angle: to_radians(90.0),
|
||||||
size: rect.dim.y,
|
dim: rect.dim.into(),
|
||||||
color: [1.0, 1.0, 1.0, 1.0],
|
color: [1.0, 1.0, 1.0, 1.0],
|
||||||
texture_index: anim_state.texture_index(),
|
texture_index: anim_state.texture_index(),
|
||||||
texture_fade: anim_state.fade,
|
texture_fade: anim_state.fade,
|
||||||
|
@ -166,12 +131,4 @@ impl UiSprite {
|
||||||
.unwrap_or([0, 0]),
|
.unwrap_or([0, 0]),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_sprite(&self) -> SpriteHandle {
|
|
||||||
self.anim.get_sprite()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_height(&self) -> f32 {
|
|
||||||
self.rect.dim.y
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,22 +1,12 @@
|
||||||
use galactica_content::{Content, SpriteHandle};
|
use galactica_content::{Content, UiTextAlign, UiTextConfig};
|
||||||
use glyphon::{cosmic_text::Align, Attrs, Buffer, Color, Metrics, Shaping, TextArea, TextBounds};
|
use glyphon::{cosmic_text::Align, Attrs, Buffer, Color, Metrics, Shaping, TextArea, TextBounds};
|
||||||
use nalgebra::{Point2, Vector2};
|
use nalgebra::Vector2;
|
||||||
|
|
||||||
use super::SpriteRect;
|
use super::SpriteRect;
|
||||||
use crate::{RenderInput, RenderState};
|
use crate::RenderState;
|
||||||
|
|
||||||
/// Represents a text area inside a sprite.
|
/// Represents a text area inside a sprite.
|
||||||
pub(crate) struct UiTextArea {
|
pub(crate) struct UiTextArea {
|
||||||
/// Parent sprite
|
|
||||||
sprite: SpriteHandle,
|
|
||||||
|
|
||||||
/// Position of parent sprite's center, in logical pixels,
|
|
||||||
/// with 0, 0 at the center of the screen
|
|
||||||
sprite_position: Point2<f32>,
|
|
||||||
|
|
||||||
/// Height of parent sprite, in logical pixels
|
|
||||||
sprite_size: f32,
|
|
||||||
|
|
||||||
/// Bounds of text area
|
/// Bounds of text area
|
||||||
rect: SpriteRect,
|
rect: SpriteRect,
|
||||||
|
|
||||||
|
@ -32,11 +22,8 @@ pub(crate) struct UiTextArea {
|
||||||
|
|
||||||
impl UiTextArea {
|
impl UiTextArea {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
ct: &Content,
|
_ct: &Content,
|
||||||
state: &mut RenderState,
|
state: &mut RenderState,
|
||||||
sprite: SpriteHandle,
|
|
||||||
sprite_position: Point2<f32>,
|
|
||||||
sprite_size: f32,
|
|
||||||
rect: SpriteRect,
|
rect: SpriteRect,
|
||||||
text_metrics: Metrics,
|
text_metrics: Metrics,
|
||||||
color: Color,
|
color: Color,
|
||||||
|
@ -44,25 +31,37 @@ impl UiTextArea {
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let mut s = Self {
|
let mut s = Self {
|
||||||
buffer: Buffer::new(&mut state.text_font_system, text_metrics),
|
buffer: Buffer::new(&mut state.text_font_system, text_metrics),
|
||||||
sprite_size: f32::NAN,
|
|
||||||
sprite,
|
|
||||||
sprite_position,
|
|
||||||
rect,
|
rect,
|
||||||
align,
|
align,
|
||||||
color,
|
color,
|
||||||
};
|
};
|
||||||
s.set_size(ct, state, sprite_size);
|
|
||||||
|
s.buffer.set_size(
|
||||||
|
&mut state.text_font_system,
|
||||||
|
s.rect.dim.x * state.window.scale_factor() as f32,
|
||||||
|
s.rect.dim.y * state.window.scale_factor() as f32,
|
||||||
|
);
|
||||||
|
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_size(&mut self, ct: &Content, state: &mut RenderState, sprite_size: f32) {
|
pub fn from(ct: &Content, state: &mut RenderState, ui: &UiTextConfig) -> Self {
|
||||||
self.sprite_size = sprite_size;
|
Self::new(
|
||||||
self.buffer.set_size(
|
ct,
|
||||||
&mut state.text_font_system,
|
state,
|
||||||
(self.rect.dim.x * self.sprite_size) * state.window.scale_factor() as f32,
|
SpriteRect {
|
||||||
(self.rect.dim.y * self.sprite_size * ct.get_sprite(self.sprite).aspect)
|
pos: ui.rect.pos,
|
||||||
* state.window.scale_factor() as f32,
|
dim: ui.rect.dim,
|
||||||
);
|
anchor_self: ui.rect.anchor_self,
|
||||||
|
anchor_parent: ui.rect.anchor_parent,
|
||||||
|
},
|
||||||
|
Metrics::new(ui.font_size, ui.line_height),
|
||||||
|
Color::rgb(255, 255, 255),
|
||||||
|
match ui.align {
|
||||||
|
UiTextAlign::Center => Align::Center,
|
||||||
|
UiTextAlign::Left => Align::Left,
|
||||||
|
},
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_text(&mut self, state: &mut RenderState, text: &str, attrs: Attrs) {
|
pub fn set_text(&mut self, state: &mut RenderState, text: &str, attrs: Attrs) {
|
||||||
|
@ -75,31 +74,27 @@ impl UiTextArea {
|
||||||
self.buffer.shape_until_scroll(&mut state.text_font_system);
|
self.buffer.shape_until_scroll(&mut state.text_font_system);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_textarea(&self, input: &RenderInput, state: &RenderState) -> TextArea {
|
pub fn get_textarea(&self, state: &RenderState) -> TextArea {
|
||||||
let h = self.sprite_size;
|
let rect = self.rect.to_centered(state);
|
||||||
let w = input.ct.get_sprite(self.sprite).aspect * h;
|
|
||||||
|
|
||||||
// Glypon works with physical pixels, so we must convert
|
// Glypon works with physical pixels, so we must do some conversion
|
||||||
let fac = state.window.scale_factor() as f32;
|
let fac = state.window.scale_factor() as f32;
|
||||||
|
let corner_ne = Vector2::new(
|
||||||
// All the units below are in logical pixels
|
(rect.pos.x - rect.dim.x / 2.0) * fac + state.window_size.width as f32 / 2.0,
|
||||||
let zero = Vector2::new(
|
state.window_size.height as f32 / 2.0 - (rect.pos.y * fac + rect.dim.y / 2.0),
|
||||||
(state.window_size.width as f32 / (2.0 * fac)) - (w / 2.0) + self.sprite_position.x,
|
|
||||||
(state.window_size.height as f32 / (2.0 * fac)) - (h / 2.0) - self.sprite_position.y,
|
|
||||||
);
|
);
|
||||||
let corner_ne = zero + Vector2::new(self.rect.pos.x * w, self.rect.pos.y * h);
|
let corner_sw = corner_ne + rect.dim * fac;
|
||||||
let corner_sw = corner_ne + Vector2::new(self.rect.dim.x * w, self.rect.dim.y * h);
|
|
||||||
|
|
||||||
TextArea {
|
TextArea {
|
||||||
buffer: &self.buffer,
|
buffer: &self.buffer,
|
||||||
top: corner_ne.y * fac,
|
top: corner_ne.y,
|
||||||
left: corner_ne.x * fac,
|
left: corner_ne.x,
|
||||||
scale: 1.0,
|
scale: 1.0,
|
||||||
bounds: TextBounds {
|
bounds: TextBounds {
|
||||||
top: (corner_ne.y * fac) as i32,
|
top: (corner_ne.y) as i32,
|
||||||
bottom: (corner_sw.y * fac) as i32,
|
bottom: (corner_sw.y) as i32,
|
||||||
left: (corner_ne.x * fac) as i32,
|
left: (corner_ne.x) as i32,
|
||||||
right: (corner_sw.x * fac) as i32,
|
right: (corner_sw.x) as i32,
|
||||||
},
|
},
|
||||||
default_color: self.color,
|
default_color: self.color,
|
||||||
}
|
}
|
||||||
|
|
|
@ -153,8 +153,9 @@ pub struct UiInstance {
|
||||||
/// The angle of this sprite, in radians
|
/// The angle of this sprite, in radians
|
||||||
pub angle: f32,
|
pub angle: f32,
|
||||||
|
|
||||||
/// The height of this sprite, in logical pixels
|
/// The width and height of this sprite, in logical pixels.
|
||||||
pub size: f32,
|
/// Aspect ratio is not automatically preserved.
|
||||||
|
pub dim: [f32; 2],
|
||||||
|
|
||||||
/// This lets us color ui sprites dynamically.
|
/// This lets us color ui sprites dynamically.
|
||||||
/// Each fragment's color is multiplied by this value.
|
/// Each fragment's color is multiplied by this value.
|
||||||
|
@ -200,33 +201,33 @@ impl BufferObject for UiInstance {
|
||||||
shader_location: 4,
|
shader_location: 4,
|
||||||
format: wgpu::VertexFormat::Float32,
|
format: wgpu::VertexFormat::Float32,
|
||||||
},
|
},
|
||||||
// Size
|
// Dim
|
||||||
wgpu::VertexAttribute {
|
wgpu::VertexAttribute {
|
||||||
offset: mem::size_of::<[f32; 4]>() as wgpu::BufferAddress,
|
offset: mem::size_of::<[f32; 4]>() as wgpu::BufferAddress,
|
||||||
shader_location: 5,
|
shader_location: 5,
|
||||||
format: wgpu::VertexFormat::Float32,
|
format: wgpu::VertexFormat::Float32x2,
|
||||||
},
|
},
|
||||||
// Color
|
// Color
|
||||||
wgpu::VertexAttribute {
|
wgpu::VertexAttribute {
|
||||||
offset: mem::size_of::<[f32; 5]>() as wgpu::BufferAddress,
|
offset: mem::size_of::<[f32; 6]>() as wgpu::BufferAddress,
|
||||||
shader_location: 6,
|
shader_location: 6,
|
||||||
format: wgpu::VertexFormat::Float32x4,
|
format: wgpu::VertexFormat::Float32x4,
|
||||||
},
|
},
|
||||||
// Texture
|
// Texture
|
||||||
wgpu::VertexAttribute {
|
wgpu::VertexAttribute {
|
||||||
offset: mem::size_of::<[f32; 9]>() as wgpu::BufferAddress,
|
offset: mem::size_of::<[f32; 10]>() as wgpu::BufferAddress,
|
||||||
shader_location: 7,
|
shader_location: 7,
|
||||||
format: wgpu::VertexFormat::Uint32x2,
|
format: wgpu::VertexFormat::Uint32x2,
|
||||||
},
|
},
|
||||||
// Texture fade
|
// Texture fade
|
||||||
wgpu::VertexAttribute {
|
wgpu::VertexAttribute {
|
||||||
offset: mem::size_of::<[f32; 11]>() as wgpu::BufferAddress,
|
offset: mem::size_of::<[f32; 12]>() as wgpu::BufferAddress,
|
||||||
shader_location: 8,
|
shader_location: 8,
|
||||||
format: wgpu::VertexFormat::Float32,
|
format: wgpu::VertexFormat::Float32,
|
||||||
},
|
},
|
||||||
// Mask
|
// Mask
|
||||||
wgpu::VertexAttribute {
|
wgpu::VertexAttribute {
|
||||||
offset: mem::size_of::<[f32; 12]>() as wgpu::BufferAddress,
|
offset: mem::size_of::<[f32; 13]>() as wgpu::BufferAddress,
|
||||||
shader_location: 9,
|
shader_location: 9,
|
||||||
format: wgpu::VertexFormat::Uint32x2,
|
format: wgpu::VertexFormat::Uint32x2,
|
||||||
},
|
},
|
||||||
|
|
Loading…
Reference in New Issue