1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
use super::obj::*;
use crate::uses::{math::*, *};
use crate::GL::{sampler::*, shader::*, tex::*, window::*, VTex2d, VaoBinding};

pub struct Sprite<'a, S> {
	pub pos: Vec2,
	pub size: Vec2,
	pub color: Color,
	pub tex: &'a VTex2d<S, u8>,
}
impl<'a, S: TexSize> Sprite<'a, S> {
	#[inline(always)]
	pub fn compare(&self, crop: &Crop, r: &SpriteImpl<S>) -> State {
		let &Self { pos, size, color, tex } = self;
		let xyzw = (State::XYZW | State::UV).or_def(geom_cmp(pos, size, crop, &r.base));
		let rgba = State::RGBA.or_def(color != r.base.color);
		let _tex = State::UV.or_def(!ptr::eq(tex, r.tex));
		let ord = State::MISMATCH.or_def((!_tex.is_empty() && atlas_cmp(tex, r.tex)) || (!rgba.is_empty() && ordering_cmp::<S, _>(color, r)));
		ord | xyzw | rgba | _tex
	}
	pub fn obj(self, crop: Crop) -> SpriteImpl<S> {
		let Self { pos, size, color, tex } = self;
		SpriteImpl {
			base: Base { pos, size, crop, color },
			tex,
		}
	}
}
pub struct SpriteImpl<S> {
	base: Base,
	tex: *const VTex2d<S, u8>,
}
impl<S: TexSize> SpriteImpl<S> {
	pub fn batchable(&self, r: &Self) -> bool {
		self.ordered() == r.ordered() && atlas_cmp(self.tex, r.tex)
	}
}
impl<S: TexSize> Object for SpriteImpl<S> {
	fn base(&self) -> &Base {
		&self.base
	}
	fn write_mesh(&self, (z, state, xyzw, rgba, uv): BatchRange) {
		if state.contains(State::XYZW | State::UV) {
			let ((x1, y1), (x2, y2), (u1, v1, u2, v2)) = <(vec2<f16>, vec2<f16>, vec4<f16>)>::to({
				let (aspect, (crop1, crop2), &Base { pos, size, .. }) = (Window::aspect(), self.base.bound_box(), self.base());
				let (xy1, xy2, uv) = (pos, pos.sum(size), unsafe { &*self.tex }.region);
				let uv = bound_uv((crop1, crop2), (xy1, xy2), uv);

				(crop1.mul(aspect), crop2.mul(aspect), uv)
			});
			const O: f16 = f16::ZERO;

			if state.contains(State::XYZW) {
				xyzw[..16].copy_from_slice(&[x1, y1, z, O, x2, y1, z, O, x2, y2, z, O, x1, y2, z, O]);
			}

			if state.contains(State::UV) {
				uv[..8].copy_from_slice(&[u1, v1, u2, v1, u2, v2, u1, v2]);
			}
		}

		if state.contains(State::RGBA) {
			let (r, g, b, a) = vec4::to(self.base.color.mul(255).clmp(0, 255).round());

			rgba[..16].copy_from_slice(&[r, g, b, a, r, g, b, a, r, g, b, a, r, g, b, a]);
		}
	}
	fn batch_draw(&self, b: &VaoBinding<u16>, (offset, num): (u16, u16)) {
		let s = UnsafeOnce!(Shader, { EXPECT!(Shader::new((gui__pos_col_tex_vs, gui__col_tex_ps))) });

		let t = unsafe { &*self.tex }.tex.Bind(sampler());
		let _ = Uniforms!(s, ("src", &t));
		b.Draw((num, offset, gl::TRIANGLES));
	}

	fn ordered(&self) -> bool {
		S::TYPE == gl::RGBA || Object::ordered(self)
	}
}

pub fn sampler() -> &'static Sampler {
	UnsafeOnce!(Rc<Sampler>, { Sampler::linear() })
}

SHADER!(
	gui__pos_col_tex_vs,
	r"#version 330 core
layout(location = 0)in vec4 Position;
layout(location = 1)in vec4 Color;
layout(location = 2)in vec2 TexCoord;
out vec4 glColor;
out vec2 glTexCoord;

void main()
{
gl_Position = vec4(Position.xyz, 1.);
glColor = Color;
glTexCoord = TexCoord;
}"
);
SHADER!(
	gui__col_tex_ps,
	r"#version 330 core
in vec4 glColor;
in vec2 glTexCoord;
layout(location = 0)out vec4 glFragColor;
uniform sampler2D src;

void main()
{
glFragColor = glColor * texture(src, glTexCoord);
}"
);