lumen_engine/node/processing/
transform.rs1use crate::node::{NodeId, NodeProperty, PortRef};
2
3use crate::gpu::{
4 BoundFrame, CompiledOutput, FrameBindContext, FrameBinding, GpuCompileNode, GpuFrameBindNode,
5 RasterHandle, compiler,
6};
7
8pub(crate) const SHADER: &str = include_str!("transform.wgsl");
9
10#[derive(Debug, Clone, Copy, PartialEq, Eq, lumen_macros::NodeEnum)]
11#[repr(i64)]
12pub enum TransformSampling {
13 Nearest = 0,
14 Linear = 1,
15}
16
17impl TransformSampling {
18 pub fn from_int(value: i64) -> Self {
19 if value == Self::Nearest as i64 {
20 Self::Nearest
21 } else {
22 Self::Linear
23 }
24 }
25}
26
27#[derive(Debug, Clone, lumen_macros::Node)]
29#[node(kind = "transform", name = "Transform", category = "processing")]
30pub struct Transform {
31 pub id: NodeId,
32 #[property(kind = "float", name = "Scale X", step = 0.1)]
34 pub scale_x: NodeProperty,
35 #[property(kind = "float", name = "Scale Y", step = 0.1)]
37 pub scale_y: NodeProperty,
38 #[property(kind = "float", name = "Translate X", step = 1)]
40 pub translate_x: NodeProperty,
41 #[property(kind = "float", name = "Translate Y", step = 1)]
43 pub translate_y: NodeProperty,
44 #[property(kind = "float", name = "Rotate", step = 1)]
46 pub rotate: NodeProperty,
47 #[property(kind = "float", name = "Pivot X", step = 1)]
49 pub pivot_x: NodeProperty,
50 #[property(kind = "float", name = "Pivot Y", step = 1)]
52 pub pivot_y: NodeProperty,
53 #[property(kind = "enum", enum_type = TransformSampling)]
55 pub sampling: NodeProperty,
56 #[input()]
57 pub source: PortRef,
58}
59
60impl Default for Transform {
61 fn default() -> Self {
62 Self {
63 id: NodeId::new(0),
64 scale_x: NodeProperty::Float(1.0),
65 scale_y: NodeProperty::Float(1.0),
66 translate_x: NodeProperty::Float(0.0),
67 translate_y: NodeProperty::Float(0.0),
68 rotate: NodeProperty::Float(0.0),
69 pivot_x: NodeProperty::Float(0.0),
70 pivot_y: NodeProperty::Float(0.0),
71 sampling: NodeProperty::Int(TransformSampling::Linear as i64),
72 source: PortRef::empty(),
73 }
74 }
75}
76
77impl GpuCompileNode for Transform {
78 fn compile_gpu(
79 &self,
80 ctx: &mut crate::gpu::CompileContext<'_>,
81 port: &PortRef,
82 ) -> crate::Result<CompiledOutput> {
83 let (source, texture, params) = ctx.compile_unary_filter(
84 self.id,
85 &self.source,
86 port,
87 "transform",
88 SHADER,
89 std::mem::size_of::<compiler::TransformParams>() as u64,
90 )?;
91 ctx.push_frame_binding(FrameBinding::Transform {
92 node_id: self.id,
93 scale_x: self.scale_x.clone(),
94 scale_y: self.scale_y.clone(),
95 translate_x: self.translate_x.clone(),
96 translate_y: self.translate_y.clone(),
97 rotate: self.rotate.clone(),
98 pivot_x: self.pivot_x.clone(),
99 pivot_y: self.pivot_y.clone(),
100 sampling: self.sampling.clone(),
101 buffer: params,
102 });
103 Ok(CompiledOutput::Raster(RasterHandle {
104 texture,
105 domain: source.domain,
106 metadata: source.metadata,
107 }))
108 }
109}
110
111impl GpuFrameBindNode for Transform {
112 fn bind_gpu_frame(
113 &self,
114 ctx: &FrameBindContext<'_>,
115 binding: &FrameBinding,
116 bound: &mut BoundFrame,
117 ) -> crate::Result<()> {
118 let FrameBinding::Transform {
119 node_id,
120 scale_x,
121 scale_y,
122 translate_x,
123 translate_y,
124 rotate,
125 pivot_x,
126 pivot_y,
127 sampling,
128 buffer,
129 } = binding
130 else {
131 return Ok(());
132 };
133 let params = compiler::TransformParams {
134 scale: [
135 scale_x.resolve_float(
136 *node_id,
137 "scale_x",
138 &ctx.expr_context(*node_id, "scale_x"),
139 )? as f32,
140 scale_y.resolve_float(
141 *node_id,
142 "scale_y",
143 &ctx.expr_context(*node_id, "scale_y"),
144 )? as f32,
145 ],
146 translate: [
147 translate_x.resolve_float(
148 *node_id,
149 "translate_x",
150 &ctx.expr_context(*node_id, "translate_x"),
151 )? as f32,
152 translate_y.resolve_float(
153 *node_id,
154 "translate_y",
155 &ctx.expr_context(*node_id, "translate_y"),
156 )? as f32,
157 ],
158 pivot: [
159 pivot_x.resolve_float(
160 *node_id,
161 "pivot_x",
162 &ctx.expr_context(*node_id, "pivot_x"),
163 )? as f32,
164 pivot_y.resolve_float(
165 *node_id,
166 "pivot_y",
167 &ctx.expr_context(*node_id, "pivot_y"),
168 )? as f32,
169 ],
170 rotate_radians: (rotate.resolve_float(
171 *node_id,
172 "rotate",
173 &ctx.expr_context(*node_id, "rotate"),
174 )? as f32)
175 .to_radians(),
176 sampling: TransformSampling::from_int(sampling.resolve_int(
177 *node_id,
178 "sampling",
179 &ctx.expr_context(*node_id, "sampling"),
180 )?) as u32,
181 _pad: [0; 4],
182 };
183 bound.write_buffer(*buffer, 0, bytemuck::bytes_of(¶ms));
184 Ok(())
185 }
186}