1use crate::engine::ecs::ComponentId;
2use crate::engine::ecs::Transform;
3use crate::engine::graphics::GpuRenderable;
4use crate::engine::graphics::MsaaMode;
5use crate::engine::graphics::post_processing::PostProcessingConfig;
6use crate::engine::graphics::primitives::InstanceHandle;
7use crate::engine::graphics::primitives::TransformMatrix;
8use crate::engine::graphics::{Skin, SkinId};
9use slotmap::{Key, SlotMap};
10use std::collections::HashMap;
11
12#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
13pub enum TextureFiltering {
14 #[default]
16 Linear,
17 Nearest,
19 NearestMagnification,
21}
22
23#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
24pub enum CameraTarget {
25 Window,
26 Xr,
27}
28
29#[derive(Debug, Clone, Copy)]
30pub struct CameraData {
31 pub view: [[f32; 4]; 4],
32 pub proj: [[f32; 4]; 4],
33 pub transform: Transform,
34}
35
36#[derive(Debug, Clone)]
37pub struct VisualCamera {
38 pub target: CameraTarget,
39 pub eyes: Vec<CameraData>,
40}
41
42#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
43pub enum MirrorViewerFamily {
44 Monoscopic,
45 Stereoscopic,
46}
47
48impl MirrorViewerFamily {
49 pub fn key_segment(self) -> &'static str {
50 match self {
51 Self::Monoscopic => "mono",
52 Self::Stereoscopic => "stereo",
53 }
54 }
55}
56
57#[derive(Debug, Clone)]
58pub struct MirrorCaptureRequest {
59 pub family: MirrorViewerFamily,
60 pub view_index: usize,
61 pub camera: CameraData,
62 pub target_key: String,
63}
64
65#[derive(Debug, Clone)]
66pub struct VisualMirror {
67 pub mirror_component: ComponentId,
68 pub captures: Vec<MirrorCaptureRequest>,
69 pub plane_origin: [f32; 3],
70 pub plane_normal: [f32; 3],
71 pub aspect_ratio: f32,
72 pub source_instance: InstanceHandle,
73 pub resolution_scale: f32,
74}
75
76#[derive(Debug, Clone, Copy)]
77pub struct DrawBatch {
78 pub material: crate::engine::graphics::MaterialHandle,
79 pub mesh: crate::engine::graphics::primitives::MeshHandle,
80 pub texture: Option<crate::engine::graphics::TextureHandle>,
81 pub texture_filtering: TextureFiltering,
82 pub quant_steps: f32,
83 pub stencil_ref: u8,
87 pub start: usize,
89 pub count: usize,
90}
91
92#[derive(Debug, Clone, Copy)]
97pub enum RenderOp {
98 EnterClip {
102 instance_index: u32,
103 parent_ref: u8,
104 new_ref: u8,
105 },
106 DrawBatch(DrawBatch),
111 ExitClip { instance_index: u32, ref_value: u8 },
114}
115
116pub struct VisualWorld {
117 instances: Vec<VisualInstance>,
118 clear_color: [f32; 4],
119 renderer_msaa_mode: MsaaMode,
120 preferred_window_size: Option<[u32; 2]>,
121 post_processing: PostProcessingConfig,
122
123 mirrors: Vec<VisualMirror>,
124
125 window_frame_dt_sec: f32,
128 xr_frame_dt_sec: Option<f32>,
129
130 bones_palette: Vec<TransformMatrix>,
137 bones_free_ranges: Vec<(u32, u32)>,
138 dirty_bones_palette: bool,
139
140 skins: SlotMap<SkinId, Skin>,
142 skin_id_by_key: std::collections::HashMap<(String, usize), SkinId>,
143
144 ambient_light: [f32; 3],
145
146 point_lights: Vec<VisualPointLight>,
147 point_light_index_by_component: std::collections::HashMap<ComponentId, usize>,
148 dirty_lights: bool,
149
150 visual_cameras: Vec<VisualCamera>,
152
153 active_xr_camera: Option<ComponentId>,
155 viewport: [f32; 2],
157 runtime_texture_handles: HashMap<String, crate::engine::graphics::TextureHandle>,
158 stencil_clip_debug_requested: bool,
159 camera_2d: [[f32; 4]; 3],
162 dirty_camera: bool,
163
164 next_handle: u32,
165 handle_to_index: std::collections::HashMap<InstanceHandle, usize>,
166 component_to_handle: std::collections::HashMap<ComponentId, InstanceHandle>,
167
168 dirty_draw_cache: bool,
170 dirty_instance_data: bool,
173
174 background_order: Vec<u32>, background_batches: Vec<DrawBatch>,
177 background_occluded_lit_order: Vec<u32>,
178 background_occluded_lit_batches: Vec<DrawBatch>,
179 background_occluded_lit_emissive_order: Vec<u32>,
180 background_occluded_lit_emissive_batches: Vec<DrawBatch>,
181 draw_order: Vec<u32>, draw_batches: Vec<DrawBatch>,
183
184 emissive_draw_order: Vec<u32>,
186 emissive_draw_batches: Vec<DrawBatch>,
187
188 cutout_order: Vec<u32>,
190 cutout_batches: Vec<DrawBatch>,
191
192 emissive_cutout_order: Vec<u32>,
194 emissive_cutout_batches: Vec<DrawBatch>,
195
196 cutout_stream: Vec<RenderOp>,
198 cutout_stream_instances: Vec<u32>,
199
200 overlay_order: Vec<u32>,
203 overlay_batches: Vec<DrawBatch>,
204
205 stencil_clip_order: Vec<u32>,
208
209 overlay_stream: Vec<RenderOp>,
213 overlay_stream_instances: Vec<u32>,
214
215 opaque_stream: Vec<RenderOp>,
217 opaque_stream_instances: Vec<u32>,
218
219 transparent_single_draw_order: Vec<u32>,
222 transparent_single_draw_batches: Vec<DrawBatch>,
223 transparent_single_stream: Vec<RenderOp>,
224 transparent_single_stream_instances: Vec<u32>,
225 transparent_multi_draw_order: Vec<u32>,
227 transparent_multi_draw_batches: Vec<DrawBatch>,
228}
229
230#[derive(Debug, Clone, Copy)]
231pub struct VisualInstance {
232 pub renderable: GpuRenderable,
233 pub transform: Transform,
234 pub color: [f32; 4],
235 pub opacity: f32,
236 pub multiple_layers: bool,
237 pub transparent_cutout: bool,
238 pub background: bool,
239 pub background_occluded_lit: bool,
240 pub overlay: bool,
241 pub emissive: f32,
242 pub texture: Option<crate::engine::graphics::TextureHandle>,
243 pub texture_filtering: TextureFiltering,
244 pub quant_steps: f32,
245
246 pub bones_base: u32,
248 pub bones_count: u32,
250
251 pub stencil_ref: u8,
253 pub is_stencil_clip: bool,
256}
257
258fn sanitize_quant_steps(steps: f32) -> f32 {
259 if !steps.is_finite() {
260 3.0
261 } else {
262 steps.clamp(1.0, 64.0)
263 }
264}
265
266impl Default for VisualWorld {
267 fn default() -> Self {
268 let ident4 = [
269 [1.0, 0.0, 0.0, 0.0],
270 [0.0, 1.0, 0.0, 0.0],
271 [0.0, 0.0, 1.0, 0.0],
272 [0.0, 0.0, 0.0, 1.0],
273 ];
274 let mut t = Transform::default();
275 t.model = ident4;
276 t.matrix_world = ident4;
277
278 Self {
279 instances: Vec::new(),
280 clear_color: [0.0, 0.0, 0.0, 1.0],
281 renderer_msaa_mode: MsaaMode::default(),
282 preferred_window_size: None,
283 post_processing: PostProcessingConfig::default(),
284 mirrors: Vec::new(),
285 window_frame_dt_sec: 0.0,
286 xr_frame_dt_sec: None,
287
288 bones_palette: vec![ident4],
289 bones_free_ranges: Vec::new(),
290 dirty_bones_palette: true,
291
292 skins: SlotMap::with_key(),
293 skin_id_by_key: std::collections::HashMap::new(),
294
295 ambient_light: [0.0, 0.0, 0.0],
296
297 point_lights: Vec::new(),
298 point_light_index_by_component: std::collections::HashMap::new(),
299 dirty_lights: true,
300
301 visual_cameras: vec![VisualCamera {
302 target: CameraTarget::Window,
303 eyes: vec![CameraData {
304 view: ident4,
305 proj: ident4,
306 transform: t,
307 }],
308 }],
309
310 active_xr_camera: None,
311 viewport: [1.0, 1.0],
312 runtime_texture_handles: HashMap::new(),
313 stencil_clip_debug_requested: false,
314 camera_2d: [
315 [1.0, 0.0, 0.0, 0.0],
316 [0.0, 1.0, 0.0, 0.0],
317 [0.0, 0.0, 1.0, 0.0],
318 ],
319 dirty_camera: true,
320
321 next_handle: 0,
322 handle_to_index: std::collections::HashMap::new(),
323 component_to_handle: std::collections::HashMap::new(),
324
325 dirty_draw_cache: true,
326 dirty_instance_data: true,
327 background_order: Vec::new(),
328 background_batches: Vec::new(),
329 background_occluded_lit_order: Vec::new(),
330 background_occluded_lit_batches: Vec::new(),
331 background_occluded_lit_emissive_order: Vec::new(),
332 background_occluded_lit_emissive_batches: Vec::new(),
333 draw_order: Vec::new(),
334 draw_batches: Vec::new(),
335 emissive_draw_order: Vec::new(),
336 emissive_draw_batches: Vec::new(),
337
338 cutout_order: Vec::new(),
339 cutout_batches: Vec::new(),
340 emissive_cutout_order: Vec::new(),
341 emissive_cutout_batches: Vec::new(),
342 cutout_stream: Vec::new(),
343 cutout_stream_instances: Vec::new(),
344
345 overlay_order: Vec::new(),
346 overlay_batches: Vec::new(),
347
348 stencil_clip_order: Vec::new(),
349
350 overlay_stream: Vec::new(),
351 overlay_stream_instances: Vec::new(),
352
353 opaque_stream: Vec::new(),
354 opaque_stream_instances: Vec::new(),
355
356 transparent_single_draw_order: Vec::new(),
357 transparent_single_draw_batches: Vec::new(),
358 transparent_single_stream: Vec::new(),
359 transparent_single_stream_instances: Vec::new(),
360 transparent_multi_draw_order: Vec::new(),
361 transparent_multi_draw_batches: Vec::new(),
362 }
363 }
364}
365
366impl VisualWorld {
367 fn is_emissive_material(material: crate::engine::graphics::MaterialHandle) -> bool {
368 matches!(
369 material,
370 crate::engine::graphics::MaterialHandle::EMISSIVE_TOON_MESH
371 | crate::engine::graphics::MaterialHandle::SKINNED_EMISSIVE_TOON_MESH
372 )
373 }
374
375 pub fn instance(&self, handle: InstanceHandle) -> Option<&VisualInstance> {
376 let idx = *self.handle_to_index.get(&handle)?;
377 self.instances.get(idx)
378 }
379
380 pub fn skin(&self, id: SkinId) -> Option<&Skin> {
381 self.skins.get(id)
382 }
383
384 pub fn skin_id_for(&self, uri: &str, skin_index: usize) -> Option<SkinId> {
385 self.skin_id_by_key
386 .get(&(uri.to_string(), skin_index))
387 .copied()
388 }
389
390 pub fn upsert_skin(
391 &mut self,
392 uri: &str,
393 skin_index: usize,
394 joint_node_indices: Vec<usize>,
395 inverse_bind_matrices: Vec<TransformMatrix>,
396 ) -> SkinId {
397 let key = (uri.to_string(), skin_index);
398 if let Some(existing) = self.skin_id_by_key.get(&key).copied() {
399 if let Some(skin) = self.skins.get_mut(existing) {
400 skin.uri = uri.to_string();
401 skin.skin_index = skin_index;
402 skin.joint_node_indices = joint_node_indices;
403 skin.inverse_bind_matrices = inverse_bind_matrices;
404 return existing;
405 }
406 }
407
408 let id = self.skins.insert(Skin {
409 id: SkinId::null(),
410 uri: uri.to_string(),
411 skin_index,
412 joint_node_indices,
413 inverse_bind_matrices,
414 });
415
416 if let Some(s) = self.skins.get_mut(id) {
417 s.id = id;
418 }
419
420 self.skin_id_by_key.insert(key, id);
421 id
422 }
423
424 fn bones_identity() -> TransformMatrix {
425 [
426 [1.0, 0.0, 0.0, 0.0],
427 [0.0, 1.0, 0.0, 0.0],
428 [0.0, 0.0, 1.0, 0.0],
429 [0.0, 0.0, 0.0, 1.0],
430 ]
431 }
432
433 fn bones_free_coalesce(&mut self) {
434 if self.bones_free_ranges.len() <= 1 {
435 return;
436 }
437
438 self.bones_free_ranges.sort_by_key(|(b, _)| *b);
439 let mut out: Vec<(u32, u32)> = Vec::with_capacity(self.bones_free_ranges.len());
440 for (base, len) in self.bones_free_ranges.drain(..) {
441 if let Some((prev_base, prev_len)) = out.last_mut() {
442 let prev_end = *prev_base + *prev_len;
443 if prev_end == base {
444 *prev_len += len;
445 continue;
446 }
447 }
448 out.push((base, len));
449 }
450 self.bones_free_ranges = out;
451 }
452
453 fn bones_alloc_range(&mut self, len: u32) -> u32 {
454 debug_assert!(!self.bones_palette.is_empty());
456 debug_assert_eq!(self.bones_palette[0], Self::bones_identity());
457
458 if len == 0 {
459 return 0;
460 }
461
462 for i in 0..self.bones_free_ranges.len() {
464 let (base, free_len) = self.bones_free_ranges[i];
465 if free_len >= len {
466 let alloc_base = base;
467 if free_len == len {
468 self.bones_free_ranges.swap_remove(i);
469 } else {
470 self.bones_free_ranges[i] = (base + len, free_len - len);
471 }
472 return alloc_base;
473 }
474 }
475
476 let base = self.bones_palette.len() as u32;
478 self.bones_palette.resize(
479 self.bones_palette.len() + len as usize,
480 Self::bones_identity(),
481 );
482 base
483 }
484
485 fn bones_free_range(&mut self, base: u32, len: u32) {
486 if len == 0 {
487 return;
488 }
489
490 if base == 0 {
492 return;
493 }
494
495 let start = base as usize;
497 let end = (base + len) as usize;
498 if end <= self.bones_palette.len() {
499 for slot in &mut self.bones_palette[start..end] {
500 *slot = Self::bones_identity();
501 }
502 }
503
504 self.bones_free_ranges.push((base, len));
505 self.bones_free_coalesce();
506 self.dirty_bones_palette = true;
507 }
508
509 pub fn set_skin_matrices(&mut self, handle: InstanceHandle, bones: &[TransformMatrix]) -> bool {
513 let debug_skin_set = std::env::var("CAT_DEBUG_SKIN_SET")
514 .ok()
515 .map(|s| {
516 let s = s.trim().to_ascii_lowercase();
517 s == "1" || s == "true" || s == "on" || s == "yes"
518 })
519 .unwrap_or(false);
520
521 let Some(&idx) = self.handle_to_index.get(&handle) else {
522 if debug_skin_set {
523 println!(
524 "[VisualWorld] set_skin_matrices: unknown handle={handle:?} bones_len={}",
525 bones.len()
526 );
527 }
528 return false;
529 };
530
531 if bones.is_empty() {
532 let old_base = self.instances[idx].bones_base;
534 let old_count = self.instances[idx].bones_count;
535 if old_count != 0 {
536 self.bones_free_range(old_base, old_count);
537 }
538 if self.instances[idx].bones_base != 0 || self.instances[idx].bones_count != 0 {
539 self.instances[idx].bones_base = 0;
540 self.instances[idx].bones_count = 0;
541 self.dirty_instance_data = true;
542 }
543 return true;
544 }
545
546 let want_count = bones.len() as u32;
547 let mut base = self.instances[idx].bones_base;
548 let old_count = self.instances[idx].bones_count;
549
550 if old_count == 0 {
551 base = self.bones_alloc_range(want_count);
552 self.instances[idx].bones_base = base;
553 self.instances[idx].bones_count = want_count;
554 self.dirty_instance_data = true;
555 } else if old_count != want_count {
556 self.bones_free_range(base, old_count);
558 base = self.bones_alloc_range(want_count);
559 self.instances[idx].bones_base = base;
560 self.instances[idx].bones_count = want_count;
561 self.dirty_instance_data = true;
562 }
563
564 let start = base as usize;
566 let end = start + bones.len();
567 if end > self.bones_palette.len() {
568 self.bones_palette.resize(end, Self::bones_identity());
569 }
570 self.bones_palette[start..end].copy_from_slice(bones);
571 self.dirty_bones_palette = true;
572
573 if debug_skin_set {
574 println!(
575 "[VisualWorld] set_skin_matrices: handle={handle:?} idx={idx} bones_base={} bones_count={} bones_len={}",
576 self.instances[idx].bones_base,
577 self.instances[idx].bones_count,
578 bones.len(),
579 );
580 }
581 true
582 }
583
584 pub fn bones_palette(&self) -> &[TransformMatrix] {
585 &self.bones_palette
586 }
587
588 pub fn take_bones_palette_dirty(&mut self) -> bool {
590 let dirty = self.dirty_bones_palette;
591 self.dirty_bones_palette = false;
592 dirty
593 }
594
595 pub fn update_skin_range(
599 &mut self,
600 handle: InstanceHandle,
601 bones_base: u32,
602 bones_count: u32,
603 ) -> bool {
604 if let Some(&idx) = self.handle_to_index.get(&handle) {
605 if self.instances[idx].bones_base == bones_base
606 && self.instances[idx].bones_count == bones_count
607 {
608 return true;
609 }
610 let old_base = self.instances[idx].bones_base;
612 let old_count = self.instances[idx].bones_count;
613 if old_count != 0 && (old_base != bones_base || old_count != bones_count) {
614 self.bones_free_range(old_base, old_count);
615 }
616 self.instances[idx].bones_base = bones_base;
617 self.instances[idx].bones_count = bones_count;
618 self.dirty_instance_data = true;
619 true
620 } else {
621 false
622 }
623 }
624}
625
626#[cfg(test)]
627mod tests {
628 use super::{RenderOp, VisualWorld};
629 use crate::engine::ecs::ComponentId;
630 use crate::engine::graphics::primitives::{
631 GpuRenderable, MaterialHandle, MeshHandle, Transform,
632 };
633 use slotmap::KeyData;
634
635 fn cid(n: u64) -> ComponentId {
636 KeyData::from_ffi(n).into()
637 }
638
639 fn dummy_renderable() -> GpuRenderable {
640 GpuRenderable::new(MeshHandle::SQUARE, MaterialHandle::TOON_MESH)
641 }
642
643 fn dummy_emissive_renderable() -> GpuRenderable {
644 GpuRenderable::new(MeshHandle::SQUARE, MaterialHandle::EMISSIVE_TOON_MESH)
645 }
646
647 #[test]
648 fn background_occluded_lit_emissive_subset_includes_only_eligible_instances() {
649 let mut visuals = VisualWorld::default();
650
651 let _ = visuals.register(
652 cid(100),
653 dummy_emissive_renderable(),
654 Transform::default(),
655 [1.0, 0.8, 0.4, 1.0],
656 1.0,
657 false,
658 false,
659 true,
660 true,
661 false,
662 1.0,
663 None,
664 3.0,
665 );
666 let _ = visuals.register(
667 cid(101),
668 dummy_emissive_renderable(),
669 Transform::default(),
670 [1.0, 0.8, 0.4, 1.0],
671 1.0,
672 false,
673 false,
674 true,
675 false,
676 false,
677 1.0,
678 None,
679 3.0,
680 );
681 let _ = visuals.register(
682 cid(102),
683 dummy_renderable(),
684 Transform::default(),
685 [0.6, 0.6, 0.6, 1.0],
686 1.0,
687 false,
688 false,
689 true,
690 true,
691 false,
692 0.0,
693 None,
694 3.0,
695 );
696
697 visuals.prepare_draw_cache();
698
699 assert!(visuals.has_background_occluded_lit_emissive());
700 assert_eq!(visuals.background_occluded_lit_order().len(), 2);
701 assert_eq!(visuals.background_occluded_lit_emissive_order().len(), 1);
702 assert_eq!(visuals.background_occluded_lit_emissive_batches().len(), 1);
703 }
704
705 #[test]
706 fn background_occluded_lit_non_emissive_keeps_subset_empty() {
707 let mut visuals = VisualWorld::default();
708
709 let _ = visuals.register(
710 cid(110),
711 dummy_renderable(),
712 Transform::default(),
713 [0.6, 0.6, 0.6, 1.0],
714 1.0,
715 false,
716 false,
717 true,
718 true,
719 false,
720 0.0,
721 None,
722 3.0,
723 );
724
725 visuals.prepare_draw_cache();
726
727 assert_eq!(visuals.background_occluded_lit_order().len(), 1);
728 assert!(!visuals.has_background_occluded_lit_emissive());
729 assert!(visuals.background_occluded_lit_emissive_order().is_empty());
730 assert!(
731 visuals
732 .background_occluded_lit_emissive_batches()
733 .is_empty()
734 );
735 }
736
737 #[test]
738 fn opaque_stream_enters_and_exits_single_root_clip() {
739 let mut visuals = VisualWorld::default();
740
741 let clip_handle = visuals.register(
742 cid(1),
743 dummy_renderable(),
744 Transform::default(),
745 [1.0, 1.0, 1.0, 1.0],
746 1.0,
747 false,
748 false,
749 false,
750 false,
751 false,
752 0.0,
753 None,
754 3.0,
755 );
756 let content_handle = visuals.register(
757 cid(2),
758 dummy_renderable(),
759 Transform::default(),
760 [0.8, 0.8, 0.8, 1.0],
761 1.0,
762 false,
763 false,
764 false,
765 false,
766 false,
767 0.0,
768 None,
769 3.0,
770 );
771
772 let _ = visuals.register_stencil_clip(clip_handle, 0);
773 let _ = visuals.update_stencil_ref(content_handle, 1);
774 visuals.prepare_draw_cache();
775
776 let (ops, instance_indices) = visuals.opaque_stream();
777 assert_eq!(ops.len(), 4);
778 assert_eq!(instance_indices.len(), 2);
779
780 match ops[0] {
781 RenderOp::EnterClip {
782 parent_ref,
783 new_ref,
784 ..
785 } => {
786 assert_eq!(parent_ref, 0);
787 assert_eq!(new_ref, 1);
788 }
789 other => panic!("expected EnterClip, got {other:?}"),
790 }
791
792 match ops[1] {
793 RenderOp::DrawBatch(batch) => {
794 assert_eq!(batch.stencil_ref, 1);
795 assert_eq!(batch.count, 1);
796 assert_eq!(instance_indices[batch.start], 0);
797 }
798 other => panic!("expected clip-source DrawBatch, got {other:?}"),
799 }
800
801 match ops[2] {
802 RenderOp::DrawBatch(batch) => {
803 assert_eq!(batch.stencil_ref, 1);
804 assert_eq!(batch.count, 1);
805 assert_eq!(instance_indices[batch.start], 1);
806 }
807 other => panic!("expected content DrawBatch, got {other:?}"),
808 }
809
810 match ops[3] {
811 RenderOp::ExitClip { ref_value, .. } => assert_eq!(ref_value, 1),
812 other => panic!("expected ExitClip, got {other:?}"),
813 }
814 }
815
816 #[test]
817 fn overlay_stream_nests_clip_regions_in_dfs_order() {
818 let mut visuals = VisualWorld::default();
819
820 let outer_clip = visuals.register(
821 cid(10),
822 dummy_renderable(),
823 Transform::default(),
824 [1.0, 1.0, 1.0, 1.0],
825 1.0,
826 false,
827 false,
828 false,
829 false,
830 true,
831 0.0,
832 None,
833 3.0,
834 );
835 let outer_content = visuals.register(
836 cid(11),
837 dummy_renderable(),
838 Transform::default(),
839 [0.8, 0.8, 0.8, 1.0],
840 1.0,
841 false,
842 false,
843 false,
844 false,
845 true,
846 0.0,
847 None,
848 3.0,
849 );
850 let inner_clip = visuals.register(
851 cid(12),
852 dummy_renderable(),
853 Transform::default(),
854 [1.0, 1.0, 1.0, 1.0],
855 1.0,
856 false,
857 false,
858 false,
859 false,
860 true,
861 0.0,
862 None,
863 3.0,
864 );
865 let inner_content = visuals.register(
866 cid(13),
867 dummy_renderable(),
868 Transform::default(),
869 [0.6, 0.6, 0.6, 1.0],
870 1.0,
871 false,
872 false,
873 false,
874 false,
875 true,
876 0.0,
877 None,
878 3.0,
879 );
880
881 let _ = visuals.register_stencil_clip(outer_clip, 0);
882 let _ = visuals.update_stencil_ref(outer_content, 1);
883 let _ = visuals.register_stencil_clip(inner_clip, 1);
884 let _ = visuals.update_stencil_ref(inner_content, 2);
885 visuals.prepare_draw_cache();
886
887 let (ops, instance_indices) = visuals.overlay_stream();
888 assert_eq!(ops.len(), 8);
889 assert_eq!(instance_indices.len(), 4);
890
891 match ops[0] {
892 RenderOp::EnterClip {
893 parent_ref,
894 new_ref,
895 ..
896 } => {
897 assert_eq!(parent_ref, 0);
898 assert_eq!(new_ref, 1);
899 }
900 other => panic!("expected outer EnterClip, got {other:?}"),
901 }
902 match ops[1] {
903 RenderOp::DrawBatch(batch) => {
904 assert_eq!(batch.stencil_ref, 1);
905 assert_eq!(batch.count, 1);
906 assert_eq!(instance_indices[batch.start], 0);
907 }
908 other => panic!("expected outer clip-source DrawBatch, got {other:?}"),
909 }
910 match ops[2] {
911 RenderOp::DrawBatch(batch) => {
912 assert_eq!(batch.stencil_ref, 1);
913 assert_eq!(batch.count, 1);
914 assert_eq!(instance_indices[batch.start], 1);
915 }
916 other => panic!("expected outer content DrawBatch, got {other:?}"),
917 }
918 match ops[3] {
919 RenderOp::EnterClip {
920 parent_ref,
921 new_ref,
922 ..
923 } => {
924 assert_eq!(parent_ref, 1);
925 assert_eq!(new_ref, 2);
926 }
927 other => panic!("expected inner EnterClip, got {other:?}"),
928 }
929 match ops[4] {
930 RenderOp::DrawBatch(batch) => {
931 assert_eq!(batch.stencil_ref, 2);
932 assert_eq!(batch.count, 1);
933 assert_eq!(instance_indices[batch.start], 2);
934 }
935 other => panic!("expected inner clip-source DrawBatch, got {other:?}"),
936 }
937 match ops[5] {
938 RenderOp::DrawBatch(batch) => {
939 assert_eq!(batch.stencil_ref, 2);
940 assert_eq!(batch.count, 1);
941 assert_eq!(instance_indices[batch.start], 3);
942 }
943 other => panic!("expected inner content DrawBatch, got {other:?}"),
944 }
945 match ops[6] {
946 RenderOp::ExitClip { ref_value, .. } => assert_eq!(ref_value, 2),
947 other => panic!("expected inner ExitClip, got {other:?}"),
948 }
949 match ops[7] {
950 RenderOp::ExitClip { ref_value, .. } => assert_eq!(ref_value, 1),
951 other => panic!("expected outer ExitClip, got {other:?}"),
952 }
953 }
954
955 #[test]
956 fn cutout_stream_enters_and_exits_single_root_clip() {
957 let mut visuals = VisualWorld::default();
958
959 let clip_handle = visuals.register(
960 cid(20),
961 dummy_renderable(),
962 Transform::default(),
963 [1.0, 1.0, 1.0, 1.0],
964 1.0,
965 false,
966 true,
967 false,
968 false,
969 false,
970 0.0,
971 None,
972 3.0,
973 );
974 let content_handle = visuals.register(
975 cid(21),
976 dummy_renderable(),
977 Transform::default(),
978 [0.8, 0.8, 0.8, 1.0],
979 1.0,
980 false,
981 true,
982 false,
983 false,
984 false,
985 0.0,
986 None,
987 3.0,
988 );
989
990 let _ = visuals.register_stencil_clip(clip_handle, 0);
991 let _ = visuals.update_stencil_ref(content_handle, 1);
992 visuals.prepare_draw_cache();
993
994 let (ops, instance_indices) = visuals.cutout_stream();
995 assert_eq!(ops.len(), 4);
996 assert_eq!(instance_indices.len(), 2);
997
998 match ops[0] {
999 RenderOp::EnterClip {
1000 parent_ref,
1001 new_ref,
1002 ..
1003 } => {
1004 assert_eq!(parent_ref, 0);
1005 assert_eq!(new_ref, 1);
1006 }
1007 other => panic!("expected EnterClip, got {other:?}"),
1008 }
1009 match ops[1] {
1010 RenderOp::DrawBatch(batch) => {
1011 assert_eq!(batch.stencil_ref, 1);
1012 assert_eq!(batch.count, 1);
1013 assert_eq!(instance_indices[batch.start], 0);
1014 }
1015 other => panic!("expected clip-source DrawBatch, got {other:?}"),
1016 }
1017 match ops[2] {
1018 RenderOp::DrawBatch(batch) => {
1019 assert_eq!(batch.stencil_ref, 1);
1020 assert_eq!(batch.count, 1);
1021 assert_eq!(instance_indices[batch.start], 1);
1022 }
1023 other => panic!("expected content DrawBatch, got {other:?}"),
1024 }
1025 match ops[3] {
1026 RenderOp::ExitClip { ref_value, .. } => assert_eq!(ref_value, 1),
1027 other => panic!("expected ExitClip, got {other:?}"),
1028 }
1029 }
1030
1031 #[test]
1032 fn transparent_single_stream_enters_and_exits_single_root_clip() {
1033 let mut visuals = VisualWorld::default();
1034
1035 let clip_handle = visuals.register(
1036 cid(30),
1037 dummy_renderable(),
1038 Transform::default(),
1039 [1.0, 1.0, 1.0, 0.5],
1040 1.0,
1041 false,
1042 false,
1043 false,
1044 false,
1045 false,
1046 0.0,
1047 None,
1048 3.0,
1049 );
1050 let content_handle = visuals.register(
1051 cid(31),
1052 dummy_renderable(),
1053 Transform::default(),
1054 [0.8, 0.8, 0.8, 0.5],
1055 1.0,
1056 false,
1057 false,
1058 false,
1059 false,
1060 false,
1061 0.0,
1062 None,
1063 3.0,
1064 );
1065
1066 let _ = visuals.register_stencil_clip(clip_handle, 0);
1067 let _ = visuals.update_stencil_ref(content_handle, 1);
1068 visuals.prepare_draw_cache();
1069
1070 let (ops, instance_indices) = visuals.transparent_single_stream();
1071 assert_eq!(ops.len(), 4);
1072 assert_eq!(instance_indices.len(), 2);
1073
1074 match ops[0] {
1075 RenderOp::EnterClip {
1076 parent_ref,
1077 new_ref,
1078 ..
1079 } => {
1080 assert_eq!(parent_ref, 0);
1081 assert_eq!(new_ref, 1);
1082 }
1083 other => panic!("expected EnterClip, got {other:?}"),
1084 }
1085 match ops[1] {
1086 RenderOp::DrawBatch(batch) => {
1087 assert_eq!(batch.stencil_ref, 1);
1088 assert_eq!(batch.count, 1);
1089 assert_eq!(instance_indices[batch.start], 0);
1090 }
1091 other => panic!("expected clip-source DrawBatch, got {other:?}"),
1092 }
1093 match ops[2] {
1094 RenderOp::DrawBatch(batch) => {
1095 assert_eq!(batch.stencil_ref, 1);
1096 assert_eq!(batch.count, 1);
1097 assert_eq!(instance_indices[batch.start], 1);
1098 }
1099 other => panic!("expected content DrawBatch, got {other:?}"),
1100 }
1101 match ops[3] {
1102 RenderOp::ExitClip { ref_value, .. } => assert_eq!(ref_value, 1),
1103 other => panic!("expected ExitClip, got {other:?}"),
1104 }
1105 }
1106
1107 #[test]
1108 fn opaque_stream_uses_transparent_clip_source_for_stencil_ops() {
1109 let mut visuals = VisualWorld::default();
1110
1111 let clip_handle = visuals.register(
1112 cid(40),
1113 dummy_renderable(),
1114 Transform::default(),
1115 [1.0, 1.0, 1.0, 0.0],
1116 1.0,
1117 false,
1118 false,
1119 false,
1120 false,
1121 false,
1122 0.0,
1123 None,
1124 3.0,
1125 );
1126 let content_handle = visuals.register(
1127 cid(41),
1128 dummy_renderable(),
1129 Transform::default(),
1130 [0.8, 0.8, 0.8, 1.0],
1131 1.0,
1132 false,
1133 false,
1134 false,
1135 false,
1136 false,
1137 0.0,
1138 None,
1139 3.0,
1140 );
1141
1142 let _ = visuals.register_stencil_clip(clip_handle, 0);
1143 let _ = visuals.update_stencil_ref(content_handle, 1);
1144 visuals.prepare_draw_cache();
1145
1146 let (ops, instance_indices) = visuals.opaque_stream();
1147 assert_eq!(ops.len(), 3);
1148 assert_eq!(instance_indices, &[0, 1]);
1149
1150 match ops[0] {
1151 RenderOp::EnterClip {
1152 parent_ref,
1153 new_ref,
1154 ..
1155 } => {
1156 assert_eq!(parent_ref, 0);
1157 assert_eq!(new_ref, 1);
1158 }
1159 other => panic!("expected EnterClip, got {other:?}"),
1160 }
1161 match ops[1] {
1162 RenderOp::DrawBatch(batch) => {
1163 assert_eq!(batch.stencil_ref, 1);
1164 assert_eq!(batch.count, 1);
1165 assert_eq!(instance_indices[batch.start], 1);
1166 }
1167 other => panic!("expected content DrawBatch, got {other:?}"),
1168 }
1169 match ops[2] {
1170 RenderOp::ExitClip { ref_value, .. } => assert_eq!(ref_value, 1),
1171 other => panic!("expected ExitClip, got {other:?}"),
1172 }
1173 }
1174
1175 #[test]
1176 fn cutout_stream_uses_opaque_clip_source_for_stencil_ops() {
1177 let mut visuals = VisualWorld::default();
1178
1179 let clip_handle = visuals.register(
1180 cid(50),
1181 dummy_renderable(),
1182 Transform::default(),
1183 [1.0, 1.0, 1.0, 1.0],
1184 1.0,
1185 false,
1186 false,
1187 false,
1188 false,
1189 false,
1190 0.0,
1191 None,
1192 3.0,
1193 );
1194 let content_handle = visuals.register(
1195 cid(51),
1196 dummy_renderable(),
1197 Transform::default(),
1198 [0.8, 0.8, 0.8, 1.0],
1199 1.0,
1200 false,
1201 true,
1202 false,
1203 false,
1204 false,
1205 0.0,
1206 None,
1207 3.0,
1208 );
1209
1210 let _ = visuals.register_stencil_clip(clip_handle, 0);
1211 let _ = visuals.update_stencil_ref(content_handle, 1);
1212 visuals.prepare_draw_cache();
1213
1214 let (ops, instance_indices) = visuals.cutout_stream();
1215 assert_eq!(ops.len(), 3);
1216 assert_eq!(instance_indices, &[0, 1]);
1217
1218 match ops[0] {
1219 RenderOp::EnterClip {
1220 parent_ref,
1221 new_ref,
1222 ..
1223 } => {
1224 assert_eq!(parent_ref, 0);
1225 assert_eq!(new_ref, 1);
1226 }
1227 other => panic!("expected EnterClip, got {other:?}"),
1228 }
1229 match ops[1] {
1230 RenderOp::DrawBatch(batch) => {
1231 assert_eq!(batch.stencil_ref, 1);
1232 assert_eq!(batch.count, 1);
1233 assert_eq!(instance_indices[batch.start], 1);
1234 }
1235 other => panic!("expected content DrawBatch, got {other:?}"),
1236 }
1237 match ops[2] {
1238 RenderOp::ExitClip { ref_value, .. } => assert_eq!(ref_value, 1),
1239 other => panic!("expected ExitClip, got {other:?}"),
1240 }
1241 }
1242
1243 #[test]
1244 fn opaque_stream_excluding_source_instance_skips_clip_ops_for_that_source() {
1245 let mut visuals = VisualWorld::default();
1246
1247 let clip_handle = visuals.register(
1248 cid(60),
1249 dummy_renderable(),
1250 Transform::default(),
1251 [1.0, 1.0, 1.0, 0.0],
1252 1.0,
1253 false,
1254 false,
1255 false,
1256 false,
1257 false,
1258 0.0,
1259 None,
1260 3.0,
1261 );
1262 let content_handle = visuals.register(
1263 cid(61),
1264 dummy_renderable(),
1265 Transform::default(),
1266 [0.8, 0.8, 0.8, 1.0],
1267 1.0,
1268 false,
1269 false,
1270 false,
1271 false,
1272 false,
1273 0.0,
1274 None,
1275 3.0,
1276 );
1277
1278 let _ = visuals.register_stencil_clip(clip_handle, 0);
1279 let _ = visuals.update_stencil_ref(content_handle, 1);
1280 visuals.prepare_draw_cache();
1281
1282 let (ops, instance_indices) = visuals.opaque_stream_excluding(Some(clip_handle));
1283 assert_eq!(instance_indices, &[1]);
1284 assert_eq!(ops.len(), 1);
1285 match ops[0] {
1286 RenderOp::DrawBatch(batch) => {
1287 assert_eq!(batch.count, 1);
1288 assert_eq!(batch.stencil_ref, 1);
1289 assert_eq!(instance_indices[batch.start], 1);
1290 }
1291 other => panic!("expected DrawBatch, got {other:?}"),
1292 }
1293 }
1294
1295 #[test]
1296 fn phase_exclusion_helpers_drop_source_from_overlay_cutout_and_transparent_passes() {
1297 let mut visuals = VisualWorld::default();
1298
1299 let overlay_handle = visuals.register(
1300 cid(70),
1301 dummy_renderable(),
1302 Transform::default(),
1303 [1.0, 1.0, 1.0, 1.0],
1304 1.0,
1305 false,
1306 false,
1307 false,
1308 false,
1309 true,
1310 0.0,
1311 None,
1312 3.0,
1313 );
1314 let cutout_handle = visuals.register(
1315 cid(71),
1316 dummy_renderable(),
1317 Transform::default(),
1318 [1.0, 1.0, 1.0, 1.0],
1319 1.0,
1320 false,
1321 true,
1322 false,
1323 false,
1324 false,
1325 0.0,
1326 None,
1327 3.0,
1328 );
1329 let transparent_single_handle = visuals.register(
1330 cid(72),
1331 dummy_renderable(),
1332 Transform::default(),
1333 [1.0, 1.0, 1.0, 0.5],
1334 1.0,
1335 false,
1336 false,
1337 false,
1338 false,
1339 false,
1340 0.0,
1341 None,
1342 3.0,
1343 );
1344 let transparent_multi_handle = visuals.register(
1345 cid(73),
1346 dummy_renderable(),
1347 Transform::default(),
1348 [1.0, 1.0, 1.0, 0.5],
1349 1.0,
1350 true,
1351 false,
1352 false,
1353 false,
1354 false,
1355 0.0,
1356 None,
1357 3.0,
1358 );
1359 visuals.prepare_draw_cache();
1360 visuals.prepare_transparent_multi_draw_cache_for_view(Transform::default().model);
1361
1362 let (_, overlay_instances) = visuals.overlay_stream_excluding(Some(overlay_handle));
1363 let (_, cutout_instances) = visuals.cutout_stream_excluding(Some(cutout_handle));
1364 let (_, transparent_single_instances) =
1365 visuals.transparent_single_stream_excluding(Some(transparent_single_handle));
1366 let (transparent_multi_order, _) =
1367 visuals.transparent_multi_draw_excluding(Some(transparent_multi_handle));
1368
1369 assert!(!overlay_instances.contains(&0));
1370 assert!(!cutout_instances.contains(&1));
1371 assert!(!transparent_single_instances.contains(&2));
1372 assert!(!transparent_multi_order.contains(&3));
1373 }
1374}
1375#[derive(Debug, Clone, Copy, Default)]
1376pub struct VisualPointLight {
1377 pub light_type: u32,
1383 pub position_ws: [f32; 3],
1384 pub intensity: f32,
1385 pub distance: f32,
1386 pub color: [f32; 3],
1387}
1388
1389impl VisualWorld {
1390 pub fn new() -> Self {
1391 Self::default()
1392 }
1393
1394 fn is_transparent(inst: &VisualInstance) -> bool {
1395 inst.opacity < 0.999 || inst.color[3] < 0.999
1398 }
1399
1400 fn view_space_z(view: TransformMatrix, model: TransformMatrix) -> f32 {
1401 let tx = model[3][0];
1404 let ty = model[3][1];
1405 let tz = model[3][2];
1406
1407 view[0][2] * tx + view[1][2] * ty + view[2][2] * tz + view[3][2]
1409 }
1410
1411 fn build_draw_batches_for_order(
1412 instances: &[VisualInstance],
1413 order: &[u32],
1414 out: &mut Vec<DrawBatch>,
1415 ) {
1416 out.clear();
1417 let mut cursor = 0usize;
1418 while cursor < order.len() {
1419 let idx0 = order[cursor] as usize;
1420 let inst0 = instances[idx0];
1421 let r0 = inst0.renderable;
1422 let material = r0.material;
1423 let mesh = r0.mesh;
1424 let texture = inst0.texture;
1425 let texture_filtering = inst0.texture_filtering;
1426 let quant_steps = sanitize_quant_steps(inst0.quant_steps);
1427 let stencil_ref = inst0.stencil_ref;
1428
1429 let start = cursor;
1430 cursor += 1;
1431
1432 while cursor < order.len() {
1433 let idx = order[cursor] as usize;
1434 let inst = instances[idx];
1435 let r = inst.renderable;
1436 if r.material == material
1437 && r.mesh == mesh
1438 && inst.texture == texture
1439 && inst.texture_filtering == texture_filtering
1440 && sanitize_quant_steps(inst.quant_steps).to_bits() == quant_steps.to_bits()
1441 && inst.stencil_ref == stencil_ref
1442 {
1443 cursor += 1;
1444 } else {
1445 break;
1446 }
1447 }
1448
1449 out.push(DrawBatch {
1450 material,
1451 mesh,
1452 texture,
1453 texture_filtering,
1454 quant_steps,
1455 stencil_ref,
1456 start,
1457 count: cursor - start,
1458 });
1459 }
1460 }
1461
1462 fn filtered_phase_order(
1463 &self,
1464 order: &[u32],
1465 excluded_instance: Option<InstanceHandle>,
1466 ) -> Vec<u32> {
1467 let Some(excluded_index) =
1468 excluded_instance.and_then(|handle| self.handle_to_index.get(&handle).copied())
1469 else {
1470 return order.to_vec();
1471 };
1472 order
1473 .iter()
1474 .copied()
1475 .filter(|&idx| idx as usize != excluded_index)
1476 .collect()
1477 }
1478
1479 fn filtered_clip_sources_by_depth(
1480 &self,
1481 max_depth: u8,
1482 excluded_instance: Option<InstanceHandle>,
1483 ) -> Vec<Vec<u32>> {
1484 let mut clip_sources_by_depth = vec![Vec::new(); max_depth as usize + 1];
1485 let excluded_index =
1486 excluded_instance.and_then(|handle| self.handle_to_index.get(&handle).copied());
1487 for (index, inst) in self.instances.iter().enumerate() {
1488 if !inst.is_stencil_clip {
1489 continue;
1490 }
1491 if Some(index) == excluded_index {
1492 continue;
1493 }
1494 clip_sources_by_depth[inst.stencil_ref as usize].push(index as u32);
1495 }
1496 clip_sources_by_depth
1497 }
1498
1499 fn build_phase_render_stream(
1508 instances: &[VisualInstance],
1509 phase_order: &[u32],
1510 out_ops: &mut Vec<RenderOp>,
1511 out_instances: &mut Vec<u32>,
1512 ) {
1513 out_ops.clear();
1514 out_instances.clear();
1515
1516 if phase_order.is_empty() {
1517 return;
1518 }
1519
1520 let max_depth = phase_order
1521 .iter()
1522 .map(|&i| instances[i as usize].stencil_ref)
1523 .chain(
1524 instances
1525 .iter()
1526 .filter(|inst| inst.is_stencil_clip)
1527 .map(|inst| inst.stencil_ref),
1528 )
1529 .max()
1530 .unwrap_or(0);
1531
1532 let depth_count = max_depth as usize + 1;
1535 let mut non_clip_by_depth: Vec<Vec<u32>> = vec![Vec::new(); depth_count];
1536 let mut phase_clip_sources_by_depth: Vec<Vec<u32>> = vec![Vec::new(); depth_count];
1537 let mut all_clip_sources_by_depth: Vec<Vec<u32>> = vec![Vec::new(); depth_count];
1538
1539 for (index, inst) in instances.iter().enumerate() {
1540 if inst.is_stencil_clip {
1541 all_clip_sources_by_depth[inst.stencil_ref as usize].push(index as u32);
1542 }
1543 }
1544
1545 for &idx in phase_order {
1546 let inst = &instances[idx as usize];
1547 let d = inst.stencil_ref as usize;
1548 if inst.is_stencil_clip {
1549 phase_clip_sources_by_depth[d].push(idx);
1550 } else {
1551 non_clip_by_depth[d].push(idx);
1552 }
1553 }
1554
1555 Self::build_overlay_level(
1556 0,
1557 max_depth,
1558 instances,
1559 &non_clip_by_depth,
1560 &all_clip_sources_by_depth,
1561 &phase_clip_sources_by_depth,
1562 out_ops,
1563 out_instances,
1564 );
1565 }
1566
1567 fn build_phase_render_stream_excluding(
1568 &self,
1569 phase_order: &[u32],
1570 excluded_instance: Option<InstanceHandle>,
1571 out_ops: &mut Vec<RenderOp>,
1572 out_instances: &mut Vec<u32>,
1573 ) {
1574 let filtered_order = self.filtered_phase_order(phase_order, excluded_instance);
1575 out_ops.clear();
1576 out_instances.clear();
1577
1578 if filtered_order.is_empty() {
1579 return;
1580 }
1581
1582 let excluded_index =
1583 excluded_instance.and_then(|handle| self.handle_to_index.get(&handle).copied());
1584 let max_depth = filtered_order
1585 .iter()
1586 .map(|&i| self.instances[i as usize].stencil_ref)
1587 .chain(
1588 self.instances
1589 .iter()
1590 .enumerate()
1591 .filter(|inst| {
1592 let (index, inst) = inst;
1593 inst.is_stencil_clip && Some(*index) != excluded_index
1594 })
1595 .map(|(_, inst)| inst.stencil_ref),
1596 )
1597 .max()
1598 .unwrap_or(0);
1599
1600 let depth_count = max_depth as usize + 1;
1601 let mut non_clip_by_depth: Vec<Vec<u32>> = vec![Vec::new(); depth_count];
1602 let mut phase_clip_sources_by_depth: Vec<Vec<u32>> = vec![Vec::new(); depth_count];
1603 let all_clip_sources_by_depth =
1604 self.filtered_clip_sources_by_depth(max_depth, excluded_instance);
1605
1606 for &idx in &filtered_order {
1607 let inst = &self.instances[idx as usize];
1608 let d = inst.stencil_ref as usize;
1609 if inst.is_stencil_clip {
1610 phase_clip_sources_by_depth[d].push(idx);
1611 } else {
1612 non_clip_by_depth[d].push(idx);
1613 }
1614 }
1615
1616 Self::build_overlay_level(
1617 0,
1618 max_depth,
1619 &self.instances,
1620 &non_clip_by_depth,
1621 &all_clip_sources_by_depth,
1622 &phase_clip_sources_by_depth,
1623 out_ops,
1624 out_instances,
1625 );
1626 }
1627
1628 fn build_overlay_level(
1629 depth: usize,
1630 max_depth: u8,
1631 instances: &[VisualInstance],
1632 non_clip_by_depth: &[Vec<u32>],
1633 all_clip_sources_by_depth: &[Vec<u32>],
1634 phase_clip_sources_by_depth: &[Vec<u32>],
1635 out_ops: &mut Vec<RenderOp>,
1636 out_instances: &mut Vec<u32>,
1637 ) {
1638 if depth >= non_clip_by_depth.len() {
1639 return;
1640 }
1641
1642 let non_clip = &non_clip_by_depth[depth];
1644 if !non_clip.is_empty() {
1645 Self::append_stream_batches(instances, non_clip, depth as u8, out_ops, out_instances);
1646 }
1647
1648 let all_clip_sources = &all_clip_sources_by_depth[depth];
1649 if !all_clip_sources.is_empty() {
1650 let parent_ref = depth as u8;
1651 let new_ref = parent_ref.saturating_add(1);
1652 let phase_clip_sources = &phase_clip_sources_by_depth[depth];
1653
1654 for &src_idx in all_clip_sources {
1656 if !phase_clip_sources.contains(&src_idx) {
1657 Self::append_clip_stream_instance(out_instances, src_idx);
1658 }
1659 out_ops.push(RenderOp::EnterClip {
1660 instance_index: src_idx,
1661 parent_ref,
1662 new_ref,
1663 });
1664 }
1665
1666 if !phase_clip_sources.is_empty() {
1669 Self::append_stream_batches(
1670 instances,
1671 phase_clip_sources,
1672 new_ref,
1673 out_ops,
1674 out_instances,
1675 );
1676 }
1677
1678 if depth + 1 <= max_depth as usize {
1680 Self::build_overlay_level(
1681 depth + 1,
1682 max_depth,
1683 instances,
1684 non_clip_by_depth,
1685 all_clip_sources_by_depth,
1686 phase_clip_sources_by_depth,
1687 out_ops,
1688 out_instances,
1689 );
1690 }
1691
1692 for &src_idx in all_clip_sources.iter().rev() {
1695 out_ops.push(RenderOp::ExitClip {
1696 instance_index: src_idx,
1697 ref_value: new_ref,
1698 });
1699 }
1700 } else if depth + 1 <= max_depth as usize {
1701 Self::build_overlay_level(
1703 depth + 1,
1704 max_depth,
1705 instances,
1706 non_clip_by_depth,
1707 all_clip_sources_by_depth,
1708 phase_clip_sources_by_depth,
1709 out_ops,
1710 out_instances,
1711 );
1712 }
1713 }
1714
1715 fn append_clip_stream_instance(out_instances: &mut Vec<u32>, instance_index: u32) {
1716 if !out_instances.contains(&instance_index) {
1717 out_instances.push(instance_index);
1718 }
1719 }
1720
1721 fn append_stream_batches(
1726 instances: &[VisualInstance],
1727 indices: &[u32],
1728 effective_ref: u8,
1729 out_ops: &mut Vec<RenderOp>,
1730 out_instances: &mut Vec<u32>,
1731 ) {
1732 let mut cursor = 0usize;
1733 while cursor < indices.len() {
1734 let idx0 = indices[cursor] as usize;
1735 let inst0 = &instances[idx0];
1736 let r0 = inst0.renderable;
1737 let material = r0.material;
1738 let mesh = r0.mesh;
1739 let texture = inst0.texture;
1740 let texture_filtering = inst0.texture_filtering;
1741 let quant_steps = sanitize_quant_steps(inst0.quant_steps);
1742
1743 let start = out_instances.len();
1744 out_instances.push(indices[cursor]);
1745 cursor += 1;
1746
1747 while cursor < indices.len() {
1748 let idx = indices[cursor] as usize;
1749 let inst = &instances[idx];
1750 let r = inst.renderable;
1751 if r.material == material
1752 && r.mesh == mesh
1753 && inst.texture == texture
1754 && inst.texture_filtering == texture_filtering
1755 && sanitize_quant_steps(inst.quant_steps).to_bits() == quant_steps.to_bits()
1756 {
1757 out_instances.push(indices[cursor]);
1758 cursor += 1;
1759 } else {
1760 break;
1761 }
1762 }
1763
1764 out_ops.push(RenderOp::DrawBatch(DrawBatch {
1765 material,
1766 mesh,
1767 texture,
1768 texture_filtering,
1769 quant_steps,
1770 stencil_ref: effective_ref,
1771 start,
1772 count: out_instances.len() - start,
1773 }));
1774 }
1775 }
1776
1777 pub fn clear_color(&self) -> [f32; 4] {
1778 self.clear_color
1779 }
1780
1781 pub fn set_clear_color(&mut self, rgba: [f32; 4]) {
1782 self.clear_color = rgba;
1783 }
1784
1785 pub fn renderer_msaa_mode(&self) -> MsaaMode {
1786 self.renderer_msaa_mode
1787 }
1788
1789 pub fn set_renderer_msaa_mode(&mut self, mode: MsaaMode) {
1790 self.renderer_msaa_mode = mode;
1791 }
1792
1793 pub fn preferred_window_size(&self) -> Option<[u32; 2]> {
1794 self.preferred_window_size
1795 }
1796
1797 pub fn set_preferred_window_size(&mut self, size: Option<[u32; 2]>) {
1798 self.preferred_window_size = size.filter(|[w, h]| *w > 0 && *h > 0);
1799 }
1800
1801 pub fn take_preferred_window_size(&mut self) -> Option<[u32; 2]> {
1802 self.preferred_window_size.take()
1803 }
1804
1805 pub fn window_frame_dt_sec(&self) -> f32 {
1806 self.window_frame_dt_sec
1807 }
1808
1809 pub fn window_frame_fps(&self) -> f32 {
1810 if self.window_frame_dt_sec > 0.0 {
1811 1.0 / self.window_frame_dt_sec
1812 } else {
1813 0.0
1814 }
1815 }
1816
1817 pub fn set_window_frame_dt_sec(&mut self, dt_sec: f32) {
1818 self.window_frame_dt_sec = dt_sec.max(0.0);
1819 }
1820
1821 pub fn xr_frame_dt_sec(&self) -> Option<f32> {
1822 self.xr_frame_dt_sec
1823 }
1824
1825 pub fn xr_frame_fps(&self) -> Option<f32> {
1826 let dt = self.xr_frame_dt_sec?;
1827 if dt > 0.0 { Some(1.0 / dt) } else { None }
1828 }
1829
1830 pub fn set_xr_frame_dt_sec(&mut self, dt_sec: Option<f32>) {
1831 self.xr_frame_dt_sec = dt_sec.and_then(|dt| {
1832 if dt.is_finite() {
1833 Some(dt.max(0.0))
1834 } else {
1835 None
1836 }
1837 });
1838 }
1839
1840 pub fn ambient_light(&self) -> [f32; 3] {
1841 self.ambient_light
1842 }
1843
1844 pub fn set_ambient_light(&mut self, rgb: [f32; 3]) {
1845 self.ambient_light = rgb;
1846 self.dirty_camera = true;
1848 }
1849
1850 pub fn clear(&mut self) {
1851 self.instances.clear();
1852 self.handle_to_index.clear();
1853 self.component_to_handle.clear();
1854 self.next_handle = 0;
1855
1856 self.point_lights.clear();
1857 self.point_light_index_by_component.clear();
1858 self.dirty_lights = true;
1859
1860 self.ambient_light = [0.0, 0.0, 0.0];
1861
1862 self.dirty_draw_cache = true;
1863 self.dirty_instance_data = true;
1864 self.dirty_camera = true;
1865 self.background_order.clear();
1866 self.background_batches.clear();
1867 self.background_occluded_lit_order.clear();
1868 self.background_occluded_lit_batches.clear();
1869 self.background_occluded_lit_emissive_order.clear();
1870 self.background_occluded_lit_emissive_batches.clear();
1871 self.draw_order.clear();
1872 self.draw_batches.clear();
1873 self.emissive_draw_order.clear();
1874 self.emissive_draw_batches.clear();
1875 self.cutout_order.clear();
1876 self.cutout_batches.clear();
1877 self.emissive_cutout_order.clear();
1878 self.emissive_cutout_batches.clear();
1879 self.cutout_stream.clear();
1880 self.cutout_stream_instances.clear();
1881
1882 self.transparent_single_draw_order.clear();
1883 self.transparent_single_draw_batches.clear();
1884 self.transparent_single_stream.clear();
1885 self.transparent_single_stream_instances.clear();
1886 self.transparent_multi_draw_order.clear();
1887 self.transparent_multi_draw_batches.clear();
1888
1889 self.active_xr_camera = None;
1890 }
1891
1892 pub fn active_xr_camera(&self) -> Option<ComponentId> {
1893 self.active_xr_camera
1894 }
1895
1896 pub fn set_active_xr_camera(&mut self, component: Option<ComponentId>) {
1897 self.active_xr_camera = component;
1898 }
1899
1900 pub fn lights_dirty(&self) -> bool {
1901 self.dirty_lights
1902 }
1903
1904 pub fn take_lights_dirty(&mut self) -> bool {
1905 let v = self.dirty_lights;
1906 self.dirty_lights = false;
1907 v
1908 }
1909
1910 pub fn point_lights(&self) -> &[VisualPointLight] {
1911 &self.point_lights
1912 }
1913
1914 pub fn upsert_point_light(&mut self, cid: ComponentId, light: VisualPointLight) {
1915 if let Some(&idx) = self.point_light_index_by_component.get(&cid) {
1916 self.point_lights[idx] = light;
1917 } else {
1918 let idx = self.point_lights.len();
1919 self.point_lights.push(light);
1920 self.point_light_index_by_component.insert(cid, idx);
1921 }
1922 self.dirty_lights = true;
1923 }
1924
1925 pub fn camera_dirty(&self) -> bool {
1926 self.dirty_camera
1927 }
1928
1929 pub fn take_camera_dirty(&mut self) -> bool {
1930 let v = self.dirty_camera;
1931 self.dirty_camera = false;
1932 v
1933 }
1934
1935 pub fn visual_cameras(&self) -> &[VisualCamera] {
1936 &self.visual_cameras
1937 }
1938
1939 pub fn visual_camera(&self, target: CameraTarget) -> Option<&VisualCamera> {
1940 self.visual_cameras.iter().find(|c| c.target == target)
1941 }
1942
1943 fn visual_camera_mut(&mut self, target: CameraTarget) -> &mut VisualCamera {
1944 if let Some(i) = self.visual_cameras.iter().position(|c| c.target == target) {
1945 return &mut self.visual_cameras[i];
1946 }
1947
1948 self.visual_cameras.push(VisualCamera {
1949 target,
1950 eyes: Vec::new(),
1951 });
1952 self.visual_cameras.last_mut().unwrap()
1953 }
1954
1955 pub fn camera_view(&self) -> [[f32; 4]; 4] {
1957 self.camera_view_for(CameraTarget::Window)
1958 }
1959
1960 pub fn camera_proj(&self) -> [[f32; 4]; 4] {
1962 self.camera_proj_for(CameraTarget::Window)
1963 }
1964
1965 pub fn camera_view_for(&self, target: CameraTarget) -> [[f32; 4]; 4] {
1966 self.camera_view_for_eye(target, 0)
1967 }
1968
1969 pub fn camera_view_for_eye(&self, target: CameraTarget, eye: usize) -> [[f32; 4]; 4] {
1970 self.visual_camera(target)
1971 .and_then(|c| c.eyes.get(eye))
1972 .map(|e| e.view)
1973 .unwrap_or([
1974 [1.0, 0.0, 0.0, 0.0],
1975 [0.0, 1.0, 0.0, 0.0],
1976 [0.0, 0.0, 1.0, 0.0],
1977 [0.0, 0.0, 0.0, 1.0],
1978 ])
1979 }
1980
1981 pub fn camera_proj_for(&self, target: CameraTarget) -> [[f32; 4]; 4] {
1982 self.camera_proj_for_eye(target, 0)
1983 }
1984
1985 pub fn camera_proj_for_eye(&self, target: CameraTarget, eye: usize) -> [[f32; 4]; 4] {
1986 self.visual_camera(target)
1987 .and_then(|c| c.eyes.get(eye))
1988 .map(|e| e.proj)
1989 .unwrap_or([
1990 [1.0, 0.0, 0.0, 0.0],
1991 [0.0, 1.0, 0.0, 0.0],
1992 [0.0, 0.0, 1.0, 0.0],
1993 [0.0, 0.0, 0.0, 1.0],
1994 ])
1995 }
1996
1997 pub fn viewport(&self) -> [f32; 2] {
1998 self.viewport
1999 }
2000
2001 pub fn set_viewport(&mut self, viewport: [f32; 2]) {
2002 self.viewport = viewport;
2003 }
2004
2005 pub fn camera_2d(&self) -> [[f32; 4]; 3] {
2006 self.camera_2d
2007 }
2008
2009 pub fn set_camera(&mut self, view: [[f32; 4]; 4], proj: [[f32; 4]; 4]) {
2010 self.set_camera_mono_for_target(CameraTarget::Window, view, proj);
2011 self.camera_2d = [
2013 [1.0, 0.0, 0.0, 0.0],
2014 [0.0, 1.0, 0.0, 0.0],
2015 [0.0, 0.0, 1.0, 0.0],
2016 ];
2017 self.dirty_camera = true;
2018 }
2019
2020 pub fn set_camera_for_target(&mut self, target: CameraTarget, eyes: Vec<CameraData>) {
2025 let c = self.visual_camera_mut(target);
2026 c.eyes = eyes;
2027 self.dirty_camera = true;
2028 }
2029
2030 pub fn set_camera_mono_for_target(
2032 &mut self,
2033 target: CameraTarget,
2034 view: [[f32; 4]; 4],
2035 proj: [[f32; 4]; 4],
2036 ) {
2037 self.set_camera_mono_for_target_with_transform(target, view, proj, Transform::default());
2038 }
2039
2040 pub fn set_camera_mono_for_target_with_transform(
2041 &mut self,
2042 target: CameraTarget,
2043 view: [[f32; 4]; 4],
2044 proj: [[f32; 4]; 4],
2045 transform: Transform,
2046 ) {
2047 self.set_camera_for_target(
2048 target,
2049 vec![CameraData {
2050 view,
2051 proj,
2052 transform,
2053 }],
2054 );
2055 }
2056
2057 pub fn set_xr_camera(&mut self, eyes: Vec<CameraData>) {
2059 self.set_camera_for_target(CameraTarget::Xr, eyes);
2060 }
2061
2062 pub fn set_camera_2d(&mut self, m: [[f32; 4]; 3]) {
2063 if self.camera_2d == m {
2064 return;
2065 }
2066 self.camera_2d = m;
2067 self.dirty_camera = true;
2068 }
2069
2070 pub fn register_mirror(&mut self, mirror: VisualMirror) {
2071 self.mirrors.push(mirror);
2072 }
2073
2074 pub fn clear_mirrors(&mut self) {
2075 self.mirrors.clear();
2076 }
2077
2078 pub fn mirrors(&self) -> &[VisualMirror] {
2079 &self.mirrors
2080 }
2081
2082 pub fn mirror_texture_key_for_view(
2083 &self,
2084 mirror_component: ComponentId,
2085 family: MirrorViewerFamily,
2086 view_index: usize,
2087 ) -> Option<&str> {
2088 self.mirrors
2089 .iter()
2090 .find(|mirror| mirror.mirror_component == mirror_component)
2091 .and_then(|mirror| {
2092 mirror
2093 .captures
2094 .iter()
2095 .find(|capture| capture.family == family && capture.view_index == view_index)
2096 .map(|capture| capture.target_key.as_str())
2097 })
2098 }
2099
2100 pub fn mirror_count(&self) -> usize {
2101 self.mirrors.len()
2102 }
2103
2104 pub fn instance_data_dirty(&self) -> bool {
2106 self.dirty_instance_data
2107 }
2108
2109 pub fn take_instance_data_dirty(&mut self) -> bool {
2111 let v = self.dirty_instance_data;
2112 self.dirty_instance_data = false;
2113 v
2114 }
2115
2116 pub fn instances(&self) -> &[VisualInstance] {
2117 &self.instances
2118 }
2119
2120 pub fn instance_count(&self) -> usize {
2121 self.instances.len()
2122 }
2123
2124 pub fn background_order(&self) -> &[u32] {
2126 &self.background_order
2127 }
2128
2129 pub fn background_batches(&self) -> &[DrawBatch] {
2130 &self.background_batches
2131 }
2132
2133 pub fn background_occluded_lit_order(&self) -> &[u32] {
2134 &self.background_occluded_lit_order
2135 }
2136
2137 pub fn background_occluded_lit_batches(&self) -> &[DrawBatch] {
2138 &self.background_occluded_lit_batches
2139 }
2140
2141 pub fn background_occluded_lit_emissive_order(&self) -> &[u32] {
2142 &self.background_occluded_lit_emissive_order
2143 }
2144
2145 pub fn background_occluded_lit_emissive_batches(&self) -> &[DrawBatch] {
2146 &self.background_occluded_lit_emissive_batches
2147 }
2148
2149 pub fn has_background_occluded_lit_emissive(&self) -> bool {
2150 !self.background_occluded_lit_emissive_order.is_empty()
2151 }
2152
2153 pub fn draw_order(&self) -> &[u32] {
2155 &self.draw_order
2156 }
2157
2158 pub fn draw_batches(&self) -> &[DrawBatch] {
2159 &self.draw_batches
2160 }
2161
2162 pub fn emissive_draw_order(&self) -> &[u32] {
2164 &self.emissive_draw_order
2165 }
2166
2167 pub fn emissive_draw_batches(&self) -> &[DrawBatch] {
2168 &self.emissive_draw_batches
2169 }
2170
2171 pub fn cutout_order(&self) -> &[u32] {
2173 &self.cutout_order
2174 }
2175
2176 pub fn cutout_batches(&self) -> &[DrawBatch] {
2177 &self.cutout_batches
2178 }
2179
2180 pub fn cutout_stream(&self) -> (&[RenderOp], &[u32]) {
2182 (&self.cutout_stream, &self.cutout_stream_instances)
2183 }
2184
2185 pub fn cutout_stream_excluding(
2186 &self,
2187 excluded_instance: Option<InstanceHandle>,
2188 ) -> (Vec<RenderOp>, Vec<u32>) {
2189 let mut ops = Vec::new();
2190 let mut instances = Vec::new();
2191 self.build_phase_render_stream_excluding(
2192 &self.cutout_order,
2193 excluded_instance,
2194 &mut ops,
2195 &mut instances,
2196 );
2197 (ops, instances)
2198 }
2199
2200 pub fn emissive_cutout_order(&self) -> &[u32] {
2202 &self.emissive_cutout_order
2203 }
2204
2205 pub fn emissive_cutout_batches(&self) -> &[DrawBatch] {
2206 &self.emissive_cutout_batches
2207 }
2208
2209 pub fn overlay_order(&self) -> &[u32] {
2211 &self.overlay_order
2212 }
2213
2214 pub fn overlay_batches(&self) -> &[DrawBatch] {
2215 &self.overlay_batches
2216 }
2217
2218 pub fn transparent_single_stream(&self) -> (&[RenderOp], &[u32]) {
2220 (
2221 &self.transparent_single_stream,
2222 &self.transparent_single_stream_instances,
2223 )
2224 }
2225
2226 pub fn transparent_single_stream_excluding(
2227 &self,
2228 excluded_instance: Option<InstanceHandle>,
2229 ) -> (Vec<RenderOp>, Vec<u32>) {
2230 let mut ops = Vec::new();
2231 let mut instances = Vec::new();
2232 self.build_phase_render_stream_excluding(
2233 &self.transparent_single_draw_order,
2234 excluded_instance,
2235 &mut ops,
2236 &mut instances,
2237 );
2238 (ops, instances)
2239 }
2240
2241 pub fn overlay_stream(&self) -> (&[RenderOp], &[u32]) {
2248 (&self.overlay_stream, &self.overlay_stream_instances)
2249 }
2250
2251 pub fn overlay_stream_excluding(
2252 &self,
2253 excluded_instance: Option<InstanceHandle>,
2254 ) -> (Vec<RenderOp>, Vec<u32>) {
2255 let mut ops = Vec::new();
2256 let mut instances = Vec::new();
2257 self.build_phase_render_stream_excluding(
2258 &self.overlay_order,
2259 excluded_instance,
2260 &mut ops,
2261 &mut instances,
2262 );
2263 (ops, instances)
2264 }
2265
2266 pub fn opaque_stream(&self) -> (&[RenderOp], &[u32]) {
2270 (&self.opaque_stream, &self.opaque_stream_instances)
2271 }
2272
2273 pub fn opaque_stream_excluding(
2274 &self,
2275 excluded_instance: Option<InstanceHandle>,
2276 ) -> (Vec<RenderOp>, Vec<u32>) {
2277 let mut ops = Vec::new();
2278 let mut instances = Vec::new();
2279 self.build_phase_render_stream_excluding(
2280 &self.draw_order,
2281 excluded_instance,
2282 &mut ops,
2283 &mut instances,
2284 );
2285 (ops, instances)
2286 }
2287
2288 pub fn stencil_clip_order(&self) -> &[u32] {
2291 &self.stencil_clip_order
2292 }
2293
2294 pub fn register_stencil_clip(&mut self, handle: InstanceHandle, stencil_ref: u8) -> bool {
2297 if let Some(&idx) = self.handle_to_index.get(&handle) {
2298 self.instances[idx].is_stencil_clip = true;
2299 self.instances[idx].stencil_ref = stencil_ref;
2300 self.dirty_draw_cache = true;
2301 true
2302 } else {
2303 false
2304 }
2305 }
2306
2307 pub fn unregister_stencil_clip(&mut self, handle: InstanceHandle) -> bool {
2309 if let Some(&idx) = self.handle_to_index.get(&handle) {
2310 self.instances[idx].is_stencil_clip = false;
2311 self.dirty_draw_cache = true;
2312 true
2313 } else {
2314 false
2315 }
2316 }
2317
2318 pub fn update_stencil_ref(&mut self, handle: InstanceHandle, stencil_ref: u8) -> bool {
2320 if let Some(&idx) = self.handle_to_index.get(&handle) {
2321 if self.instances[idx].stencil_ref == stencil_ref {
2322 return true;
2323 }
2324 self.instances[idx].stencil_ref = stencil_ref;
2325 self.dirty_draw_cache = true;
2326 true
2327 } else {
2328 false
2329 }
2330 }
2331
2332 pub fn transparent_single_draw_order(&self) -> &[u32] {
2334 &self.transparent_single_draw_order
2335 }
2336
2337 pub fn transparent_single_draw_batches(&self) -> &[DrawBatch] {
2338 &self.transparent_single_draw_batches
2339 }
2340
2341 pub fn transparent_multi_draw_order(&self) -> &[u32] {
2343 &self.transparent_multi_draw_order
2344 }
2345
2346 pub fn transparent_multi_draw_batches(&self) -> &[DrawBatch] {
2347 &self.transparent_multi_draw_batches
2348 }
2349
2350 pub fn transparent_multi_draw_excluding(
2351 &self,
2352 excluded_instance: Option<InstanceHandle>,
2353 ) -> (Vec<u32>, Vec<DrawBatch>) {
2354 let order =
2355 self.filtered_phase_order(&self.transparent_multi_draw_order, excluded_instance);
2356 let mut batches = Vec::new();
2357 Self::build_draw_batches_for_order(&self.instances, &order, &mut batches);
2358 (order, batches)
2359 }
2360
2361 pub fn prepare_draw_cache(&mut self) -> bool {
2365 if !self.dirty_draw_cache {
2366 return false;
2367 }
2368
2369 self.background_order.clear();
2370 self.background_occluded_lit_order.clear();
2371 self.background_occluded_lit_emissive_order.clear();
2372 self.background_occluded_lit_emissive_batches.clear();
2373 self.draw_order.clear();
2374 self.emissive_draw_order.clear();
2375 self.cutout_order.clear();
2376 self.emissive_cutout_order.clear();
2377 self.transparent_single_draw_order.clear();
2378 self.overlay_order.clear();
2379 self.stencil_clip_order.clear();
2380 for i in 0..self.instances.len() {
2382 let inst = &self.instances[i];
2383 if inst.is_stencil_clip {
2384 self.stencil_clip_order.push(i as u32);
2385 }
2386 if inst.overlay {
2387 self.overlay_order.push(i as u32);
2388 } else if inst.background {
2389 if inst.background_occluded_lit {
2390 self.background_occluded_lit_order.push(i as u32);
2391 } else {
2392 self.background_order.push(i as u32);
2393 }
2394 } else if inst.transparent_cutout {
2395 self.cutout_order.push(i as u32);
2396 } else if !Self::is_transparent(inst) {
2397 self.draw_order.push(i as u32);
2398 } else if !inst.multiple_layers {
2399 self.transparent_single_draw_order.push(i as u32);
2400 }
2401 }
2402 self.stencil_clip_order
2403 .sort_by_key(|&i| self.instances[i as usize].stencil_ref);
2404
2405 self.background_order.sort_by_key(|&i| {
2408 let inst = self.instances[i as usize];
2409 let r = inst.renderable;
2410 let tex = inst.texture.map(|t| t.0).unwrap_or(u32::MAX);
2411 (
2412 r.material.0,
2413 r.mesh.0,
2414 tex,
2415 inst.texture_filtering as u8,
2416 sanitize_quant_steps(inst.quant_steps).to_bits(),
2417 )
2418 });
2419 let instances = &self.instances;
2420 let background_draw_order = &self.background_order;
2421 Self::build_draw_batches_for_order(
2422 instances,
2423 background_draw_order,
2424 &mut self.background_batches,
2425 );
2426
2427 self.background_occluded_lit_order.sort_by_key(|&i| {
2429 let inst = self.instances[i as usize];
2430 let r = inst.renderable;
2431 let tex = inst.texture.map(|t| t.0).unwrap_or(u32::MAX);
2432 (
2433 r.material.0,
2434 r.mesh.0,
2435 tex,
2436 inst.texture_filtering as u8,
2437 sanitize_quant_steps(inst.quant_steps).to_bits(),
2438 )
2439 });
2440 let background_occluded_lit_draw_order = &self.background_occluded_lit_order;
2441 Self::build_draw_batches_for_order(
2442 instances,
2443 background_occluded_lit_draw_order,
2444 &mut self.background_occluded_lit_batches,
2445 );
2446
2447 if self
2448 .background_occluded_lit_order
2449 .iter()
2450 .any(|&i| Self::is_emissive_material(self.instances[i as usize].renderable.material))
2451 {
2452 self.background_occluded_lit_emissive_order.extend(
2453 self.background_occluded_lit_order
2454 .iter()
2455 .copied()
2456 .filter(|&i| {
2457 Self::is_emissive_material(self.instances[i as usize].renderable.material)
2458 }),
2459 );
2460 Self::build_draw_batches_for_order(
2461 instances,
2462 &self.background_occluded_lit_emissive_order,
2463 &mut self.background_occluded_lit_emissive_batches,
2464 );
2465 }
2466
2467 self.draw_order.sort_by_key(|&i| {
2471 let inst = self.instances[i as usize];
2472 let r = inst.renderable;
2473 let tex = inst.texture.map_or(0, |t| t.0.wrapping_add(1));
2474 (
2475 inst.stencil_ref,
2476 r.material.0,
2477 tex,
2478 r.mesh.0,
2479 inst.texture_filtering as u8,
2480 sanitize_quant_steps(inst.quant_steps).to_bits(),
2481 )
2482 });
2483
2484 let draw_order = &self.draw_order;
2485 Self::build_draw_batches_for_order(instances, draw_order, &mut self.draw_batches);
2486
2487 Self::build_phase_render_stream(
2489 instances,
2490 &self.draw_order,
2491 &mut self.opaque_stream,
2492 &mut self.opaque_stream_instances,
2493 );
2494
2495 self.emissive_draw_order
2496 .extend(self.draw_order.iter().copied().filter(|&i| {
2497 Self::is_emissive_material(self.instances[i as usize].renderable.material)
2498 }));
2499 let emissive_draw_order = &self.emissive_draw_order;
2500 Self::build_draw_batches_for_order(
2501 instances,
2502 emissive_draw_order,
2503 &mut self.emissive_draw_batches,
2504 );
2505
2506 self.cutout_order.sort_by_key(|&i| {
2508 let inst = self.instances[i as usize];
2509 let r = inst.renderable;
2510 let tex = inst.texture.map(|t| t.0).unwrap_or(u32::MAX);
2511 (
2512 inst.stencil_ref,
2513 r.material.0,
2514 r.mesh.0,
2515 tex,
2516 inst.texture_filtering as u8,
2517 sanitize_quant_steps(inst.quant_steps).to_bits(),
2518 )
2519 });
2520 let cutout_order = &self.cutout_order;
2521 Self::build_draw_batches_for_order(instances, cutout_order, &mut self.cutout_batches);
2522
2523 Self::build_phase_render_stream(
2524 instances,
2525 &self.cutout_order,
2526 &mut self.cutout_stream,
2527 &mut self.cutout_stream_instances,
2528 );
2529
2530 self.emissive_cutout_order
2531 .extend(self.cutout_order.iter().copied().filter(|&i| {
2532 Self::is_emissive_material(self.instances[i as usize].renderable.material)
2533 }));
2534 let emissive_cutout_order = &self.emissive_cutout_order;
2535 Self::build_draw_batches_for_order(
2536 instances,
2537 emissive_cutout_order,
2538 &mut self.emissive_cutout_batches,
2539 );
2540
2541 self.transparent_single_draw_order.sort_by_key(|&i| {
2543 let inst = self.instances[i as usize];
2544 let r = inst.renderable;
2545 let tex = inst.texture.map(|t| t.0).unwrap_or(u32::MAX);
2546 (
2547 inst.stencil_ref,
2548 r.material.0,
2549 r.mesh.0,
2550 tex,
2551 inst.texture_filtering as u8,
2552 sanitize_quant_steps(inst.quant_steps).to_bits(),
2553 )
2554 });
2555 let transparent_single_draw_order = &self.transparent_single_draw_order;
2556 Self::build_draw_batches_for_order(
2557 instances,
2558 transparent_single_draw_order,
2559 &mut self.transparent_single_draw_batches,
2560 );
2561
2562 Self::build_phase_render_stream(
2563 instances,
2564 &self.transparent_single_draw_order,
2565 &mut self.transparent_single_stream,
2566 &mut self.transparent_single_stream_instances,
2567 );
2568
2569 self.overlay_order.sort_by_key(|&i| {
2575 let inst = self.instances[i as usize];
2576 let r = inst.renderable;
2577 let tex = inst.texture.map_or(0, |t| t.0.wrapping_add(1));
2578 (
2579 inst.stencil_ref,
2580 r.material.0,
2581 tex,
2582 r.mesh.0,
2583 inst.texture_filtering as u8,
2584 sanitize_quant_steps(inst.quant_steps).to_bits(),
2585 )
2586 });
2587 let overlay_order = &self.overlay_order;
2588 Self::build_draw_batches_for_order(instances, overlay_order, &mut self.overlay_batches);
2589
2590 Self::build_phase_render_stream(
2592 instances,
2593 &self.overlay_order,
2594 &mut self.overlay_stream,
2595 &mut self.overlay_stream_instances,
2596 );
2597
2598 self.dirty_draw_cache = false;
2599 true
2600 }
2601
2602 pub fn prepare_transparent_multi_draw_cache_for_eye(
2603 &mut self,
2604 target: CameraTarget,
2605 eye: usize,
2606 ) {
2607 let view = self.camera_view_for_eye(target, eye);
2608 self.prepare_transparent_multi_draw_cache_for_view(view);
2609 }
2610
2611 pub fn prepare_transparent_multi_draw_cache_for_view(&mut self, view: [[f32; 4]; 4]) {
2613 self.transparent_multi_draw_order.clear();
2614
2615 for i in 0..self.instances.len() {
2616 let inst = &self.instances[i];
2617 if inst.overlay {
2618 continue;
2619 }
2620 if inst.background {
2621 continue;
2622 }
2623 if inst.transparent_cutout {
2624 continue;
2625 }
2626 if inst.multiple_layers && Self::is_transparent(inst) {
2627 self.transparent_multi_draw_order.push(i as u32);
2628 }
2629 }
2630
2631 if self.transparent_multi_draw_order.is_empty() {
2632 self.transparent_multi_draw_batches.clear();
2633 return;
2634 }
2635
2636 self.transparent_multi_draw_order.sort_by(|&a, &b| {
2638 let ia = self.instances[a as usize];
2639 let ib = self.instances[b as usize];
2640 let za = Self::view_space_z(view, ia.transform.model);
2641 let zb = Self::view_space_z(view, ib.transform.model);
2642 za.partial_cmp(&zb)
2643 .unwrap_or(std::cmp::Ordering::Equal)
2644 .then_with(|| a.cmp(&b))
2645 });
2646
2647 let instances = &self.instances;
2648 let transparent_draw_order = &self.transparent_multi_draw_order;
2649 Self::build_draw_batches_for_order(
2650 instances,
2651 transparent_draw_order,
2652 &mut self.transparent_multi_draw_batches,
2653 );
2654 }
2655
2656 pub fn register(
2657 &mut self,
2658 cid: ComponentId,
2659 renderable: GpuRenderable,
2660 transform: Transform,
2661 color: [f32; 4],
2662 opacity: f32,
2663 multiple_layers: bool,
2664 transparent_cutout: bool,
2665 background: bool,
2666 background_occluded_lit: bool,
2667 overlay: bool,
2668 emissive: f32,
2669 texture: Option<crate::engine::graphics::TextureHandle>,
2670 quant_steps: f32,
2671 ) -> InstanceHandle {
2672 let handle = InstanceHandle(self.next_handle);
2673 self.next_handle = self.next_handle.wrapping_add(1);
2674
2675 let idx = self.instances.len();
2676 self.instances.push(VisualInstance {
2677 renderable,
2678 transform,
2679 color,
2680 opacity: if opacity.is_finite() {
2681 opacity.clamp(0.0, 1.0)
2682 } else {
2683 1.0
2684 },
2685 multiple_layers,
2686 transparent_cutout,
2687 background,
2688 background_occluded_lit,
2689 overlay,
2690 emissive: if emissive.is_finite() {
2691 emissive.max(0.0)
2692 } else {
2693 0.0
2694 },
2695 texture,
2696 texture_filtering: TextureFiltering::default(),
2697 quant_steps: sanitize_quant_steps(quant_steps),
2698
2699 bones_base: 0,
2700 bones_count: 0,
2701
2702 stencil_ref: 0,
2703 is_stencil_clip: false,
2704 });
2705 self.handle_to_index.insert(handle, idx);
2706 self.component_to_handle.insert(cid, handle);
2707
2708 self.dirty_draw_cache = true;
2709 self.dirty_instance_data = true;
2710 handle
2711 }
2712
2713 pub fn update_quant_steps(&mut self, handle: InstanceHandle, quant_steps: f32) -> bool {
2714 if let Some(&idx) = self.handle_to_index.get(&handle) {
2715 let q = sanitize_quant_steps(quant_steps);
2716 if self.instances[idx].quant_steps.to_bits() == q.to_bits() {
2717 return true;
2718 }
2719 self.instances[idx].quant_steps = q;
2720 self.dirty_draw_cache = true;
2722 true
2723 } else {
2724 false
2725 }
2726 }
2727
2728 pub fn remove(&mut self, handle: InstanceHandle) -> bool {
2729 if let Some(idx) = self.handle_to_index.remove(&handle) {
2730 let old_base = self.instances[idx].bones_base;
2732 let old_count = self.instances[idx].bones_count;
2733 if old_count != 0 {
2734 self.bones_free_range(old_base, old_count);
2735 }
2736
2737 self.instances.swap_remove(idx);
2738
2739 if idx < self.instances.len() {
2740 if let Some((moved_handle, _)) = self
2742 .handle_to_index
2743 .iter()
2744 .find(|(_, i)| **i == self.instances.len())
2745 {
2746 self.handle_to_index.insert(*moved_handle, idx);
2747 }
2748 }
2749
2750 self.component_to_handle.retain(|_, &mut h| h != handle);
2751
2752 self.dirty_draw_cache = true;
2753 self.dirty_instance_data = true;
2754 true
2755 } else {
2756 false
2757 }
2758 }
2759
2760 pub fn update_transform(&mut self, handle: InstanceHandle, transform: Transform) -> bool {
2761 if let Some(&idx) = self.handle_to_index.get(&handle) {
2762 self.instances[idx].transform = transform;
2763 self.dirty_instance_data = true;
2764 true
2766 } else {
2767 false
2768 }
2769 }
2770
2771 pub fn update_model(&mut self, handle: InstanceHandle, model: TransformMatrix) -> bool {
2772 if let Some(&idx) = self.handle_to_index.get(&handle) {
2773 self.instances[idx].transform.model = model;
2774 self.instances[idx].transform.matrix_world = model;
2775 self.dirty_instance_data = true;
2776 true
2778 } else {
2779 false
2780 }
2781 }
2782
2783 pub fn update_color(&mut self, handle: InstanceHandle, color: [f32; 4]) -> bool {
2784 if let Some(&idx) = self.handle_to_index.get(&handle) {
2785 self.instances[idx].color = color;
2786 self.dirty_instance_data = true;
2787 self.dirty_draw_cache = true;
2789 true
2790 } else {
2791 false
2792 }
2793 }
2794
2795 pub fn update_opacity(&mut self, handle: InstanceHandle, opacity: f32) -> bool {
2796 self.update_opacity_state(handle, opacity, None)
2797 }
2798
2799 pub fn update_opacity_state(
2800 &mut self,
2801 handle: InstanceHandle,
2802 opacity: f32,
2803 multiple_layers: impl Into<Option<bool>>,
2804 ) -> bool {
2805 if let Some(&idx) = self.handle_to_index.get(&handle) {
2806 let o = if opacity.is_finite() {
2807 opacity.clamp(0.0, 1.0)
2808 } else {
2809 1.0
2810 };
2811 let mut changed = false;
2812
2813 if (self.instances[idx].opacity - o).abs() >= f32::EPSILON {
2814 self.instances[idx].opacity = o;
2815 changed = true;
2816 }
2817
2818 if let Some(ml) = multiple_layers.into() {
2819 if self.instances[idx].multiple_layers != ml {
2820 self.instances[idx].multiple_layers = ml;
2821 changed = true;
2822 }
2823 }
2824
2825 if !changed {
2826 return true;
2827 }
2828 self.dirty_instance_data = true;
2829 self.dirty_draw_cache = true;
2831 true
2832 } else {
2833 false
2834 }
2835 }
2836
2837 pub fn update_transparent_cutout(&mut self, handle: InstanceHandle, enabled: bool) -> bool {
2838 if let Some(&idx) = self.handle_to_index.get(&handle) {
2839 if self.instances[idx].transparent_cutout == enabled {
2840 return true;
2841 }
2842 self.instances[idx].transparent_cutout = enabled;
2843 self.dirty_draw_cache = true;
2845 true
2846 } else {
2847 false
2848 }
2849 }
2850
2851 pub fn update_emissive(&mut self, handle: InstanceHandle, emissive: f32) -> bool {
2852 if let Some(&idx) = self.handle_to_index.get(&handle) {
2853 self.instances[idx].emissive = if emissive.is_finite() {
2854 emissive.max(0.0)
2855 } else {
2856 0.0
2857 };
2858 self.dirty_instance_data = true;
2859 true
2860 } else {
2861 false
2862 }
2863 }
2864
2865 pub fn update_material(
2866 &mut self,
2867 handle: InstanceHandle,
2868 material: crate::engine::graphics::MaterialHandle,
2869 ) -> bool {
2870 if let Some(&idx) = self.handle_to_index.get(&handle) {
2871 if self.instances[idx].renderable.material == material {
2872 return true;
2873 }
2874 self.instances[idx].renderable.material = material;
2875 self.dirty_draw_cache = true;
2876 true
2877 } else {
2878 false
2879 }
2880 }
2881
2882 pub fn post_processing(&self) -> &PostProcessingConfig {
2883 &self.post_processing
2884 }
2885
2886 pub fn post_processing_mut(&mut self) -> &mut PostProcessingConfig {
2887 &mut self.post_processing
2888 }
2889
2890 pub fn set_post_processing(&mut self, config: PostProcessingConfig) {
2891 self.post_processing = config;
2892 }
2893
2894 pub fn runtime_texture_handle(
2895 &self,
2896 key: &str,
2897 ) -> Option<crate::engine::graphics::TextureHandle> {
2898 self.runtime_texture_handles.get(key).copied()
2899 }
2900
2901 pub fn stencil_clip_debug_requested(&self) -> bool {
2902 self.stencil_clip_debug_requested
2903 }
2904
2905 pub fn set_stencil_clip_debug_requested(&mut self, requested: bool) {
2906 self.stencil_clip_debug_requested = requested;
2907 }
2908
2909 pub fn set_runtime_texture_handle(
2910 &mut self,
2911 key: impl Into<String>,
2912 handle: crate::engine::graphics::TextureHandle,
2913 ) {
2914 self.runtime_texture_handles.insert(key.into(), handle);
2915 }
2916
2917 pub fn update_texture(
2918 &mut self,
2919 handle: InstanceHandle,
2920 texture: Option<crate::engine::graphics::TextureHandle>,
2921 ) -> bool {
2922 if let Some(&idx) = self.handle_to_index.get(&handle) {
2923 self.instances[idx].texture = texture;
2924 self.dirty_draw_cache = true;
2926 true
2927 } else {
2928 false
2929 }
2930 }
2931
2932 pub fn update_texture_filtering(
2933 &mut self,
2934 handle: InstanceHandle,
2935 filtering: TextureFiltering,
2936 ) -> bool {
2937 if let Some(&idx) = self.handle_to_index.get(&handle) {
2938 self.instances[idx].texture_filtering = filtering;
2939 self.dirty_draw_cache = true;
2941 true
2942 } else {
2943 false
2944 }
2945 }
2946
2947 pub fn update(
2948 &mut self,
2949 handle: InstanceHandle,
2950 renderable: GpuRenderable,
2951 transform: Transform,
2952 ) -> bool {
2953 if let Some(&idx) = self.handle_to_index.get(&handle) {
2954 let color = self.instances[idx].color;
2956 let opacity = self.instances[idx].opacity;
2957 let multiple_layers = self.instances[idx].multiple_layers;
2958 let transparent_cutout = self.instances[idx].transparent_cutout;
2959 let background = self.instances[idx].background;
2960 let background_occluded_lit = self.instances[idx].background_occluded_lit;
2961 let overlay = self.instances[idx].overlay;
2962 let emissive = self.instances[idx].emissive;
2963 let texture = self.instances[idx].texture;
2964 let texture_filtering = self.instances[idx].texture_filtering;
2965 let quant_steps = self.instances[idx].quant_steps;
2966 let bones_base = self.instances[idx].bones_base;
2967 let bones_count = self.instances[idx].bones_count;
2968 let stencil_ref = self.instances[idx].stencil_ref;
2969 let is_stencil_clip = self.instances[idx].is_stencil_clip;
2970 self.instances[idx] = VisualInstance {
2971 renderable,
2972 transform,
2973 color,
2974 opacity,
2975 multiple_layers,
2976 transparent_cutout,
2977 background,
2978 background_occluded_lit,
2979 overlay,
2980 emissive,
2981 texture,
2982 texture_filtering,
2983 quant_steps,
2984 bones_base,
2985 bones_count,
2986 stencil_ref,
2987 is_stencil_clip,
2988 };
2989 self.dirty_draw_cache = true; self.dirty_instance_data = true;
2991 true
2992 } else {
2993 false
2994 }
2995 }
2996}