1use crate::context::DrawContext;
2use bytemuck::{Pod, Zeroable};
3use fontdue::Font;
4use spitfire_fontdue::TextVertex;
5use spitfire_glow::{
6 graphics::{Graphics, Shader, Texture},
7 renderer::{GlowVertexAttrib, GlowVertexAttribs},
8};
9use std::borrow::Cow;
10use vek::Rgba;
11
12#[derive(Debug, Copy, Clone, Pod, Zeroable)]
13#[repr(C)]
14pub struct Vertex {
15 pub position: [f32; 2],
16 pub uv: [f32; 3],
17 pub color: [f32; 4],
18}
19
20impl Default for Vertex {
21 fn default() -> Self {
22 Self {
23 position: Default::default(),
24 uv: Default::default(),
25 color: [1.0, 1.0, 1.0, 1.0],
26 }
27 }
28}
29
30impl GlowVertexAttribs for Vertex {
31 const ATTRIBS: &'static [(&'static str, GlowVertexAttrib)] = &[
32 (
33 "a_position",
34 GlowVertexAttrib::Float {
35 channels: 2,
36 normalized: false,
37 },
38 ),
39 (
40 "a_uv",
41 GlowVertexAttrib::Float {
42 channels: 3,
43 normalized: false,
44 },
45 ),
46 (
47 "a_color",
48 GlowVertexAttrib::Float {
49 channels: 4,
50 normalized: false,
51 },
52 ),
53 ];
54}
55
56impl TextVertex<Rgba<f32>> for Vertex {
57 fn apply(&mut self, position: [f32; 2], tex_coord: [f32; 3], user_data: Rgba<f32>) {
58 self.position = position;
59 self.uv = tex_coord;
60 self.color = user_data.into_array();
61 }
62}
63
64pub trait Drawable {
65 fn draw(&self, context: &mut DrawContext, graphics: &mut Graphics<Vertex>);
66}
67
68#[derive(Debug, Clone)]
69pub enum ResourceRef<T> {
70 Name(Cow<'static, str>),
71 Object(T),
72}
73
74impl<T> ResourceRef<T> {
75 pub fn name(value: impl Into<Cow<'static, str>>) -> Self {
76 Self::Name(value.into())
77 }
78
79 pub fn object(value: T) -> Self {
80 Self::Object(value)
81 }
82}
83
84impl<T> From<&'static str> for ResourceRef<T> {
85 fn from(value: &'static str) -> Self {
86 Self::name(value)
87 }
88}
89
90pub type ShaderRef = ResourceRef<Shader>;
91pub type TextureRef = ResourceRef<Texture>;
92
93#[derive(Debug, Default, Clone)]
94pub struct FontMap {
95 keys: Vec<Cow<'static, str>>,
96 values: Vec<Font>,
97}
98
99impl FontMap {
100 pub fn insert(&mut self, name: impl Into<Cow<'static, str>>, font: Font) {
101 let name = name.into();
102 if let Some(index) = self.index_of(&name) {
103 self.values[index] = font;
104 } else {
105 self.keys.push(name);
106 self.values.push(font);
107 }
108 }
109
110 pub fn remove(&mut self, name: &str) -> Option<Font> {
111 if let Some(index) = self.index_of(name) {
112 self.keys.remove(index);
113 Some(self.values.remove(index))
114 } else {
115 None
116 }
117 }
118
119 pub fn index_of(&self, name: &str) -> Option<usize> {
120 self.keys.iter().position(|key| key == name)
121 }
122
123 pub fn get(&self, name: &str) -> Option<&Font> {
124 if let Some(index) = self.index_of(name) {
125 self.values.get(index)
126 } else {
127 None
128 }
129 }
130
131 pub fn keys(&self) -> &[Cow<'static, str>] {
132 &self.keys
133 }
134
135 pub fn values(&self) -> &[Font] {
136 &self.values
137 }
138
139 pub fn iter(&self) -> impl Iterator<Item = (&Cow<'static, str>, &Font)> {
140 self.keys.iter().zip(self.values.iter())
141 }
142}