├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── README.md ├── assets ├── alpha_input.png ├── alpha_input_output.png ├── alpha_output.png ├── bend.png ├── box.png ├── icons.svg ├── intersection.png ├── plane.png ├── render.png ├── root.png ├── smooth_minimum.png ├── sphere.png ├── subtraction.png ├── torus.png ├── transform.png ├── twist.png └── union.png ├── logo.svg ├── screenshots ├── all_operators.PNG ├── graph.png └── user_interface.gif └── src ├── bounds.rs ├── color.rs ├── constants.rs ├── graph.rs ├── interaction.rs ├── main.rs ├── network.rs ├── operator.rs ├── preview.rs ├── program.rs ├── renderer.rs ├── shader_builder.rs └── texture.rs /.gitignore: -------------------------------------------------------------------------------- 1 | # Rust stuff 2 | /target 3 | **/*.rs.bk 4 | 5 | # IntelliJ IDEA stuff 6 | .idea 7 | *.iml -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "sdfperf" 3 | version = "0.1.0" 4 | authors = ["Michael Walczyk "] 5 | 6 | [dependencies] 7 | gl = "0.6.5" 8 | glutin = "0.10.1" 9 | cgmath = "0.15.0" 10 | uuid = { version = "0.5.1", features = ["v4"] } 11 | image = "0.18.0" -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SDFPerf 2 | 🔌 A node-based editor for experimenting with signed distance fields and raymarching. 3 | 4 |

5 | 6 |

7 | 8 | 9 | ## Description 10 |

11 | 12 |

13 | 14 | `sdfperf` is a work-in-progress node-based editor for creating and manipulating signed-distance fields (SDFs). Currently, the system supports basic SDF primitives (spheres, cubes, planes, toruses) as well as basic domain manipulation (translations, scales, bends, twists) and CSG operations (unions, intersections, differences, smooth-minimums). 15 | 16 | Each time a new connection is made, the underlying shader graph is re-built and a new raymarching shader string is generated. 17 | 18 | ## Tested On 19 | - Windows 8.1, Windows 10, Ubuntu 18.04 20 | - NVIDIA GeForce GTX 970M, NVIDIA GeForce GTX 980 21 | - Rust compiler version `1.37.0-nightly` (nightly may not be required) 22 | 23 | NOTE: this project will only run on graphics cards that support OpenGL [Direct State Access](https://www.khronos.org/opengl/wiki/Direct_State_Access) (DSA). 24 | 25 | ## To Build 26 | 1. Clone this repo. 27 | 2. Make sure 🦀 [Rust](https://www.rust-lang.org/en-US/) installed and `cargo` is in your `PATH`. 28 | 3. Inside the repo, run: `cargo build --release`. 29 | 30 | ## To Use 31 |

32 | 33 |

34 | 35 | The UI displays a virtual preview of the final scene, which can be navigated with simple camera controls. Additionally, the user can switch between 5 distinct shading modes with the number keys (1-5): normals, ambient occlusion (AO), diffuse, z-depth, and ray depth. Certain nodes, such as the `translation` operator, have parameters that can be manipulated with the arrow keys. 36 | -------------------------------------------------------------------------------- /assets/alpha_input.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mwalczyk/sdfperf/e265d73381e87f4adb4d61646a01e0b0fcd9da9f/assets/alpha_input.png -------------------------------------------------------------------------------- /assets/alpha_input_output.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mwalczyk/sdfperf/e265d73381e87f4adb4d61646a01e0b0fcd9da9f/assets/alpha_input_output.png -------------------------------------------------------------------------------- /assets/alpha_output.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mwalczyk/sdfperf/e265d73381e87f4adb4d61646a01e0b0fcd9da9f/assets/alpha_output.png -------------------------------------------------------------------------------- /assets/bend.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mwalczyk/sdfperf/e265d73381e87f4adb4d61646a01e0b0fcd9da9f/assets/bend.png -------------------------------------------------------------------------------- /assets/box.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mwalczyk/sdfperf/e265d73381e87f4adb4d61646a01e0b0fcd9da9f/assets/box.png -------------------------------------------------------------------------------- /assets/icons.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 21 | 25 | 30 | 36 | 41 | 46 | 52 | 57 | 63 | 69 | 74 | 79 | 85 | 86 | 90 | 95 | 101 | 106 | 111 | 117 | 118 | 122 | 127 | 133 | 138 | 143 | 149 | 150 | 154 | 159 | 165 | 170 | 175 | 181 | 182 | 186 | 191 | 197 | 202 | 207 | 213 | 214 | 218 | 223 | 229 | 234 | 239 | 245 | 250 | 256 | 262 | 267 | 272 | 278 | 279 | 283 | 288 | 294 | 299 | 304 | 310 | 311 | 315 | 320 | 326 | 331 | 336 | 342 | 343 | 344 | 370 | 375 | 380 | 381 | 383 | 384 | 386 | image/svg+xml 387 | 389 | 390 | 391 | 392 | 393 | 397 | 402 | 409 | 413 | 421 | 427 | 428 | 429 | 434 | 443 | 449 | 455 | 456 | 462 | 468 | 469 | 470 | 477 | 484 | 491 | 498 | 503 | 512 | 516 | 522 | 528 | 529 | 530 | 535 | 542 | 549 | 550 | 555 | 562 | 568 | 569 | 574 | 581 | 589 | 590 | 595 | 597 | 604 | 621 | 622 | 623 | 628 | 637 | 645 | 646 | 651 | 658 | 659 | P 679 | 686 | 687 | Tr 707 | 714 | 715 | Tw 735 | 742 | 743 | Be 763 | 767 | 768 | 773 | 780 | 784 | 791 | 792 | 793 | 798 | 800 | 807 | 815 | 822 | 829 | 830 | 834 | 841 | 849 | 856 | 857 | 861 | 868 | 876 | 883 | 884 | 893 | 903 | 913 | 922 | 932 | 941 | 950 | 960 | 970 | 979 | 988 | 998 | 1008 | 1017 | 1018 | 1019 | 1020 | -------------------------------------------------------------------------------- /assets/intersection.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mwalczyk/sdfperf/e265d73381e87f4adb4d61646a01e0b0fcd9da9f/assets/intersection.png -------------------------------------------------------------------------------- /assets/plane.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mwalczyk/sdfperf/e265d73381e87f4adb4d61646a01e0b0fcd9da9f/assets/plane.png -------------------------------------------------------------------------------- /assets/render.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mwalczyk/sdfperf/e265d73381e87f4adb4d61646a01e0b0fcd9da9f/assets/render.png -------------------------------------------------------------------------------- /assets/root.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mwalczyk/sdfperf/e265d73381e87f4adb4d61646a01e0b0fcd9da9f/assets/root.png -------------------------------------------------------------------------------- /assets/smooth_minimum.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mwalczyk/sdfperf/e265d73381e87f4adb4d61646a01e0b0fcd9da9f/assets/smooth_minimum.png -------------------------------------------------------------------------------- /assets/sphere.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mwalczyk/sdfperf/e265d73381e87f4adb4d61646a01e0b0fcd9da9f/assets/sphere.png -------------------------------------------------------------------------------- /assets/subtraction.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mwalczyk/sdfperf/e265d73381e87f4adb4d61646a01e0b0fcd9da9f/assets/subtraction.png -------------------------------------------------------------------------------- /assets/torus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mwalczyk/sdfperf/e265d73381e87f4adb4d61646a01e0b0fcd9da9f/assets/torus.png -------------------------------------------------------------------------------- /assets/transform.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mwalczyk/sdfperf/e265d73381e87f4adb4d61646a01e0b0fcd9da9f/assets/transform.png -------------------------------------------------------------------------------- /assets/twist.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mwalczyk/sdfperf/e265d73381e87f4adb4d61646a01e0b0fcd9da9f/assets/twist.png -------------------------------------------------------------------------------- /assets/union.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mwalczyk/sdfperf/e265d73381e87f4adb4d61646a01e0b0fcd9da9f/assets/union.png -------------------------------------------------------------------------------- /logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 20 | 22 | 32 | 39 | 44 | 53 | 60 | 61 | 69 | 73 | 74 | 78 | 83 | 89 | 94 | 99 | 105 | 110 | 116 | 122 | 127 | 132 | 138 | 143 | 149 | 155 | 160 | 165 | 171 | 172 | 176 | 181 | 187 | 192 | 197 | 203 | 208 | 214 | 220 | 225 | 230 | 236 | 237 | 241 | 246 | 252 | 257 | 262 | 268 | 273 | 279 | 285 | 290 | 295 | 301 | 302 | 306 | 311 | 317 | 322 | 327 | 333 | 338 | 344 | 350 | 355 | 360 | 366 | 371 | 377 | 383 | 388 | 393 | 399 | 400 | 404 | 409 | 415 | 420 | 425 | 431 | 436 | 442 | 448 | 453 | 458 | 464 | 465 | 469 | 474 | 480 | 485 | 490 | 496 | 497 | 501 | 506 | 512 | 517 | 522 | 528 | 533 | 539 | 545 | 550 | 555 | 561 | 562 | 566 | 571 | 577 | 582 | 587 | 593 | 598 | 604 | 610 | 615 | 620 | 626 | 627 | 628 | 650 | 652 | 653 | 655 | image/svg+xml 656 | 658 | 659 | 660 | 661 | 662 | 667 | 671 | 674 | 677 | 680 | 683 | 689 | 691 | 696 | 701 | 702 | 703 | 704 | 710 | 713 | 719 | 723 | 729 | 735 | 741 | 747 | 753 | 759 | 765 | 771 | 777 | 778 | 779 | 781 | 784 | 787 | 793 | 795 | 798 | 801 | 804 | 807 | 825 | 833 | 835 | 843 | 851 | 859 | 860 | 861 | 862 | 863 | 864 | 865 | 866 | 867 | 868 | 869 | 903 | 904 | 905 | -------------------------------------------------------------------------------- /screenshots/all_operators.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mwalczyk/sdfperf/e265d73381e87f4adb4d61646a01e0b0fcd9da9f/screenshots/all_operators.PNG -------------------------------------------------------------------------------- /screenshots/graph.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mwalczyk/sdfperf/e265d73381e87f4adb4d61646a01e0b0fcd9da9f/screenshots/graph.png -------------------------------------------------------------------------------- /screenshots/user_interface.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mwalczyk/sdfperf/e265d73381e87f4adb4d61646a01e0b0fcd9da9f/screenshots/user_interface.gif -------------------------------------------------------------------------------- /src/bounds.rs: -------------------------------------------------------------------------------- 1 | use cgmath::{Matrix, Matrix4, SquareMatrix, Vector2, Vector3}; 2 | 3 | pub enum Edge { 4 | Left, 5 | Right, 6 | Top, 7 | Bottom, 8 | } 9 | 10 | #[derive(Copy, Clone, PartialEq)] 11 | pub struct Rect { 12 | upper_left: Vector2, 13 | size: Vector2, 14 | model_matrix: Matrix4, 15 | } 16 | 17 | impl Rect { 18 | pub fn new(upper_left: Vector2, size: Vector2) -> Rect { 19 | let mut rect = Rect { 20 | upper_left, 21 | size, 22 | model_matrix: Matrix4::identity(), 23 | }; 24 | 25 | rect.rebuild_model_matrix(); 26 | rect 27 | } 28 | 29 | pub fn expanded_from(other: &Rect, amount: &Vector2) -> Rect { 30 | Rect::new(other.upper_left - amount * 0.5, other.size + amount) 31 | } 32 | 33 | pub fn square(upper_left: Vector2, size: f32) -> Rect { 34 | Rect::new(upper_left, Vector2::new(size, size)) 35 | } 36 | 37 | /// Returns the upper-left corner of the rectangle. 38 | pub fn get_upper_left(&self) -> &Vector2 { 39 | &self.upper_left 40 | } 41 | 42 | /// Returns the size (width and height) of the rectangle. 43 | pub fn get_size(&self) -> &Vector2 { 44 | &self.size 45 | } 46 | 47 | /// Returns the model matrix represented by this rectangle. 48 | pub fn get_model_matrix(&self) -> &Matrix4 { 49 | &self.model_matrix 50 | } 51 | 52 | /// Sets the upper-left corner of the rectangle. 53 | pub fn set_upper_left(&mut self, to: &Vector2) { 54 | self.upper_left = *to; 55 | self.rebuild_model_matrix(); 56 | } 57 | 58 | /// Sets the size (width and height) of the rectangle. 59 | pub fn set_size(&mut self, to: &Vector2) { 60 | self.size = *to; 61 | self.rebuild_model_matrix(); 62 | } 63 | 64 | pub fn snap_to_nearest(&mut self, increment: &Vector2) { 65 | // TODO 66 | } 67 | 68 | pub fn expand_from_center(&self, delta: &Vector2) -> Rect { 69 | Rect::new(self.upper_left - delta * 0.5, self.size + delta) 70 | } 71 | 72 | pub fn center_on_edge(&mut self, other: &Rect, edge: Edge) { 73 | let center = other.midpoint(edge) - self.get_size() * 0.5; 74 | self.set_upper_left(¢er); 75 | } 76 | 77 | pub fn translate(&mut self, offset: &Vector2) { 78 | self.upper_left += *offset; 79 | self.rebuild_model_matrix(); 80 | } 81 | 82 | pub fn inside(&self, point: &Vector2) -> bool { 83 | if point.x > self.upper_left.x && point.x < (self.upper_left.x + self.size.x) 84 | && point.y > self.upper_left.y && point.y < (self.upper_left.y + self.size.y) 85 | { 86 | return true; 87 | } 88 | false 89 | } 90 | 91 | pub fn inside_with_padding(&self, point: &Vector2, padding: f32) -> bool { 92 | if point.x > (self.upper_left.x - padding) 93 | && point.x < (self.upper_left.x + self.size.x + padding) 94 | && point.y > (self.upper_left.y - padding) 95 | && point.y < (self.upper_left.y + self.size.y + padding) 96 | { 97 | return true; 98 | } 99 | false 100 | } 101 | 102 | pub fn centroid(&self) -> Vector2 { 103 | Vector2::new( 104 | self.upper_left.x + self.size.x * 0.5, 105 | self.upper_left.y + self.size.y * 0.5, 106 | ) 107 | } 108 | 109 | pub fn midpoint(&self, edge: Edge) -> Vector2 { 110 | match edge { 111 | Edge::Left => Vector2::new(self.upper_left.x, self.upper_left.y + self.size.y * 0.5), 112 | Edge::Right => Vector2::new( 113 | self.upper_left.x + self.size.x, 114 | self.upper_left.y + self.size.y * 0.5, 115 | ), 116 | Edge::Top => Vector2::new(self.upper_left.x + self.size.x * 0.5, self.upper_left.y), 117 | Edge::Bottom => Vector2::new( 118 | self.upper_left.x + self.size.x * 0.5, 119 | self.upper_left.y + self.size.y, 120 | ), 121 | } 122 | } 123 | 124 | /// Caches a 4x4 model matrix that describes this bounding 125 | /// rectangle. This will be rebuilt any time the bounding 126 | /// rectangle changes size or location. 127 | fn rebuild_model_matrix(&mut self) { 128 | let translation = 129 | Matrix4::from_translation(Vector3::new(self.upper_left.x, self.upper_left.y, 0.0)); 130 | let scale = Matrix4::from_nonuniform_scale(self.size.x, self.size.y, 0.0); 131 | 132 | self.model_matrix = translation * scale; 133 | } 134 | } 135 | 136 | impl Default for Rect { 137 | fn default() -> Self { 138 | Rect { 139 | upper_left: Vector2::new(0.0, 0.0), 140 | size: Vector2::new(1.0, 1.0), 141 | model_matrix: Matrix4::identity(), 142 | } 143 | } 144 | } 145 | -------------------------------------------------------------------------------- /src/color.rs: -------------------------------------------------------------------------------- 1 | use std::ops::{Add, AddAssign, Sub, SubAssign}; 2 | 3 | use cgmath::Vector4; 4 | 5 | #[derive(Copy, Clone, Debug, PartialEq)] 6 | pub struct Color { 7 | pub r: f32, 8 | pub g: f32, 9 | pub b: f32, 10 | pub a: f32, 11 | } 12 | 13 | impl Color { 14 | pub fn new(r: f32, g: f32, b: f32, a: f32) -> Color { 15 | Color { r, g, b, a } 16 | } 17 | 18 | pub fn mono(rgb: f32, a: f32) -> Color { 19 | Color::new(rgb, rgb, rgb, a) 20 | } 21 | 22 | pub fn from_hex(code: u32, alpha: f32) -> Color { 23 | let r = ((code >> 16) & 0xFF) as f32 / 255.0; 24 | let g = ((code >> 8) & 0xFF) as f32 / 255.0; 25 | let b = ((code) & 0xFF) as f32 / 255.0; 26 | Color::new(r, g, b, alpha) 27 | } 28 | 29 | pub fn white() -> Color { 30 | Color::mono(1.0, 1.0) 31 | } 32 | 33 | pub fn black() -> Color { 34 | Color::mono(0.0, 1.0) 35 | } 36 | } 37 | 38 | impl Add for Color { 39 | type Output = Color; 40 | 41 | fn add(self, other: Color) -> Color { 42 | Color::new( 43 | self.r + other.r, 44 | self.g + other.g, 45 | self.b + other.b, 46 | self.a + other.a, 47 | ) 48 | } 49 | } 50 | 51 | impl AddAssign for Color { 52 | fn add_assign(&mut self, other: Color) { 53 | *self = Color::new( 54 | self.r + other.r, 55 | self.g + other.g, 56 | self.b + other.b, 57 | self.a + other.a, 58 | ); 59 | } 60 | } 61 | 62 | impl Sub for Color { 63 | type Output = Color; 64 | 65 | fn sub(self, other: Color) -> Color { 66 | Color::new( 67 | self.r - other.r, 68 | self.g - other.g, 69 | self.b - other.b, 70 | self.a - other.a, 71 | ) 72 | } 73 | } 74 | 75 | impl SubAssign for Color { 76 | fn sub_assign(&mut self, other: Color) { 77 | *self = Color::new( 78 | self.r - other.r, 79 | self.g - other.g, 80 | self.b - other.b, 81 | self.a - other.a, 82 | ); 83 | } 84 | } 85 | 86 | impl From> for Color { 87 | fn from(item: Vector4) -> Self { 88 | Color::new(item.x, item.y, item.z, item.w) 89 | } 90 | } 91 | 92 | impl Into> for Color { 93 | fn into(self) -> Vector4 { 94 | Vector4::new(self.r, self.g, self.b, self.a) 95 | } 96 | } 97 | 98 | #[test] 99 | fn test_white_hex() { 100 | assert_eq!(Color::from_hex(0xFFFFFF, 1.0), Color::white()); 101 | } 102 | 103 | #[test] 104 | fn test_black_hex() { 105 | assert_eq!(Color::from_hex(0x000000, 1.0), Color::black()); 106 | } 107 | -------------------------------------------------------------------------------- /src/constants.rs: -------------------------------------------------------------------------------- 1 | use cgmath::Vector2; 2 | 3 | // Main window 4 | pub const WINDOW_RESOLUTION: Vector2 = Vector2 { x: 1400.0, y: 700.0 }; 5 | pub const WINDOW_MULTISAMPLES: u16 = 8; 6 | pub const WINDOW_TITLE: &str = "signed-distance fields"; 7 | 8 | // Preview region 9 | pub const PREVIEW_RESOLUTION: Vector2 = Vector2{ x: 300.0, y: 300.0 }; 10 | pub const PREVIEW_ROTATION_SENSITIVITY: f32 = 0.25; 11 | pub const PREVIEW_TRANSLATION_SENSITIVITY: f32 = 0.01; 12 | 13 | // Interface controls 14 | pub const ZOOM_INCREMENT: f32 = 0.05; 15 | 16 | // Network 17 | pub const NETWORK_BACKGROUND_COLOR: u32 = 0x2B2B2B; 18 | pub const NETWORK_BACKGROUND_ALPHA: f32 = 1.0; 19 | 20 | // Operators 21 | pub const OPERATOR_SIZE: Vector2 = Vector2 { x: 100.0, y: 50.0 }; 22 | pub const OPERATIVE_SLOT_SIZE: Vector2 = Vector2 { x: 12.0, y: 12.0 }; 23 | pub const OPERATOR_ICON_SIZE: Vector2 = Vector2 { x: 40.0, y: 40.0 }; 24 | pub const OPERATOR_ICON_OFFSET: Vector2 = Vector2 { x: 4.0, y: 4.0 }; 25 | 26 | // Parameters 27 | pub const PARAMETER_CAPACITY: usize = 4; 28 | pub const PARAMETER_SSBO_CAPACITY: usize = 256; 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /src/graph.rs: -------------------------------------------------------------------------------- 1 | use std::cmp::max; 2 | 3 | pub trait Connected { 4 | fn has_inputs(&self) -> bool; 5 | fn has_outputs(&self) -> bool; 6 | fn get_number_of_available_inputs(&self) -> usize; 7 | fn update_active_inputs_count(&mut self, count: usize); 8 | fn on_connect(&mut self); 9 | fn on_disconnect(&mut self); 10 | } 11 | 12 | #[derive(Copy, Clone, PartialEq, Eq)] 13 | pub struct Node { 14 | pub data: T, 15 | } 16 | 17 | impl Node { 18 | fn new(data: T) -> Node { 19 | Node { data } 20 | } 21 | } 22 | 23 | #[derive(Clone, PartialEq, Eq)] 24 | pub struct Edges { 25 | /// The indices of any nodes that are inputs to the node backed by 26 | /// this `Edges` instance 27 | pub inputs: Vec, 28 | 29 | /// The indices of any nodes that are outputs from the node backed by 30 | /// this `Edges` instance 31 | pub outputs: Vec, 32 | 33 | /// The data associated with this edge 34 | pub data: T, 35 | } 36 | 37 | impl Edges { 38 | fn new(data: T) -> Edges { 39 | Edges { 40 | inputs: Vec::new(), 41 | outputs: Vec::new(), 42 | data, 43 | } 44 | } 45 | } 46 | 47 | /// A specialized directed acyclic graph (DAG) implementation that 48 | /// allows individual nodes to specify whether or not they can 49 | /// accept incoming or outgoing connections. For example, a particular 50 | /// "root" node might only have outgoing edges, while disallowing 51 | /// any incoming edges. 52 | pub struct Graph { 53 | /// The nodes in the graph 54 | pub nodes: Vec>, 55 | 56 | /// A list of `Edges` structs, where each `Edges` corresponds 57 | /// to the node with the same index in `nodes` 58 | pub edges: Vec>, 59 | } 60 | 61 | impl Graph { 62 | pub fn new() -> Graph { 63 | Graph { 64 | nodes: Vec::new(), 65 | edges: Vec::new(), 66 | } 67 | } 68 | 69 | /// Returns an immutable reference to the node at `index`. 70 | pub fn get_node(&self, index: usize) -> Option<&Node> { 71 | self.nodes.get(index) 72 | } 73 | 74 | /// Returns a mutable reference to the node at `index`. 75 | pub fn get_node_mut(&mut self, index: usize) -> Option<&mut Node> { 76 | self.nodes.get_mut(index) 77 | } 78 | 79 | /// Returns an immutable reference to the graph's list of nodes. 80 | pub fn get_nodes(&self) -> &Vec> { 81 | &self.nodes 82 | } 83 | 84 | /// Returns a mutable reference to the graph's list of nodes. 85 | pub fn get_nodes_mut(&mut self) -> &mut Vec> { 86 | &mut self.nodes 87 | } 88 | 89 | /// Returns an immutable reference to the graph's list of edges. 90 | pub fn get_edges(&self) -> &Vec> { 91 | &self.edges 92 | } 93 | 94 | /// Returns a mutable reference to the graph's list of edges. 95 | pub fn get_edges_mut(&mut self) -> &mut Vec> { 96 | &mut self.edges 97 | } 98 | 99 | /// Adds a new node to the graph that owns `data_n` and whose 100 | /// corresponding list of edges owns `data_e`. 101 | pub fn add_node(&mut self, data_n: N, data_e: E) { 102 | self.nodes.push(Node::new(data_n)); 103 | self.edges.push(Edges::new(data_e)); 104 | } 105 | 106 | /// Removes the node at `index` from the graph. 107 | pub fn remove_node(&mut self, index: usize) { 108 | // The (original) index of the last node, which 109 | // will be swapped into the deleted node's place. 110 | let swapped_index = self.nodes.len() - 1; 111 | 112 | let removed_node = self.nodes.swap_remove(index); 113 | let removed_edges = self.edges.swap_remove(index); 114 | 115 | // Prune edges. 116 | for (i, edges) in self.edges.iter_mut().enumerate() { 117 | // Delete edges that started at the removed node and 118 | // update the number of active inputs. 119 | edges.inputs.retain(|&input| input != index); 120 | 121 | let count = edges.inputs.len(); 122 | self.nodes[i].data.update_active_inputs_count(count); 123 | 124 | // Delete edges that terminated at the removed node. 125 | edges.outputs.retain(|&output| output != index); 126 | 127 | // Update any edges that were pointing to or from the 128 | // swapped node. 129 | for i in edges.inputs.iter_mut() { 130 | if *i == swapped_index { 131 | *i = index; 132 | } 133 | } 134 | for i in edges.outputs.iter_mut() { 135 | if *i == swapped_index { 136 | *i = index; 137 | } 138 | } 139 | } 140 | } 141 | 142 | /// Removes the edge between nodes `a` and `b` (if it 143 | /// exists). 144 | pub fn remove_edge(&mut self, src: usize, dst: usize) { 145 | self.edges[src].outputs.retain(|&index| index != dst); 146 | self.edges[dst].inputs.retain(|&index| index != src); 147 | 148 | // Update the number of active inputs leading to 149 | // node `b`. 150 | let count = self.edges[dst].inputs.len(); 151 | self.nodes[dst].data.update_active_inputs_count(count); 152 | } 153 | 154 | pub fn add_edge(&mut self, src: usize, dst: usize) { 155 | if src != dst && self.nodes[src].data.has_outputs() && self.nodes[dst].data.has_inputs() { 156 | // If node `b` has reached its input capacity, replace 157 | // the edge connecting its last input with `b` with 158 | // the new edge. 159 | if self.nodes[dst].data.get_number_of_available_inputs() == 0 { 160 | let old = self.edges[dst].inputs.pop().unwrap(); 161 | self.remove_edge(old, dst); 162 | } else { 163 | 164 | } 165 | 166 | // Call the `on_connect` method for each node. 167 | self.nodes[dst].data.on_connect(); 168 | 169 | // Update the edges. 170 | self.edges[src].outputs.push(dst); 171 | self.edges[dst].inputs.push(src); 172 | } else { 173 | println!("Connection failed"); 174 | } 175 | } 176 | 177 | /// Performs a post-order traversal of the graph, returning 178 | /// the node indices in the proper order. 179 | pub fn traverse(&mut self, root: usize) -> Vec { 180 | let mut indices = Vec::new(); 181 | let mut visited = Vec::new(); 182 | 183 | // Traverse the graph, starting at the root. 184 | visited.push(root); 185 | self.recurse(root, &mut indices, &mut visited); 186 | 187 | indices 188 | } 189 | 190 | /// Examine a `root` op's inputs and recurse backwards until 191 | /// reaching a leaf node (i.e. an op with no other inputs). 192 | fn recurse(&self, root: usize, indices: &mut Vec, visited: &mut Vec) { 193 | for index in self.edges[root].inputs.iter() { 194 | self.recurse(*index, indices, visited); 195 | } 196 | 197 | // Finally, push back the root index: note that 198 | // here we choose to ignore duplicate entries. 199 | // This occurs when a node is connected to multiple 200 | // nodes at varying depths in the graph. 201 | // 202 | // In other scenarios, we might want to allow this 203 | // insertion to happen, regardless if the index 204 | // exists in `indices` already. 205 | if !indices.contains(&root) { 206 | indices.push(root); 207 | } 208 | } 209 | } 210 | -------------------------------------------------------------------------------- /src/interaction.rs: -------------------------------------------------------------------------------- 1 | use cgmath::{Vector2, Zero}; 2 | 3 | use bounds::Rect; 4 | 5 | pub struct MouseInfo { 6 | /// The current position of the mouse 7 | pub curr: Vector2, 8 | 9 | /// The last position of the mouse 10 | pub last: Vector2, 11 | 12 | /// The last position that the user clicked 13 | pub clicked: Vector2, 14 | 15 | /// A flag denoting whether or not the left 16 | /// mouse button is currently pressed 17 | pub ldown: bool, 18 | 19 | /// A flag denoting whether or not the right 20 | /// mouse button is currently pressed 21 | pub rdown: bool, 22 | 23 | /// A flag denoting whether or not the middle 24 | /// mouse button is currently pressed 25 | pub mdown: bool, 26 | 27 | /// The scroll status of the mouse 28 | pub scroll: f32, 29 | } 30 | 31 | impl MouseInfo { 32 | pub fn new() -> MouseInfo { 33 | MouseInfo { 34 | curr: Vector2::zero(), 35 | last: Vector2::zero(), 36 | clicked: Vector2::zero(), 37 | ldown: false, 38 | rdown: false, 39 | mdown: false, 40 | scroll: 1.0, 41 | } 42 | } 43 | 44 | pub fn velocity(&self) -> Vector2 { 45 | self.curr - self.last 46 | } 47 | } 48 | pub enum InteractionState { 49 | Deselected, 50 | Selected, 51 | Hover, 52 | ConnectSource, 53 | ConnectDestination, 54 | // TODO: change these to `DragFrom` and `DragTo` or `Drag` and `Drop` 55 | } 56 | 57 | /// A trait that represents a rectangular region of the 58 | /// display window that the user can interact with. 59 | pub trait Panel { 60 | /// Returns the bounding rectangular defined by this panel. 61 | fn get_bounds(&self) -> &Rect; 62 | 63 | /// Returns the current interaction state of the panel. 64 | fn get_state(&self) -> InteractionState; 65 | 66 | /// Handles any mouse events. 67 | fn handle_interaction(&mut self, info: &MouseInfo); 68 | } 69 | 70 | pub struct Button { 71 | bounds: Rect, 72 | state: InteractionState, 73 | } 74 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | #![allow(dead_code)] 2 | #![allow(unused_variables)] 3 | #![allow(unused_imports)] 4 | #![allow(unreachable_code)] 5 | #![feature(vec_remove_item)] 6 | extern crate cgmath; 7 | extern crate gl; 8 | extern crate glutin; 9 | extern crate image; 10 | extern crate uuid; 11 | 12 | mod bounds; 13 | mod color; 14 | mod constants; 15 | mod graph; 16 | mod interaction; 17 | mod network; 18 | mod operator; 19 | mod preview; 20 | mod program; 21 | mod renderer; 22 | mod shader_builder; 23 | mod texture; 24 | 25 | // TODO: 26 | // - Limit generators (i.e. sphere) to ONE output, since 27 | // the current graph traversal code doesn't work if the 28 | // same generator is connected to multiple other nodes. 29 | // the other option would be to properly handle this 30 | // during graph traversal so that the shader code for this 31 | // generator is duplicated. This would mean that transforms 32 | // should be their own family of operator as well. 33 | 34 | use color::Color; 35 | use interaction::{MouseInfo, Panel}; 36 | use operator::{DomainType, Op, OpFamily, Parameters, PrimitiveType}; 37 | use network::Network; 38 | use preview::Shading; 39 | use program::Program; 40 | use renderer::Renderer; 41 | use shader_builder::ShaderBuilder; 42 | 43 | use glutin::GlContext; 44 | use cgmath::{Vector2, Vector3, Vector4, Zero}; 45 | 46 | fn clear() { 47 | unsafe { 48 | let clear = Color::from_hex(constants::NETWORK_BACKGROUND_COLOR, constants::NETWORK_BACKGROUND_ALPHA); 49 | gl::ClearColor(clear.r, clear.g, clear.b, clear.a); 50 | gl::Clear(gl::COLOR_BUFFER_BIT); 51 | } 52 | } 53 | 54 | fn main() { 55 | let mut events_loop = glutin::EventsLoop::new(); 56 | let window = glutin::WindowBuilder::new() 57 | .with_dimensions(constants::WINDOW_RESOLUTION.x as u32, constants::WINDOW_RESOLUTION.y as u32) 58 | .with_title(constants::WINDOW_TITLE); 59 | let context = glutin::ContextBuilder::new().with_multisampling(constants::WINDOW_MULTISAMPLES); 60 | let gl_window = glutin::GlWindow::new(window, context, &events_loop).unwrap(); 61 | unsafe { gl_window.make_current() }.unwrap(); 62 | gl::load_with(|symbol| gl_window.get_proc_address(symbol) as *const _); 63 | 64 | // Keep track of the current window size and interaction state 65 | let mut current_size = Vector2::new(constants::WINDOW_RESOLUTION.x, constants::WINDOW_RESOLUTION.y); 66 | let mut mouse = MouseInfo::new(); 67 | 68 | // Main objects 69 | let mut network = Network::new(current_size); 70 | let mut builder = ShaderBuilder::new(); 71 | 72 | loop { 73 | events_loop.poll_events(|event| { 74 | match event { 75 | glutin::Event::WindowEvent { event, .. } => match event { 76 | glutin::WindowEvent::Closed => (), 77 | 78 | glutin::WindowEvent::Resized(w, h) => { 79 | current_size = Vector2 { x: w as f32, y: h as f32 }; 80 | gl_window.resize(w, h); 81 | } 82 | 83 | glutin::WindowEvent::MouseMoved { position, .. } => { 84 | mouse.last = mouse.curr; 85 | mouse.curr = Vector2::new(position.0 as f32, position.1 as f32); 86 | 87 | // Zero center and zoom. 88 | mouse.curr -= current_size * 0.5; 89 | //mouse.curr *= mouse.scroll; 90 | 91 | network.handle_interaction(&mouse); 92 | } 93 | 94 | glutin::WindowEvent::MouseWheel { delta, .. } => { 95 | if let glutin::MouseScrollDelta::LineDelta(_, line_y) = delta { 96 | if line_y == 1.0 { 97 | mouse.scroll -= constants::ZOOM_INCREMENT; 98 | } else { 99 | mouse.scroll += constants::ZOOM_INCREMENT; 100 | } 101 | network.handle_interaction(&mouse); 102 | } 103 | } 104 | 105 | glutin::WindowEvent::MouseInput { state, button, .. } => { 106 | if let glutin::ElementState::Pressed = state { 107 | // Store the current mouse position. 108 | mouse.clicked = mouse.curr; 109 | 110 | // Store mouse button presses. 111 | match button { 112 | glutin::MouseButton::Left => mouse.ldown = true, 113 | glutin::MouseButton::Right => mouse.rdown = true, 114 | glutin::MouseButton::Middle => mouse.mdown = true, 115 | _ => (), 116 | } 117 | network.handle_interaction(&mouse); 118 | } else { 119 | mouse.ldown = false; 120 | mouse.rdown = false; 121 | mouse.mdown = false; 122 | } 123 | } 124 | 125 | glutin::WindowEvent::KeyboardInput { input, .. } => { 126 | if let glutin::ElementState::Pressed = input.state { 127 | if let Some(key) = input.virtual_keycode { 128 | if input.modifiers.shift && key != glutin::VirtualKeyCode::LShift { 129 | // If the `shift` modifier is down, add a new op. 130 | let family = match key { 131 | glutin::VirtualKeyCode::S => { 132 | Some(OpFamily::Primitive(PrimitiveType::Sphere)) 133 | } 134 | glutin::VirtualKeyCode::B => { 135 | Some(OpFamily::Primitive(PrimitiveType::Box)) 136 | } 137 | glutin::VirtualKeyCode::P => { 138 | Some(OpFamily::Primitive(PrimitiveType::Plane)) 139 | } 140 | glutin::VirtualKeyCode::T => { 141 | Some(OpFamily::Primitive(PrimitiveType::Torus)) 142 | } 143 | glutin::VirtualKeyCode::U => { 144 | Some(OpFamily::Primitive(PrimitiveType::Union)) 145 | } 146 | glutin::VirtualKeyCode::D => { 147 | Some(OpFamily::Primitive(PrimitiveType::Subtraction)) 148 | } 149 | glutin::VirtualKeyCode::I => { 150 | Some(OpFamily::Primitive(PrimitiveType::Intersection)) 151 | } 152 | glutin::VirtualKeyCode::M => { 153 | Some(OpFamily::Primitive(PrimitiveType::SmoothMinimum)) 154 | } 155 | glutin::VirtualKeyCode::R => { 156 | Some(OpFamily::Primitive(PrimitiveType::Render)) 157 | } 158 | glutin::VirtualKeyCode::Key1 => { 159 | Some(OpFamily::Domain(DomainType::Root)) 160 | } 161 | glutin::VirtualKeyCode::Key2 => { 162 | Some(OpFamily::Domain(DomainType::Transform)) 163 | } 164 | glutin::VirtualKeyCode::Key3 => { 165 | Some(OpFamily::Domain(DomainType::Twist)) 166 | } 167 | glutin::VirtualKeyCode::Key4 => { 168 | Some(OpFamily::Domain(DomainType::Bend)) 169 | } 170 | _ => None, 171 | }; 172 | if let Some(family) = family { 173 | network.add_op( 174 | family, 175 | mouse.curr - constants::OPERATOR_SIZE * 0.5, 176 | constants::OPERATOR_SIZE, 177 | ); 178 | } 179 | } else { 180 | // Handle other key commands. 181 | match key { 182 | glutin::VirtualKeyCode::Delete => network.delete_selected(), 183 | glutin::VirtualKeyCode::H => { 184 | mouse.scroll = 1.0; 185 | network.preview.home(); 186 | } 187 | glutin::VirtualKeyCode::P => network.toggle_preview(), 188 | glutin::VirtualKeyCode::Key1 => { 189 | network.preview.set_shading(Shading::Depth) 190 | } 191 | glutin::VirtualKeyCode::Key2 => { 192 | network.preview.set_shading(Shading::Steps) 193 | } 194 | glutin::VirtualKeyCode::Key3 => { 195 | network.preview.set_shading(Shading::AmbientOcclusion) 196 | } 197 | glutin::VirtualKeyCode::Key4 => { 198 | network.preview.set_shading(Shading::Normals) 199 | } 200 | glutin::VirtualKeyCode::Key5 => { 201 | network.preview.set_shading(Shading::Diffuse) 202 | } 203 | glutin::VirtualKeyCode::Equals => { 204 | network.increment_param(&Vector4::new( 205 | 0.0, 206 | 0.0, 207 | 0.0, 208 | 0.05, 209 | )); 210 | } 211 | glutin::VirtualKeyCode::Minus => { 212 | network.increment_param(&Vector4::new( 213 | 0.0, 214 | 0.0, 215 | 0.0, 216 | -0.05, 217 | )); 218 | } 219 | glutin::VirtualKeyCode::Left => { 220 | network.increment_param(&Vector4::new( 221 | 0.05, 222 | 0.0, 223 | 0.0, 224 | 0.0, 225 | )); 226 | } 227 | glutin::VirtualKeyCode::Right => { 228 | network.increment_param(&Vector4::new( 229 | -0.05, 230 | 0.0, 231 | 0.0, 232 | 0.0, 233 | )); 234 | } 235 | glutin::VirtualKeyCode::Up => { 236 | network.increment_param(&Vector4::new( 237 | 0.0, 238 | -0.05, 239 | 0.0, 240 | 0.0, 241 | )); 242 | } 243 | glutin::VirtualKeyCode::Down => { 244 | network.increment_param(&Vector4::new( 245 | 0.0, 246 | 0.05, 247 | 0.0, 248 | 0.0, 249 | )); 250 | } 251 | _ => (), 252 | } 253 | } 254 | } 255 | } 256 | } 257 | _ => (), 258 | }, 259 | _ => (), 260 | } 261 | }); 262 | 263 | clear(); 264 | 265 | // Check to see if the graph needs to be rebuilt. 266 | if network.dirty() { 267 | if let Some(root) = network.render_id { 268 | let indices = network.graph.traverse(root); 269 | let program = builder.build_sources(&network, indices); 270 | network.preview.set_valid_program(program); 271 | network.clean(); 272 | } else { 273 | network.preview.set_valid_program(None); 274 | } 275 | } 276 | 277 | // Draw the graph (ops, connections, preview window, etc.). 278 | network.draw(); 279 | 280 | gl_window.swap_buffers().unwrap(); 281 | } 282 | } 283 | -------------------------------------------------------------------------------- /src/network.rs: -------------------------------------------------------------------------------- 1 | use cgmath::{self, Vector2, Vector3, Vector4, Zero}; 2 | use uuid::Uuid; 3 | 4 | use bounds::Rect; 5 | use color::Color; 6 | use graph::{Connected, Graph}; 7 | use interaction::{InteractionState, MouseInfo, Panel}; 8 | use operator::{ConnectionType, Connectivity, DomainType, Op, OpFamily, PrimitiveType}; 9 | use preview::Preview; 10 | use renderer::{DrawParams, LineConnectivity, LineMode, Renderer}; 11 | use texture::Texture; 12 | 13 | use std::cmp::max; 14 | use std::collections::HashMap; 15 | use std::io; 16 | use std::fs::{self, DirEntry}; 17 | use std::path::Path; 18 | use std::ffi::OsStr; 19 | 20 | /// Palette: 21 | /// 22 | /// Background: 0x2B2B2B (dark gray) 23 | /// Accent: 0x373737 (light gray) 24 | /// Generator: 0x8F719D (purple) 25 | /// Combiner: 0xA8B6C5 (blue) 26 | /// Render: 0xC77832 (orange) 27 | /// Selection: 0x76B264 (green) 28 | /// Error: 0xA0502B (dark orange) 29 | /// Other: 0xFEC56D (yellow) 30 | /// 31 | pub struct Grid { 32 | size: Vector2, 33 | spacing: Vector2, 34 | pub points_vertical: Vec, 35 | pub points_horizontal: Vec, 36 | } 37 | 38 | impl Grid { 39 | pub fn new(size: Vector2, spacing: Vector2) -> Grid { 40 | let mut points_vertical = Vec::new(); 41 | let mut points_horizontal = Vec::new(); 42 | 43 | let lines_x = size.x as usize / spacing.x; 44 | let lines_y = size.y as usize / spacing.y; 45 | let spacing_x = size.x / lines_x as f32; 46 | let spacing_y = size.y / lines_y as f32; 47 | 48 | let offset = size * 0.5; 49 | 50 | // Draw vertical lines. 51 | for i in 0..lines_x { 52 | points_vertical.extend_from_slice(&[ 53 | i as f32 * spacing_x - offset.x, 54 | -offset.y, 55 | 0.0, 56 | 0.0, 57 | i as f32 * spacing_x - offset.x, 58 | offset.y, 59 | 1.0, 60 | 1.0, 61 | ]); 62 | } 63 | 64 | // Draw horizontal lines. 65 | for i in 0..lines_y { 66 | points_horizontal.extend_from_slice(&[ 67 | -offset.x, 68 | i as f32 * spacing_y - offset.y, 69 | 0.0, 70 | 0.0, 71 | offset.x, 72 | i as f32 * spacing_y - offset.y, 73 | 1.0, 74 | 1.0, 75 | ]); 76 | } 77 | 78 | Grid { 79 | size, 80 | spacing, 81 | points_vertical, 82 | points_horizontal, 83 | } 84 | } 85 | } 86 | 87 | type Connection = usize; 88 | 89 | pub struct Network { 90 | /// An adjacency list representation of ops 91 | pub graph: Graph, 92 | 93 | /// The sprite renderer that will be used to draw all nodes and 94 | /// edges of the graph 95 | pub renderer: Renderer, 96 | 97 | /// The preview of the shader that is represented by the 98 | /// current network 99 | pub preview: Preview, 100 | 101 | /// The wireframe grid that will be drawn in the background of 102 | /// the network editor 103 | pub grid: Grid, 104 | 105 | /// The index of the currently selected op (if there is one) 106 | pub selection_id: Option, 107 | 108 | /// The index of the render op (if there is one) 109 | pub render_id: Option, 110 | 111 | /// A flag that controls whether or not the shader graph 112 | /// needs to be rebuilt 113 | dirty: bool, 114 | 115 | /// A flag that controls whether or not the preview will 116 | /// be drawn 117 | show_preview: bool, 118 | 119 | /// A flag that controls whether or not ops will be snapped 120 | /// to a grid when dragged 121 | snapping: bool, 122 | 123 | /// A map of asset names to textures, used to render various 124 | /// UI elements 125 | assets: HashMap, 126 | } 127 | 128 | enum Pair { 129 | Both(T, T), 130 | One(T), 131 | None, 132 | } 133 | 134 | /// Get mutable references at index `a` and `b`. 135 | fn index_twice(slc: &mut [T], a: usize, b: usize) -> Pair<&mut T> { 136 | if max(a, b) >= slc.len() { 137 | Pair::None 138 | } else if a == b { 139 | Pair::One(&mut slc[max(a, b)]) 140 | } else { 141 | unsafe { 142 | let ar = &mut *(slc.get_unchecked_mut(a) as *mut _); 143 | let br = &mut *(slc.get_unchecked_mut(b) as *mut _); 144 | Pair::Both(ar, br) 145 | } 146 | } 147 | } 148 | 149 | impl Network { 150 | /// Constructs a new, empty network. 151 | pub fn new(size: Vector2) -> Network { 152 | let mut network = Network { 153 | graph: Graph::new(), 154 | renderer: Renderer::new(size), 155 | preview: Preview::new(), 156 | grid: Grid::new(size, Vector2::new(20, 20)), 157 | selection_id: None, 158 | render_id: None, 159 | dirty: false, 160 | show_preview: true, 161 | snapping: true, 162 | assets: HashMap::new(), 163 | }; 164 | network.load_assets(); 165 | network 166 | } 167 | 168 | /// Returns `true` if the shader graph needs to be rebuilt and 169 | /// `false` otherwise. 170 | pub fn dirty(&self) -> bool { 171 | self.dirty 172 | } 173 | 174 | /// Sets the `dirty` flag to `false`. 175 | pub fn clean(&mut self) { 176 | self.dirty = false; 177 | } 178 | 179 | /// Toggles drawing of the preview window. 180 | pub fn toggle_preview(&mut self) { 181 | self.show_preview = !self.show_preview; 182 | } 183 | 184 | /// Scales the distance field represented by the currently 185 | /// selected op (if one exists). 186 | pub fn increment_param(&mut self, values: &Vector4) { 187 | if let Some(selected) = self.selection_id { 188 | let node = self.graph.nodes.get_mut(selected).unwrap(); 189 | 190 | let params = node.data.get_params_mut(); 191 | let data = params.get_data_mut(); 192 | data[0] += values.x; 193 | data[1] += values.y; 194 | data[2] += values.z; 195 | data[3] += values.w; 196 | } 197 | } 198 | 199 | /// Deletes the currently selected op (if the selection is not empty). 200 | pub fn delete_selected(&mut self) { 201 | if let Some(selected) = self.selection_id { 202 | // Before removing this vertex from the graph, 203 | // check to see if it was connected to the root 204 | // (if one exists). If so, then the shader 205 | // graph needs to be rebuilt. 206 | if let Some(id) = self.render_id { 207 | for edge in self.graph.edges[selected].outputs.iter() { 208 | if *edge == id { 209 | self.dirty = true; 210 | self.render_id = None; 211 | break; 212 | } 213 | } 214 | } 215 | 216 | // The last node in the graph's list of nodes 217 | // will be moved, so its parameter index needs 218 | // to be reset. 219 | if let Some(node) = self.graph.nodes.last_mut() { 220 | node.data.params.set_index(selected); 221 | } 222 | 223 | // Finally, remove the node and reset the selection. 224 | self.graph.remove_node(selected); 225 | self.selection_id = None; 226 | } 227 | } 228 | 229 | /// Adds a new op of type `family` to the network at coordinates 230 | /// `position` and dimensions `size`. 231 | pub fn add_op(&mut self, family: OpFamily, position: Vector2, size: Vector2) { 232 | // Create the operator. 233 | let mut op = Op::new(family, position, size); 234 | 235 | // We need to re-assign this op's parameter index so 236 | // that the resulting shader code properly indexes into 237 | // the SSBO of parameter data. 238 | op.params.set_index(self.graph.nodes.len()); 239 | 240 | // Add the operator to the current graph. 241 | self.graph.add_node(op, 0); 242 | } 243 | 244 | /// Adds a new connection between two ops. 245 | pub fn add_connection(&mut self, a: usize, b: usize) { 246 | self.graph.add_edge(a, b); 247 | 248 | if let Pair::Both(node_a, node_b) = index_twice(&mut self.graph.nodes, a, b) { 249 | // If we previously connected to a render op, then we 250 | // know that the graph must be rebuilt. 251 | if let Some(_) = self.render_id { 252 | self.dirty = true; 253 | println!("Active render node in-line: re-building graph"); 254 | } 255 | 256 | // If we are connecting to a render op, then the shader 257 | // must be rebuilt. 258 | if let OpFamily::Primitive(PrimitiveType::Render) = node_b.data.family { 259 | self.render_id = Some(b); 260 | self.dirty = true; 261 | println!("Connected to render node: building graph"); 262 | } 263 | 264 | // Deselect both ops. 265 | node_a.data.state = InteractionState::Deselected; 266 | node_b.data.state = InteractionState::Deselected; 267 | } else { 268 | println!("Attempting to connect two ops with the same index - something is wrong here") 269 | } 270 | } 271 | 272 | /// Handles all mouse events. 273 | pub fn handle_interaction(&mut self, mouse: &MouseInfo) { 274 | let mut connecting = false; 275 | let mut src: Option = None; 276 | let mut dst: Option = None; 277 | 278 | for (index, node) in self.graph.nodes.iter_mut().enumerate() { 279 | if let InteractionState::ConnectSource = node.data.state { 280 | if mouse.ldown { 281 | // If this operator is currently being connected to another: 282 | // 1) Set the `connecting` flag to `true`, as the user is 283 | // performing a potential op connection 284 | // 2) Store its graph index as a potential connect source 285 | // 3) Skip the rest of this loop iteration 286 | connecting = true; 287 | src = Some(index); 288 | continue; 289 | } else { 290 | // Otherwise, deselect this op 291 | node.data.state = InteractionState::Deselected; 292 | } 293 | } 294 | 295 | // Is the mouse inside of this op's bounding box? 296 | if node.data.bounds_body.inside(&mouse.curr) { 297 | // Is there an op currently selected? 298 | if let Some(selected) = self.selection_id { 299 | // Is this op the selected op? 300 | if selected == index { 301 | // Is the mouse down? 302 | if mouse.ldown { 303 | // TODO: let mut velocity = ..; 304 | if self.snapping { 305 | // TODO 306 | } 307 | node.data.translate(&mouse.velocity()); 308 | } 309 | continue; 310 | } 311 | } 312 | 313 | // This op is not the selected op, but we are inside of it's 314 | // bounding box. Is the mouse down? 315 | if mouse.ldown { 316 | // Are we inside the bounds of this op's output slot? 317 | if node.data 318 | .bounds_output 319 | .inside_with_padding(&mouse.curr, 12.0) 320 | { 321 | // This op is now a potential connection source. 322 | node.data.state = InteractionState::ConnectSource; 323 | 324 | // Store the connection source index. 325 | src = Some(index); 326 | } else { 327 | // This op has been selected. 328 | node.data.state = InteractionState::Selected; 329 | 330 | // Store the selected UUID. 331 | self.selection_id = Some(index); 332 | } 333 | } else { 334 | // Otherwise, the mouse is still inside the bounds of this op, 335 | // so we must be hovering over it. 336 | node.data.state = InteractionState::Hover; 337 | } 338 | 339 | // The mouse is not inside of this op's bounding box. 340 | } else { 341 | // Is there an op currently selected? 342 | if let Some(selected) = self.selection_id { 343 | // Is this op the selected op? 344 | if selected == index { 345 | // Is the mouse down? 346 | if mouse.ldown { 347 | // The user has clicked somewhere else in the 348 | // network, so reset the selection. 349 | self.selection_id = None; 350 | } else { 351 | // Keep this op selected. 352 | node.data.state = InteractionState::Selected; 353 | } 354 | } else { 355 | // Deselect the op. 356 | node.data.state = InteractionState::Deselected; 357 | } 358 | } else { 359 | // Deselect the op. 360 | node.data.state = InteractionState::Deselected; 361 | } 362 | } 363 | } 364 | 365 | // If the mouse is dragging from the output slot of one operator, 366 | // check if a potential connection has happened (i.e. the mouse 367 | // is now over an input slot of a different operator). 368 | if connecting { 369 | for (index, node) in self.graph.nodes.iter_mut().enumerate() { 370 | // Is the mouse now inside of a different op's input slot region? 371 | if node.data 372 | .bounds_input 373 | .inside_with_padding(&mouse.curr, 12.0) 374 | { 375 | node.data.state = InteractionState::ConnectDestination; 376 | if let Some(src) = src { 377 | dst = Some(index); 378 | } 379 | } 380 | } 381 | } 382 | 383 | if let (Some(src), Some(dst)) = (src, dst) { 384 | let src_family = self.graph.get_node(src).unwrap().data.family; 385 | let dst_family = self.graph.get_node(dst).unwrap().data.family; 386 | 387 | if dst_family.has_inputs() { 388 | 389 | if src_family.can_connect_to(dst_family) { 390 | println!("Valid connection between ops with IDs: {}, {}", src, dst); 391 | self.add_connection(src, dst); 392 | } 393 | } 394 | } 395 | 396 | self.preview.handle_interaction(&mouse); 397 | } 398 | 399 | /// Draws all of the operators and edges that make 400 | /// up this graph. 401 | pub fn draw(&mut self) { 402 | self.draw_grid(); 403 | self.draw_all_edges(); 404 | self.draw_all_nodes(); 405 | 406 | if self.show_preview { 407 | self.gather_params(); 408 | 409 | self.preview.prepare(self.renderer.get_projection()); 410 | self.renderer.draw_rect_inner(); 411 | } 412 | } 413 | 414 | /// Pick a draw color based on the current interaction state of this 415 | /// operator and the op type. 416 | fn color_for_op(&self, op: &Op) -> Color { 417 | let mut color = match op.family { 418 | OpFamily::Domain(domain) => Color::from_hex(0x515151, 1.0), 419 | OpFamily::Primitive(primitive) => match primitive { 420 | PrimitiveType::Sphere 421 | | PrimitiveType::Box 422 | | PrimitiveType::Plane 423 | | PrimitiveType::Torus => Color::from_hex(0x8F719D, 1.0), 424 | PrimitiveType::Union 425 | | PrimitiveType::Subtraction 426 | | PrimitiveType::Intersection 427 | | PrimitiveType::SmoothMinimum => Color::from_hex(0x8A7BA4, 1.0), 428 | PrimitiveType::Render => Color::from_hex(0xC77832, 1.0), 429 | }, 430 | }; 431 | 432 | // Add a contribution based on the op's current interaction state. 433 | if let InteractionState::Hover = op.state { 434 | color += Color::mono(0.05, 0.0); 435 | } 436 | color 437 | } 438 | 439 | /// Draws all ops in the network. 440 | fn draw_all_nodes(&mut self) { 441 | for node in self.graph.get_nodes().iter() { 442 | let op = &node.data; 443 | 444 | // Draw the op and other components: 445 | // - If the op is selected, draw a selection box behind it 446 | // - If the op is being used as a connection source or 447 | // destination, draw the appropriate connection slot 448 | let slot_color = Color::from_hex(0x373737, 1.0); 449 | match op.state { 450 | InteractionState::Selected => { 451 | let bounds_select = 452 | Rect::expanded_from(&op.bounds_body, &Vector2::new(6.0, 6.0)); 453 | self.renderer.draw( 454 | DrawParams::Rectangle(&bounds_select), 455 | &Color::from_hex(0x76B264, 1.0), 456 | None, 457 | None, 458 | ); 459 | } 460 | InteractionState::ConnectSource => self.renderer.draw( 461 | DrawParams::Rectangle(&op.bounds_output), 462 | &slot_color, 463 | None, 464 | None, 465 | ), 466 | InteractionState::ConnectDestination => self.renderer.draw( 467 | DrawParams::Rectangle(&op.bounds_input), 468 | &slot_color, 469 | None, 470 | None, 471 | ), 472 | _ => (), 473 | } 474 | 475 | // Draw the body of the op. 476 | let draw_color = self.color_for_op(op); 477 | let alpha_key = match op.family.get_connectivity() { 478 | Connectivity::InputOutput => "alpha_input_output".to_string(), 479 | Connectivity::Input => "alpha_input".to_string(), 480 | Connectivity::Output => "alpha_output".to_string(), 481 | }; 482 | let alpha_map = self.assets.get(&alpha_key).unwrap(); 483 | self.renderer.draw( 484 | DrawParams::Rectangle(&op.bounds_body), 485 | &draw_color, 486 | None, 487 | Some(alpha_map), 488 | ); 489 | 490 | // Draw the icon on top of the op (if one exists). 491 | let color_map = self.assets.get(op.family.to_string()).unwrap(); 492 | self.renderer.draw( 493 | DrawParams::Rectangle(&op.bounds_icon), 494 | &draw_color, 495 | Some(color_map), 496 | None, 497 | ); 498 | } 499 | } 500 | 501 | /// Gathers the draw data required to draw a curve between 502 | /// `a` and `b`. 503 | fn curve_between( 504 | &self, 505 | a: &Vector2, 506 | b: &Vector2, 507 | c: &Vector2, 508 | d: &Vector2, 509 | ) { 510 | const LOD: usize = 20; 511 | let mut points = Vec::with_capacity(LOD * 4); 512 | for i in 0..LOD { 513 | let t = (i as f32) / (LOD as f32); 514 | let t_inv = 1.0 - t; 515 | 516 | // Coefficients for a cubic polynomial. 517 | let b0 = t * t * t; 518 | let b1 = 3.0 * t * t * t_inv; 519 | let b2 = 3.0 * t * t_inv * t_inv; 520 | let b3 = t_inv * t_inv * t_inv; 521 | 522 | let point = a * b0 + b * b1 + c * b2 + d * b3; 523 | 524 | points.extend_from_slice(&[point.x, point.y, t, t]); 525 | } 526 | 527 | // Add the first point. 528 | points.extend_from_slice(&[a.x, a.y, 0.0, 0.0]); 529 | 530 | self.renderer.draw( 531 | DrawParams::Line(&points, LineMode::Solid, LineConnectivity::Strip), 532 | &Color::mono(0.75, 1.0), 533 | None, 534 | None, 535 | ); 536 | } 537 | 538 | /// Gathers the draw data required to draw a straight line between 539 | /// `a` and `b`. 540 | fn line_between(&self, a: &Vector2, b: &Vector2) { 541 | let points = vec![a.x, a.y, 0.0, 0.0, b.x, b.y, 1.0, 1.0]; 542 | 543 | self.renderer.draw( 544 | DrawParams::Line(&points, LineMode::Dashed, LineConnectivity::Segment), 545 | &Color::mono(0.75, 0.25), 546 | None, 547 | None, 548 | ); 549 | } 550 | 551 | /// Draws all edges between ops in the network. 552 | fn draw_all_edges(&self) { 553 | for (src, edges) in self.graph.edges.iter().enumerate() { 554 | for dst in edges.outputs.iter() { 555 | let src_node = self.graph.get_node(src).unwrap(); 556 | let dst_node = self.graph.get_node(*dst).unwrap(); 557 | let src_family = src_node.data.family; 558 | let dst_family = dst_node.data.family; 559 | let src_centroid = src_node.data.bounds_output.centroid(); 560 | let dst_centroid = dst_node.data.bounds_input.centroid(); 561 | 562 | match src_family.get_connection_type(dst_family) { 563 | // Draw a bezier curve between these two operators. 564 | ConnectionType::Direct => { 565 | let a = src_centroid; 566 | let d = dst_centroid; 567 | let mid = (a + d) * 0.5; 568 | 569 | let b = Vector2::new(mid.x, a.y); 570 | let c = Vector2::new(mid.x, d.y); 571 | self.curve_between(&a, &b, &c, &d); 572 | } 573 | // Draw a straight, dashed line (export) between these 574 | // two operators. 575 | ConnectionType::Indirect => { 576 | self.line_between(&src_centroid, &dst_centroid); 577 | } 578 | // An invalid connection - this should never happen, in practice. 579 | _ => (), 580 | } 581 | } 582 | } 583 | } 584 | 585 | /// Draws a grid in the network editor. 586 | fn draw_grid(&mut self) { 587 | let draw_color = Color::from_hex(0x373737, 0.25); 588 | 589 | self.renderer.draw( 590 | DrawParams::Line( 591 | &self.grid.points_vertical, 592 | LineMode::Solid, 593 | LineConnectivity::Segment, 594 | ), 595 | &draw_color, 596 | None, 597 | None, 598 | ); 599 | 600 | self.renderer.draw( 601 | DrawParams::Line( 602 | &self.grid.points_horizontal, 603 | LineMode::Solid, 604 | LineConnectivity::Segment, 605 | ), 606 | &draw_color, 607 | None, 608 | None, 609 | ); 610 | } 611 | 612 | /// Aggregates all of the operator parameters. 613 | fn gather_params(&self) { 614 | let mut all_params = Vec::new(); 615 | for node in self.graph.nodes.iter() { 616 | all_params.extend_from_slice(node.data.params.get_data()); 617 | //all_params.push(node.data.params.data); 618 | } 619 | 620 | self.preview.update_params(all_params); 621 | } 622 | 623 | /// Loads all texture assets. 624 | fn load_assets(&mut self) { 625 | for entry in fs::read_dir("assets").unwrap() { 626 | let path = entry.unwrap().path(); 627 | let file = path.file_stem().unwrap(); 628 | let ext = path.extension(); 629 | 630 | if ext == Some(OsStr::new("png")) { 631 | self.assets 632 | .insert(file.to_str().unwrap().to_string(), Texture::new(&path)); 633 | } 634 | } 635 | } 636 | } 637 | -------------------------------------------------------------------------------- /src/operator.rs: -------------------------------------------------------------------------------- 1 | use bounds::{Edge, Rect}; 2 | use constants; 3 | use graph::Connected; 4 | use interaction::InteractionState; 5 | use renderer::{DrawParams, Drawable}; 6 | 7 | use cgmath::{Vector2, Vector3, Vector4, Zero}; 8 | use uuid::Uuid; 9 | 10 | use std::sync::atomic::{AtomicUsize, Ordering}; 11 | 12 | static COUNTER: AtomicUsize = AtomicUsize::new(0); 13 | 14 | pub enum Connectivity { 15 | InputOutput, 16 | Input, 17 | Output, 18 | } 19 | 20 | pub enum ConnectionType { 21 | /// A connection between two ops that are compatible and from 22 | /// the same family 23 | Direct, 24 | 25 | /// A connection between two ops that are compatible but from 26 | /// different families 27 | Indirect, 28 | 29 | /// An invalid connection 30 | Invalid, 31 | } 32 | 33 | #[derive(Copy, Clone, PartialEq)] 34 | pub struct Parameters { 35 | /// The actual parameter data 36 | data: [f32; constants::PARAMETER_CAPACITY], 37 | 38 | /// The names of each component of this parameter 39 | names: [&'static str; constants::PARAMETER_CAPACITY], 40 | 41 | /// The index of this parameter in the SSBO that will hold 42 | /// all of the op parameters at runtime 43 | index: usize, 44 | 45 | /// The minimum value of each component of this parameter - 46 | /// in other words, `data[0]` should always be greater than 47 | /// or equal to `min[0]` 48 | min: [f32; constants::PARAMETER_CAPACITY], 49 | 50 | /// The maximum value of each component of this parameter - 51 | /// in other words, `data[0]` should always be less than 52 | /// or equal to `max[0]` 53 | max: [f32; constants::PARAMETER_CAPACITY], 54 | 55 | /// The step size that will be taken when a component of 56 | /// this parameter is incremented or decremented 57 | step: [f32; constants::PARAMETER_CAPACITY], 58 | } 59 | 60 | impl Parameters { 61 | pub fn new( 62 | data: [f32; constants::PARAMETER_CAPACITY], 63 | names: [&'static str; constants::PARAMETER_CAPACITY], 64 | index: usize, 65 | min: [f32; constants::PARAMETER_CAPACITY], 66 | max: [f32; constants::PARAMETER_CAPACITY], 67 | step: [f32; constants::PARAMETER_CAPACITY], 68 | ) -> Parameters { 69 | Parameters { 70 | data, 71 | names, 72 | index, 73 | min, 74 | max, 75 | step, 76 | } 77 | } 78 | 79 | pub fn get_data(&self) -> &[f32; constants::PARAMETER_CAPACITY] { 80 | &self.data 81 | } 82 | 83 | pub fn get_data_mut(&mut self) -> &mut [f32; constants::PARAMETER_CAPACITY] { 84 | &mut self.data 85 | } 86 | 87 | pub fn get_names(&self) -> &[&'static str; constants::PARAMETER_CAPACITY] { 88 | &self.names 89 | } 90 | 91 | pub fn get_index(&self) -> usize { 92 | self.index 93 | } 94 | 95 | pub fn get_min(&self) -> &[f32; constants::PARAMETER_CAPACITY] { 96 | &self.min 97 | } 98 | 99 | pub fn get_max(&self) -> &[f32; constants::PARAMETER_CAPACITY] { 100 | &self.max 101 | } 102 | 103 | pub fn get_step(&self) -> &[f32; constants::PARAMETER_CAPACITY] { 104 | &self.step 105 | } 106 | 107 | pub fn set_data(&mut self, values: [f32; constants::PARAMETER_CAPACITY]) { 108 | for (i, v) in values.iter().enumerate() { 109 | self.data[i] += v; 110 | } 111 | } 112 | 113 | pub fn set_index(&mut self, index: usize) { 114 | self.index = index; 115 | } 116 | } 117 | 118 | impl Default for Parameters { 119 | fn default() -> Self { 120 | Parameters::new( 121 | [0.0; constants::PARAMETER_CAPACITY], 122 | ["param0", "param1", "param2", "param3"], 123 | 0, 124 | [0.0; constants::PARAMETER_CAPACITY], 125 | [0.0; constants::PARAMETER_CAPACITY], 126 | [0.0; constants::PARAMETER_CAPACITY], 127 | ) 128 | } 129 | } 130 | 131 | #[derive(Copy, Clone, PartialEq)] 132 | pub enum DomainType { 133 | Root, 134 | Transform, 135 | Twist, 136 | Bend, 137 | } 138 | 139 | #[derive(Copy, Clone, PartialEq)] 140 | pub enum DataType { 141 | Time, 142 | Math, 143 | Sin, 144 | Cos, 145 | Noise, 146 | Mouse, 147 | Audio, 148 | } 149 | 150 | #[derive(Copy, Clone, PartialEq)] 151 | pub enum PrimitiveType { 152 | Sphere, 153 | Box, 154 | Plane, 155 | Torus, 156 | Union, 157 | Subtraction, 158 | Intersection, 159 | SmoothMinimum, 160 | Render, 161 | } 162 | 163 | #[derive(Copy, Clone, PartialEq)] 164 | pub enum DisplacementType { 165 | Noise, 166 | Sin, 167 | Cos, 168 | } 169 | 170 | /// An `OpFamily` (operator family) is a nested enum that designates 171 | /// the parent type of a particular operator. 172 | #[derive(Copy, Clone, PartialEq)] 173 | pub enum OpFamily { 174 | // TODO: Data, 175 | // TODO: Displacement, 176 | Domain(DomainType), 177 | Primitive(PrimitiveType), 178 | } 179 | 180 | impl OpFamily { 181 | /// Converts the nested enum variant into a human-readable string format. 182 | pub fn to_string(&self) -> &'static str { 183 | match *self { 184 | OpFamily::Domain(domain) => match domain { 185 | DomainType::Root => "root", 186 | DomainType::Transform => "transform", 187 | DomainType::Twist => "twist", 188 | DomainType::Bend => "bend", 189 | }, 190 | OpFamily::Primitive(primitive) => match primitive { 191 | PrimitiveType::Sphere => "sphere", 192 | PrimitiveType::Box => "box", 193 | PrimitiveType::Plane => "plane", 194 | PrimitiveType::Torus => "torus", 195 | PrimitiveType::Union => "union", 196 | PrimitiveType::Subtraction => "subtraction", 197 | PrimitiveType::Intersection => "intersection", 198 | PrimitiveType::SmoothMinimum => "smooth_minimum", 199 | PrimitiveType::Render => "render", 200 | }, 201 | } 202 | } 203 | 204 | /// Returns an enum that describes the connectivity of this op family 205 | /// (whether it accepts inputs, outputs, or both). 206 | pub fn get_connectivity(&self) -> Connectivity { 207 | match *self { 208 | OpFamily::Domain(domain) => match domain { 209 | DomainType::Root => Connectivity::Output, 210 | _ => Connectivity::InputOutput, 211 | }, 212 | OpFamily::Primitive(primitive) => match primitive { 213 | PrimitiveType::Render => Connectivity::Input, 214 | _ => Connectivity::InputOutput, 215 | }, 216 | } 217 | } 218 | 219 | /// Returns the maximum number of ops that can be connected to this 220 | /// op's input slot. Note that there is no equivalent `get_output_capacity` 221 | /// method, since an op's output slot can be connected to a potentially 222 | /// unbounded number of other ops. 223 | pub fn get_input_capacity(&self) -> usize { 224 | match *self { 225 | OpFamily::Domain(domain) => match domain { 226 | DomainType::Root => 0, 227 | _ => 1, 228 | }, 229 | OpFamily::Primitive(primitive) => match primitive { 230 | PrimitiveType::Union 231 | | PrimitiveType::Subtraction 232 | | PrimitiveType::Intersection 233 | | PrimitiveType::SmoothMinimum => 2, 234 | _ => 1, 235 | }, 236 | } 237 | } 238 | 239 | /// Returns `true` if this op's input slot can be connected to another 240 | /// op's output slot and `false` otherwise. 241 | pub fn has_inputs(&self) -> bool { 242 | self.get_input_capacity() > 0 243 | } 244 | 245 | /// Returns `true` if this op's output slot can be connected to another 246 | /// op's input slot and `false` otherwise. 247 | pub fn has_outputs(&self) -> bool { 248 | match *self { 249 | OpFamily::Domain(domain) => true, 250 | OpFamily::Primitive(primitive) => match primitive { 251 | PrimitiveType::Render => false, 252 | _ => true, 253 | }, 254 | } 255 | } 256 | 257 | /// Returns a formattable string of shader code that corresponds to 258 | /// this op family. 259 | pub fn get_code_template(&self) -> String { 260 | match *self { 261 | OpFamily::Domain(domain) => match domain { 262 | DomainType::Root => " 263 | vec3 p_NAME = p; 264 | float s_NAME = 1.0;" 265 | .to_string(), 266 | DomainType::Transform => " 267 | float s_NAME = params[INDEX].w * s_INPUT_A; 268 | vec3 t_NAME = params[INDEX].xyz; 269 | vec3 p_NAME = p_INPUT_A / s_NAME + t_NAME;" 270 | .to_string(), 271 | DomainType::Twist => " 272 | float s_NAME = s_INPUT_A; 273 | vec3 p_NAME = domain_twist(p_INPUT_A, params[INDEX].x);" 274 | .to_string(), 275 | DomainType::Bend => " 276 | float s_NAME = s_INPUT_A; 277 | vec3 p_NAME = domain_bend(p_INPUT_A, params[INDEX].x);" 278 | .to_string(), 279 | }, 280 | OpFamily::Primitive(primitive) => match primitive { 281 | PrimitiveType::Sphere => { 282 | "float NAME = sdf_sphere(p_INPUT_A, vec3(0.0), 1.0) * s_INPUT_A;".to_string() 283 | } 284 | PrimitiveType::Box => { 285 | "float NAME = sdf_box(p_INPUT_A, vec3(1.0)) * s_INPUT_A;".to_string() 286 | } 287 | PrimitiveType::Plane => { 288 | "float NAME = sdf_plane(p_INPUT_A, -1.0) * s_INPUT_A;".to_string() 289 | } 290 | PrimitiveType::Torus => { 291 | "float NAME = sdf_torus(p_INPUT_A, vec2(1.0, 0.5)) * s_INPUT_A;".to_string() 292 | } 293 | PrimitiveType::Union => "float NAME = op_union(INPUT_A, INPUT_B);".to_string(), 294 | PrimitiveType::Subtraction => { 295 | "float NAME = op_subtract(INPUT_A, INPUT_B);".to_string() 296 | } 297 | PrimitiveType::Intersection => { 298 | "float NAME = op_intersect(INPUT_A, INPUT_B);".to_string() 299 | } 300 | PrimitiveType::SmoothMinimum => { 301 | "float NAME = op_smooth_min(INPUT_A, INPUT_B, params[INDEX].x);".to_string() 302 | } 303 | PrimitiveType::Render => "float NAME = INPUT_A;".to_string(), 304 | }, 305 | } 306 | } 307 | 308 | /// Returns `true` if this op family can connect to `other`, either 309 | /// directly or indirectly. 310 | pub fn can_connect_to(&self, other: OpFamily) -> bool { 311 | match *self { 312 | // This operator is a domain operator. 313 | OpFamily::Domain(domain) => match other { 314 | OpFamily::Domain(other_domain) => return true, 315 | OpFamily::Primitive(other_primitive) => match other_primitive { 316 | PrimitiveType::Sphere 317 | | PrimitiveType::Box 318 | | PrimitiveType::Plane 319 | | PrimitiveType::Torus => return true, 320 | _ => return false, 321 | }, 322 | }, 323 | // This operator is a primitive operator. 324 | OpFamily::Primitive(primitive) => match other { 325 | OpFamily::Domain(other_domain) => return false, 326 | // Generators such as spheres, boxes, planes, and toruses can 327 | // only be used as the source operator in primitive -> primitive 328 | // interactions. 329 | OpFamily::Primitive(other_primitive) => match other_primitive { 330 | PrimitiveType::Sphere 331 | | PrimitiveType::Box 332 | | PrimitiveType::Plane 333 | | PrimitiveType::Torus => return false, 334 | _ => return true, 335 | }, 336 | }, 337 | } 338 | } 339 | 340 | /// Returns the connection type between this op family and `other`. A 341 | /// connection can be either direct, indirect, or invalid. 342 | pub fn get_connection_type(&self, other: OpFamily) -> ConnectionType { 343 | match *self { 344 | // This operator is a domain operator. 345 | OpFamily::Domain(domain) => match other { 346 | OpFamily::Domain(other_domain) => ConnectionType::Direct, 347 | OpFamily::Primitive(other_primitive) => ConnectionType::Indirect, 348 | }, 349 | // This operator is a primitive operator. 350 | OpFamily::Primitive(primitive) => match other { 351 | OpFamily::Domain(other_domain) => ConnectionType::Invalid, 352 | OpFamily::Primitive(other_primitive) => ConnectionType::Direct, 353 | }, 354 | } 355 | } 356 | 357 | /// Returns the default parameters for this op family. 358 | pub fn get_default_params(&self) -> Parameters { 359 | match *self { 360 | OpFamily::Domain(domain) => match domain { 361 | DomainType::Transform => Parameters::new( 362 | [0.0, 0.0, 0.0, 1.0], 363 | ["translate_x", "translate_y", "translate_z", "scale"], 364 | 0, 365 | [-10.0, -10.0, -10.0, 0.1], 366 | [10.0, 10.0, 10.0, 10.0], 367 | [0.5, 0.5, 0.5, 0.1], 368 | ), 369 | DomainType::Twist => Parameters::new( 370 | [4.0, 4.0, 0.0, 0.0], 371 | ["twist_x", "twist_y", "", ""], 372 | 0, 373 | [0.0, 0.0, 0.0, 0.0], 374 | [20.0, 20.0, 0.0, 0.0], 375 | [1.0, 1.0, 0.0, 0.0], 376 | ), 377 | DomainType::Bend => Parameters::new( 378 | [0.5, 0.5, 0.0, 0.0], 379 | ["bend_x", "bend_y", "", ""], 380 | 0, 381 | [0.0, 0.0, 0.0, 0.0], 382 | [2.0, 2.0, 0.0, 0.0], 383 | [0.05, 0.05, 0.0, 0.0], 384 | ), 385 | _ => Parameters::default(), 386 | }, 387 | OpFamily::Primitive(primitive) => match primitive { 388 | PrimitiveType::SmoothMinimum => Parameters::new( 389 | [1.0, 0.0, 0.0, 0.0], 390 | ["exponent", "", "", ""], 391 | 0, 392 | [0.0, 0.0, 0.0, 0.0], 393 | [1.0, 0.0, 0.0, 0.0], 394 | [0.1, 0.0, 0.0, 0.0], 395 | ), 396 | _ => Parameters::default(), 397 | }, 398 | } 399 | } 400 | } 401 | 402 | pub struct Op { 403 | /// The number of ops currently connected to this op 404 | pub active_inputs: usize, 405 | 406 | /// The bounding box of the op 407 | pub bounds_body: Rect, 408 | 409 | /// The bounding box of the op's input slot 410 | pub bounds_input: Rect, 411 | 412 | /// The bounding box of the op's output slot 413 | pub bounds_output: Rect, 414 | 415 | /// The bounding box of the op's icon 416 | pub bounds_icon: Rect, 417 | 418 | /// The current interaction state of the op 419 | pub state: InteractionState, 420 | 421 | /// A unique, numeric identifier - no two ops will have the same UUID 422 | pub uuid: Uuid, 423 | 424 | /// The name of the op (i.e. "sphere_0") as it will appear in the shader 425 | pub name: String, 426 | 427 | /// The op family 428 | pub family: OpFamily, 429 | 430 | /// This op's parameters, which may or may not be used by the shader 431 | pub params: Parameters, 432 | } 433 | 434 | impl Op { 435 | pub fn new(family: OpFamily, position: Vector2, size: Vector2) -> Op { 436 | // Increment counter. 437 | let count = COUNTER.fetch_add(1, Ordering::SeqCst); 438 | 439 | // Set up bounding boxes. 440 | let bounds_body = Rect::new(position, size); 441 | 442 | let mut bounds_input = Rect::new(Vector2::zero(), constants::OPERATIVE_SLOT_SIZE); 443 | bounds_input.center_on_edge(&bounds_body, Edge::Left); 444 | 445 | let mut bounds_output = Rect::new(Vector2::zero(), constants::OPERATIVE_SLOT_SIZE); 446 | bounds_output.center_on_edge(&bounds_body, Edge::Right); 447 | 448 | let mut bounds_icon = Rect::new(position, constants::OPERATOR_ICON_SIZE); 449 | bounds_icon.translate(&constants::OPERATOR_ICON_OFFSET); 450 | 451 | let name = format!("{}_{}", family.to_string(), count); 452 | 453 | Op { 454 | active_inputs: 0, 455 | bounds_body, 456 | bounds_input, 457 | bounds_output, 458 | bounds_icon, 459 | state: InteractionState::Deselected, 460 | uuid: Uuid::new_v4(), 461 | name, 462 | family, 463 | params: family.get_default_params(), 464 | } 465 | } 466 | 467 | /// Translates the op in the network editor by an amount 468 | /// `offset`. Internally, this translates each of the 469 | /// bounding rectangles that are owned by this op. 470 | pub fn translate(&mut self, offset: &Vector2) { 471 | self.bounds_body.translate(offset); 472 | self.bounds_input.translate(offset); 473 | self.bounds_output.translate(offset); 474 | self.bounds_icon.translate(offset); 475 | } 476 | 477 | /// Returns the complete code snippet corresponding to 478 | /// this op after it has been connected to `input_a` and 479 | /// `input_b` (both of which are optional). 480 | pub fn get_code(&self, input_a: Option<&str>, input_b: Option<&str>) -> String { 481 | let mut code = self.family.get_code_template(); 482 | code = code.replace("NAME", &self.name); 483 | 484 | code = code.replace("INDEX", &self.params.index.to_string()); 485 | 486 | if let Some(a) = input_a { 487 | code = code.replace("INPUT_A", a); 488 | } 489 | if let Some(b) = input_b { 490 | code = code.replace("INPUT_B", b); 491 | } 492 | code 493 | } 494 | 495 | /// Returns an immutable reference to this op's parameters. 496 | pub fn get_params(&self) -> &Parameters { 497 | &self.params 498 | } 499 | 500 | /// Returns a mutable reference to this op's parameters. 501 | pub fn get_params_mut(&mut self) -> &mut Parameters { 502 | &mut self.params 503 | } 504 | } 505 | 506 | impl Connected for Op { 507 | fn has_inputs(&self) -> bool { 508 | self.family.has_inputs() 509 | } 510 | 511 | fn has_outputs(&self) -> bool { 512 | self.family.has_outputs() 513 | } 514 | 515 | fn get_number_of_available_inputs(&self) -> usize { 516 | self.family.get_input_capacity() - self.active_inputs 517 | } 518 | 519 | fn update_active_inputs_count(&mut self, count: usize) { 520 | self.active_inputs = count; 521 | } 522 | 523 | fn on_connect(&mut self) { 524 | self.active_inputs += 1; 525 | } 526 | 527 | fn on_disconnect(&mut self) { 528 | self.active_inputs -= 1; 529 | } 530 | } 531 | 532 | impl<'a> Drawable<'a> for Op { 533 | fn get_draw_params(&'a self) -> DrawParams<'a> { 534 | DrawParams::Rectangle(&self.bounds_body) 535 | } 536 | } 537 | -------------------------------------------------------------------------------- /src/preview.rs: -------------------------------------------------------------------------------- 1 | use gl::{self, types::*}; 2 | use cgmath::{EuclideanSpace, InnerSpace, Matrix4, Point3, SquareMatrix, Vector2, Vector3, Vector4, 3 | Zero}; 4 | 5 | use bounds::Rect; 6 | use color::Color; 7 | use constants; 8 | use interaction::{MouseInfo, Panel}; 9 | use program::Program; 10 | 11 | use std::mem; 12 | use std::ptr; 13 | use std::os::raw::c_void; 14 | 15 | #[derive(Copy, Clone)] 16 | pub enum Shading { 17 | /// Display the z-depth of each fragment 18 | Depth, 19 | 20 | /// Display the number of steps taken along the ray 21 | Steps, 22 | 23 | /// Display the scene with ambient occlusion 24 | AmbientOcclusion, 25 | 26 | /// Display the normals of the underlying distance field 27 | Normals, 28 | 29 | /// Display the scene with diffuse lighting 30 | Diffuse, 31 | } 32 | 33 | struct VirtualCamera { 34 | /// The position of the camera 35 | position: Point3, 36 | 37 | /// The up vector of the camera 38 | up: Vector3, 39 | 40 | /// The direction that the camera is currently facing 41 | front: Vector3, 42 | 43 | /// The cross product of this camera's `up` and `front` vectors 44 | right: Vector3, 45 | 46 | /// The vertical angle of the camera 47 | pitch: f32, 48 | 49 | /// The horizontal angle of the camera 50 | yaw: f32, 51 | } 52 | 53 | impl VirtualCamera { 54 | fn new() -> VirtualCamera { 55 | VirtualCamera { 56 | position: Point3::new(0.0, 0.0, 5.0), 57 | up: Vector3::unit_y(), 58 | front: Vector3::new(0.0, 0.0, -1.0), 59 | right: Vector3::unit_x(), 60 | pitch: 0.0, 61 | yaw: -90.0, 62 | } 63 | } 64 | 65 | fn home(&mut self) { 66 | self.position = Point3::new(0.0, 0.0, 5.0); 67 | self.pitch = 0.0; 68 | self.yaw = -90.0; 69 | } 70 | 71 | fn rebuild_basis(&mut self) { 72 | self.front = Vector3::new( 73 | self.yaw.to_radians().cos() * self.pitch.to_radians().cos(), 74 | self.pitch.to_radians().sin(), 75 | self.yaw.to_radians().sin() * self.pitch.to_radians().cos(), 76 | ).normalize(); 77 | 78 | self.right = self.front.cross(self.up).normalize() 79 | } 80 | } 81 | 82 | pub struct Preview { 83 | /// The valid shader program, if one exists 84 | program_valid: Option, 85 | 86 | /// The fallback program that will be used if `program_valid` 87 | /// is `None` 88 | program_error: Program, 89 | 90 | /// The bounding box of the preview window 91 | bounds: Rect, 92 | 93 | /// The virtual camera that will be used to view the scene 94 | camera: VirtualCamera, 95 | 96 | /// The current shading mode that will be applied to the scene 97 | shading: Shading, 98 | 99 | /// The OpenGL handle to the shader storage buffer object (SSBO) 100 | /// that will hold all of the op parameters 101 | ssbo: GLuint, 102 | } 103 | 104 | impl Preview { 105 | pub fn new() -> Preview { 106 | static FALLBACK_VS_SRC: &'static str = " 107 | #version 430 108 | 109 | layout(location = 0) in vec2 position; 110 | layout(location = 1) in vec2 texcoord; 111 | layout (location = 0) out vec2 vs_texcoord; 112 | 113 | uniform mat4 u_model_matrix; 114 | uniform mat4 u_projection_matrix; 115 | 116 | void main() { 117 | vs_texcoord = texcoord; 118 | 119 | gl_Position = u_projection_matrix * u_model_matrix * vec4(position, 0.0, 1.0); 120 | }"; 121 | 122 | static FALLBACK_FS_SRC: &'static str = " 123 | #version 430 124 | 125 | layout (location = 0) in vec2 vs_texcoord; 126 | layout (location = 0) out vec4 o_color; 127 | 128 | void main() { 129 | const float tile = 15.0; 130 | vec2 uv = vs_texcoord * tile; 131 | vec2 ipos = floor(uv); 132 | 133 | float total = dot(ipos, vec2(1.0)); 134 | float checkerboard = mod(total, 2.0); 135 | const float alpha = 0.25; 136 | 137 | o_color = vec4(vec3(checkerboard), alpha); 138 | }"; 139 | 140 | let program_error = 141 | Program::new(FALLBACK_VS_SRC.to_string(), FALLBACK_FS_SRC.to_string()).unwrap(); 142 | 143 | let mut ssbo = 0; 144 | unsafe { 145 | let ssbo_size = (constants::PARAMETER_SSBO_CAPACITY * mem::size_of::>()) as GLsizeiptr; 146 | 147 | gl::CreateBuffers(1, &mut ssbo); 148 | gl::NamedBufferStorage(ssbo, ssbo_size, ptr::null(), gl::DYNAMIC_STORAGE_BIT); 149 | } 150 | Preview { 151 | program_valid: None, 152 | program_error, 153 | bounds: Rect::new(Vector2::new(400.0, 50.0), constants::PREVIEW_RESOLUTION), 154 | camera: VirtualCamera::new(), 155 | shading: Shading::Normals, 156 | ssbo, 157 | } 158 | } 159 | 160 | /// Sets the shader program that will be used to render a 161 | /// miniature preview window in the lower right-hand corner 162 | /// of the network. 163 | /// 164 | /// If `program` is `None`, then the renderer will use a 165 | /// fall-back shader to indicate the error state of the 166 | /// current graph. 167 | pub fn set_valid_program(&mut self, program: Option) { 168 | self.program_valid = program; 169 | } 170 | 171 | /// Writes `data` to the OpenGL buffer that this preview 172 | /// will use to populate shader parameters during rendering. 173 | pub fn update_params(&self, data: Vec) { 174 | unsafe { 175 | let data_size = (data.len() * mem::size_of::()) as GLsizeiptr; 176 | gl::NamedBufferSubData(self.ssbo, 0, data_size, data.as_ptr() as *const c_void); 177 | } 178 | } 179 | 180 | /// Sets the shading mode. 181 | pub fn set_shading(&mut self, shading: Shading) { 182 | self.shading = shading; 183 | } 184 | 185 | /// Homes the virtual preview camera. 186 | pub fn home(&mut self) { 187 | self.camera.home(); 188 | } 189 | 190 | /// If a preview program has be assigned, render a miniature 191 | /// preview window in the lower right-hand corner of the 192 | /// network. 193 | pub fn prepare(&self, projection: &Matrix4) { 194 | if let Some(ref program) = self.program_valid { 195 | self.bind_transforms(); 196 | program.bind(); 197 | program.uniform_3f("u_camera_position", &self.camera.position.to_vec()); 198 | program.uniform_3f("u_camera_front", &self.camera.front); 199 | program.uniform_1ui("u_shading", self.shading as u32); 200 | program.uniform_matrix_4f("u_model_matrix", &self.bounds.get_model_matrix()); 201 | program.uniform_matrix_4f("u_projection_matrix", &projection); 202 | } else { 203 | self.program_error.bind(); 204 | self.program_error 205 | .uniform_matrix_4f("u_model_matrix", &self.bounds.get_model_matrix()); 206 | self.program_error 207 | .uniform_matrix_4f("u_projection_matrix", &projection); 208 | } 209 | } 210 | 211 | pub fn handle_interaction(&mut self, mouse: &MouseInfo) { 212 | if self.bounds.inside(&mouse.curr) { 213 | let offset = -mouse.velocity(); 214 | 215 | // Handle camera rotation. 216 | if mouse.ldown { 217 | self.camera.yaw += offset.x * constants::PREVIEW_ROTATION_SENSITIVITY; 218 | self.camera.pitch += offset.y * constants::PREVIEW_ROTATION_SENSITIVITY; 219 | self.camera.pitch.min(89.0).max(-89.0); 220 | self.camera.rebuild_basis(); 221 | } 222 | 223 | // Handle camera translation. 224 | if mouse.rdown { 225 | self.camera.position += self.camera.right * offset.x * constants::PREVIEW_TRANSLATION_SENSITIVITY; 226 | self.camera.position += self.camera.front * offset.y * constants::PREVIEW_TRANSLATION_SENSITIVITY; 227 | } 228 | } 229 | } 230 | 231 | fn bind_transforms(&self) { 232 | unsafe { 233 | gl::BindBufferBase(gl::SHADER_STORAGE_BUFFER, 0, self.ssbo); 234 | } 235 | } 236 | } 237 | 238 | impl Drop for Preview { 239 | fn drop(&mut self) { 240 | unsafe { 241 | gl::DeleteBuffers(1, &self.ssbo); 242 | } 243 | } 244 | } 245 | -------------------------------------------------------------------------------- /src/program.rs: -------------------------------------------------------------------------------- 1 | use gl; 2 | use gl::types::*; 3 | 4 | use cgmath; 5 | use cgmath::{Array, Matrix, Matrix4, Vector2, Vector3, Vector4}; 6 | 7 | use std::ptr; 8 | use std::str; 9 | use std::ffi::CString; 10 | use std::collections::HashMap; 11 | 12 | pub struct Uniform { 13 | location: i32, 14 | size: i32, 15 | // TODO this should be converted to a new type, like: https://github.com/glium/glium/blob/master/src/uniforms/value.rs 16 | ty: GLenum, 17 | } 18 | 19 | pub struct Program { 20 | pub id: GLuint, 21 | vs_src: String, 22 | fs_src: String, 23 | uniforms: HashMap, 24 | } 25 | 26 | impl Program { 27 | /// Compiles a shader of type `stage` from the source held in `src`. 28 | fn compile_shader(src: &String, stage: GLenum) -> Result { 29 | let shader; 30 | unsafe { 31 | shader = gl::CreateShader(stage); 32 | 33 | // Attempt to compile the shader. 34 | let c_str = CString::new(src.as_bytes()).unwrap(); 35 | gl::ShaderSource(shader, 1, &c_str.as_ptr(), ptr::null()); 36 | gl::CompileShader(shader); 37 | 38 | // Get the compile status. 39 | let mut status = gl::FALSE as GLint; 40 | gl::GetShaderiv(shader, gl::COMPILE_STATUS, &mut status); 41 | 42 | // Fail on error 43 | if status != (gl::TRUE as GLint) { 44 | let mut len = 0; 45 | gl::GetShaderiv(shader, gl::INFO_LOG_LENGTH, &mut len); 46 | let mut buffer = Vec::with_capacity(len as usize); 47 | 48 | // Subtract 1 to skip the trailing null character. 49 | buffer.set_len((len as usize) - 1); 50 | 51 | gl::GetShaderInfoLog( 52 | shader, 53 | len, 54 | ptr::null_mut(), 55 | buffer.as_mut_ptr() as *mut GLchar, 56 | ); 57 | 58 | let error = String::from_utf8(buffer) 59 | .ok() 60 | .expect("ShaderInfoLog not valid utf8"); 61 | return Err(error); 62 | } 63 | } 64 | 65 | Ok(shader) 66 | } 67 | 68 | fn link_program(vs: GLuint, fs: GLuint) -> Result { 69 | unsafe { 70 | let program = gl::CreateProgram(); 71 | gl::AttachShader(program, vs); 72 | gl::AttachShader(program, fs); 73 | gl::LinkProgram(program); 74 | 75 | // Get the link status. 76 | let mut status = gl::FALSE as GLint; 77 | gl::GetProgramiv(program, gl::LINK_STATUS, &mut status); 78 | 79 | // If there was an error, return the error string. 80 | if status != (gl::TRUE as GLint) { 81 | let mut len: GLint = 0; 82 | gl::GetProgramiv(program, gl::INFO_LOG_LENGTH, &mut len); 83 | let mut buffer = Vec::with_capacity(len as usize); 84 | 85 | // Subtract 1 to skip the trailing null character. 86 | buffer.set_len((len as usize) - 1); 87 | 88 | gl::GetProgramInfoLog( 89 | program, 90 | len, 91 | ptr::null_mut(), 92 | buffer.as_mut_ptr() as *mut GLchar, 93 | ); 94 | gl::DeleteShader(fs); 95 | gl::DeleteShader(vs); 96 | 97 | let error = String::from_utf8(buffer) 98 | .ok() 99 | .expect("ProgramInfoLog not valid utf8"); 100 | return Err(error); 101 | } 102 | 103 | Ok(program) 104 | } 105 | } 106 | 107 | fn perform_reflection(&mut self) { 108 | unsafe { 109 | use std::mem; 110 | 111 | // Retrieve the number of active uniforms. 112 | let mut active_uniforms: GLint = mem::uninitialized(); 113 | gl::GetProgramiv(self.id, gl::ACTIVE_UNIFORMS, &mut active_uniforms); 114 | 115 | // Retrieve the maximum length of each uniform name. 116 | let mut max_name_length: GLint = 0; 117 | gl::GetProgramiv(self.id, gl::ACTIVE_UNIFORM_MAX_LENGTH, &mut max_name_length); 118 | 119 | // Query for information about each uniform entry. 120 | for i in 0..active_uniforms { 121 | let mut name_bytes = Vec::with_capacity(max_name_length as usize); 122 | let mut name_length = 0; 123 | let mut size = 0; 124 | let mut ty = gl::NONE; 125 | 126 | gl::GetActiveUniform( 127 | self.id, 128 | i as GLuint, 129 | max_name_length, 130 | &mut name_length, 131 | &mut size, 132 | &mut ty, 133 | name_bytes.as_mut_ptr() as *mut GLchar, 134 | ); 135 | 136 | // Convert the byte array to a string. 137 | name_bytes.set_len(name_length as usize); 138 | let name = String::from_utf8(name_bytes).unwrap(); 139 | 140 | // Finally, get the uniform's location. 141 | let location = 142 | gl::GetUniformLocation(self.id, CString::new(name.clone()).unwrap().as_ptr()); 143 | 144 | println!( 145 | "Uniform Entry with name {:?}: size {}, type {}, location {}", 146 | name, size, ty, location 147 | ); 148 | self.uniforms.insert(name, Uniform { location, size, ty }); 149 | } 150 | } 151 | } 152 | 153 | pub fn new(vs_src: String, fs_src: String) -> Option { 154 | // Make sure that compiling each of the shaders was successful. 155 | let compile_vs_res = Program::compile_shader(&vs_src, gl::VERTEX_SHADER); 156 | let compile_fs_res = Program::compile_shader(&fs_src, gl::FRAGMENT_SHADER); 157 | 158 | match (compile_vs_res, compile_fs_res) { 159 | (Ok(vs_id), Ok(fs_id)) => { 160 | // Make sure that linking the shader program was successful. 161 | if let Ok(id) = Program::link_program(vs_id, fs_id) { 162 | // If everything went ok, return the shader program. 163 | let mut valid_program = Program { 164 | id, 165 | vs_src, 166 | fs_src, 167 | uniforms: HashMap::new(), 168 | }; 169 | valid_program.perform_reflection(); 170 | 171 | return Some(valid_program); 172 | } else { 173 | return None; 174 | } 175 | } 176 | // Both shader stages resulted in an error. 177 | (Err(vs_err), Err(fs_err)) => { 178 | println!("{}", vs_err); 179 | println!("{}", fs_err); 180 | return None; 181 | } 182 | // The vertex shader resulted in an error. 183 | (Err(vs_err), Ok(_)) => { 184 | println!("{}", vs_err); 185 | return None; 186 | } 187 | // The fragment shader resulted in an error. 188 | (Ok(_), Err(fs_err)) => { 189 | println!("{}", fs_err); 190 | return None; 191 | } 192 | } 193 | } 194 | 195 | pub fn bind(&self) { 196 | unsafe { 197 | gl::UseProgram(self.id); 198 | } 199 | } 200 | 201 | pub fn unbind(&self) { 202 | unsafe { 203 | gl::UseProgram(0); 204 | } 205 | } 206 | 207 | pub fn uniform_1i(&self, name: &str, value: i32) { 208 | unsafe { 209 | let location = gl::GetUniformLocation(self.id, CString::new(name).unwrap().as_ptr()); 210 | gl::ProgramUniform1i(self.id, location, value as gl::types::GLint); 211 | } 212 | } 213 | 214 | pub fn uniform_2i(&self, name: &str, value: &cgmath::Vector2) { 215 | unsafe { 216 | let location = gl::GetUniformLocation(self.id, CString::new(name).unwrap().as_ptr()); 217 | gl::ProgramUniform2iv(self.id, location, 1, value.as_ptr()); 218 | } 219 | } 220 | 221 | pub fn uniform_3i(&self, name: &str, value: &cgmath::Vector3) { 222 | unsafe { 223 | let location = gl::GetUniformLocation(self.id, CString::new(name).unwrap().as_ptr()); 224 | gl::ProgramUniform3iv(self.id, location, 1, value.as_ptr()); 225 | } 226 | } 227 | 228 | pub fn uniform_4i(&self, name: &str, value: &cgmath::Vector4) { 229 | unsafe { 230 | let location = gl::GetUniformLocation(self.id, CString::new(name).unwrap().as_ptr()); 231 | gl::ProgramUniform4iv(self.id, location, 1, value.as_ptr()); 232 | } 233 | } 234 | 235 | pub fn uniform_1ui(&self, name: &str, value: u32) { 236 | unsafe { 237 | let location = gl::GetUniformLocation(self.id, CString::new(name).unwrap().as_ptr()); 238 | gl::ProgramUniform1ui(self.id, location, value as gl::types::GLuint); 239 | } 240 | } 241 | 242 | pub fn uniform_2ui(&self, name: &str, value: &cgmath::Vector2) { 243 | unsafe { 244 | let location = gl::GetUniformLocation(self.id, CString::new(name).unwrap().as_ptr()); 245 | gl::ProgramUniform2uiv(self.id, location, 1, value.as_ptr()); 246 | } 247 | } 248 | 249 | pub fn uniform_3ui(&self, name: &str, value: &cgmath::Vector3) { 250 | unsafe { 251 | let location = gl::GetUniformLocation(self.id, CString::new(name).unwrap().as_ptr()); 252 | gl::ProgramUniform3uiv(self.id, location, 1, value.as_ptr()); 253 | } 254 | } 255 | 256 | pub fn uniform_4ui(&self, name: &str, value: &cgmath::Vector4) { 257 | unsafe { 258 | let location = gl::GetUniformLocation(self.id, CString::new(name).unwrap().as_ptr()); 259 | gl::ProgramUniform4uiv(self.id, location, 1, value.as_ptr()); 260 | } 261 | } 262 | 263 | pub fn uniform_1f(&self, name: &str, value: f32) { 264 | unsafe { 265 | let location = gl::GetUniformLocation(self.id, CString::new(name).unwrap().as_ptr()); 266 | gl::ProgramUniform1f(self.id, location, value as gl::types::GLfloat); 267 | } 268 | } 269 | 270 | pub fn uniform_2f(&self, name: &str, value: &cgmath::Vector2) { 271 | unsafe { 272 | let location = gl::GetUniformLocation(self.id, CString::new(name).unwrap().as_ptr()); 273 | gl::ProgramUniform2fv(self.id, location, 1, value.as_ptr()); 274 | } 275 | } 276 | 277 | pub fn uniform_3f(&self, name: &str, value: &cgmath::Vector3) { 278 | unsafe { 279 | let location = gl::GetUniformLocation(self.id, CString::new(name).unwrap().as_ptr()); 280 | gl::ProgramUniform3fv(self.id, location, 1, value.as_ptr()); 281 | } 282 | } 283 | 284 | pub fn uniform_4f(&self, name: &str, value: &cgmath::Vector4) { 285 | unsafe { 286 | let location = gl::GetUniformLocation(self.id, CString::new(name).unwrap().as_ptr()); 287 | gl::ProgramUniform4fv(self.id, location, 1, value.as_ptr()); 288 | } 289 | } 290 | 291 | pub fn uniform_matrix_3f(&self, name: &str, value: &cgmath::Matrix3) { 292 | unsafe { 293 | let location = gl::GetUniformLocation(self.id, CString::new(name).unwrap().as_ptr()); 294 | gl::ProgramUniformMatrix3fv(self.id, location, 1, gl::FALSE, value.as_ptr()); 295 | } 296 | } 297 | 298 | pub fn uniform_matrix_4f(&self, name: &str, value: &cgmath::Matrix4) { 299 | unsafe { 300 | let location = gl::GetUniformLocation(self.id, CString::new(name).unwrap().as_ptr()); 301 | gl::ProgramUniformMatrix4fv(self.id, location, 1, gl::FALSE, value.as_ptr()); 302 | } 303 | } 304 | } 305 | 306 | impl Drop for Program { 307 | fn drop(&mut self) { 308 | unsafe { 309 | gl::DeleteProgram(self.id); 310 | } 311 | } 312 | } 313 | -------------------------------------------------------------------------------- /src/renderer.rs: -------------------------------------------------------------------------------- 1 | use gl::{self, types::*}; 2 | use cgmath::{self, Matrix, Matrix4, One, PerspectiveFov, SquareMatrix, Vector2, Vector4, Zero}; 3 | 4 | use bounds::Rect; 5 | use color::Color; 6 | use program::Program; 7 | use texture::Texture; 8 | 9 | use std::mem; 10 | use std::ptr; 11 | use std::os::raw::c_void; 12 | use std::ffi::CString; 13 | use std::time::{Duration, SystemTime}; 14 | 15 | #[derive(Copy, Clone)] 16 | pub enum LineMode { 17 | Solid, 18 | Dashed, 19 | } 20 | 21 | #[derive(Copy, Clone)] 22 | pub enum LineConnectivity { 23 | Segment, 24 | Strip, 25 | } 26 | 27 | #[derive(Clone)] 28 | pub enum DrawParams<'a> { 29 | Rectangle(&'a Rect), 30 | Line(&'a Vec, LineMode, LineConnectivity), 31 | } 32 | 33 | pub trait Drawable<'a> { 34 | fn get_draw_params(&'a self) -> DrawParams<'a>; 35 | } 36 | 37 | pub struct Renderer { 38 | /// The shader program that will be used to draw sprites 39 | program_draw: Program, 40 | 41 | /// The projection matrix used to render the network orthographically 42 | projection: Matrix4, 43 | 44 | /// The VAO that contains vertex attribute descriptions for sprite 45 | /// rendering 46 | vao: u32, 47 | 48 | /// The VBO that contains the vertex data necessary for rendering 49 | /// rectangular sprites 50 | vbo_rect: u32, 51 | 52 | /// The VBO that will be dynamically updated with vertex data 53 | /// for rendering lines 54 | vbo_line: u32, 55 | 56 | /// The zoom of the network editor 57 | zoom: f32, 58 | 59 | /// The resolution (in pixels) of the network editor 60 | size: Vector2, 61 | 62 | /// An application timer 63 | time: SystemTime, 64 | } 65 | 66 | impl Renderer { 67 | /// Constructs and returns a new renderer instance. 68 | pub fn new(size: Vector2) -> Renderer { 69 | static VERTEX_DATA: [GLfloat; 24] = [ 70 | // Positions followed by texture coordinates. 71 | 72 | // First triangle 73 | 0.0, 0.0, 0.0, 1.0, // UL 74 | 1.0, 0.0, 1.0, 1.0, // UR 75 | 0.0, 1.0, 0.0, 0.0, // LL 76 | 77 | // Second triangle 78 | 1.0, 0.0, 1.0, 1.0, // UR 79 | 1.0, 1.0, 1.0, 0.0, // LR 80 | 0.0, 1.0, 0.0, 0.0 // LL 81 | ]; 82 | 83 | static DRAW_VS_SRC: &'static str = " 84 | #version 430 85 | 86 | layout(location = 0) in vec2 position; 87 | layout(location = 1) in vec2 texcoord; 88 | 89 | layout (location = 0) out vec2 vs_texcoord; 90 | 91 | uniform mat4 u_model_matrix; 92 | uniform mat4 u_projection_matrix; 93 | 94 | void main() 95 | { 96 | vs_texcoord = texcoord; 97 | 98 | gl_Position = u_projection_matrix * u_model_matrix * vec4(position, 0.0, 1.0); 99 | }"; 100 | 101 | static DRAW_FS_SRC: &'static str = " 102 | #version 430 103 | 104 | uniform float u_time; 105 | uniform vec4 u_draw_color = vec4(1.0); 106 | uniform uint u_draw_mode = 0; 107 | 108 | layout(binding = 0) uniform sampler2D u_color_map; 109 | layout(binding = 1) uniform sampler2D u_alpha_map; 110 | uniform bool u_use_color_map; 111 | uniform bool u_use_alpha_map; 112 | 113 | layout (location = 0) in vec2 vs_texcoord; 114 | 115 | layout (location = 0) out vec4 o_color; 116 | 117 | const uint DRAW_MODE_RECTANGLES = 0; 118 | const uint DRAW_MODE_LINES_SOLID = 1; 119 | const uint DRAW_MODE_LINES_DASHED = 2; 120 | 121 | void main() 122 | { 123 | vec2 uv = vs_texcoord; 124 | 125 | float alpha = u_draw_color.a;; 126 | if (u_draw_mode == DRAW_MODE_LINES_DASHED) 127 | { 128 | const float stripes = 10.0; 129 | //alpha = max(step(0.5, fract(uv.s * stripes - u_time)), 0.25); 130 | } 131 | 132 | // the alpha map overrides the default alpha 133 | if (u_use_alpha_map) 134 | { 135 | alpha = texture(u_alpha_map, uv).r; 136 | } 137 | 138 | if (u_use_color_map) 139 | { 140 | uv.t = 1.0 - uv.t; 141 | vec4 color = texture(u_color_map, uv); 142 | color.a *= alpha; 143 | o_color = color; 144 | } 145 | else 146 | { 147 | o_color = vec4(u_draw_color.rgb, alpha); 148 | } 149 | }"; 150 | 151 | // Compile the shader program. 152 | let program_draw = Program::new(DRAW_VS_SRC.to_string(), DRAW_FS_SRC.to_string()).unwrap(); 153 | 154 | // Setup buffers. 155 | let mut vao = 0; 156 | let mut vbo_rect = 0; 157 | let mut vbo_line = 0; 158 | 159 | unsafe { 160 | // Enable alpha blending. 161 | gl::Enable(gl::BLEND); 162 | gl::BlendFunc(gl::SRC_ALPHA, gl::ONE_MINUS_SRC_ALPHA); 163 | 164 | // Create the VBO for rendering rectangles. 165 | let vbo_rect_size = (VERTEX_DATA.len() * mem::size_of::()) as GLsizeiptr; 166 | gl::CreateBuffers(1, &mut vbo_rect); 167 | gl::NamedBufferData( 168 | vbo_rect, 169 | vbo_rect_size, 170 | mem::transmute(&VERTEX_DATA[0]), 171 | gl::STATIC_DRAW, 172 | ); 173 | 174 | // Create the VBO for rendering lines. 175 | let vbo_line_size = (1000 * mem::size_of::()) as GLsizeiptr; 176 | gl::CreateBuffers(1, &mut vbo_line); 177 | gl::NamedBufferStorage( 178 | vbo_line, 179 | vbo_line_size, 180 | ptr::null(), 181 | gl::DYNAMIC_STORAGE_BIT, 182 | ); 183 | 184 | // This is not strictly necessary, but we do it for completeness sake. 185 | let num_pos_components: i32 = 2; 186 | let num_tex_components: i32 = 2; 187 | let pos_attr = 188 | gl::GetAttribLocation(program_draw.id, CString::new("position").unwrap().as_ptr()); 189 | let tex_attr = 190 | gl::GetAttribLocation(program_draw.id, CString::new("texcoord").unwrap().as_ptr()); 191 | let tex_offset = (num_pos_components as usize * mem::size_of::()) as GLuint; 192 | 193 | // Create the VAO and setup vertex attributes. 194 | gl::CreateVertexArrays(1, &mut vao); 195 | 196 | // Position attribute. 197 | gl::EnableVertexArrayAttrib(vao, pos_attr as GLuint); 198 | gl::VertexArrayAttribFormat( 199 | vao, 200 | pos_attr as GLuint, 201 | num_pos_components, 202 | gl::FLOAT, 203 | gl::FALSE as GLboolean, 204 | 0, 205 | ); 206 | gl::VertexArrayAttribBinding(vao, pos_attr as GLuint, 0); 207 | 208 | // Texture coordinates attribute. 209 | gl::EnableVertexArrayAttrib(vao, tex_attr as GLuint); 210 | gl::VertexArrayAttribFormat( 211 | vao, 212 | tex_attr as GLuint, 213 | num_tex_components, 214 | gl::FLOAT, 215 | gl::FALSE as GLboolean, 216 | tex_offset, 217 | ); 218 | gl::VertexArrayAttribBinding(vao, tex_attr as GLuint, 0); 219 | 220 | // Associate the VBO with bind point 0. 221 | gl::VertexArrayVertexBuffer( 222 | vao, 223 | 0, 224 | vbo_rect, 225 | 0, 226 | ((num_pos_components + num_tex_components) as usize * mem::size_of::()) as i32, 227 | ); 228 | } 229 | 230 | let mut renderer = Renderer { 231 | program_draw, 232 | projection: Matrix4::zero(), 233 | vao, 234 | vbo_rect, 235 | vbo_line, 236 | zoom: 1.0, 237 | size, 238 | time: SystemTime::now(), 239 | }; 240 | renderer.zoom(1.0); 241 | renderer 242 | } 243 | 244 | /// Returns the renderer's current projection matrix. 245 | pub fn get_projection(&self) -> &Matrix4 { 246 | &self.projection 247 | } 248 | 249 | /// Returns the internal size (width, height) of the render region. 250 | pub fn get_size(&self) -> &Vector2 { 251 | &self.size 252 | } 253 | 254 | /// Zooms the network in or out by modifying the underlying 255 | /// projection matrix. If `zoom` is `1.0`, this is 256 | /// effectively the "home" position. 257 | pub fn zoom(&mut self, zoom: f32) { 258 | self.zoom = zoom; 259 | self.rebuild_projection_matrix(); 260 | } 261 | 262 | /// Resizes the network. 263 | pub fn resize(&mut self, resolution: &Vector2) { 264 | self.size = *resolution; 265 | self.rebuild_projection_matrix(); 266 | } 267 | 268 | /// Rebuild the projection matrix: 269 | /// L, R, B, T, N, F 270 | fn rebuild_projection_matrix(&mut self) { 271 | self.projection = cgmath::ortho( 272 | -(self.size.x * 0.5) * self.zoom, 273 | (self.size.x * 0.5) * self.zoom, 274 | (self.size.y * 0.5) * self.zoom, 275 | -(self.size.y * 0.5) * self.zoom, 276 | -1.0, 277 | 1.0, 278 | ); 279 | 280 | // Set the uniform. 281 | self.program_draw 282 | .uniform_matrix_4f("u_projection_matrix", &self.projection); 283 | } 284 | 285 | /// Draws a primitive. 286 | pub fn draw( 287 | &self, 288 | params: DrawParams, 289 | color: &Color, 290 | color_map: Option<&Texture>, 291 | alpha_map: Option<&Texture>, 292 | ) { 293 | self.program_draw.bind(); 294 | 295 | let mut model = Matrix4::identity(); 296 | if let DrawParams::Rectangle(bounds) = params { 297 | model = *bounds.get_model_matrix(); 298 | } 299 | 300 | // Set shared uniforms. 301 | self.program_draw 302 | .uniform_matrix_4f("u_model_matrix", &model); 303 | self.program_draw 304 | .uniform_4f("u_draw_color", &(*color).into()); 305 | self.program_draw 306 | .uniform_1f("u_time", self.get_elapsed_seconds()); 307 | 308 | // Bind the color map, if available. 309 | if let Some(color_map) = color_map { 310 | self.program_draw.uniform_1i("u_use_color_map", true as i32); 311 | color_map.bind(0); 312 | } else { 313 | self.program_draw 314 | .uniform_1i("u_use_color_map", false as i32); 315 | } 316 | 317 | // Bind the alpha map, if available. 318 | if let Some(alpha_map) = alpha_map { 319 | self.program_draw.uniform_1i("u_use_alpha_map", true as i32); 320 | alpha_map.bind(1); 321 | } else { 322 | self.program_draw 323 | .uniform_1i("u_use_alpha_map", false as i32); 324 | } 325 | 326 | // Issue draw call. 327 | match params { 328 | DrawParams::Rectangle(_) => { 329 | self.program_draw.uniform_1ui("u_draw_mode", 0); 330 | self.draw_rect_inner(); 331 | } 332 | DrawParams::Line(data, mode, connectivity) => { 333 | self.program_draw 334 | .uniform_1ui("u_draw_mode", mode as u32 + 1); 335 | self.draw_line_inner(&data, connectivity); 336 | } 337 | } 338 | 339 | self.program_draw.unbind(); 340 | } 341 | 342 | /// Draws a rectangle. 343 | pub fn draw_rect_inner(&self) { 344 | unsafe { 345 | gl::VertexArrayVertexBuffer( 346 | self.vao, 347 | 0, 348 | self.vbo_rect, 349 | 0, 350 | (4 * mem::size_of::()) as i32, 351 | ); 352 | 353 | gl::BindVertexArray(self.vao); 354 | gl::DrawArrays(gl::TRIANGLES, 0, 6); 355 | } 356 | } 357 | 358 | /// Draws a line (or polyline segment). 359 | pub fn draw_line_inner(&self, data: &Vec, connectivity: LineConnectivity) { 360 | unsafe { 361 | // Upload the vertex data. 362 | let data_size = (data.len() * mem::size_of::()) as GLsizeiptr; 363 | gl::NamedBufferSubData(self.vbo_line, 0, data_size, data.as_ptr() as *const c_void); 364 | 365 | gl::VertexArrayVertexBuffer( 366 | self.vao, 367 | 0, 368 | self.vbo_line, 369 | 0, 370 | (4 * mem::size_of::()) as i32, 371 | ); 372 | 373 | let primitive = match connectivity { 374 | LineConnectivity::Segment => gl::LINES, 375 | LineConnectivity::Strip => gl::LINE_STRIP, 376 | }; 377 | 378 | gl::BindVertexArray(self.vao); 379 | gl::DrawArrays(primitive, 0, (data.len() / 4) as i32); 380 | } 381 | } 382 | 383 | /// Returns the number of seconds that have elapsed since the program 384 | /// was launched. 385 | fn get_elapsed_seconds(&self) -> f32 { 386 | let elapsed = self.time.elapsed().unwrap(); 387 | let milliseconds = elapsed.as_secs() * 1000 + elapsed.subsec_nanos() as u64 / 1_000_000; 388 | 389 | (milliseconds as f32) / 1000.0 390 | } 391 | } 392 | 393 | impl Drop for Renderer { 394 | fn drop(&mut self) { 395 | unsafe { 396 | gl::DeleteBuffers(1, &self.vbo_rect); 397 | gl::DeleteBuffers(1, &self.vbo_line); 398 | gl::DeleteVertexArrays(1, &self.vao); 399 | } 400 | } 401 | } 402 | -------------------------------------------------------------------------------- /src/shader_builder.rs: -------------------------------------------------------------------------------- 1 | use network::Network; 2 | use operator::{DomainType, Op, OpFamily, PrimitiveType}; 3 | use program::Program; 4 | 5 | use uuid::Uuid; 6 | 7 | pub struct ShaderBuilder { 8 | shader_code: String, 9 | } 10 | 11 | impl ShaderBuilder { 12 | pub fn new() -> ShaderBuilder { 13 | ShaderBuilder { 14 | shader_code: String::new(), 15 | } 16 | } 17 | 18 | /// Given a list of op indices in the proper post-order, builds 19 | /// and returns the appropriate shader code. 20 | pub fn build_sources(&mut self, network: &Network, indices: Vec) -> Option { 21 | static HEADER: &str = " 22 | #version 430 23 | 24 | layout (location = 0) in vec2 vs_texcoord; 25 | 26 | layout (location = 0) out vec4 o_color; 27 | 28 | uniform vec3 u_camera_position; 29 | uniform vec3 u_camera_front; 30 | uniform uint u_shading; 31 | uniform float u_time; 32 | 33 | // The SSBO that will contain a parameter vector for each op in 34 | // the graph. Note that according to the spec, there can only be 35 | // one array of variable size per SSBO. 36 | layout (std430, binding = 0) buffer params_block 37 | { 38 | vec4 params[]; 39 | }; 40 | 41 | const uint MAX_STEPS = 256u; 42 | const float MAX_TRACE_DISTANCE = 64.0; 43 | const float MIN_HIT_DISTANCE = 0.001; 44 | 45 | struct ray 46 | { 47 | vec3 o; 48 | vec3 d; 49 | }; 50 | 51 | struct result 52 | { 53 | float id; 54 | float total_distance; 55 | int total_steps; 56 | }; 57 | 58 | mat3 lookat(in vec3 t, in vec3 p) 59 | { 60 | vec3 k = normalize(t - p); 61 | vec3 i = cross(k, vec3(0.0, 1.0, 0.0)); 62 | vec3 j = cross(i, k); 63 | return mat3(i, j, k); 64 | } 65 | 66 | vec3 domain_twist(in vec3 p, float t) 67 | { 68 | float c = cos(t * p.y); 69 | float s = sin(t * p.y); 70 | mat2 m = mat2(c, -s, s, c); 71 | vec3 q = vec3(m * p.xz, p.y); 72 | return q; 73 | } 74 | 75 | vec3 domain_bend(in vec3 p, float t) 76 | { 77 | float c = cos(t * p.y); 78 | float s = sin(t * p.y); 79 | mat2 m = mat2(c, -s, s, c); 80 | vec3 q = vec3(m * p.xy, p.z); 81 | return q; 82 | } 83 | 84 | float op_union(float a, float b) 85 | { 86 | return min(a, b); 87 | } 88 | 89 | float op_subtract(float a, float b) 90 | { 91 | return max(-a, b); 92 | } 93 | 94 | float op_intersect(float a, float b) 95 | { 96 | return max(a, b); 97 | } 98 | 99 | float op_smooth_min(float a, float b, float k) 100 | { 101 | float h = clamp(0.5 + 0.5 * (b - a) / k, 0.0, 1.0); 102 | return mix(b, a, h) - k * h * (1.0 - h); 103 | } 104 | 105 | float sdf_sphere(in vec3 p, in vec3 center, float radius) 106 | { 107 | return length(center - p) - radius; 108 | } 109 | 110 | float sdf_box(in vec3 p, in vec3 b) 111 | { 112 | vec3 d = abs(p) - b; 113 | return min(max(d.x, max(d.y, d.z)), 0.0) + length(max(d, 0.0)); 114 | } 115 | 116 | float sdf_plane(in vec3 p, in float h) 117 | { 118 | return p.y - h; 119 | } 120 | 121 | float sdf_torus(in vec3 p, in vec2 t) 122 | { 123 | vec2 d = vec2(length(p.xz)- t.x, p.y); 124 | return length(d) - t.y; 125 | } 126 | 127 | vec2 map(in vec3 p) 128 | { 129 | // start of generated code 130 | "; 131 | 132 | static FOOTER: &str = " 133 | } 134 | 135 | vec3 calculate_normal(in vec3 p) 136 | { 137 | const vec3 e = vec3(0.001, 0.0, 0.0); 138 | vec3 n = vec3(map(p + e.xyy).y - map(p - e.xyy).y, // Gradient x 139 | map(p + e.yxy).y - map(p - e.yxy).y, // Gradient y 140 | map(p + e.yyx).y - map(p - e.yyx).y); // Gradient z 141 | 142 | return normalize(n); 143 | } 144 | 145 | float ambient_occlusion(in vec3 p, in vec3 n) 146 | { 147 | const float attenuation = 0.5; 148 | float ao; 149 | float accum = 0.0; 150 | float scale = 1.0; 151 | for(int step = 0; step < 5; step++) 152 | { 153 | float hr = 0.01 + 0.02 * float(step * step); 154 | vec3 aopos = n * hr + p; 155 | 156 | float dist = map(aopos).y; 157 | ao = -(dist - hr); 158 | accum += ao * scale; 159 | scale *= attenuation; 160 | } 161 | ao = 1.0 - clamp(accum, 0.0, 1.0); 162 | 163 | return ao; 164 | } 165 | 166 | result raymarch(in ray r) 167 | { 168 | result res = result(-1.0, 0.0, 0); 169 | for (int i = 0; i < MAX_STEPS; ++i) 170 | { 171 | vec3 p = r.o + r.d * res.total_distance; 172 | vec2 hit_info = map(p); 173 | float hit_id = hit_info.x; 174 | float hit_dist = hit_info.y; 175 | res.total_distance += hit_dist; 176 | 177 | if (hit_dist < MIN_HIT_DISTANCE) 178 | { 179 | res.id = hit_id; 180 | break; 181 | } 182 | 183 | if(res.total_distance > MAX_TRACE_DISTANCE) 184 | { 185 | res.total_distance = 0.0; 186 | break; 187 | } 188 | 189 | res.total_steps++; 190 | } 191 | return res; 192 | } 193 | 194 | const uint SHADING_DEPTH = 0; 195 | const uint SHADING_STEPS = 1; 196 | const uint SHADING_AMBIENT_OCCLUSION = 2; 197 | const uint SHADING_NORMALS = 3; 198 | const uint SHADING_DIFFUSE = 4; 199 | 200 | vec3 shading(in ray r, in result res) 201 | { 202 | vec3 hit = r.o + r.d * res.total_distance; 203 | if (u_shading == SHADING_DEPTH) 204 | { 205 | float depth = hit.z / MAX_TRACE_DISTANCE; 206 | return vec3(pow(depth, 0.5)); 207 | } 208 | else if (u_shading == SHADING_STEPS) 209 | { 210 | float pct = float(res.total_steps) / MAX_STEPS; 211 | const vec3 c_a = vec3(0.0, 0.0, 1.0); 212 | const vec3 c_b = vec3(0.0, 1.0, 1.0); 213 | const vec3 c_c = vec3(1.0, 1.0, 0.0); 214 | const vec3 c_d = vec3(1.0, 0.0, 0.0); 215 | 216 | const float a = 0.00; 217 | const float b = 0.33; 218 | const float c = 0.66; 219 | const float d = 1.00; 220 | 221 | vec3 color = mix(c_a, c_b, smoothstep(a, b, pct)); 222 | color = mix(color, c_c, smoothstep(b, c, pct)); 223 | color = mix(color, c_d, smoothstep(c, d, pct)); 224 | return color; 225 | } 226 | else 227 | { 228 | // calculate normals 229 | vec3 n = calculate_normal(hit); 230 | if (u_shading == SHADING_AMBIENT_OCCLUSION) 231 | { 232 | float ao = ambient_occlusion(hit, n); 233 | return vec3(pow(ao, 3.0)); 234 | } 235 | else if (u_shading == SHADING_NORMALS) 236 | { 237 | return n * 0.5 + 0.5; 238 | } 239 | else 240 | { 241 | const vec3 l = vec3(0.0, 2.0, 3.0); 242 | vec3 to_light = normalize(l - hit); 243 | float d = max(0.0, dot(n, to_light)); 244 | float ao = ambient_occlusion(hit, n); 245 | return vec3(d * pow(ao, 3.0)); 246 | } 247 | } 248 | } 249 | 250 | ray generate_ray() 251 | { 252 | // uv-coordinates in the range [-1..1] 253 | vec2 uv = vs_texcoord * 2.0 - 1.0; 254 | 255 | const float pi = 3.14159265359; 256 | const float fov = 50.0; 257 | const float fovx = pi * fov / 360.0; 258 | float fovy = fovx * 1.0; // iResolution.y/iResolution.x; 259 | float ulen = tan(fovx); 260 | float vlen = tan(fovy); 261 | 262 | const vec3 camera_up = vec3(0.0, 1.0, 0.0); 263 | vec2 cam_uv = uv; 264 | vec3 camera_right = normalize(cross(camera_up, u_camera_front)); 265 | vec3 pixel = u_camera_position + u_camera_front + camera_right * cam_uv.x * ulen + camera_up * cam_uv.y * vlen; 266 | 267 | vec3 ro = u_camera_position; 268 | vec3 rd = normalize(pixel - u_camera_position); 269 | 270 | return ray(ro, rd); 271 | } 272 | 273 | void main() 274 | { 275 | ray r = generate_ray(); 276 | result res = raymarch(r); 277 | 278 | const vec3 background = vec3(0.0); 279 | vec3 color = background; 280 | switch(int(res.id)) 281 | { 282 | case 0: 283 | color = shading(r, res); 284 | break; 285 | case 1: 286 | // Placeholder 287 | break; 288 | case 2: 289 | // Placeholder 290 | break; 291 | // etc... 292 | default: 293 | color = background; 294 | break; 295 | } 296 | 297 | o_color = vec4(color, 1.0); 298 | }"; 299 | 300 | // Clear the cached shader code (if there was any). 301 | self.shader_code = String::new(); 302 | 303 | // Build the `map` function by traversing the graph of ops. 304 | for index in indices { 305 | if let Some(node) = network.graph.get_node(index) { 306 | let mut formatted = match node.data.family { 307 | OpFamily::Domain(domain) => match domain { 308 | // Root operators have no inputs. 309 | DomainType::Root => node.data.get_code(None, None), 310 | 311 | // All other domain operators have a single input. 312 | _ => { 313 | if network.graph.edges[index].inputs.len() < 1 { 314 | return None; 315 | } 316 | let a = network.graph.edges[index].inputs[0]; 317 | node.data 318 | .get_code(Some(&network.graph.get_node(a).unwrap().data.name), None) 319 | } 320 | }, 321 | 322 | OpFamily::Primitive(primitive) => match primitive { 323 | // All generators have a single input, corresponding to 324 | // their (potentially transformed) root. 325 | PrimitiveType::Sphere 326 | | PrimitiveType::Box 327 | | PrimitiveType::Plane 328 | | PrimitiveType::Torus => { 329 | if network.graph.edges[index].inputs.len() < 1 { 330 | return None; 331 | } 332 | let a = network.graph.edges[index].inputs[0]; 333 | node.data 334 | .get_code(Some(&network.graph.get_node(a).unwrap().data.name), None) 335 | } 336 | 337 | // All combinators have two inputs. 338 | PrimitiveType::Union 339 | | PrimitiveType::Subtraction 340 | | PrimitiveType::Intersection 341 | | PrimitiveType::SmoothMinimum => { 342 | // If this operator doesn't have at least 2 inputs, 343 | // then we exit early, since this isn't a valid 344 | // shader graph. 345 | if network.graph.edges[index].inputs.len() < 2 { 346 | return None; 347 | } 348 | 349 | let a = network.graph.edges[index].inputs[0]; 350 | let b = network.graph.edges[index].inputs[1]; 351 | node.data.get_code( 352 | Some(&network.graph.get_node(a).unwrap().data.name), 353 | Some(&network.graph.get_node(b).unwrap().data.name), 354 | ) 355 | } 356 | 357 | // The render operator only has a single input. 358 | PrimitiveType::Render => { 359 | // If this operator doesn't have at least 1 input, 360 | // then we exit early, since this isn't a valid 361 | // shader graph. 362 | if network.graph.edges[index].inputs.len() < 1 { 363 | return None; 364 | } 365 | 366 | let a = network.graph.edges[index].inputs[0]; 367 | let mut code = node.data.get_code( 368 | Some(&network.graph.get_node(a).unwrap().data.name), 369 | None, 370 | ); 371 | 372 | // Add the final `return` in the `map(..)` function. 373 | code.push('\n'); 374 | code.push('\t'); 375 | code.push_str(&format!("return vec2(0.0, {});", &node.data.name)); 376 | code 377 | } 378 | }, 379 | }; 380 | 381 | // Add a tab indent before each new line of shader code and a newline 382 | // character after. 383 | self.shader_code.push('\t'); 384 | self.shader_code.push_str(&formatted); 385 | self.shader_code.push('\n'); 386 | } 387 | } 388 | 389 | let mut fs_src = String::new(); 390 | fs_src.push_str(HEADER); 391 | fs_src.push_str(&self.shader_code[..]); 392 | fs_src.push_str(FOOTER); 393 | println!("Final shader code:"); 394 | println!("{}", self.shader_code); 395 | 396 | let vs_src = " 397 | #version 430 398 | 399 | layout(location = 0) in vec2 position; 400 | layout(location = 1) in vec2 texcoord; 401 | layout (location = 0) out vec2 vs_texcoord; 402 | 403 | uniform mat4 u_model_matrix; 404 | uniform mat4 u_projection_matrix; 405 | 406 | void main() { 407 | vs_texcoord = texcoord; 408 | 409 | gl_Position = u_projection_matrix * u_model_matrix * vec4(position, 0.0, 1.0); 410 | }" 411 | .to_string(); 412 | 413 | Program::new(vs_src, fs_src) 414 | } 415 | } 416 | -------------------------------------------------------------------------------- /src/texture.rs: -------------------------------------------------------------------------------- 1 | use gl; 2 | use gl::types::*; 3 | use image::{self, GenericImage}; 4 | use cgmath::{self, Vector2}; 5 | 6 | use std::fs::File; 7 | use std::path::Path; 8 | use std::os::raw::c_void; 9 | 10 | pub struct Texture { 11 | pixels: Vec, 12 | 13 | resolution: Vector2, 14 | 15 | id: GLuint, 16 | } 17 | 18 | impl Texture { 19 | pub fn new(path: &Path) -> Texture { 20 | let image = image::open(path).unwrap().to_rgba(); 21 | let (w, h) = image.dimensions(); 22 | let pixels: Vec = image.into_raw(); 23 | 24 | let mut id = 0; 25 | unsafe { 26 | // Create the texture and set parameters. 27 | gl::CreateTextures(gl::TEXTURE_2D, 1, &mut id); 28 | gl::TextureParameteri(id, gl::TEXTURE_MIN_FILTER, gl::LINEAR_MIPMAP_LINEAR as i32); 29 | gl::TextureParameteri(id, gl::TEXTURE_MAG_FILTER, gl::LINEAR as i32); 30 | gl::TextureParameteri(id, gl::TEXTURE_WRAP_S, gl::CLAMP_TO_EDGE as i32); 31 | gl::TextureParameteri(id, gl::TEXTURE_WRAP_T, gl::CLAMP_TO_EDGE as i32); 32 | 33 | // Allocate storage. 34 | gl::TextureStorage2D(id, 1, gl::RGBA8, w as i32, h as i32); 35 | gl::TextureSubImage2D( 36 | id, 37 | 0, 38 | 0, 39 | 0, 40 | w as i32, 41 | h as i32, 42 | gl::RGBA, 43 | gl::UNSIGNED_BYTE, 44 | pixels.as_ptr() as *const c_void, 45 | ); 46 | } 47 | 48 | let tex = Texture { 49 | pixels, 50 | resolution: Vector2::new(w as f32, h as f32), 51 | id, 52 | }; 53 | tex.generate_mip_maps(); 54 | tex 55 | } 56 | 57 | pub fn bind(&self, unit: u32) { 58 | unsafe { 59 | gl::BindTextureUnit(unit, self.id); 60 | } 61 | } 62 | 63 | pub fn unbind(&self, unit: u32) { 64 | unsafe { 65 | gl::BindTextureUnit(unit, 0); 66 | } 67 | } 68 | 69 | pub fn generate_mip_maps(&self) { 70 | unsafe { 71 | gl::GenerateTextureMipmap(self.id); 72 | } 73 | } 74 | } 75 | --------------------------------------------------------------------------------