Skip to main content

grafix_toolbox/gui/primitive/
sprite.rs

1use super::*;
2
3pub struct Sprite<'r, S> {
4	pub pos: Vec2,
5	pub size: Vec2,
6	pub color: Color,
7	pub tex: &'r VTex2d<S, u8>,
8}
9impl<S: TexSize> Sprite<'_, S> {
10	pub fn compare(&self, crop: &Geom, r: &SpriteImpl<S>) -> State {
11		let Self { pos, size, color, tex: tex_new } = *self;
12		let xyzw = (State::XYZW | State::UV).or_def(geom_cmp(pos, size, crop, &r.base));
13		let rgba = State::RGBA.or_def(color != r.base.color);
14		let ord = State::MISMATCH.or_def(!ptr::eq(r.tex, tex_new) || (!rgba.is_empty() && ordering_cmp::<S, _>(color, r)));
15		ord | xyzw | rgba
16	}
17	pub fn obj(self, crop: Geom) -> SpriteImpl<S> {
18		let Self { pos, size, color, tex } = self;
19		SpriteImpl { base: Base { pos, size, crop, color }, tex }
20	}
21}
22pub struct SpriteImpl<S> {
23	base: Base,
24	tex: *const VTex2d<S, u8>,
25}
26impl<S: TexSize> SpriteImpl<S> {
27	pub fn batchable(&self, r: &Self) -> bool {
28		self.ordered() == r.ordered() && atlas_cmp(self.tex, r.tex)
29	}
30}
31impl<S: TexSize> Primitive for SpriteImpl<S> {
32	fn base(&self) -> &Base {
33		&self.base
34	}
35	fn write_mesh(&self, aspect: Vec2, BatchedObj { z, state, xyzw, rgba, uv }: BatchedObj) {
36		if state.contains(State::XYZW | State::UV) {
37			let ((x1, y1), (x2, y2), (u1, v1, u2, v2)) = <_>::to({
38				let (crop @ (p1, p2), &Base { pos, size, .. }) = (self.base.bound_box(), self.base());
39				let (xy1, xy2, uv) = (pos, pos.sum(size), unsafe { &*self.tex }.region);
40				let uv = bound_uv(crop, (xy1, xy2), uv);
41
42				(p1.mul(aspect), p2.mul(aspect), uv)
43			});
44			let O = f16::ZERO;
45
46			if state.contains(State::XYZW) {
47				xyzw[..16].copy_from_slice(&[x1, y1, z, O, x2, y1, z, O, x2, y2, z, O, x1, y2, z, O]);
48			}
49
50			if state.contains(State::UV) {
51				uv[..8].copy_from_slice(&[u1, v1, u2, v1, u2, v2, u1, v2]);
52			}
53		}
54
55		if state.contains(State::RGBA) {
56			let (r, g, b, a) = vec4(self.base.color.mul(255).clmp(0, 255).round());
57
58			rgba[..16].copy_from_slice(&[r, g, b, a, r, g, b, a, r, g, b, a, r, g, b, a]);
59		}
60	}
61	fn batch_draw(&self, b: &VaoBind<u16>, (offset, num): (u16, u16)) {
62		let s = LeakyStatic!(Shader, { [vs_gui__pos_col_tex, ps_gui__col_tex].pipe(Shader::pure) });
63
64		let t = unsafe { &*self.tex }.atlas.Bind(sampler());
65		let _ = Uniforms!(s, ("iTex", t));
66		b.Draw((num, offset, gl::TRIANGLES));
67	}
68
69	fn ordered(&self) -> bool {
70		S::TYPE == gl::RGBA || Primitive::ordered(self)
71	}
72}
73
74pub fn sampler() -> &'static Sampler {
75	LeakyStatic!(Sampler, { Sampler::linear() })
76}
77
78SHADER!(
79	vs_gui__pos_col_tex,
80	r"layout(location = 0) in vec4 Position;
81	layout(location = 1) in vec4 Color;
82	layout(location = 2) in vec2 TexCoord;
83	out vec4 glColor;
84	out vec2 glUV;
85
86	void main() {
87		gl_Position = vec4(Position.xyz, 1);
88		glColor = Color;
89		glUV = TexCoord;
90	}"
91);
92SHADER!(
93	ps_gui__col_tex,
94	r"in vec4 glColor;
95	in vec2 glUV;
96	layout(location = 0) out vec4 glFragColor;
97	uniform sampler2D iTex;
98
99	void main() { glFragColor = glColor * texture(iTex, glUV); }"
100);