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); }" );