1use super::Program;
2use std::rc::Rc;
3
4#[derive(PartialEq, Eq)]
5#[allow(clippy::upper_case_acronyms)]
6pub enum Backend {
7 GLSL,
8}
9
10#[derive(Copy, Clone)]
11pub enum OutputType {
12 DistanceOnly,
13 DistanceWithRgb,
14}
15
16pub struct CodeGenContext<'a> {
17 function_name: &'a str,
18 dynamic_constants: bool,
19 variable_index: usize,
20 constant_index: usize,
21
22 variable_stack: Vec<Rc<str>>,
23 position_variable_stack: Vec<Rc<str>>,
24 current_position: Rc<str>,
25}
26
27impl<'a> CodeGenContext<'a> {
28 pub fn new(
29 initial_position_variable: &str,
30 function_name: &'a str,
31 dynamic_constants: bool,
32 ) -> Self {
33 let position: Rc<str> = Rc::from(initial_position_variable);
34
35 Self {
36 function_name,
37 dynamic_constants,
38 variable_index: 0,
39 constant_index: 0,
40 variable_stack: Vec::new(),
41 position_variable_stack: vec![position.clone()],
42 current_position: position,
43 }
44 }
45
46 pub fn push_variable(&mut self) -> Rc<str> {
47 let name = format!("sdf{}", self.variable_index);
48 self.variable_index += 1;
49
50 let name: Rc<str> = Rc::from(name.as_str());
51 self.variable_stack.push(name.clone());
52 name
53 }
54
55 pub fn uint32(&mut self) -> String {
56 let components = &[".x", ".y", ".z", ".w"];
57
58 let s = format!(
59 "{}_constants[{}_constants_offset + {}]{}",
60 self.function_name,
61 self.function_name,
62 if self.dynamic_constants {
63 self.constant_index >> 2
64 } else {
65 self.constant_index
66 },
67 if self.dynamic_constants {
68 components[self.constant_index & 0x3]
69 } else {
70 ""
71 }
72 );
73 self.constant_index += 1;
74 s
75 }
76
77 pub fn float32(&mut self) -> String {
78 format!("uintBitsToFloat({})", self.uint32())
79 }
80
81 pub fn vec2(&mut self) -> String {
82 format!("vec2({}, {})", self.float32(), self.float32())
83 }
84
85 pub fn vec3(&mut self) -> String {
86 format!(
87 "vec3({}, {}, {})",
88 self.float32(),
89 self.float32(),
90 self.float32()
91 )
92 }
93
94 pub fn vec4(&mut self) -> String {
95 format!(
96 "vec4({}, {}, {}, {})",
97 self.float32(),
98 self.float32(),
99 self.float32(),
100 self.float32()
101 )
102 }
103
104 pub fn quat(&mut self) -> String {
105 self.vec4()
106 }
107
108 pub fn material(&mut self) -> String {
109 self.vec3()
110 }
111
112 pub fn pop_variable(&mut self) -> Option<Rc<str>> {
113 self.variable_stack.pop()
114 }
115
116 pub fn pop_transform(&mut self) {
117 self.current_position = self.position_variable_stack.pop().unwrap();
118 }
119
120 pub fn current_position(&self) -> Rc<str> {
121 self.current_position.clone()
122 }
123
124 pub fn push_transform(&mut self) -> (Rc<str>, Rc<str>) {
126 let s = format!("transform{}", self.variable_index);
127 let s: Rc<str> = Rc::from(s.as_str());
128 self.variable_index += 1;
129
130 self.position_variable_stack
131 .push(self.current_position.clone());
132 let old_position = self.current_position.clone();
133 self.current_position = s.clone();
134 (s, old_position)
135 }
136}
137
138pub struct CodeGen {
139 backend: Backend,
140}
141
142impl CodeGen {
143 pub fn glsl() -> Self {
144 Self {
145 backend: Backend::GLSL,
146 }
147 }
148
149 pub fn get_library_code(&self) -> &'static str {
151 match self.backend {
152 Backend::GLSL => saft_sdf::get_glsl_sdf_library_code(),
153 }
154 }
155
156 fn build_glsl_code(
157 program: &Program,
158 function_name: &str,
159 output_type: OutputType,
160 dynamic_constants: bool,
161 ) -> String {
162 use super::Opcode::*;
163 use std::fmt::Write;
164
165 let mut code = String::new();
166 code.push_str("// !!! START OF GENERATED CODE !!!\n");
167
168 let output_glsl_type = match output_type {
169 OutputType::DistanceOnly => "float",
170 OutputType::DistanceWithRgb => "vec4",
171 };
172
173 if !dynamic_constants {
174 let _ = write!(
175 &mut code,
176 "\tconst uint {}_constants[{}] = uint[](",
177 function_name,
178 program.constants.len()
179 );
180
181 for (i, c) in program.constants.iter().enumerate() {
182 if i != 0 {
183 code.push(',');
184 }
185
186 let _ = write!(&mut code, "{}", c.to_bits());
187 }
188
189 code.push_str(");\n");
190 }
191
192 let mut ctx = CodeGenContext::new("pos", function_name, dynamic_constants);
193
194 let _ = writeln!(
195 &mut code,
196 "{} {}_base(vec3 pos) {{",
197 output_glsl_type, function_name
198 );
199
200 let prefix = match output_type {
201 OutputType::DistanceOnly => "sd",
202 OutputType::DistanceWithRgb => "sdrgb",
203 };
204
205 for opcode in &program.opcodes {
206 match opcode {
207 Plane => {
208 let variable_name = ctx.push_variable();
209 let plane = ctx.vec4();
210 let _ = writeln!(
211 &mut code,
212 "\t{} {} = {}_plane({}, {});",
213 output_glsl_type,
214 variable_name,
215 prefix,
216 ctx.current_position(),
217 plane,
218 );
219 }
220 Sphere => {
221 let variable_name = ctx.push_variable();
222 let center = ctx.vec3();
223 let radius = ctx.float32();
224 let _ = writeln!(
225 &mut code,
226 "\t{} {} = {}_sphere({}, {}, {});",
227 output_glsl_type,
228 variable_name,
229 prefix,
230 ctx.current_position(),
231 center,
232 radius,
233 );
234 }
235 Capsule => {
236 let variable_name = ctx.push_variable();
237 let points = [ctx.vec3(), ctx.vec3()];
238 let radius = ctx.float32();
239 let _ = writeln!(
240 &mut code,
241 "\t{} {} = {}_capsule({}, {}, {}, {});",
242 output_glsl_type,
243 variable_name,
244 prefix,
245 ctx.current_position(),
246 points[0],
247 points[1],
248 radius,
249 );
250 }
251 RoundedCylinder => {
252 let variable_name = ctx.push_variable();
253 let cylinder_radius = ctx.float32();
254 let half_height = ctx.float32();
255 let rounding_radius = ctx.float32();
256 let _ = writeln!(
257 &mut code,
258 "\t{} {} = {}_rounded_cylinder({}, {}, {}, {});",
259 output_glsl_type,
260 variable_name,
261 prefix,
262 ctx.current_position(),
263 cylinder_radius,
264 half_height,
265 rounding_radius,
266 );
267 }
268 TaperedCapsule => {
269 let variable_name = ctx.push_variable();
270 let p0 = ctx.vec3();
271 let r0 = ctx.float32();
272 let p1 = ctx.vec3();
273 let r1 = ctx.float32();
274 let _ = writeln!(
275 &mut code,
276 "\t{} {} = {}_tapered_capsule({}, {}, {}, {}, {});",
277 output_glsl_type,
278 variable_name,
279 prefix,
280 ctx.current_position(),
281 p0,
282 p1,
283 r0,
284 r1,
285 );
286 }
287 Cone => {
288 let variable_name = ctx.push_variable();
289 let r = ctx.float32();
290 let h = ctx.float32();
291 let _ = writeln!(
292 &mut code,
293 "\t{} {} = {}_cone({}, {}, {});",
294 output_glsl_type,
295 variable_name,
296 prefix,
297 ctx.current_position(),
298 r,
299 h,
300 );
301 }
302 RoundedBox => {
303 let variable_name = ctx.push_variable();
304 let half_size = ctx.vec3();
305 let radius = ctx.float32();
306 let _ = writeln!(
307 &mut code,
308 "\t{} {} = {}_rounded_box({}, {}, {});",
309 output_glsl_type,
310 variable_name,
311 prefix,
312 ctx.current_position(),
313 half_size,
314 radius,
315 );
316 }
317 Torus => {
318 let _ = writeln!(
320 &mut code,
321 "\t{} {} = {}_torus({}, {}, {});",
322 output_glsl_type,
323 ctx.push_variable(),
324 prefix,
325 ctx.current_position(),
326 ctx.float32(),
327 ctx.float32(),
328 );
329 }
330 TorusSector => {
331 let _ = writeln!(
333 &mut code,
334 "\t{} {} = {}_torus_sector({}, {}, {}, {});",
335 output_glsl_type,
336 ctx.push_variable(),
337 prefix,
338 ctx.current_position(),
339 ctx.float32(),
340 ctx.float32(),
341 ctx.vec2(),
342 );
343 }
344 BiconvexLens => {
345 let variable_name = ctx.push_variable();
346 let lower_sagitta = ctx.float32();
347 let upper_sagitta = ctx.float32();
348 let chord = ctx.float32();
349 let _ = writeln!(
350 &mut code,
351 "\t{} {} = {}_biconvex_lens({}, {}, {}, {});",
352 output_glsl_type,
353 variable_name,
354 prefix,
355 ctx.current_position(),
356 lower_sagitta,
357 upper_sagitta,
358 chord,
359 );
360 }
361 Material => {
362 let sd = ctx.pop_variable().unwrap();
363 let material = ctx.material();
364 let variable_name = ctx.push_variable();
365 let _ = writeln!(
366 &mut code,
367 "\t{} {} = {}_material({}, {});",
368 output_glsl_type, variable_name, prefix, sd, material
369 );
370 }
371 Union => {
372 let sd1 = ctx.pop_variable().unwrap();
373 let sd2 = ctx.pop_variable().unwrap();
374 let variable_name = ctx.push_variable();
375 let _ = writeln!(
376 &mut code,
377 "\t{} {} = {}_op_union({}, {});",
378 output_glsl_type, variable_name, prefix, sd1, sd2
379 );
380 }
381 UnionSmooth => {
382 let sd1 = ctx.pop_variable().unwrap();
383 let sd2 = ctx.pop_variable().unwrap();
384 let variable_name = ctx.push_variable();
385 let size = ctx.float32();
386 let _ = writeln!(
387 &mut code,
388 "\t{} {} = {}_op_union_smooth({}, {}, {});",
389 output_glsl_type, variable_name, prefix, sd1, sd2, size
390 );
391 }
392 Subtract => {
393 let sd1 = ctx.pop_variable().unwrap();
394 let sd2 = ctx.pop_variable().unwrap();
395 let variable_name = ctx.push_variable();
396 let _ = writeln!(
397 &mut code,
398 "\t{} {} = {}_op_subtract({}, {});",
399 output_glsl_type, variable_name, prefix, sd1, sd2
400 );
401 }
402 SubtractSmooth => {
403 let sd1 = ctx.pop_variable().unwrap();
404 let sd2 = ctx.pop_variable().unwrap();
405 let variable_name = ctx.push_variable();
406 let size = ctx.float32();
407 let _ = writeln!(
408 &mut code,
409 "\t{} {} = {}_op_subtract_smooth({}, {}, {});",
410 output_glsl_type, variable_name, prefix, sd1, sd2, size
411 );
412 }
413 Intersect => {
414 let sd1 = ctx.pop_variable().unwrap();
415 let sd2 = ctx.pop_variable().unwrap();
416 let variable_name = ctx.push_variable();
417 let _ = writeln!(
418 &mut code,
419 "\t{} {} = {}_op_intersect({}, {});",
420 output_glsl_type, variable_name, prefix, sd1, sd2
421 );
422 }
423 IntersectSmooth => {
424 let sd1 = ctx.pop_variable().unwrap();
425 let sd2 = ctx.pop_variable().unwrap();
426 let variable_name = ctx.push_variable();
427 let size = ctx.float32();
428 let _ = writeln!(
429 &mut code,
430 "\t{} {} = {}_op_intersect_smooth({}, {}, {});",
431 output_glsl_type, variable_name, prefix, sd1, sd2, size
432 );
433 }
434 PushTranslation => {
435 let translation = ctx.vec3();
436 let (new_position, old_position) = ctx.push_transform();
437 let _ = writeln!(
438 &mut code,
439 "\tvec3 {} = {} + {};",
440 new_position, old_position, translation
441 );
442 }
443 PopTransform => {
444 ctx.pop_transform();
445 }
446 PushRotation => {
447 let rotation = ctx.quat();
448 let (new_position, old_position) = ctx.push_transform();
449 let _ = writeln!(
450 &mut code,
451 "\tvec3 {} = mul_quat({}, {});",
452 new_position, rotation, old_position
453 );
454 }
455 PushScale => {
456 let scale = ctx.float32();
457 let (new_position, old_position) = ctx.push_transform();
458 let _ = writeln!(
459 &mut code,
460 "\tvec3 {} = {} * {};",
461 new_position, old_position, scale
462 );
463 }
464 PopScale => {
465 ctx.pop_transform();
466 let inv_scale = ctx.float32();
467 let sd = ctx.pop_variable().unwrap();
468 let variable_name = ctx.push_variable();
469 let _ = writeln!(
470 &mut code,
471 "\t{} {} = {}_op_scale_distance({}, {});",
472 output_glsl_type, variable_name, prefix, sd, inv_scale
473 );
474 }
475 End => {
476 break;
477 }
478 }
479 }
480
481 let ret = ctx.pop_variable().unwrap();
482 let _ = writeln!(&mut code, "\treturn {};\n}}", ret);
483
484 let _ = writeln!(
485 &mut code,
486 "float {}(vec3 pos) {{ return {}_base(pos){}; }}",
487 function_name,
488 function_name,
489 match output_type {
490 OutputType::DistanceOnly => "",
491 OutputType::DistanceWithRgb => ".w",
492 }
493 );
494
495 match output_type {
496 OutputType::DistanceWithRgb => {
497 let _ = writeln!(
498 &mut code,
499 "vec3 {}_color(vec3 pos) {{ return {}_base(pos).rgb; }}",
500 function_name, function_name,
501 );
502 }
503 OutputType::DistanceOnly => {
504 let _ = writeln!(
505 &mut code,
506 "vec3 {}_color(vec3 /*pos*/) {{ return vec3(1.0, 1.0, 1.0); }}",
507 function_name,
508 );
509 }
510 }
511
512 code.push_str("// !!! END OF GENERATED CODE !!!\n");
513 code
514 }
515
516 pub fn to_code(
518 &self,
519 program: &Program,
520 function_name: &str,
521 output_type: OutputType,
522 dynamic_constants: bool,
523 ) -> String {
524 match self.backend {
525 Backend::GLSL => {
526 Self::build_glsl_code(program, function_name, output_type, dynamic_constants)
527 }
528 }
529 }
530}