Skip to main content

mittens_engine/engine/ecs/system/
render_to_texture_system.rs

1use crate::engine::ecs::component::TextureComponent;
2use crate::engine::ecs::{ComponentId, World};
3use crate::engine::graphics::{TextureUploader, VisualWorld};
4use std::collections::{HashMap, HashSet};
5
6pub const INTERNAL_RENDERER_STENCIL_CLIP_DEBUG_SELECTOR: &str = "render_graph.stencil_clip.debug";
7
8#[derive(Debug, Clone, PartialEq, Eq)]
9pub enum RenderTextureConsumerKind {
10    TextureRenderImage,
11}
12
13#[derive(Debug, Clone, PartialEq, Eq)]
14pub struct RenderTextureConsumerRegistration {
15    pub component: ComponentId,
16    pub selector: String,
17    pub kind: RenderTextureConsumerKind,
18}
19
20#[derive(Debug, Clone, PartialEq, Eq)]
21pub enum RenderTextureProducerKind {
22    InternalRendererImage,
23    SceneCapture,
24    CubeCapture,
25    Mirror,
26    Portal,
27}
28
29#[derive(Debug, Clone, PartialEq, Eq)]
30pub struct RenderTextureProducerRequest {
31    pub selector: String,
32    pub kind: RenderTextureProducerKind,
33}
34
35#[derive(Debug, Default)]
36pub struct RenderToTextureSystem {
37    consumers_by_component: HashMap<ComponentId, RenderTextureConsumerRegistration>,
38    producer_requests_by_selector: HashMap<String, RenderTextureProducerRequest>,
39}
40
41impl RenderToTextureSystem {
42    pub fn new() -> Self {
43        Self::default()
44    }
45
46    pub fn register_texture(&mut self, world: &mut World, component: ComponentId) {
47        let Some(texture) = world.get_component_by_id_as::<TextureComponent>(component) else {
48            self.unregister_texture(component);
49            return;
50        };
51
52        if let Some(selector) = texture.render_image.clone() {
53            let previous = self.consumers_by_component.insert(
54                component,
55                RenderTextureConsumerRegistration {
56                    component,
57                    selector: selector.clone(),
58                    kind: RenderTextureConsumerKind::TextureRenderImage,
59                },
60            );
61            if let Some(previous) = previous {
62                self.remove_selector_if_unused(&previous.selector);
63            }
64
65            self.producer_requests_by_selector.insert(
66                selector.clone(),
67                RenderTextureProducerRequest {
68                    selector,
69                    kind: producer_kind_for_selector(texture.render_image.as_deref().unwrap()),
70                },
71            );
72        } else {
73            self.unregister_texture(component);
74        }
75    }
76
77    fn unregister_texture(&mut self, component: ComponentId) {
78        if let Some(previous) = self.consumers_by_component.remove(&component) {
79            self.remove_selector_if_unused(&previous.selector);
80        }
81    }
82
83    fn remove_selector_if_unused(&mut self, selector: &str) {
84        let still_used = self
85            .consumers_by_component
86            .values()
87            .any(|registration| registration.selector == selector);
88        if !still_used {
89            self.producer_requests_by_selector.remove(selector);
90        }
91    }
92
93    pub fn consumer_registrations(
94        &self,
95    ) -> impl Iterator<Item = &RenderTextureConsumerRegistration> {
96        self.consumers_by_component.values()
97    }
98
99    pub fn producer_requests(&self) -> impl Iterator<Item = &RenderTextureProducerRequest> {
100        self.producer_requests_by_selector.values()
101    }
102
103    pub fn flush_pending(&mut self, visuals: &mut VisualWorld, uploader: &mut dyn TextureUploader) {
104        visuals.set_stencil_clip_debug_requested(
105            self.producer_requests_by_selector
106                .contains_key(INTERNAL_RENDERER_STENCIL_CLIP_DEBUG_SELECTOR),
107        );
108
109        let selectors: HashSet<String> = self
110            .producer_requests_by_selector
111            .values()
112            .map(|request| request.selector.clone())
113            .collect();
114        let mirror_capture_selectors: HashSet<String> = visuals
115            .mirrors()
116            .iter()
117            .flat_map(|mirror| {
118                mirror
119                    .captures
120                    .iter()
121                    .map(|capture| capture.target_key.clone())
122            })
123            .collect();
124
125        for selector in selectors {
126            if visuals.runtime_texture_handle(&selector).is_some() {
127                continue;
128            }
129
130            match uploader.upload_texture_rgba8(&[0, 0, 0, 255], 1, 1) {
131                Ok(handle) => visuals.set_runtime_texture_handle(selector, handle),
132                Err(err) => {
133                    println!(
134                        "[RenderToTextureSystem] failed to allocate runtime texture handle: {err}"
135                    );
136                }
137            }
138        }
139
140        for selector in mirror_capture_selectors {
141            if visuals.runtime_texture_handle(&selector).is_some() {
142                continue;
143            }
144
145            match uploader.upload_texture_rgba8(&[0, 0, 0, 255], 1, 1) {
146                Ok(handle) => visuals.set_runtime_texture_handle(selector, handle),
147                Err(err) => {
148                    println!(
149                        "[RenderToTextureSystem] failed to allocate mirror runtime texture handle: {err}"
150                    );
151                }
152            }
153        }
154    }
155}
156
157fn producer_kind_for_selector(selector: &str) -> RenderTextureProducerKind {
158    if selector.starts_with("capture.mirror.") {
159        RenderTextureProducerKind::Mirror
160    } else {
161        RenderTextureProducerKind::InternalRendererImage
162    }
163}