1use crate::material_graph::NodeSpec;
5
6const COORD: &str = "vec3<f32>(in.uv, 0.0)";
7const ZERO: &str = "vec3<f32>(0.0)";
8const ONE: &str = "vec3<f32>(1.0)";
9const HALF: &str = "vec3<f32>(0.5)";
10const WORLD: &str = "in.world_pos";
11
12macro_rules! node {
13 ($kind:expr, $cat:expr, [$($i:expr),*], [$($p:expr),*], $expr:expr) => {
14 NodeSpec { kind: $kind, category: $cat, inputs: &[$($i),*], params: &[$($p),*], expr: $expr, color: false }
15 };
16}
17
18pub const MORE_SPECS: &[NodeSpec] = &[
20 NodeSpec {
22 kind: "Shader output",
23 category: "Output",
24 inputs: &[("Color", "vec3<f32>(0.8)"), ("Alpha", ONE), ("Emission", ZERO), ("Light amount", ONE), ("Roughness", "vec3<f32>(0.6)"), ("Vertex offset", ZERO)],
25 params: &[],
26 expr: "",
27 color: false,
28 },
29 NodeSpec { kind: "Effect output", category: "Output", inputs: &[("Color", "color")], params: &[], expr: "", color: false },
30 node!("Reroute", "Utility", [("Value", ZERO)], [], "{0}"),
32 node!("Comment", "Utility", [], [], "vec3<f32>(0.0)"),
33 node!("Inspector number", "Utility", [], [("slot 0-7 (user N)", 0.0, 0.0, 7.0), ("default", 1.0, -1000.0, 1000.0), ("zero uses default (0/1)", 1.0, 0.0, 1.0)], "vec3<f32>(0.0)"),
34 node!("Time scaled", "Utility", [], [("speed", 1.0, -50.0, 50.0)], "vec3<f32>(globals.time.x * {p0})"),
35 node!("Pulse", "Utility", [], [("hertz", 1.0, 0.0, 20.0)], "vec3<f32>(0.5 + 0.5 * sin(globals.time.x * {p0} * 6.2831853))"),
36 node!("View direction", "Geometry", [], [], "view"),
38 node!("Reflection", "Geometry", [], [], "reflect(-view, n)"),
39 node!("Sky reflection", "Light", [], [], "env_color(reflect(-view, n))"),
40 node!("Sun direction", "Light", [], [], "normalize(globals.sun_dir.xyz)"),
41 node!("Sun colour", "Light", [], [], "globals.sun_color.rgb"),
42 node!("Ambient light", "Light", [], [], "globals.ambient.rgb"),
43 node!("Shadow", "Light", [], [], "vec3<f32>(shadow_factor(in.world_pos, n))"),
44 node!("Lighting", "Light", [("Base", "vec3<f32>(0.8)")], [("roughness", 0.6, 0.0, 1.0)], "lighting(in.world_pos, n, {0}, {p0})"),
45 node!("Camera position", "Geometry", [], [], "globals.camera_pos.xyz"),
46 node!("Object position", "Geometry", [], [], "object.model[3].xyz"),
47 node!("Object random", "Geometry", [], [], "vec3<f32>(hash31(object.model[3].xyz))"),
48 node!("Screen position", "Geometry", [], [], "vec3<f32>(in.clip.xy * globals.viewport.zw, 0.0)"),
49 node!("Screen color", "Effect", [("Coords", COORD)], [], "scene_color(({0}).xy)"),
51 node!("Pixel colour", "Effect", [], [], "color"),
52 node!("Pixel size", "Effect", [], [], "vec3<f32>(pixel_size(), 0.0)"),
53 node!("Aspect ratio", "Effect", [], [], "vec3<f32>(screen_aspect())"),
54 node!("Blur", "Effect", [("Coords", COORD)], [("radius px", 2.0, 0.0, 30.0)], "((scene_color(({0}).xy) + scene_color(({0}).xy + vec2<f32>({p0}, 0.0) * pixel_size()) + scene_color(({0}).xy - vec2<f32>({p0}, 0.0) * pixel_size()) + scene_color(({0}).xy + vec2<f32>(0.0, {p0}) * pixel_size()) + scene_color(({0}).xy - vec2<f32>(0.0, {p0}) * pixel_size())) / 5.0)"),
55 node!("Chromatic split", "Effect", [("Coords", COORD)], [("amount px", 3.0, 0.0, 30.0)], "vec3<f32>(scene_color(({0}).xy + vec2<f32>({p0}, 0.0) * pixel_size()).r, scene_color(({0}).xy).g, scene_color(({0}).xy - vec2<f32>({p0}, 0.0) * pixel_size()).b)"),
56 node!("Edge detect", "Effect", [("Coords", COORD)], [("strength", 4.0, 0.0, 20.0)], "vec3<f32>(clamp((abs(lib_luma(scene_color(({0}).xy + vec2<f32>(pixel_size().x, 0.0))) - lib_luma(scene_color(({0}).xy - vec2<f32>(pixel_size().x, 0.0)))) + abs(lib_luma(scene_color(({0}).xy + vec2<f32>(0.0, pixel_size().y))) - lib_luma(scene_color(({0}).xy - vec2<f32>(0.0, pixel_size().y))))) * {p0}, 0.0, 1.0))"),
57 node!("Pixelate", "Effect", [("Coords", COORD)], [("cells across", 160.0, 8.0, 1000.0)], "scene_color(floor(({0}).xy * {p0}) / {p0})"),
58 node!("Vignette", "Effect", [("Coords", COORD)], [("strength", 0.4, 0.0, 2.0)], "vec3<f32>(lib_vignette(({0}).xy, {p0}))"),
59 node!("Scanlines", "Effect", [("Coords", COORD)], [("lines", 300.0, 10.0, 2000.0)], "vec3<f32>(0.5 + 0.5 * sin(({0}).y * {p0} * 3.1415926))"),
60 node!("Negate", "Math", [("Value", ZERO)], [], "(-{0})"),
62 node!("Square root", "Math", [("Value", HALF)], [], "sqrt(max({0}, vec3<f32>(0.0)))"),
63 node!("Floor", "Math", [("Value", HALF)], [], "floor({0})"),
64 node!("Ceil", "Math", [("Value", HALF)], [], "ceil({0})"),
65 node!("Round", "Math", [("Value", HALF)], [], "round({0})"),
66 node!("Sign", "Math", [("Value", ZERO)], [], "sign({0})"),
67 node!("Saturate", "Math", [("Value", ZERO)], [], "clamp({0}, vec3<f32>(0.0), vec3<f32>(1.0))"),
68 node!("Modulo", "Math", [("A", ZERO), ("B", ONE)], [], "({0} - {1} * floor({0} / max({1}, vec3<f32>(0.0001))))"),
69 node!("Multiply add", "Math", [("A", ONE), ("B", ONE), ("C", ZERO)], [], "({0} * {1} + {2})"),
70 node!("Cosine", "Math", [("Value", ZERO)], [("frequency", 1.0, 0.0, 100.0)], "(cos({0} * {p0}) * 0.5 + vec3<f32>(0.5))"),
71 node!("Triangle wave", "Math", [("Value", ZERO)], [("frequency", 1.0, 0.0, 100.0)], "(abs(fract({0} * {p0}) - vec3<f32>(0.5)) * 2.0)"),
72 node!("Sawtooth", "Math", [("Value", ZERO)], [("frequency", 1.0, 0.0, 100.0)], "fract({0} * {p0})"),
73 node!("Square wave", "Math", [("Value", ZERO)], [("frequency", 1.0, 0.0, 100.0)], "step(vec3<f32>(0.5), fract({0} * {p0}))"),
74 node!("Posterize", "Math", [("Value", HALF)], [("steps", 4.0, 2.0, 64.0)], "(floor({0} * {p0} + vec3<f32>(0.5)) / {p0})"),
75 node!("Greater than", "Math", [("A", ZERO), ("B", HALF)], [], "select(vec3<f32>(0.0), vec3<f32>(1.0), {0} > {1})"),
76 node!("Branch", "Math", [("A", ZERO), ("B", HALF), ("If A > B", ONE), ("Otherwise", ZERO)], [], "select({3}, {2}, {0} > {1})"),
77 node!("Red", "Vector", [("Vector", ZERO)], [], "vec3<f32>(({0}).x)"),
79 node!("Green", "Vector", [("Vector", ZERO)], [], "vec3<f32>(({0}).y)"),
80 node!("Blue", "Vector", [("Vector", ZERO)], [], "vec3<f32>(({0}).z)"),
81 node!("Combine", "Vector", [("R", ZERO), ("G", ZERO), ("B", ZERO)], [], "vec3<f32>(({0}).x, ({1}).x, ({2}).x)"),
82 node!("Length", "Vector", [("Vector", ZERO)], [], "vec3<f32>(length({0}))"),
83 node!("Distance", "Vector", [("A", ZERO), ("B", ZERO)], [], "vec3<f32>(distance({0}, {1}))"),
84 node!("Dot", "Vector", [("A", ZERO), ("B", ZERO)], [], "vec3<f32>(dot({0}, {1}))"),
85 node!("Cross", "Vector", [("A", ZERO), ("B", ZERO)], [], "cross({0}, {1})"),
86 node!("Normalize", "Vector", [("Vector", "vec3<f32>(0.0, 1.0, 0.0)")], [], "({0} / max(length({0}), 0.00001))"),
87 node!("Reflect", "Vector", [("Incident", "vec3<f32>(0.0, -1.0, 0.0)"), ("Normal", "vec3<f32>(0.0, 1.0, 0.0)")], [], "reflect({0}, normalize({1} + vec3<f32>(0.00001)))"),
88 node!("Rotate coords", "Vector", [("Coords", COORD)], [("angle", 0.0, -6.3, 6.3)], "vec3<f32>(lib_rot2(({0}).xy - vec2<f32>(0.5), {p0}) + vec2<f32>(0.5), 0.0)"),
89 node!("Scale offset coords", "Vector", [("Coords", COORD)], [("scale x", 1.0, 0.01, 100.0), ("scale y", 1.0, 0.01, 100.0), ("offset x", 0.0, -100.0, 100.0), ("offset y", 0.0, -100.0, 100.0)], "vec3<f32>((({0}).xy - vec2<f32>(0.5)) * vec2<f32>({p0}, {p1}) + vec2<f32>(0.5) + vec2<f32>({p2}, {p3}), 0.0)"),
90 node!("Scroll coords", "Vector", [("Coords", COORD)], [("speed x", 0.1, -10.0, 10.0), ("speed y", 0.0, -10.0, 10.0)], "vec3<f32>(({0}).xy + vec2<f32>({p0}, {p1}) * globals.time.x, 0.0)"),
91 node!("Polar coords", "Vector", [("Coords", COORD)], [], "vec3<f32>(lib_polar(({0}).xy), 0.0)"),
92 node!("Mirror tile", "Vector", [("Coords", COORD)], [("tiles", 2.0, 0.1, 50.0)], "vec3<f32>(abs(fract(({0}).xy * {p0} * 0.5) * 2.0 - vec2<f32>(1.0)), 0.0)"),
93 node!("Flipbook", "Vector", [("Coords", COORD)], [("columns", 4.0, 1.0, 32.0), ("rows", 4.0, 1.0, 32.0), ("frames per second", 8.0, 0.0, 60.0)], "vec3<f32>(lib_flipbook(({0}).xy, {p0}, {p1}, floor(globals.time.x * {p2})), 0.0)"),
94 node!("Distort coords", "Vector", [("Coords", COORD), ("Amount", "vec3<f32>(0.05)")], [("scale", 4.0, 0.1, 100.0)], "({0} + vec3<f32>(lib_noise(({0}).xy * {p0}) - 0.5, lib_noise(({0}).xy * {p0} + vec2<f32>(7.3, 1.7)) - 0.5, 0.0) * ({1}).x)"),
95 node!("Normal map", "Normal", [("Map", "vec3<f32>(0.5, 0.5, 1.0)"), ("UV", COORD)], [("strength", 1.0, 0.0, 4.0)], "lib_normal_map(n, in.world_pos, ({1}).xy, {0}, {p0})"),
96 node!("Bump from height", "Normal", [("Height", ZERO)], [("strength", 1.0, 0.0, 10.0)], "lib_bump(({0}).x, in.world_pos, n, {p0})"),
97 node!("Overlay", "Colour", [("Base", HALF), ("Blend", HALF)], [], "lib_overlay(clamp({0}, vec3<f32>(0.0), vec3<f32>(1.0)), {1})"),
99 node!("Screen blend", "Colour", [("Base", HALF), ("Blend", HALF)], [], "lib_screen({0}, {1})"),
100 node!("Colourize", "Colour", [("Value", HALF), ("Tint", "vec3<f32>(1.0, 0.7, 0.4)")], [], "(vec3<f32>(lib_luma({0})) * {1})"),
101 node!("Ramp 3 colours", "Colour", [("Position", HALF), ("Low", "vec3<f32>(0.02, 0.03, 0.15)"), ("Middle", "vec3<f32>(0.8, 0.2, 0.3)"), ("High", "vec3<f32>(1.0, 0.9, 0.4)")], [], "mix(mix({1}, {2}, clamp(({0}).x * 2.0, 0.0, 1.0)), {3}, clamp(({0}).x * 2.0 - 1.0, 0.0, 1.0))"),
102 node!("Gamma", "Colour", [("Colour", HALF)], [("gamma", 2.2, 0.1, 5.0)], "pow(max({0}, vec3<f32>(0.0)), vec3<f32>(1.0 / {p0}))"),
103 node!("Exposure", "Colour", [("Colour", HALF)], [("stops", 0.0, -8.0, 8.0)], "({0} * exp2({p0}))"),
104 node!("Levels", "Colour", [("Colour", HALF)], [("black", 0.0, 0.0, 1.0), ("white", 1.0, 0.0, 2.0)], "clamp(({0} - vec3<f32>({p0})) / vec3<f32>({p1} - {p0} + 0.0001), vec3<f32>(0.0), vec3<f32>(1.0))"),
105 node!("Threshold", "Colour", [("Colour", HALF)], [("level", 0.5, 0.0, 1.0)], "vec3<f32>(step({p0}, lib_luma({0})))"),
106 node!("Tone map", "Colour", [("Colour", HALF)], [], "lib_tonemap({0})"),
107 node!("Noise 3D", "Noise", [("Coords", WORLD)], [("scale", 2.0, 0.01, 100.0)], "vec3<f32>(fbm3({0} * {p0}, 4))"),
109 node!("Ridged noise", "Noise", [("Coords", WORLD)], [("scale", 2.0, 0.01, 100.0)], "vec3<f32>(lib_ridged(({0}).xz * {p0}))"),
110 node!("Turbulence", "Noise", [("Coords", WORLD)], [("scale", 2.0, 0.01, 100.0)], "vec3<f32>(lib_turbulence(({0}).xz * {p0}))"),
111 node!("White noise", "Noise", [("Coords", COORD)], [("cells", 64.0, 1.0, 1000.0)], "vec3<f32>(lib_hash21(floor(({0}).xy * {p0})))"),
112 node!("Sparkle", "Noise", [("Coords", COORD)], [("cells", 80.0, 4.0, 1000.0), ("density", 0.03, 0.0, 1.0)], "vec3<f32>(step(1.0 - {p1}, lib_hash21(floor(({0}).xy * {p0}))))"),
113 node!("Caustics", "Procedural", [("Coords", WORLD)], [("scale", 1.5, 0.05, 50.0), ("speed", 0.6, 0.0, 10.0)], "vec3<f32>(lib_caustic(({0}).xz * {p0}, globals.time.x * {p1}))"),
114 node!("Hexagons", "Procedural", [("Coords", COORD)], [("scale", 6.0, 0.5, 100.0), ("edge width", 0.06, 0.0, 0.5)], "vec3<f32>(smoothstep(0.5 - {p1}, 0.5, lib_hex(({0}).xy * {p0})))"),
115 node!("Wave rings", "Procedural", [("Coords", COORD)], [("frequency", 20.0, 1.0, 200.0), ("speed", 2.0, -20.0, 20.0)], "vec3<f32>(sin(length(({0}).xy - vec2<f32>(0.5)) * {p0} - globals.time.x * {p1}) * 0.5 + 0.5)"),
116 node!("Linear gradient", "Procedural", [("Coords", COORD)], [], "vec3<f32>(clamp(({0}).x, 0.0, 1.0))"),
117 node!("Radial gradient", "Procedural", [("Coords", COORD)], [], "vec3<f32>(clamp(1.0 - length(({0}).xy - vec2<f32>(0.5)) * 2.0, 0.0, 1.0))"),
118 node!("Angular gradient", "Procedural", [("Coords", COORD)], [], "vec3<f32>(atan2(({0}).y - 0.5, ({0}).x - 0.5) / 6.2831853 + 0.5)"),
119 node!("Circle", "Procedural", [("Coords", COORD)], [("radius", 0.3, 0.0, 1.0), ("softness", 0.02, 0.0, 0.5)], "vec3<f32>(1.0 - smoothstep({p0}, {p0} + {p1} + 0.0001, length(({0}).xy - vec2<f32>(0.5))))"),
120 node!("Box", "Procedural", [("Coords", COORD)], [("width", 0.6, 0.0, 1.0), ("height", 0.4, 0.0, 1.0), ("softness", 0.02, 0.0, 0.5)], "vec3<f32>(1.0 - smoothstep(0.0, {p2} + 0.0001, lib_sdf_box(({0}).xy - vec2<f32>(0.5), vec2<f32>({p0}, {p1}) * 0.5)))"),
121 node!("Wood rings", "Procedural", [("Coords", WORLD)], [("scale", 4.0, 0.1, 50.0), ("wobble", 0.3, 0.0, 2.0)], "vec3<f32>(lib_wood({0} * {p0}, {p1}))"),
122 node!("Marble veins", "Procedural", [("Coords", WORLD)], [("scale", 1.5, 0.05, 20.0), ("warp", 4.0, 0.0, 20.0)], "vec3<f32>(sin((({0}).x * {p0} + fbm3({0} * {p0}, 5) * {p1}) * 6.2831853) * 0.5 + 0.5)"),
123];
124
125pub const MESH_ONLY: &[&str] = &[
127 "Texture", "World position", "Normal", "Height", "Object colour", "Fresnel", "Slope mask", "Sun light", "Camera distance", "View direction", "Reflection", "Sky reflection", "Sun direction", "Sun colour",
128 "Ambient light", "Shadow", "Lighting", "Camera position", "Object position", "Object random", "Screen position", "Normal map", "Bump from height", "Noise 3D", "Ridged noise", "Turbulence", "Caustics", "Wood rings",
129 "Marble veins",
130];
131
132pub const EFFECT_ONLY: &[&str] = &["Screen color", "Pixel colour", "Pixel size", "Aspect ratio", "Blur", "Chromatic split", "Edge detect", "Pixelate", "Vignette", "Scanlines"];
134
135pub fn help(kind: &str) -> &'static str {
137 match kind {
138 "Output" => "The material result: colour, roughness, metal, glow, opacity, occlusion and a normal.",
139 "Shader output" => "The shader result. Light amount 0 = flat colour, 1 = fully lit. Vertex offset moves vertices in world space (waves, wind).",
140 "Effect output" => "The final colour of the camera image.",
141 "Color" => "A constant colour.",
142 "Value" => "A constant number.",
143 "Texture" => "Samples one of the material images (slot 0 albedo, 1 normal, 2 ORM).",
144 "UV" => "Texture coordinates of the surface (0..1).",
145 "World position" => "Position of the pixel in the world.",
146 "Normal" => "Direction the surface faces.",
147 "Height" => "World height (Y) of the pixel.",
148 "Time" => "Seconds since the game started.",
149 "Fresnel" => "Brighter at grazing angles (rim light, glass, water edges).",
150 "Reroute" => "A tiny pass-through point to tidy long links.",
151 "Comment" => "A frame with a note. Nodes inside it move with it.",
152 "Inspector number" => "A number you can change per object in the Inspector (MeshRenderer user 1..8) or from a script.",
153 "Time scaled" => "Time multiplied by a speed.",
154 "Pulse" => "A smooth 0..1 wave over time.",
155 "Screen color" => "Colour of the camera image at the given coordinates (blur, distortion).",
156 "Pixel colour" => "The current pixel of the camera image.",
157 "Blur" => "Cheap 5-tap blur of the camera image.",
158 "Chromatic split" => "Shifts the red and blue channels apart.",
159 "Edge detect" => "White where the picture changes sharply.",
160 "Pixelate" => "Blocky retro look.",
161 "Vignette" => "Darkens the screen edges.",
162 "Scanlines" => "CRT-like horizontal lines.",
163 "Mix" => "Blends A and B by Factor (0 = A, 1 = B).",
164 "Branch" => "If A > B returns the third input, otherwise the fourth.",
165 "Combine" => "Builds a vector from the red channels of three inputs.",
166 "Normal map" => "Turns a normal-map colour into a surface direction. Connect the result to Output > Normal.",
167 "Bump from height" => "Makes bumps from a height value. Connect the result to Output > Normal.",
168 "Flipbook" => "Plays a grid of frames (sprite sheet) as animation.",
169 "Distort coords" => "Wobbles coordinates with noise (heat haze, water).",
170 "Ramp 3 colours" => "Maps 0..1 to a three-colour gradient.",
171 "Voronoi" => "Cell pattern: distance to the nearest random point.",
172 "Caustics" => "Moving light patterns like under water.",
173 "Hexagons" => "Honeycomb pattern edges.",
174 "Wood rings" => "Tree-ring pattern for wood.",
175 "Marble veins" => "Wavy veins for marble.",
176 "Lighting" => "Applies scene lighting (sun, shadows, lamps) to a colour.",
177 "Shadow" => "1 in light, 0 in the sun shadow.",
178 "Sky reflection" => "What the sky looks like in the mirror direction.",
179 _ => "",
180 }
181}
182
183pub fn help_for(kind: &str, russian: bool) -> &'static str {
185 if let Some(h) = crate::material_nodes_v4::HELP.iter().find(|h| h.0 == kind) {
186 return if russian { h.2 } else { h.1 };
187 }
188 crate::material_nodes_ext::help(kind, russian).unwrap_or_else(|| help(kind))
189}