Skip to main content

mittens_engine/engine/ecs/component/
renderable.rs

1use crate::engine::ecs::ComponentId;
2use crate::engine::ecs::component::Component;
3use crate::engine::ecs::component::ce_helpers::{ce, ce_call, num};
4use crate::engine::graphics::mesh::MeshFactory;
5use crate::engine::graphics::primitives::{
6    CpuMeshHandle, InstanceHandle, MaterialHandle, Renderable,
7};
8use crate::engine::graphics::render_assets::RenderAssets;
9
10#[derive(Debug, Clone, PartialEq)]
11pub enum AuthoredRenderableShape {
12    Builtin(&'static str),
13    Cone {
14        segments: u32,
15    },
16    Icosahedron {
17        tessellations: u32,
18        sphericalness: f32,
19    },
20    PartialAnnulus2d {
21        inner_radius: f32,
22        outer_radius: f32,
23        start_angle_radians: f32,
24        sweep_angle_radians: f32,
25        segments: u32,
26    },
27    Star {
28        points: u32,
29        inner_radius_fraction: f32,
30        outer_bevel_segments: u32,
31        inner_bevel_segments: u32,
32    },
33    Heart {
34        segments: u32,
35    },
36    WireframeBox {
37        thickness: f32,
38    },
39}
40
41/// Renderable component.
42#[derive(Debug, Clone)]
43pub struct RenderableComponent {
44    pub renderable: Renderable,
45    pub authored_shape: Option<AuthoredRenderableShape>,
46
47    /// VisualWorld instance handle created for this renderable.
48    pub handle: Option<InstanceHandle>,
49
50    component: Option<ComponentId>,
51}
52
53impl RenderableComponent {
54    pub fn new(renderable: Renderable) -> Self {
55        Self {
56            renderable,
57            authored_shape: None,
58            handle: None,
59            component: None,
60        }
61    }
62
63    pub fn from_cpu_mesh_handle(h: CpuMeshHandle, material: MaterialHandle) -> Self {
64        Self::new(Renderable::new(h, material))
65    }
66
67    pub fn get_handle(&self) -> Option<InstanceHandle> {
68        self.handle
69    }
70
71    /// Predefined renderable: 2D triangle (shared built-in mesh handle).
72    pub fn triangle() -> Self {
73        let mut s =
74            Self::from_cpu_mesh_handle(CpuMeshHandle::TRIANGLE_2D, MaterialHandle::TOON_MESH);
75        s.authored_shape = Some(AuthoredRenderableShape::Builtin("triangle"));
76        s
77    }
78
79    /// Predefined renderable: 2D triangle (unique CPU mesh registered into `render_assets`).
80    pub fn triangle_dynamic(render_assets: &mut RenderAssets) -> Self {
81        let h = render_assets.register_mesh(MeshFactory::triangle_2d());
82        Self::new(
83            Renderable::new(h, MaterialHandle::TOON_MESH)
84                .with_base_mesh(CpuMeshHandle::TRIANGLE_2D),
85        )
86    }
87
88    /// Predefined renderable: 2D square/quad (shared built-in mesh handle).
89    pub fn square() -> Self {
90        let mut s = Self::from_cpu_mesh_handle(CpuMeshHandle::QUAD_2D, MaterialHandle::TOON_MESH);
91        s.authored_shape = Some(AuthoredRenderableShape::Builtin("square"));
92        s
93    }
94
95    /// Predefined renderable: 2D plane/quad (alias of `square`).
96    pub fn plane() -> Self {
97        Self::square()
98    }
99
100    /// Predefined renderable: 2D square/quad (unique CPU mesh registered into `render_assets`).
101    pub fn square_dynamic(render_assets: &mut RenderAssets) -> Self {
102        let h = render_assets.register_mesh(MeshFactory::quad_2d());
103        Self::new(
104            Renderable::new(h, MaterialHandle::TOON_MESH).with_base_mesh(CpuMeshHandle::QUAD_2D),
105        )
106    }
107
108    /// Predefined renderable: cube primitive (shared built-in mesh handle).
109    pub fn cube() -> Self {
110        let mut s = Self::from_cpu_mesh_handle(CpuMeshHandle::CUBE, MaterialHandle::TOON_MESH);
111        s.authored_shape = Some(AuthoredRenderableShape::Builtin("cube"));
112        s
113    }
114
115    /// Predefined renderable: cube primitive (unique CPU mesh registered into `render_assets`).
116    pub fn cube_dynamic(render_assets: &mut RenderAssets) -> Self {
117        let h = render_assets.register_mesh(MeshFactory::cube());
118        Self::new(Renderable::new(h, MaterialHandle::TOON_MESH).with_base_mesh(CpuMeshHandle::CUBE))
119    }
120
121    /// Unit wireframe box with twelve solid edges of configurable relative thickness.
122    pub fn wireframe_box(render_assets: &mut RenderAssets, thickness: f32) -> Self {
123        let thickness = thickness.clamp(1.0e-4, 1.0);
124        let handle = render_assets.wireframe_box_mesh(thickness);
125        let mut component = Self::new(
126            Renderable::new(handle, MaterialHandle::TOON_MESH).with_base_mesh(CpuMeshHandle::CUBE),
127        );
128        component.authored_shape = Some(AuthoredRenderableShape::WireframeBox { thickness });
129        component
130    }
131
132    /// Predefined renderable: sphere primitive (shared built-in mesh handle).
133    pub fn sphere() -> Self {
134        let mut s = Self::from_cpu_mesh_handle(CpuMeshHandle::SPHERE, MaterialHandle::TOON_MESH);
135        s.authored_shape = Some(AuthoredRenderableShape::Builtin("sphere"));
136        s
137    }
138
139    /// Predefined renderable: sphere primitive (unique CPU mesh registered into `render_assets`).
140    pub fn sphere_dynamic(render_assets: &mut RenderAssets) -> Self {
141        let h = render_assets.register_mesh(MeshFactory::sphere());
142        Self::new(
143            Renderable::new(h, MaterialHandle::TOON_MESH).with_base_mesh(CpuMeshHandle::SPHERE),
144        )
145    }
146
147    /// Predefined renderable: cone primitive (shared built-in mesh handle).
148    pub fn cone() -> Self {
149        let mut s = Self::from_cpu_mesh_handle(CpuMeshHandle::CONE, MaterialHandle::TOON_MESH);
150        s.authored_shape = Some(AuthoredRenderableShape::Builtin("cone"));
151        s
152    }
153
154    /// Predefined renderable: cone primitive (unique CPU mesh registered into `render_assets`).
155    pub fn cone_dynamic(render_assets: &mut RenderAssets, segments: u32) -> Self {
156        let h = render_assets.register_mesh(MeshFactory::cone(segments));
157        let mut s = Self::new(
158            Renderable::new(h, MaterialHandle::TOON_MESH).with_base_mesh(CpuMeshHandle::CONE),
159        );
160        s.authored_shape = Some(AuthoredRenderableShape::Cone { segments });
161        s
162    }
163
164    pub fn icosahedron(
165        render_assets: &mut RenderAssets,
166        tessellations: u32,
167        sphericalness: f32,
168    ) -> Self {
169        let h = render_assets.register_mesh(MeshFactory::icosahedron(tessellations, sphericalness));
170        let mut s = Self::new(Renderable::new(h, MaterialHandle::TOON_MESH));
171        s.authored_shape = Some(AuthoredRenderableShape::Icosahedron {
172            tessellations,
173            sphericalness,
174        });
175        s
176    }
177
178    /// Predefined renderable: tetrahedron primitive (shared built-in mesh handle).
179    pub fn tetrahedron() -> Self {
180        let mut s =
181            Self::from_cpu_mesh_handle(CpuMeshHandle::TETRAHEDRON, MaterialHandle::TOON_MESH);
182        s.authored_shape = Some(AuthoredRenderableShape::Builtin("tetrahedron"));
183        s
184    }
185
186    /// Predefined renderable: tetrahedron primitive (unique CPU mesh registered into `render_assets`).
187    pub fn tetrahedron_dynamic(render_assets: &mut RenderAssets) -> Self {
188        let h = render_assets.register_mesh(MeshFactory::tetrahedron());
189        Self::new(
190            Renderable::new(h, MaterialHandle::TOON_MESH)
191                .with_base_mesh(CpuMeshHandle::TETRAHEDRON),
192        )
193    }
194
195    /// Predefined renderable: tetrahedron (alias of `tetrahedron`).
196    pub fn color_tetrahedron() -> Self {
197        Self::tetrahedron()
198    }
199
200    /// Predefined renderable: 2D circle (shared built-in mesh handle).
201    pub fn circle2d() -> Self {
202        let mut s = Self::from_cpu_mesh_handle(CpuMeshHandle::CIRCLE_2D, MaterialHandle::TOON_MESH);
203        s.authored_shape = Some(AuthoredRenderableShape::Builtin("circle2d"));
204        s
205    }
206
207    pub fn partial_annulus_2d(
208        render_assets: &mut RenderAssets,
209        inner_radius: f32,
210        outer_radius: f32,
211        start_angle_radians: f32,
212        sweep_angle_radians: f32,
213        segments: u32,
214    ) -> Self {
215        let mesh = MeshFactory::partial_annulus_2d(
216            inner_radius,
217            outer_radius,
218            start_angle_radians,
219            sweep_angle_radians,
220            segments,
221        );
222        let h = render_assets.register_mesh(mesh);
223        let mut s = Self::new(Renderable::new(h, MaterialHandle::TOON_MESH));
224        s.authored_shape = Some(AuthoredRenderableShape::PartialAnnulus2d {
225            inner_radius,
226            outer_radius,
227            start_angle_radians,
228            sweep_angle_radians,
229            segments,
230        });
231        s
232    }
233
234    pub fn star(
235        render_assets: &mut RenderAssets,
236        points: u32,
237        inner_radius_fraction: f32,
238        outer_bevel_segments: u32,
239        inner_bevel_segments: u32,
240    ) -> Self {
241        let h = render_assets.register_mesh(MeshFactory::star(
242            points,
243            inner_radius_fraction,
244            outer_bevel_segments,
245            inner_bevel_segments,
246        ));
247        let mut s = Self::new(Renderable::new(h, MaterialHandle::TOON_MESH));
248        s.authored_shape = Some(AuthoredRenderableShape::Star {
249            points,
250            inner_radius_fraction,
251            outer_bevel_segments,
252            inner_bevel_segments,
253        });
254        s
255    }
256
257    pub fn heart(render_assets: &mut RenderAssets, segments: u32) -> Self {
258        let h = render_assets.register_mesh(MeshFactory::heart(segments));
259        let mut s = Self::new(Renderable::new(h, MaterialHandle::TOON_MESH));
260        s.authored_shape = Some(AuthoredRenderableShape::Heart { segments });
261        s
262    }
263}
264
265impl Component for RenderableComponent {
266    fn name(&self) -> &'static str {
267        "renderable"
268    }
269
270    fn set_id(&mut self, component: ComponentId) {
271        self.component = Some(component);
272    }
273
274    fn as_any(&self) -> &dyn std::any::Any {
275        self
276    }
277
278    fn as_any_mut(&mut self) -> &mut dyn std::any::Any {
279        self
280    }
281
282    fn init(&mut self, emit: &mut dyn crate::engine::ecs::SignalEmitter, component: ComponentId) {
283        emit.push_intent_now(
284            component,
285            crate::engine::ecs::IntentValue::RegisterRenderable {
286                component_ids: vec![component],
287            },
288        );
289    }
290
291    fn cleanup(
292        &mut self,
293        emit: &mut dyn crate::engine::ecs::SignalEmitter,
294        component: ComponentId,
295    ) {
296        emit.push_intent_now(
297            component,
298            crate::engine::ecs::IntentValue::RemoveRenderable {
299                component_ids: vec![component],
300            },
301        );
302    }
303
304    fn to_mms_ast(
305        &self,
306        _world: &crate::engine::ecs::World,
307    ) -> crate::scripting::ast::ComponentExpression {
308        match &self.authored_shape {
309            Some(AuthoredRenderableShape::Builtin(name)) => ce_call("Renderable", name, vec![]),
310            Some(AuthoredRenderableShape::Cone { segments }) => {
311                ce_call("Renderable", "cone", vec![num(*segments as f64)])
312            }
313            Some(AuthoredRenderableShape::Icosahedron {
314                tessellations,
315                sphericalness,
316            }) => ce_call(
317                "Renderable",
318                "icosahedron",
319                vec![num(*tessellations as f64), num(*sphericalness as f64)],
320            ),
321            Some(AuthoredRenderableShape::PartialAnnulus2d {
322                inner_radius,
323                outer_radius,
324                start_angle_radians,
325                sweep_angle_radians,
326                segments,
327            }) => ce_call(
328                "Renderable",
329                "partial_annulus_2d",
330                vec![
331                    num(*inner_radius as f64),
332                    num(*outer_radius as f64),
333                    num(*start_angle_radians as f64),
334                    num(*sweep_angle_radians as f64),
335                    num(*segments as f64),
336                ],
337            ),
338            Some(AuthoredRenderableShape::Star {
339                points,
340                inner_radius_fraction,
341                outer_bevel_segments,
342                inner_bevel_segments,
343            }) => ce_call(
344                "Renderable",
345                "star",
346                vec![
347                    num(*points as f64),
348                    num(*inner_radius_fraction as f64),
349                    num(*outer_bevel_segments as f64),
350                    num(*inner_bevel_segments as f64),
351                ],
352            ),
353            Some(AuthoredRenderableShape::Heart { segments }) => {
354                ce_call("Renderable", "heart", vec![num(*segments as f64)])
355            }
356            Some(AuthoredRenderableShape::WireframeBox { thickness }) => {
357                ce_call("Renderable", "wireframe_box", vec![num(*thickness as f64)])
358            }
359            None => ce("Renderable"),
360        }
361    }
362}