1use crate::renderer::{
2 GlowBatch, GlowBlending, GlowRenderer, GlowState, GlowTextureFiltering, GlowTextureFormat,
3 GlowUniformValue, GlowVertexAttrib, GlowVertexAttribs,
4};
5use bytemuck::{Pod, Zeroable};
6use glow::{
7 BLEND, CLAMP_TO_EDGE, COLOR_ATTACHMENT0, COLOR_BUFFER_BIT, Context, FILL, FRAGMENT_SHADER,
8 FRAMEBUFFER, FRONT_AND_BACK, Framebuffer as GlowFrameBuffer, HasContext, NEAREST,
9 PixelUnpackData, Program as GlowProgram, SCISSOR_TEST, Shader as GlowShader, TEXTURE_2D_ARRAY,
10 TEXTURE_MAG_FILTER, TEXTURE_MIN_FILTER, TEXTURE_WRAP_R, TEXTURE_WRAP_S, TEXTURE_WRAP_T,
11 Texture as GlowTexture, UNSIGNED_BYTE, VERTEX_SHADER,
12};
13use spitfire_core::{VertexStream, VertexStreamRenderer};
14use std::{
15 borrow::Cow,
16 cell::{Cell, Ref, RefCell},
17 collections::HashMap,
18 rc::Rc,
19};
20use vek::{FrustumPlanes, Mat4, Rect, Transform, Vec2};
21
22#[derive(Debug, Copy, Clone, Pod, Zeroable)]
23#[repr(C)]
24pub struct Vertex3d {
25 pub position: [f32; 3],
26 pub normal: [f32; 3],
27 pub uv: [f32; 3],
28 pub color: [f32; 4],
29}
30
31impl GlowVertexAttribs for Vertex3d {
32 const ATTRIBS: &'static [(&'static str, GlowVertexAttrib)] = &[
33 (
34 "a_position",
35 GlowVertexAttrib::Float {
36 channels: 3,
37 normalized: false,
38 },
39 ),
40 (
41 "a_normal",
42 GlowVertexAttrib::Float {
43 channels: 3,
44 normalized: false,
45 },
46 ),
47 (
48 "a_uv",
49 GlowVertexAttrib::Float {
50 channels: 3,
51 normalized: false,
52 },
53 ),
54 (
55 "a_color",
56 GlowVertexAttrib::Float {
57 channels: 4,
58 normalized: false,
59 },
60 ),
61 ];
62}
63
64impl Default for Vertex3d {
65 fn default() -> Self {
66 Self {
67 position: Default::default(),
68 normal: [0.0, 0.0, 1.0],
69 uv: Default::default(),
70 color: [1.0, 1.0, 1.0, 1.0],
71 }
72 }
73}
74
75#[derive(Debug, Clone)]
76pub struct MaybeContext(Rc<RefCell<(Context, bool)>>);
77
78impl MaybeContext {
79 pub fn get(&'_ self) -> Option<Ref<'_, Context>> {
80 let access = self.0.borrow();
81 if access.1 {
82 Some(Ref::map(access, |access| &access.0))
83 } else {
84 None
85 }
86 }
87}
88
89#[derive(Debug)]
90struct StrongContext(MaybeContext);
91
92impl Drop for StrongContext {
93 fn drop(&mut self) {
94 (self.0).0.borrow_mut().1 = false;
95 }
96}
97
98impl StrongContext {
99 fn get(&'_ self) -> Option<Ref<'_, Context>> {
100 self.0.get()
101 }
102
103 fn new(context: Context) -> Self {
104 Self(MaybeContext(Rc::new(RefCell::new((context, true)))))
105 }
106}
107
108pub trait GraphicsTarget<V: GlowVertexAttribs> {
109 fn state(&self) -> &GraphicsState<V>;
110 fn state_mut(&mut self) -> &mut GraphicsState<V>;
111}
112
113pub struct GraphicsState<V: GlowVertexAttribs> {
114 pub main_camera: Camera,
115 pub color: [f32; 4],
116 pub stream: VertexStream<V, GraphicsBatch>,
117}
118
119impl<V: GlowVertexAttribs> Default for GraphicsState<V> {
120 fn default() -> Self {
121 Self {
122 main_camera: Default::default(),
123 color: [1.0, 1.0, 1.0, 1.0],
124 stream: Default::default(),
125 }
126 }
127}
128
129impl<V: GlowVertexAttribs> Clone for GraphicsState<V> {
130 fn clone(&self) -> Self {
131 Self {
132 main_camera: self.main_camera,
133 color: self.color,
134 stream: self.stream.clone(),
135 }
136 }
137}
138
139impl<V: GlowVertexAttribs> GraphicsState<V> {
140 pub fn fork(&self) -> Self {
141 Self {
142 main_camera: self.main_camera,
143 color: self.color,
144 stream: self.stream.fork(),
145 }
146 }
147}
148
149impl<V: GlowVertexAttribs> GraphicsTarget<V> for GraphicsState<V> {
150 fn state(&self) -> &Self {
151 self
152 }
153
154 fn state_mut(&mut self) -> &mut Self {
155 self
156 }
157}
158
159pub struct Graphics<V: GlowVertexAttribs> {
160 pub state: GraphicsState<V>,
161 glow_state: GlowState,
162 context: StrongContext,
163 surface_stack: Vec<(Surface, Vec2<f32>, [f32; 4])>,
164}
165
166impl<V: GlowVertexAttribs> Drop for Graphics<V> {
167 fn drop(&mut self) {
168 if let Some(context) = self.context.get() {
169 self.glow_state.dispose(&context);
170 }
171 }
172}
173
174impl<V: GlowVertexAttribs> Graphics<V> {
175 pub fn new(context: Context) -> Self {
176 Self {
177 state: Default::default(),
178 glow_state: Default::default(),
179 context: StrongContext::new(context),
180 surface_stack: Default::default(),
181 }
182 }
183
184 pub fn context(&'_ self) -> Option<Ref<'_, Context>> {
185 self.context.get()
186 }
187
188 pub fn surface(&self, attachments: Vec<SurfaceAttachment>) -> Result<Surface, String> {
189 if attachments.is_empty() {
190 return Err("Surface must have at least one texture!".to_owned());
191 }
192 for (index, attachment) in attachments.iter().enumerate() {
193 if attachment.texture.depth() < attachment.layer as _ {
194 return Err(format!(
195 "Surface texture #{} has layer: {} out of texture depth range: {}",
196 index,
197 attachment.layer,
198 attachment.texture.depth()
199 ));
200 }
201 }
202 if let [first, rest @ ..] = attachments.as_slice() {
203 let width = first.texture.width();
204 let height = first.texture.height();
205 if rest
206 .iter()
207 .any(|item| item.texture.width() != width || item.texture.height() != height)
208 {
209 return Err(format!(
210 "Some surface texture has different size than expected: {width} x {height}"
211 ));
212 }
213 }
214 unsafe {
215 if let Some(context) = self.context.get() {
216 let framebuffer = context.create_framebuffer()?;
217 context.bind_framebuffer(FRAMEBUFFER, Some(framebuffer));
218 for (index, attachment) in attachments.iter().enumerate() {
219 context.framebuffer_texture_layer(
220 FRAMEBUFFER,
221 COLOR_ATTACHMENT0 + index as u32,
222 Some(attachment.texture.handle()),
223 0,
224 attachment.layer as _,
225 );
226 }
227 context.bind_framebuffer(FRAMEBUFFER, None);
228 Ok(Surface {
229 inner: Rc::new(SurfaceInner {
230 context: self.context.0.clone(),
231 framebuffer,
232 attachments,
233 color: Default::default(),
234 }),
235 })
236 } else {
237 Err("Invalid context".to_owned())
238 }
239 }
240 }
241
242 pub fn pixel_texture(&self, color: [u8; 3]) -> Result<Texture, String> {
243 self.texture(1, 1, 1, GlowTextureFormat::Rgb, Some(&color))
244 }
245
246 pub fn texture(
247 &self,
248 width: u32,
249 height: u32,
250 depth: u32,
251 format: GlowTextureFormat,
252 data: Option<&[u8]>,
253 ) -> Result<Texture, String> {
254 unsafe {
255 if let Some(context) = self.context.get() {
256 let texture = context.create_texture()?;
257 let mut result = Texture {
258 inner: Rc::new(TextureInner {
259 context: self.context.0.clone(),
260 texture,
261 size: Cell::new((0, 0, 0)),
262 format: Cell::new(format),
263 }),
264 };
265 result.upload(width, height, depth, format, data);
266 Ok(result)
267 } else {
268 Err("Invalid context".to_owned())
269 }
270 }
271 }
272
273 pub fn shader(&self, vertex: &str, fragment: &str) -> Result<Shader, String> {
274 unsafe {
275 if let Some(context) = self.context.get() {
276 let vertex_shader = context.create_shader(VERTEX_SHADER)?;
277 let fragment_shader = context.create_shader(FRAGMENT_SHADER)?;
278 let program = context.create_program()?;
279 context.shader_source(vertex_shader, vertex);
280 context.compile_shader(vertex_shader);
281 if !context.get_shader_compile_status(vertex_shader) {
282 return Err(format!(
283 "Vertex Shader: {}",
284 context.get_shader_info_log(vertex_shader)
285 ));
286 }
287 context.shader_source(fragment_shader, fragment);
288 context.compile_shader(fragment_shader);
289 if !context.get_shader_compile_status(fragment_shader) {
290 return Err(format!(
291 "Fragment Shader: {}",
292 context.get_shader_info_log(fragment_shader)
293 ));
294 }
295 context.attach_shader(program, vertex_shader);
296 context.attach_shader(program, fragment_shader);
297 context.link_program(program);
298 if !context.get_program_link_status(program) {
299 return Err(format!(
300 "Shader Program: {}",
301 context.get_program_info_log(program)
302 ));
303 }
304 Ok(Shader {
305 inner: Rc::new(ShaderInner {
306 context: self.context.0.clone(),
307 program,
308 vertex_shader,
309 fragment_shader,
310 shared_uniforms: Default::default(),
311 }),
312 })
313 } else {
314 Err("Invalid context".to_owned())
315 }
316 }
317 }
318
319 pub fn prepare_frame(&self, clear: bool) -> Result<(), String> {
320 unsafe {
321 if let Some(context) = self.context.get() {
322 context.viewport(
323 0,
324 0,
325 self.state.main_camera.screen_size.x as _,
326 self.state.main_camera.screen_size.y as _,
327 );
328 context.bind_texture(TEXTURE_2D_ARRAY, None);
329 context.bind_vertex_array(None);
330 context.use_program(None);
331 context.disable(BLEND);
332 context.disable(SCISSOR_TEST);
333 if clear {
334 let [r, g, b, a] = self.state.color;
335 context.clear_color(r, g, b, a);
336 context.clear(COLOR_BUFFER_BIT);
337 }
338 context.polygon_mode(FRONT_AND_BACK, FILL);
339 Ok(())
340 } else {
341 Err("Invalid context".to_owned())
342 }
343 }
344 }
345
346 pub fn draw(&mut self) -> Result<(), String> {
347 if let Some(context) = self.context.get() {
348 let mut renderer = GlowRenderer::<GraphicsBatch>::new(&context, &mut self.glow_state);
349 self.state.stream.batch_end();
350 renderer.render(&mut self.state.stream)?;
351 self.state.stream.clear();
352 Ok(())
353 } else {
354 Err("Invalid context".to_owned())
355 }
356 }
357
358 pub fn push_surface(&mut self, surface: Surface) -> Result<(), String> {
359 unsafe {
360 let old_size = self.state.main_camera.screen_size;
361 let old_color = self.state.color;
362 self.state.main_camera.screen_size.x = surface.width() as _;
363 self.state.main_camera.screen_size.y = surface.height() as _;
364 self.state.color = surface.color();
365 if let Some(context) = self.context.get() {
366 context.bind_framebuffer(FRAMEBUFFER, Some(surface.handle()));
367 self.surface_stack.push((surface, old_size, old_color));
368 Ok(())
369 } else {
370 Err("Invalid context".to_owned())
371 }
372 }
373 }
374
375 pub fn pop_surface(&mut self) -> Result<Option<Surface>, String> {
376 unsafe {
377 if let Some(context) = self.context.get() {
378 if let Some((surface, size, color)) = self.surface_stack.pop() {
379 self.state.main_camera.screen_size = size;
380 self.state.color = color;
381 if let Some((surface, _, _)) = self.surface_stack.last() {
382 context.bind_framebuffer(FRAMEBUFFER, Some(surface.handle()));
383 } else {
384 context.bind_framebuffer(FRAMEBUFFER, None);
385 }
386 Ok(Some(surface))
387 } else {
388 Ok(None)
389 }
390 } else {
391 Err("Invalid context".to_owned())
392 }
393 }
394 }
395}
396
397impl<V: GlowVertexAttribs> GraphicsTarget<V> for Graphics<V> {
398 fn state(&self) -> &GraphicsState<V> {
399 &self.state
400 }
401
402 fn state_mut(&mut self) -> &mut GraphicsState<V> {
403 &mut self.state
404 }
405}
406
407#[derive(Debug, Default, Clone, Copy)]
408pub enum CameraScaling {
409 #[default]
410 None,
411 Constant(f32),
412 Stretch(Vec2<f32>),
413 FitHorizontal(f32),
414 FitVertical(f32),
415 FitToView {
416 size: Vec2<f32>,
417 inside: bool,
418 },
419}
420
421impl CameraScaling {
422 pub fn world_size(self, viewport_size: Vec2<f32>) -> Vec2<f32> {
423 match self {
424 Self::None => viewport_size,
425 Self::Constant(value) => viewport_size * value,
426 Self::Stretch(size) => size,
427 Self::FitHorizontal(value) => Vec2 {
428 x: value,
429 y: value * viewport_size.y / viewport_size.x,
430 },
431 Self::FitVertical(value) => Vec2 {
432 x: value * viewport_size.x / viewport_size.y,
433 y: value,
434 },
435 Self::FitToView { size, inside } => {
436 let source_aspect = size.x / size.y;
437 let target_aspect = viewport_size.x / viewport_size.y;
438 if (target_aspect >= source_aspect) != inside {
439 Vec2 {
440 x: viewport_size.x * size.y / viewport_size.y,
441 y: size.y,
442 }
443 } else {
444 Vec2 {
445 x: size.x,
446 y: viewport_size.y * size.x / viewport_size.x,
447 }
448 }
449 }
450 }
451 }
452}
453
454#[derive(Debug, Default, Clone, Copy)]
455pub struct Camera {
456 pub screen_alignment: Vec2<f32>,
457 pub screen_size: Vec2<f32>,
458 pub scaling: CameraScaling,
459 pub transform: Transform<f32, f32, f32>,
460}
461
462impl Camera {
463 pub fn screen_projection_matrix(&self) -> Mat4<f32> {
464 Mat4::orthographic_without_depth_planes(FrustumPlanes {
465 left: 0.0,
466 right: self.screen_size.x,
467 top: 0.0,
468 bottom: self.screen_size.y,
469 near: -1.0,
470 far: 1.0,
471 })
472 }
473
474 pub fn screen_matrix(&self) -> Mat4<f32> {
475 self.screen_projection_matrix()
476 }
477
478 pub fn world_size(&self) -> Vec2<f32> {
479 self.scaling.world_size(self.screen_size)
480 }
481
482 pub fn world_offset(&self) -> Vec2<f32> {
483 self.world_size() * -self.screen_alignment
484 }
485
486 pub fn world_projection_matrix(&self) -> Mat4<f32> {
487 let size = self.world_size();
488 let offset = size * -self.screen_alignment;
489 Mat4::orthographic_without_depth_planes(FrustumPlanes {
490 left: offset.x,
491 right: size.x + offset.x,
492 top: offset.y,
493 bottom: size.y + offset.y,
494 near: -1.0,
495 far: 1.0,
496 })
497 }
498
499 pub fn world_view_matrix(&self) -> Mat4<f32> {
500 (Mat4::<f32>::scaling_3d(self.transform.scale)
501 * Mat4::<f32>::from(self.transform.orientation)
502 * Mat4::<f32>::translation_3d(self.transform.position))
503 .inverted()
504 }
505
506 pub fn world_matrix(&self) -> Mat4<f32> {
507 self.world_projection_matrix() * self.world_view_matrix()
508 }
509
510 pub fn world_polygon(&self) -> [Vec2<f32>; 4] {
511 let matrix = self.world_matrix().inverted();
512 [
513 matrix.mul_point(Vec2::new(-1.0, -1.0)),
514 matrix.mul_point(Vec2::new(1.0, -1.0)),
515 matrix.mul_point(Vec2::new(1.0, 1.0)),
516 matrix.mul_point(Vec2::new(-1.0, 1.0)),
517 ]
518 }
519
520 pub fn world_rectangle(&self) -> Rect<f32, f32> {
521 let [tl, tr, br, bl] = self.world_polygon();
522 let xf = tl.x.min(tr.x).min(br.x).min(bl.x);
523 let xt = tl.x.max(tr.x).max(br.x).max(bl.x);
524 let yf = tl.y.min(tr.y).min(br.y).min(bl.y);
525 let yt = tl.y.max(tr.y).max(br.y).max(bl.y);
526 Rect {
527 x: xf,
528 y: yf,
529 w: xt - xf,
530 h: yt - yf,
531 }
532 }
533
534 pub fn view_to_screen_point(&self, position: Vec2<f32>) -> Vec2<f32> {
535 self.screen_matrix().inverted().mul_point(position)
536 }
537
538 pub fn screen_to_view_point(&self, position: Vec2<f32>) -> Vec2<f32> {
539 self.screen_matrix().mul_point(position)
540 }
541
542 pub fn view_to_world_point(&self, position: Vec2<f32>) -> Vec2<f32> {
543 self.world_matrix().inverted().mul_point(position)
544 }
545
546 pub fn world_to_view_point(&self, position: Vec2<f32>) -> Vec2<f32> {
547 self.world_matrix().mul_point(position)
548 }
549
550 pub fn screen_to_world_point(&self, position: Vec2<f32>) -> Vec2<f32> {
551 let position = self.screen_to_view_point(position);
552 self.view_to_world_point(position)
553 }
554
555 pub fn world_to_screen_point(&self, position: Vec2<f32>) -> Vec2<f32> {
556 let position = self.world_to_view_point(position);
557 self.view_to_screen_point(position)
558 }
559
560 pub fn view_to_screen_direction(&self, direction: Vec2<f32>) -> Vec2<f32> {
561 self.screen_matrix().inverted().mul_direction(direction)
562 }
563
564 pub fn screen_to_view_direction(&self, direction: Vec2<f32>) -> Vec2<f32> {
565 self.screen_matrix().mul_direction(direction)
566 }
567
568 pub fn view_to_world_direction(&self, direction: Vec2<f32>) -> Vec2<f32> {
569 self.world_matrix().inverted().mul_direction(direction)
570 }
571
572 pub fn world_to_view_direction(&self, direction: Vec2<f32>) -> Vec2<f32> {
573 self.world_matrix().mul_direction(direction)
574 }
575
576 pub fn screen_to_world_direction(&self, direction: Vec2<f32>) -> Vec2<f32> {
577 let direction = self.screen_to_view_direction(direction);
578 self.view_to_world_direction(direction)
579 }
580
581 pub fn world_to_screen_direction(&self, direction: Vec2<f32>) -> Vec2<f32> {
582 let direction = self.world_to_view_direction(direction);
583 self.view_to_screen_direction(direction)
584 }
585}
586
587#[derive(Debug, Default, Clone, PartialEq)]
588pub struct GraphicsBatch {
589 pub shader: Option<Shader>,
590 pub uniforms: HashMap<Cow<'static, str>, GlowUniformValue>,
591 pub textures: Vec<(Texture, GlowTextureFiltering)>,
592 pub blending: GlowBlending,
594 pub scissor: Option<Rect<i32, i32>>,
595 pub wireframe: bool,
596}
597
598#[allow(clippy::from_over_into)]
599impl Into<GlowBatch> for GraphicsBatch {
600 fn into(self) -> GlowBatch {
601 GlowBatch {
602 shader_program: self.shader.as_ref().map(|shader| shader.handle()),
603 uniforms: if let Some(shader) = self.shader.as_ref() {
604 let uniforms = &*shader.inner.shared_uniforms.borrow();
605 if uniforms.is_empty() {
606 self.uniforms
607 } else {
608 uniforms
609 .iter()
610 .map(|(k, v)| (k.clone(), *v))
611 .chain(self.uniforms)
612 .collect()
613 }
614 } else {
615 self.uniforms
616 },
617 textures: self
618 .textures
619 .into_iter()
620 .map(|(texture, filtering)| {
621 let (min, mag) = filtering.into_gl();
622 (texture.handle(), TEXTURE_2D_ARRAY, min, mag)
623 })
624 .collect(),
625 blending: self.blending.into_gl(),
626 scissor: self.scissor.map(|v| [v.x, v.y, v.w, v.h]),
627 wireframe: self.wireframe,
628 }
629 }
630}
631
632#[derive(Debug, Clone, PartialEq)]
633pub struct SurfaceAttachment {
634 pub texture: Texture,
635 pub layer: usize,
636}
637
638impl From<Texture> for SurfaceAttachment {
639 fn from(texture: Texture) -> Self {
640 Self { texture, layer: 0 }
641 }
642}
643
644#[derive(Debug)]
645struct SurfaceInner {
646 context: MaybeContext,
647 framebuffer: GlowFrameBuffer,
648 attachments: Vec<SurfaceAttachment>,
649 color: Cell<[f32; 4]>,
650}
651
652impl Drop for SurfaceInner {
653 fn drop(&mut self) {
654 unsafe {
655 if let Some(context) = self.context.get() {
656 context.delete_framebuffer(self.framebuffer);
657 }
658 }
659 }
660}
661
662#[derive(Debug, Clone)]
663pub struct Surface {
664 inner: Rc<SurfaceInner>,
665}
666
667impl Surface {
668 pub fn handle(&self) -> GlowFrameBuffer {
669 self.inner.framebuffer
670 }
671
672 pub fn width(&self) -> u32 {
673 self.inner.attachments[0].texture.width()
674 }
675
676 pub fn height(&self) -> u32 {
677 self.inner.attachments[0].texture.height()
678 }
679
680 pub fn attachments(&self) -> &[SurfaceAttachment] {
681 &self.inner.attachments
682 }
683
684 pub fn color(&self) -> [f32; 4] {
685 self.inner.color.get()
686 }
687
688 pub fn set_color(&mut self, value: [f32; 4]) {
689 self.inner.color.set(value);
690 }
691}
692
693impl PartialEq for Surface {
694 fn eq(&self, other: &Self) -> bool {
695 Rc::ptr_eq(&self.inner, &other.inner)
696 }
697}
698
699#[derive(Debug)]
700struct TextureInner {
701 context: MaybeContext,
702 texture: GlowTexture,
703 format: Cell<GlowTextureFormat>,
704 size: Cell<(u32, u32, u32)>,
705}
706
707impl Drop for TextureInner {
708 fn drop(&mut self) {
709 unsafe {
710 if let Some(context) = self.context.get() {
711 context.delete_texture(self.texture);
712 }
713 }
714 }
715}
716
717#[derive(Debug, Clone)]
718pub struct Texture {
719 inner: Rc<TextureInner>,
720}
721
722impl Texture {
723 pub fn handle(&self) -> GlowTexture {
724 self.inner.texture
725 }
726
727 pub fn width(&self) -> u32 {
728 self.inner.size.get().0
729 }
730
731 pub fn height(&self) -> u32 {
732 self.inner.size.get().1
733 }
734
735 pub fn depth(&self) -> u32 {
736 self.inner.size.get().2
737 }
738
739 pub fn format(&self) -> GlowTextureFormat {
740 self.inner.format.get()
741 }
742
743 pub fn upload(
744 &mut self,
745 width: u32,
746 height: u32,
747 depth: u32,
748 format: GlowTextureFormat,
749 data: Option<&[u8]>,
750 ) {
751 unsafe {
752 if let Some(context) = self.inner.context.get() {
753 context.bind_texture(TEXTURE_2D_ARRAY, Some(self.inner.texture));
754 context.tex_parameter_i32(TEXTURE_2D_ARRAY, TEXTURE_WRAP_S, CLAMP_TO_EDGE as _);
755 context.tex_parameter_i32(TEXTURE_2D_ARRAY, TEXTURE_WRAP_T, CLAMP_TO_EDGE as _);
756 context.tex_parameter_i32(TEXTURE_2D_ARRAY, TEXTURE_WRAP_R, CLAMP_TO_EDGE as _);
757 context.tex_parameter_i32(TEXTURE_2D_ARRAY, TEXTURE_MIN_FILTER, NEAREST as _);
758 context.tex_parameter_i32(TEXTURE_2D_ARRAY, TEXTURE_MAG_FILTER, NEAREST as _);
759 #[cfg(target_arch = "wasm32")]
761 context.tex_image_3d(
762 TEXTURE_2D_ARRAY,
763 0,
764 format.into_gl() as _,
765 width as _,
766 height as _,
767 0,
768 depth as _,
769 format.into_gl(),
770 UNSIGNED_BYTE,
771 PixelUnpackData::Slice(data),
772 );
773 #[cfg(not(target_arch = "wasm32"))]
774 context.tex_image_3d(
775 TEXTURE_2D_ARRAY,
776 0,
777 format.into_gl() as _,
778 width as _,
779 height as _,
780 depth as _,
781 0,
782 format.into_gl(),
783 UNSIGNED_BYTE,
784 PixelUnpackData::Slice(data),
785 );
786 self.inner.size.set((width, height, depth));
787 self.inner.format.set(format);
788 }
789 }
790 }
791}
792
793impl PartialEq for Texture {
794 fn eq(&self, other: &Self) -> bool {
795 Rc::ptr_eq(&self.inner, &other.inner)
796 }
797}
798
799#[derive(Debug)]
800struct ShaderInner {
801 context: MaybeContext,
802 program: GlowProgram,
803 vertex_shader: GlowShader,
804 fragment_shader: GlowShader,
805 shared_uniforms: RefCell<HashMap<Cow<'static, str>, GlowUniformValue>>,
806}
807
808impl Drop for ShaderInner {
809 fn drop(&mut self) {
810 unsafe {
811 if let Some(context) = self.context.get() {
812 context.delete_program(self.program);
813 context.delete_shader(self.vertex_shader);
814 context.delete_shader(self.fragment_shader);
815 }
816 }
817 }
818}
819
820#[derive(Debug, Clone)]
821pub struct Shader {
822 inner: Rc<ShaderInner>,
823}
824
825impl Shader {
826 pub const PASS_VERTEX_2D: &'static str = r#"#version 300 es
827 layout(location = 0) in vec2 a_position;
828 layout(location = 2) in vec4 a_color;
829 out vec4 v_color;
830
831 void main() {
832 gl_Position = vec4(a_position, 0.0, 1.0);
833 v_color = a_color;
834 }
835 "#;
836
837 pub const PASS_VERTEX_3D: &'static str = r#"#version 300 es
838 layout(location = 0) in vec3 a_position;
839 layout(location = 3) in vec4 a_color;
840 out vec4 v_color;
841
842 void main() {
843 gl_Position = vec4(a_position, 1.0);
844 v_color = a_color;
845 }
846 "#;
847
848 pub const PASS_FRAGMENT: &'static str = r#"#version 300 es
849 precision highp float;
850 precision highp int;
851 in vec4 v_color;
852 out vec4 o_color;
853
854 void main() {
855 o_color = v_color;
856 }
857 "#;
858
859 pub const COLORED_VERTEX_2D: &'static str = r#"#version 300 es
860 layout(location = 0) in vec2 a_position;
861 layout(location = 2) in vec4 a_color;
862 out vec4 v_color;
863 uniform mat4 u_projection_view;
864
865 void main() {
866 gl_Position = u_projection_view * vec4(a_position, 0.0, 1.0);
867 v_color = a_color;
868 }
869 "#;
870
871 pub const COLORED_VERTEX_3D: &'static str = r#"#version 300 es
872 layout(location = 0) in vec3 a_position;
873 layout(location = 3) in vec4 a_color;
874 out vec4 v_color;
875 uniform mat4 u_projection_view;
876
877 void main() {
878 gl_Position = u_projection_view * vec4(a_position, 1.0);
879 v_color = a_color;
880 }
881 "#;
882
883 pub const TEXTURED_VERTEX_2D: &'static str = r#"#version 300 es
884 layout(location = 0) in vec2 a_position;
885 layout(location = 1) in vec3 a_uv;
886 layout(location = 2) in vec4 a_color;
887 out vec4 v_color;
888 out vec3 v_uv;
889 uniform mat4 u_projection_view;
890
891 void main() {
892 gl_Position = u_projection_view * vec4(a_position, 0.0, 1.0);
893 v_color = a_color;
894 v_uv = a_uv;
895 }
896 "#;
897
898 pub const TEXTURED_VERTEX_3D: &'static str = r#"#version 300 es
899 layout(location = 0) in vec3 a_position;
900 layout(location = 2) in vec3 a_uv;
901 layout(location = 3) in vec4 a_color;
902 out vec4 v_color;
903 out vec3 v_uv;
904 uniform mat4 u_projection_view;
905
906 void main() {
907 gl_Position = u_projection_view * vec4(a_position, 1.0);
908 v_color = a_color;
909 v_uv = a_uv;
910 }
911 "#;
912
913 pub const TEXTURED_FRAGMENT: &'static str = r#"#version 300 es
914 precision highp float;
915 precision highp int;
916 precision highp sampler2DArray;
917 in vec4 v_color;
918 in vec3 v_uv;
919 out vec4 o_color;
920 uniform sampler2DArray u_image;
921
922 void main() {
923 o_color = texture(u_image, v_uv) * v_color;
924 }
925 "#;
926
927 pub const TEXT_VERTEX: &'static str = r#"#version 300 es
928 layout(location = 0) in vec2 a_position;
929 layout(location = 1) in vec3 a_uv;
930 layout(location = 2) in vec4 a_color;
931 out vec4 v_color;
932 out vec3 v_uv;
933 uniform mat4 u_projection_view;
934
935 void main() {
936 gl_Position = u_projection_view * vec4(a_position, 0.0, 1.0);
937 v_color = a_color;
938 v_uv = a_uv;
939 }
940 "#;
941
942 pub const TEXT_FRAGMENT: &'static str = r#"#version 300 es
943 precision highp float;
944 precision highp int;
945 precision highp sampler2DArray;
946 in vec4 v_color;
947 in vec3 v_uv;
948 out vec4 o_color;
949 uniform sampler2DArray u_image;
950
951 void main() {
952 float alpha = texture(u_image, v_uv).x;
953 o_color = vec4(v_color.xyz, v_color.w * alpha);
954 }
955 "#;
956
957 pub fn handle(&self) -> GlowProgram {
958 self.inner.program
959 }
960
961 pub fn set_shared_uniform(
962 &mut self,
963 id: impl Into<Cow<'static, str>>,
964 value: GlowUniformValue,
965 ) {
966 self.inner
967 .shared_uniforms
968 .borrow_mut()
969 .insert(id.into(), value);
970 }
971
972 pub fn unset_shared_uniform(&mut self, id: &str) {
973 self.inner.shared_uniforms.borrow_mut().remove(id);
974 }
975
976 pub fn get_shared_uniform(&self, id: &str) -> Option<GlowUniformValue> {
977 self.inner.shared_uniforms.borrow().get(id).cloned()
978 }
979
980 pub fn clear_shared_uniforms(&mut self) {
981 self.inner.shared_uniforms.borrow_mut().clear();
982 }
983}
984
985impl PartialEq for Shader {
986 fn eq(&self, other: &Self) -> bool {
987 Rc::ptr_eq(&self.inner, &other.inner)
988 }
989}