1pub mod render;
2use fission_core::internal::{InternalLowerer, InternalLoweringCx, InternalRenderNode};
3use fission_core::op::Color;
4use fission_core::ui::{Container, Widget};
5
6use fission_ir::op::{EmbedKind, LayoutOp};
7use serde::{Deserialize, Serialize};
8
9#[derive(Debug, Clone, Serialize, Deserialize)]
10pub struct Point3D {
11 pub x: f32,
12 pub y: f32,
13 pub z: f32,
14}
15
16impl Point3D {
17 pub fn new(x: f32, y: f32, z: f32) -> Self {
18 Self { x, y, z }
19 }
20}
21
22#[derive(Debug, Clone, Serialize, Deserialize)]
23pub enum Primitive3D {
24 Cube {
25 center: Point3D,
26 size: f32,
27 color: Color,
28 },
29 Sphere {
30 center: Point3D,
31 radius: f32,
32 color: Color,
33 },
34 Mesh {
35 vertices: Vec<Point3D>,
36 indices: Vec<u32>,
37 color: Color,
38 },
39}
40
41#[derive(Debug, Clone, Serialize, Deserialize)]
42pub struct Scene3D {
43 pub width: Option<f32>,
44 pub height: Option<f32>,
45 pub primitives: Vec<Primitive3D>,
46}
47
48impl Scene3D {
49 pub fn new() -> Self {
50 Self {
51 width: None,
52 height: None,
53 primitives: Vec::new(),
54 }
55 }
56
57 pub fn width(mut self, w: f32) -> Self {
58 self.width = Some(w);
59 self
60 }
61
62 pub fn height(mut self, h: f32) -> Self {
63 self.height = Some(h);
64 self
65 }
66
67 pub fn add_primitive(mut self, primitive: Primitive3D) -> Self {
68 self.primitives.push(primitive);
69 self
70 }
71}
72
73impl From<Scene3D> for Widget {
74 fn from(component: Scene3D) -> Self {
75 let this = &component;
76 let mut container = Container::new(fission_core::internal::custom_render_widget(
77 InternalRenderNode {
78 debug_tag: "fission_3d::Scene3D".into(),
79 lowerer: Some(std::sync::Arc::new(Scene3DInternalLowerer {
80 scene: this.clone(),
81 })),
82 render_object: None,
83 },
84 ));
85 if let Some(w) = this.width {
86 container = container.width(w);
87 } else {
88 container = container.flex_grow(1.0);
89 }
90 if let Some(h) = this.height {
91 container = container.height(h);
92 } else {
93 if this.width.is_none() {
94 container = container.flex_grow(1.0);
95 }
96 }
97 container.into()
98 }
99}
100
101#[derive(Debug)]
102pub struct Scene3DInternalLowerer {
103 pub scene: Scene3D,
104}
105
106impl InternalLowerer for Scene3DInternalLowerer {
107 fn lower_dyn(&self, cx: &mut InternalLoweringCx) -> fission_ir::WidgetId {
108 let node_id = cx.next_node_id();
109
110 let w = self
111 .scene
112 .width
113 .unwrap_or_else(|| (cx.env.viewport_size.width - 264.0).max(400.0));
114 let h = self
115 .scene
116 .height
117 .unwrap_or_else(|| (cx.env.viewport_size.height - 200.0).max(300.0));
118
119 let payload = bincode::serialize(&self.scene.primitives).unwrap_or_default();
124 let op = fission_ir::Op::Layout(LayoutOp::Embed {
125 kind: EmbedKind::Custom(payload),
126 widget_id: fission_ir::WidgetId::explicit("fission_3d_scene"),
127 width: Some(w),
128 height: Some(h),
129 });
130
131 cx.insert_node(node_id, op, vec![])
132 }
133}