/* @TODO: - auto-select level set by sampling a bunch of points - bring time back, w pause/rewind/adjust time speed (start out paused?) - fullscreen key - options for: - max framerate - mouse sensitivity - fov, focal length - AA quality - # iterations, distance cutoff ---release--- - show that θ = σ(z) / sqrt(x² + y²) (x,y,z) → (x cosθ + y sinθ, y cosθ - x sinθ, z) is lipschitz continuous, & add it - feedback for copy/paste (flash screen or something) - clean up code w a big state object - Params instead of depth for GenRandom - allow multiple endpoints (cube & sphere & ...) - save seeds to a file then let user go back&forth through past sdfs - mathematical analysis - documentation - GenRandom integers (+ gen_random_scale_bias) - better SDL api: Context + Window<'a> impl !Send+!Sync ----- cool seeds: commit ae29a61c9917da5ad9fbb7a24151bff506669ffb "cool stuff" 18413841503509874975 **17878446840930313726 commit 35cbbb40298389efcd2fe87a9c6458d49c1c567e "add torus, box frame" 2876923889725946210 *12145962426879404199 commit d7f810524a30843417253f80e454f1d9173aaeb3 "more functions" 2607779313513160780 16956394651920792998 3714031566539178742 --- the era of serialization a263736466a169536d6f6f74684d696e82a169536d6f6f74684d696e82a1634d696e82a166537068657265a163463332fa3ee5212ca167436f6d706f736583a167436f6d706f736582a167436f6d706f736582684964656e74697479684964656e74697479675369676d6f6964a166537068657265a163463332fa3f7bf840684964656e74697479a1634d697883a165546f727573a266726164697573a163463332fa3f1b29f269746869636b6e657373a163463332fa3e08d8c8a167436f6d706f736583a16f496e66696e6974654d6972726f7273a163463332fa3e59bc38a16443756265a163463332fa3f155e80684964656e74697479a163463332fa3f42e49aa168426f784672616d65a26473697a65a163463332fa4034884d69746869636b6e657373a163463332fa3d0bd15a6e636f6c6f725f66756e6374696f6ea167436f6d706f736582a16641726374616ea163463332fa3eea41e4a16f496e66696e6974654d6972726f7273a163463332fa3f5ddffe a263736466a167436f6d706f736583a166526f7461746583a163463332fa3f76cab2a163463332fa3d81cad0a163463332fa3f76ebd4a1634d696e82a167436f6d706f736583675369676d6f6964a1634d696e82a1634d697883a167436f6d706f736583684964656e74697479a166537068657265a163463332fa3e5a3e68684964656e74697479a167436f6d706f736583684964656e74697479a166537068657265a163463332fa3f46ade4684964656e74697479a163463332fa3f551da4a1634d696e82a167436f6d706f736583684964656e74697479a166537068657265a163463332fa3f306be8684964656e74697479a167436f6d706f736583684964656e74697479a166537068657265a163463332fa3ca99ac0684964656e74697479684964656e74697479a167436f6d706f736583a16f496e66696e6974654d6972726f7273a163463332fa3e9febeca167436f6d706f736583a16f496e66696e6974654d6972726f7273a163463332fa3ee05424a167436f6d706f736583675369676d6f6964a1634d696e82a166537068657265a163463332fa3e16dcf0a166537068657265a163463332fa3f48f0dc684964656e74697479684964656e74697479684964656e74697479684964656e746974796e636f6c6f725f66756e6374696f6ea167436f6d706f736582a16353696ea163463332fa3f1c2a8e675369676d6f6964 a263736466a167436f6d706f736583a165537153696ea163463332fa3e784c98a1634d697883a166537068657265a163463332fa3ea1ce4ca1634d696e82a166537068657265a163463332fa3f55f124a167436f6d706f736583675369676d6f6964a166537068657265a163463332fa3ef84fb0684964656e74697479a163463332fa3d2f72c0684964656e746974796e636f6c6f725f66756e6374696f6ea16f496e66696e6974654d6972726f7273a163463332fa3e9d85d0 */ extern crate nalgebra; pub mod sdf; mod sdl; pub mod win; use nalgebra::{Matrix3, Matrix4, Rotation3, Vector3}; use std::time::Instant; type Vec3 = Vector3; type Mat3 = Matrix3; type Mat4 = Matrix4; type Rot3 = Rotation3; struct View { pos: Vec3, rotation: Mat3, level_set: f32, } impl Default for View { fn default() -> Self { // don't start out right next to the origin, since weird stuff might be happening there let pos = Vec3::new(0.0, 0.0, 4.0); let rotation = Mat3::identity(); Self { pos, rotation, level_set: 0.0, } } } impl View { /// `rotation() * vec3(0, 0, -1)` is the direction the camera is pointing fn rotation(&self) -> Mat3 { self.rotation } fn yaw_by(&mut self, yaw: f32) { self.rotation *= Rot3::from_euler_angles(0.0, yaw, 0.0); } fn pitch_by(&mut self, pitch: f32) { self.rotation *= Rot3::from_euler_angles(pitch, 0.0, 0.0); } fn translation(&self) -> Mat4 { Mat4::new_translation(&self.pos) } #[allow(dead_code)] fn transform(&self) -> Mat4 { self.translation() * self.rotation().to_homogeneous() } } fn gen_program_from_scene( window: &mut win::Window, program: &mut win::Program, scene: &sdf::Scene, ) -> Result<(), String> { let fshader_source = include_str!("fshader_main.glsl"); let mut sdf = String::new(); let mut get_color = String::new(); scene.sdf.to_glsl_function("sdf", &mut sdf); scene .color_function .to_glsl_function("get_color_", &mut get_color); let fshader_source = fshader_source.replace("%SDF%", &sdf) .replace("%COLOR%", &get_color); //println!("{fshader_source}"); println!("scene: {}", scene.export_string()); window .link_program( program, "IN vec2 v_pos; OUT vec2 pos; uniform float u_aspect_ratio; void main() { pos = v_pos * vec2(u_aspect_ratio, 1.0); gl_Position = vec4(v_pos, 0.0, 1.0); }", &fshader_source, ) .map_err(|e| format!("Error compiling shader:\n{e}"))?; Ok(()) } fn get_rng() -> impl rand::Rng { use rand::SeedableRng; rand::rngs::SmallRng::seed_from_u64(rand::random::()) } fn try_main() -> Result<(), String> { let mut window = win::Window::new("AutoSDF", 1280, 720, true) .map_err(|e| format!("Error creating window: {e}"))?; let mut program = window.new_program(); let config = sdf::SceneConfig { sdf_max_depth: 7, color_max_depth: 6, }; let mut scene = sdf::Scene::good_random(&mut get_rng(), &config); gen_program_from_scene(&mut window, &mut program, &scene).unwrap_or_else(|e| eprintln!("Error: {e}") ); //gen_program_from_string(&mut window, &mut program, "a263736466a167436f6d706f736583a1695472616e736c61746583a163463332fa3ea4c00ca163463332fa3e85dc00a163463332fa3f2bbdaea167436f6d706f736583a166526f7461746583a163463332fa3f750dc2a163463332fa3f5a7f0ea163463332fa3f2df98ca1634d696e82a167436f6d706f736583a167436f6d706f736582a16353696ea163463332fa3f7cc2a0a167436f6d706f736582684964656e74697479684964656e74697479a166537068657265a163463332fa3f26f8f6684964656e74697479a167436f6d706f736583a166526f7461746583a163463332fa3f1bfed8a163463332fa3f1e1e30a163463332fa3eddc6b0a1634d697883a167436f6d706f736583684964656e74697479a166537068657265a163463332fa3ea149ec684964656e74697479a167436f6d706f736583684964656e74697479a166537068657265a163463332fa3f6b0018684964656e74697479a163463332fa3e60a8d8684964656e74697479684964656e74697479684964656e746974796e636f6c6f725f66756e6374696f6ea165537153696ea163463332fa3ebaa7ec")?; let mut buffer = window.create_buffer(); let data: &[[f32; 2]] = &[ [-1.0, -1.0], [1.0, -1.0], [1.0, 1.0], [-1.0, -1.0], [1.0, 1.0], [-1.0, 1.0], ]; window.set_buffer_data(&mut buffer, data); let mut array = window.create_vertex_array(buffer, &program); window.array_attrib2f(&mut array, "v_pos", 0); let mut view = View::default(); window.set_mouse_relative(true); let mut frame_time = Instant::now(); let mut show_debug_info = false; let mut total_time = 0.0; 'mainloop: loop { let frame_dt = frame_time.elapsed().as_secs_f32(); frame_time = Instant::now(); while let Some(event) = window.next_event() { use win::Event::*; use win::Key::*; match event { Quit | KeyDown { key: Escape, .. } => break 'mainloop, KeyDown { key: F1, .. } => show_debug_info = !show_debug_info, KeyDown { key: R, .. } => { scene = sdf::Scene::good_random(&mut get_rng(), &config); match gen_program_from_scene(&mut window, &mut program, &scene) { Ok(()) => { view.level_set = 0.0; } Err(e) => { eprintln!("Error: {e}") } }; } KeyDown { key: C, modifier, .. } if modifier.ctrl() => { // copy scene match window.set_clipboard_text(&scene.export_string()) { Ok(()) => { } Err(e) => { eprintln!("couldn't copy text to clipboard: {e}") } } } KeyDown { key: V, modifier, .. } if modifier.ctrl() => { // paste scene match window.get_clipboard_text() { Ok(s) => { match sdf::Scene::import_string(&s) { Some(new_scene) => { scene = new_scene; match gen_program_from_scene(&mut window, &mut program, &scene) { Ok(()) => { view.level_set = 0.0; } Err(e) => { eprintln!("Error: {e}") } } } None => { eprintln!("bad string") } } } Err(e) => { // very unlikely to happen eprintln!("couldn't get clipboard text: {e}") } } } KeyDown { key: N0, .. } => view.level_set = 0.0, MouseMotion { xrel, yrel, .. } => { let mouse_sensitivity = 0.05; view.yaw_by(-xrel as f32 * mouse_sensitivity * frame_dt); view.pitch_by(-yrel as f32 * mouse_sensitivity * frame_dt); } _ => {} } } { // movement let mut dx = 0.0; let mut dy = 0.0; let mut dz = 0.0; let mut dl = 0.0; use win::Key::*; if window.any_key_down(&[W, Up]) { dz -= 1.0; } if window.any_key_down(&[S, Down]) { dz += 1.0; } if window.any_key_down(&[A, Left]) { dx -= 1.0; } if window.any_key_down(&[D, Right]) { dx += 1.0; } if window.is_key_down(Q) { dy += 1.0; } if window.is_key_down(E) { dy -= 1.0; } if window.any_key_down(&[PageUp, NumPad9, Equals]) { dl += 1.0; } if window.any_key_down(&[PageDown, NumPad3, Minus]) { dl -= 1.0; } let mut speed_multiplier = if window.is_shift_down() { 10.0 } else { 1.0 }; speed_multiplier *= if window.is_ctrl_down() { 0.1 } else { 1.0 }; let motion = Vec3::new(dx, dy, dz); if let Some(motion) = motion.try_normalize(0.001) { let move_speed = 4.0 * speed_multiplier; let motion = motion * frame_dt * move_speed; let motion = view.rotation() * motion; view.pos += motion; } let level_set_speed = 1.0 * speed_multiplier; view.level_set += dl * frame_dt * level_set_speed; } window.viewport_full_screen(); window.clear_screen(win::ColorF32::BLACK); window.use_program(&program); window.uniform1f("u_aspect_ratio", window.aspect_ratio()); window.uniform1f("u_time", total_time); window.uniform1f("u_fov", std::f32::consts::PI * 0.25); window.uniform1f("u_focal_length", 1.0); window.uniform1f("u_level_set", view.level_set); window.uniform1i("u_hsv", 0); window.uniform3x3f("u_rotation", view.rotation().as_slice()); window.uniform3f_slice("u_translation", view.pos.as_slice()); window.draw_array(&array); window.swap(); if show_debug_info { println!("frame time = {:?}ms", frame_dt * 1000.0); } total_time += frame_dt; } Ok(()) } fn main() { if let Err(e) = try_main() { win::display_error_message(&e); } }