1use std::collections::{HashMap, HashSet, VecDeque};
6use std::fmt;
7
8#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
11pub enum ShaderValueType {
12 Float,
13 Vec2,
14 Vec3,
15 Vec4,
16 Mat2,
17 Mat3,
18 Mat4,
19 Int,
20 IVec2,
21 IVec3,
22 IVec4,
23 UInt,
24 Bool,
25 Sampler2D,
26 SamplerCube,
27 Sampler2DArray,
28 Sampler3D,
29 Void,
30}
31
32impl ShaderValueType {
33 pub fn glsl_type(&self) -> &'static str {
34 match self {
35 Self::Float => "float",
36 Self::Vec2 => "vec2",
37 Self::Vec3 => "vec3",
38 Self::Vec4 => "vec4",
39 Self::Mat2 => "mat2",
40 Self::Mat3 => "mat3",
41 Self::Mat4 => "mat4",
42 Self::Int => "int",
43 Self::IVec2 => "ivec2",
44 Self::IVec3 => "ivec3",
45 Self::IVec4 => "ivec4",
46 Self::UInt => "uint",
47 Self::Bool => "bool",
48 Self::Sampler2D => "sampler2D",
49 Self::SamplerCube => "samplerCube",
50 Self::Sampler2DArray => "sampler2DArray",
51 Self::Sampler3D => "sampler3D",
52 Self::Void => "void",
53 }
54 }
55
56 pub fn component_count(&self) -> usize {
57 match self {
58 Self::Float | Self::Int | Self::UInt | Self::Bool => 1,
59 Self::Vec2 | Self::IVec2 => 2,
60 Self::Vec3 | Self::IVec3 => 3,
61 Self::Vec4 | Self::IVec4 => 4,
62 Self::Mat2 => 4,
63 Self::Mat3 => 9,
64 Self::Mat4 => 16,
65 _ => 0,
66 }
67 }
68
69 pub fn is_numeric(&self) -> bool {
70 matches!(self, Self::Float | Self::Vec2 | Self::Vec3 | Self::Vec4
71 | Self::Int | Self::IVec2 | Self::IVec3 | Self::IVec4 | Self::UInt)
72 }
73
74 pub fn is_texture(&self) -> bool {
75 matches!(self, Self::Sampler2D | Self::SamplerCube
76 | Self::Sampler2DArray | Self::Sampler3D)
77 }
78
79 pub fn can_connect_to(&self, target: ShaderValueType) -> bool {
80 if *self == target { return true; }
81 if *self == Self::Float && matches!(target, Self::Vec2 | Self::Vec3 | Self::Vec4) {
83 return true;
84 }
85 if *self == Self::Vec3 && target == Self::Vec4 { return true; }
87 if *self == Self::Int && target == Self::Float { return true; }
89 false
90 }
91
92 pub fn coerce_expr(&self, target: ShaderValueType, expr: &str) -> String {
93 if *self == target { return expr.to_string(); }
94 match (*self, target) {
95 (Self::Float, Self::Vec2) => format!("vec2({})", expr),
96 (Self::Float, Self::Vec3) => format!("vec3({})", expr),
97 (Self::Float, Self::Vec4) => format!("vec4({}, 1.0)", expr),
98 (Self::Vec3, Self::Vec4) => format!("vec4({}, 1.0)", expr),
99 (Self::Int, Self::Float) => format!("float({})", expr),
100 _ => expr.to_string(),
101 }
102 }
103
104 pub fn default_value(&self) -> &'static str {
105 match self {
106 Self::Float => "0.0",
107 Self::Vec2 => "vec2(0.0)",
108 Self::Vec3 => "vec3(0.0)",
109 Self::Vec4 => "vec4(0.0)",
110 Self::Int => "0",
111 Self::Bool => "false",
112 _ => "0.0",
113 }
114 }
115}
116
117impl fmt::Display for ShaderValueType {
118 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
119 write!(f, "{}", self.glsl_type())
120 }
121}
122
123#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
126pub struct ShaderNodeId(pub u32);
127
128#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
129pub struct ShaderPortId {
130 pub node: ShaderNodeId,
131 pub port: u16,
132 pub is_output: bool,
133}
134
135impl ShaderPortId {
136 pub fn input(node: ShaderNodeId, port: u16) -> Self {
137 Self { node, port, is_output: false }
138 }
139 pub fn output(node: ShaderNodeId, port: u16) -> Self {
140 Self { node, port, is_output: true }
141 }
142}
143
144#[derive(Debug, Clone)]
145pub struct PortDef {
146 pub name: &'static str,
147 pub value_type: ShaderValueType,
148 pub optional: bool,
149 pub default_expr: Option<String>,
150}
151
152impl PortDef {
153 pub fn required(name: &'static str, ty: ShaderValueType) -> Self {
154 Self { name, value_type: ty, optional: false, default_expr: None }
155 }
156 pub fn optional(name: &'static str, ty: ShaderValueType, default: &str) -> Self {
157 Self { name, value_type: ty, optional: true, default_expr: Some(default.to_string()) }
158 }
159}
160
161#[derive(Debug, Clone)]
162pub struct ShaderConnection {
163 pub from: ShaderPortId,
164 pub to: ShaderPortId,
165}
166
167#[derive(Debug, Clone)]
170pub enum ShaderNodeKind {
171 ConstFloat(f32),
173 ConstVec2([f32; 2]),
174 ConstVec3([f32; 3]),
175 ConstVec4([f32; 4]),
176 ConstInt(i32),
177 ConstBool(bool),
178 Time,
179 Resolution,
180 FragCoord,
181 VertexNormal,
182 VertexTangent,
183 VertexUV,
184 VertexUV2,
185 VertexColor,
186 WorldPosition,
187 ViewDirection,
188 CameraPosition,
189 ModelMatrix,
190 ViewMatrix,
191 ProjectionMatrix,
192 NormalMatrix,
193 CustomUniformFloat { name: String },
194 CustomUniformVec3 { name: String },
195 CustomUniformVec4 { name: String },
196 CustomUniformSampler2D { name: String },
197
198 Add,
200 Subtract,
201 Multiply,
202 Divide,
203 Power,
204 SquareRoot,
205 AbsoluteValue,
206 Negate,
207 OneMinus,
208 Reciprocal,
209 Floor,
210 Ceiling,
211 Round,
212 Fraction,
213 Sign,
214 Modulo,
215 Min,
216 Max,
217 Clamp,
218 Saturate,
219 Lerp,
220 SmoothStep,
221 Step,
222
223 Sin,
225 Cos,
226 Tan,
227 Asin,
228 Acos,
229 Atan,
230 Atan2,
231 Degrees,
232 Radians,
233
234 Exp,
236 Exp2,
237 Log,
238 Log2,
239
240 Dot,
242 Cross,
243 Length,
244 Normalize,
245 Reflect,
246 Refract,
247 FaceForward,
248 Distance,
249 Mix,
250 VectorSplit, VectorMerge, VectorSwizzle { swizzle: [u8; 4], out_count: u8 },
253
254 MatrixMultiply,
256 MatrixTranspose,
257 MatrixInverse,
258 TransformPoint,
259 TransformVector,
260 TransformNormal,
261
262 SampleTexture2D,
264 SampleTextureCube,
265 SampleTexture2DLod,
266 SampleTexture2DGrad,
267 SampleNormalMap,
268 TextureSize,
269
270 ColorToLinear,
272 ColorToGamma,
273 HsvToRgb,
274 RgbToHsv,
275 Luminance,
276 ColorBalance,
277 Hue,
278 Saturation,
279 Brightness,
280
281 FresnelSchlick,
283 GGXDistribution,
284 SmithGeometry,
285 BRDFSpecular,
286 BRDFDiffuse,
287 EnvBRDFApprox,
288 SubsurfaceApprox,
289 EmissionBlend,
290
291 SdfSample { sdf_graph_id: u32 },
293 SdfNormal { sdf_graph_id: u32 },
294 SdfAO { sdf_graph_id: u32 },
295
296 IfElse,
298 IsNaN,
299 IsInf,
300 Dpdx,
301 Dpdy,
302 FWidth,
303 Noise2D,
304 Noise3D,
305 VoronoiNoise,
306 FbmNoise { octaves: u8 },
307 CellularNoise,
308 WhiteNoise,
309 GradientNoise,
310
311 PbrOutput,
313 UnlitOutput,
314 PostProcessOutput,
315 CustomOutput { name: String, value_type: ShaderValueType },
316 VertexOffset,
317 DepthOutput,
318 CustomVarying { name: String, value_type: ShaderValueType },
319
320 Reroute,
322 Comment { text: String, width: f32, height: f32 },
323 Group { label: String, nodes: Vec<ShaderNodeId> },
324 SubGraph { graph_id: u32, label: String },
325}
326
327impl ShaderNodeKind {
328 pub fn label(&self) -> String {
329 match self {
330 Self::ConstFloat(v) => format!("{:.3}", v),
331 Self::ConstVec2(v) => format!("({:.2}, {:.2})", v[0], v[1]),
332 Self::ConstVec3(v) => format!("({:.2},{:.2},{:.2})", v[0], v[1], v[2]),
333 Self::ConstVec4(v) => format!("({:.2},{:.2},{:.2},{:.2})", v[0],v[1],v[2],v[3]),
334 Self::ConstInt(v) => format!("{}", v),
335 Self::ConstBool(v) => format!("{}", v),
336 Self::Time => "Time".into(),
337 Self::Resolution => "Resolution".into(),
338 Self::FragCoord => "FragCoord".into(),
339 Self::VertexNormal => "Vertex Normal".into(),
340 Self::VertexTangent => "Vertex Tangent".into(),
341 Self::VertexUV => "UV0".into(),
342 Self::VertexUV2 => "UV1".into(),
343 Self::VertexColor => "Vertex Color".into(),
344 Self::WorldPosition => "World Position".into(),
345 Self::ViewDirection => "View Dir".into(),
346 Self::CameraPosition => "Camera Position".into(),
347 Self::ModelMatrix => "Model Matrix".into(),
348 Self::ViewMatrix => "View Matrix".into(),
349 Self::ProjectionMatrix => "Projection Matrix".into(),
350 Self::NormalMatrix => "Normal Matrix".into(),
351 Self::CustomUniformFloat { name } => format!("Uniform: {}", name),
352 Self::CustomUniformVec3 { name } => format!("Uniform: {}", name),
353 Self::CustomUniformVec4 { name } => format!("Uniform: {}", name),
354 Self::CustomUniformSampler2D { name } => format!("Texture: {}", name),
355 Self::Add => "Add".into(),
356 Self::Subtract => "Subtract".into(),
357 Self::Multiply => "Multiply".into(),
358 Self::Divide => "Divide".into(),
359 Self::Power => "Power".into(),
360 Self::SquareRoot => "Sqrt".into(),
361 Self::AbsoluteValue => "Abs".into(),
362 Self::Negate => "Negate".into(),
363 Self::OneMinus => "1 - x".into(),
364 Self::Reciprocal => "1 / x".into(),
365 Self::Floor => "Floor".into(),
366 Self::Ceiling => "Ceil".into(),
367 Self::Round => "Round".into(),
368 Self::Fraction => "Frac".into(),
369 Self::Sign => "Sign".into(),
370 Self::Modulo => "Modulo".into(),
371 Self::Min => "Min".into(),
372 Self::Max => "Max".into(),
373 Self::Clamp => "Clamp".into(),
374 Self::Saturate => "Saturate".into(),
375 Self::Lerp => "Lerp".into(),
376 Self::SmoothStep => "Smoothstep".into(),
377 Self::Step => "Step".into(),
378 Self::Sin => "Sin".into(),
379 Self::Cos => "Cos".into(),
380 Self::Tan => "Tan".into(),
381 Self::Asin => "Asin".into(),
382 Self::Acos => "Acos".into(),
383 Self::Atan => "Atan".into(),
384 Self::Atan2 => "Atan2".into(),
385 Self::Degrees => "Degrees".into(),
386 Self::Radians => "Radians".into(),
387 Self::Exp => "Exp".into(),
388 Self::Exp2 => "Exp2".into(),
389 Self::Log => "Log".into(),
390 Self::Log2 => "Log2".into(),
391 Self::Dot => "Dot".into(),
392 Self::Cross => "Cross".into(),
393 Self::Length => "Length".into(),
394 Self::Normalize => "Normalize".into(),
395 Self::Reflect => "Reflect".into(),
396 Self::Refract => "Refract".into(),
397 Self::FaceForward => "FaceForward".into(),
398 Self::Distance => "Distance".into(),
399 Self::Mix => "Mix".into(),
400 Self::VectorSplit => "Split".into(),
401 Self::VectorMerge => "Merge".into(),
402 Self::VectorSwizzle { swizzle, out_count } => {
403 let names = ['x','y','z','w'];
404 let s: String = swizzle[..*out_count as usize].iter()
405 .map(|&i| names[i as usize % 4])
406 .collect();
407 format!(".{}", s)
408 }
409 Self::MatrixMultiply => "MatMul".into(),
410 Self::MatrixTranspose => "Transpose".into(),
411 Self::MatrixInverse => "Inverse".into(),
412 Self::TransformPoint => "Transform Point".into(),
413 Self::TransformVector => "Transform Vector".into(),
414 Self::TransformNormal => "Transform Normal".into(),
415 Self::SampleTexture2D => "Sample 2D".into(),
416 Self::SampleTextureCube => "Sample Cube".into(),
417 Self::SampleTexture2DLod => "Sample 2D LOD".into(),
418 Self::SampleTexture2DGrad => "Sample 2D Grad".into(),
419 Self::SampleNormalMap => "Normal Map".into(),
420 Self::TextureSize => "Texture Size".into(),
421 Self::ColorToLinear => "Linear".into(),
422 Self::ColorToGamma => "Gamma".into(),
423 Self::HsvToRgb => "HSV→RGB".into(),
424 Self::RgbToHsv => "RGB→HSV".into(),
425 Self::Luminance => "Luminance".into(),
426 Self::ColorBalance => "Color Balance".into(),
427 Self::Hue => "Hue".into(),
428 Self::Saturation => "Saturation".into(),
429 Self::Brightness => "Brightness".into(),
430 Self::FresnelSchlick => "Fresnel".into(),
431 Self::GGXDistribution => "GGX NDF".into(),
432 Self::SmithGeometry => "Smith G".into(),
433 Self::BRDFSpecular => "BRDF Specular".into(),
434 Self::BRDFDiffuse => "BRDF Diffuse".into(),
435 Self::EnvBRDFApprox => "Env BRDF".into(),
436 Self::SubsurfaceApprox => "SSS Approx".into(),
437 Self::EmissionBlend => "Emission".into(),
438 Self::SdfSample { sdf_graph_id } => format!("SDF Sample #{}", sdf_graph_id),
439 Self::SdfNormal { sdf_graph_id } => format!("SDF Normal #{}", sdf_graph_id),
440 Self::SdfAO { sdf_graph_id } => format!("SDF AO #{}", sdf_graph_id),
441 Self::IfElse => "If/Else".into(),
442 Self::IsNaN => "IsNaN".into(),
443 Self::IsInf => "IsInf".into(),
444 Self::Dpdx => "dFdx".into(),
445 Self::Dpdy => "dFdy".into(),
446 Self::FWidth => "fwidth".into(),
447 Self::Noise2D => "Noise 2D".into(),
448 Self::Noise3D => "Noise 3D".into(),
449 Self::VoronoiNoise => "Voronoi".into(),
450 Self::FbmNoise { octaves } => format!("FBM ({}oct)", octaves),
451 Self::CellularNoise => "Cellular".into(),
452 Self::WhiteNoise => "White Noise".into(),
453 Self::GradientNoise => "Gradient Noise".into(),
454 Self::PbrOutput => "PBR Output".into(),
455 Self::UnlitOutput => "Unlit Output".into(),
456 Self::PostProcessOutput => "Post Output".into(),
457 Self::CustomOutput { name, .. } => format!("Output: {}", name),
458 Self::VertexOffset => "Vertex Offset".into(),
459 Self::DepthOutput => "Depth Output".into(),
460 Self::CustomVarying { name, .. } => format!("Varying: {}", name),
461 Self::Reroute => "•".into(),
462 Self::Comment { text, .. } => text.chars().take(32).collect(),
463 Self::Group { label, .. } => label.clone(),
464 Self::SubGraph { label, .. } => format!("[{}]", label),
465 }
466 }
467
468 pub fn category(&self) -> &'static str {
469 match self {
470 Self::ConstFloat(_) | Self::ConstVec2(_) | Self::ConstVec3(_)
471 | Self::ConstVec4(_) | Self::ConstInt(_) | Self::ConstBool(_)
472 | Self::Time | Self::Resolution | Self::FragCoord
473 | Self::VertexNormal | Self::VertexTangent | Self::VertexUV
474 | Self::VertexUV2 | Self::VertexColor | Self::WorldPosition
475 | Self::ViewDirection | Self::CameraPosition
476 | Self::ModelMatrix | Self::ViewMatrix | Self::ProjectionMatrix
477 | Self::NormalMatrix | Self::CustomUniformFloat { .. }
478 | Self::CustomUniformVec3 { .. } | Self::CustomUniformVec4 { .. }
479 | Self::CustomUniformSampler2D { .. } => "Input",
480
481 Self::Add | Self::Subtract | Self::Multiply | Self::Divide
482 | Self::Power | Self::SquareRoot | Self::AbsoluteValue
483 | Self::Negate | Self::OneMinus | Self::Reciprocal
484 | Self::Floor | Self::Ceiling | Self::Round | Self::Fraction
485 | Self::Sign | Self::Modulo | Self::Min | Self::Max
486 | Self::Clamp | Self::Saturate | Self::Lerp
487 | Self::SmoothStep | Self::Step => "Math",
488
489 Self::Sin | Self::Cos | Self::Tan | Self::Asin | Self::Acos
490 | Self::Atan | Self::Atan2 | Self::Degrees | Self::Radians => "Trig",
491
492 Self::Exp | Self::Exp2 | Self::Log | Self::Log2 => "Exponential",
493
494 Self::Dot | Self::Cross | Self::Length | Self::Normalize
495 | Self::Reflect | Self::Refract | Self::FaceForward
496 | Self::Distance | Self::Mix | Self::VectorSplit
497 | Self::VectorMerge | Self::VectorSwizzle { .. } => "Vector",
498
499 Self::MatrixMultiply | Self::MatrixTranspose | Self::MatrixInverse
500 | Self::TransformPoint | Self::TransformVector
501 | Self::TransformNormal => "Matrix",
502
503 Self::SampleTexture2D | Self::SampleTextureCube
504 | Self::SampleTexture2DLod | Self::SampleTexture2DGrad
505 | Self::SampleNormalMap | Self::TextureSize => "Texture",
506
507 Self::ColorToLinear | Self::ColorToGamma | Self::HsvToRgb
508 | Self::RgbToHsv | Self::Luminance | Self::ColorBalance
509 | Self::Hue | Self::Saturation | Self::Brightness => "Color",
510
511 Self::FresnelSchlick | Self::GGXDistribution | Self::SmithGeometry
512 | Self::BRDFSpecular | Self::BRDFDiffuse | Self::EnvBRDFApprox
513 | Self::SubsurfaceApprox | Self::EmissionBlend => "PBR",
514
515 Self::SdfSample { .. } | Self::SdfNormal { .. }
516 | Self::SdfAO { .. } => "SDF",
517
518 Self::IfElse | Self::IsNaN | Self::IsInf | Self::Dpdx
519 | Self::Dpdy | Self::FWidth => "Utility",
520
521 Self::Noise2D | Self::Noise3D | Self::VoronoiNoise
522 | Self::FbmNoise { .. } | Self::CellularNoise
523 | Self::WhiteNoise | Self::GradientNoise => "Noise",
524
525 Self::PbrOutput | Self::UnlitOutput | Self::PostProcessOutput
526 | Self::CustomOutput { .. } | Self::VertexOffset
527 | Self::DepthOutput | Self::CustomVarying { .. } => "Output",
528
529 Self::Reroute | Self::Comment { .. }
530 | Self::Group { .. } | Self::SubGraph { .. } => "Organization",
531 }
532 }
533
534 pub fn port_definitions(&self) -> (Vec<PortDef>, Vec<PortDef>) {
536 use ShaderValueType::*;
537 match self {
538 Self::ConstFloat(_) => (vec![], vec![PortDef::required("value", Float)]),
539 Self::ConstVec2(_) => (vec![], vec![PortDef::required("value", Vec2)]),
540 Self::ConstVec3(_) => (vec![], vec![PortDef::required("value", Vec3)]),
541 Self::ConstVec4(_) => (vec![], vec![PortDef::required("value", Vec4)]),
542 Self::ConstInt(_) => (vec![], vec![PortDef::required("value", Int)]),
543 Self::ConstBool(_) => (vec![], vec![PortDef::required("value", Bool)]),
544
545 Self::Time => (vec![], vec![PortDef::required("time", Float)]),
546 Self::Resolution => (vec![], vec![PortDef::required("resolution", Vec2)]),
547 Self::FragCoord => (vec![], vec![PortDef::required("fragcoord", Vec4)]),
548 Self::VertexNormal => (vec![], vec![PortDef::required("normal", Vec3)]),
549 Self::VertexTangent => (vec![], vec![PortDef::required("tangent", Vec4)]),
550 Self::VertexUV => (vec![], vec![PortDef::required("uv", Vec2)]),
551 Self::VertexUV2 => (vec![], vec![PortDef::required("uv2", Vec2)]),
552 Self::VertexColor => (vec![], vec![PortDef::required("color", Vec4)]),
553 Self::WorldPosition => (vec![], vec![PortDef::required("position", Vec3)]),
554 Self::ViewDirection => (vec![], vec![PortDef::required("viewDir", Vec3)]),
555 Self::CameraPosition => (vec![], vec![PortDef::required("camPos", Vec3)]),
556 Self::ModelMatrix => (vec![], vec![PortDef::required("model", Mat4)]),
557 Self::ViewMatrix => (vec![], vec![PortDef::required("view", Mat4)]),
558 Self::ProjectionMatrix => (vec![], vec![PortDef::required("proj", Mat4)]),
559 Self::NormalMatrix => (vec![], vec![PortDef::required("normalMat", Mat3)]),
560
561 Self::CustomUniformFloat { .. } => (vec![], vec![PortDef::required("value", Float)]),
562 Self::CustomUniformVec3 { .. } => (vec![], vec![PortDef::required("value", Vec3)]),
563 Self::CustomUniformVec4 { .. } => (vec![], vec![PortDef::required("value", Vec4)]),
564 Self::CustomUniformSampler2D { .. } => (vec![], vec![PortDef::required("tex", Sampler2D)]),
565
566 Self::Add | Self::Subtract | Self::Multiply | Self::Divide => (
567 vec![PortDef::required("a", Vec4), PortDef::required("b", Vec4)],
568 vec![PortDef::required("result", Vec4)],
569 ),
570 Self::Power => (
571 vec![PortDef::required("base", Float), PortDef::required("exp", Float)],
572 vec![PortDef::required("result", Float)],
573 ),
574 Self::SquareRoot | Self::AbsoluteValue | Self::Negate
575 | Self::OneMinus | Self::Reciprocal | Self::Floor
576 | Self::Ceiling | Self::Round | Self::Fraction | Self::Sign => (
577 vec![PortDef::required("x", Vec4)],
578 vec![PortDef::required("result", Vec4)],
579 ),
580 Self::Modulo | Self::Min | Self::Max => (
581 vec![PortDef::required("a", Vec4), PortDef::required("b", Vec4)],
582 vec![PortDef::required("result", Vec4)],
583 ),
584 Self::Clamp => (
585 vec![PortDef::required("x", Vec4),
586 PortDef::optional("min", Vec4, "0.0"),
587 PortDef::optional("max", Vec4, "1.0")],
588 vec![PortDef::required("result", Vec4)],
589 ),
590 Self::Saturate => (
591 vec![PortDef::required("x", Vec4)],
592 vec![PortDef::required("result", Vec4)],
593 ),
594 Self::Lerp | Self::Mix => (
595 vec![PortDef::required("a", Vec4),
596 PortDef::required("b", Vec4),
597 PortDef::required("t", Float)],
598 vec![PortDef::required("result", Vec4)],
599 ),
600 Self::SmoothStep => (
601 vec![PortDef::required("edge0", Float),
602 PortDef::required("edge1", Float),
603 PortDef::required("x", Float)],
604 vec![PortDef::required("result", Float)],
605 ),
606 Self::Step => (
607 vec![PortDef::required("edge", Float), PortDef::required("x", Float)],
608 vec![PortDef::required("result", Float)],
609 ),
610
611 Self::Sin | Self::Cos | Self::Tan | Self::Asin | Self::Acos
612 | Self::Atan | Self::Degrees | Self::Radians => (
613 vec![PortDef::required("x", Vec4)],
614 vec![PortDef::required("result", Vec4)],
615 ),
616 Self::Atan2 => (
617 vec![PortDef::required("y", Float), PortDef::required("x", Float)],
618 vec![PortDef::required("result", Float)],
619 ),
620
621 Self::Exp | Self::Exp2 | Self::Log | Self::Log2 => (
622 vec![PortDef::required("x", Float)],
623 vec![PortDef::required("result", Float)],
624 ),
625
626 Self::Dot => (
627 vec![PortDef::required("a", Vec3), PortDef::required("b", Vec3)],
628 vec![PortDef::required("result", Float)],
629 ),
630 Self::Cross => (
631 vec![PortDef::required("a", Vec3), PortDef::required("b", Vec3)],
632 vec![PortDef::required("result", Vec3)],
633 ),
634 Self::Length => (
635 vec![PortDef::required("v", Vec3)],
636 vec![PortDef::required("length", Float)],
637 ),
638 Self::Normalize => (
639 vec![PortDef::required("v", Vec3)],
640 vec![PortDef::required("normalized", Vec3)],
641 ),
642 Self::Reflect => (
643 vec![PortDef::required("i", Vec3), PortDef::required("n", Vec3)],
644 vec![PortDef::required("reflected", Vec3)],
645 ),
646 Self::Refract => (
647 vec![PortDef::required("i", Vec3),
648 PortDef::required("n", Vec3),
649 PortDef::required("eta", Float)],
650 vec![PortDef::required("refracted", Vec3)],
651 ),
652 Self::FaceForward => (
653 vec![PortDef::required("n", Vec3),
654 PortDef::required("i", Vec3),
655 PortDef::required("nref", Vec3)],
656 vec![PortDef::required("result", Vec3)],
657 ),
658 Self::Distance => (
659 vec![PortDef::required("a", Vec3), PortDef::required("b", Vec3)],
660 vec![PortDef::required("distance", Float)],
661 ),
662
663 Self::VectorSplit => (
664 vec![PortDef::required("v", Vec4)],
665 vec![PortDef::required("x", Float),
666 PortDef::required("y", Float),
667 PortDef::required("z", Float),
668 PortDef::required("w", Float)],
669 ),
670 Self::VectorMerge => (
671 vec![PortDef::optional("x", Float, "0.0"),
672 PortDef::optional("y", Float, "0.0"),
673 PortDef::optional("z", Float, "0.0"),
674 PortDef::optional("w", Float, "1.0")],
675 vec![PortDef::required("v", Vec4)],
676 ),
677 Self::VectorSwizzle { out_count, .. } => {
678 let names = ["x","y","z","w"];
679 let out_ty = match out_count {
680 1 => Float,
681 2 => Vec2,
682 3 => Vec3,
683 _ => Vec4,
684 };
685 (
686 vec![PortDef::required("v", Vec4)],
687 vec![PortDef::required(names[(*out_count as usize).min(3)], out_ty)],
688 )
689 }
690
691 Self::MatrixMultiply => (
692 vec![PortDef::required("a", Mat4), PortDef::required("b", Mat4)],
693 vec![PortDef::required("result", Mat4)],
694 ),
695 Self::MatrixTranspose | Self::MatrixInverse => (
696 vec![PortDef::required("m", Mat4)],
697 vec![PortDef::required("result", Mat4)],
698 ),
699 Self::TransformPoint => (
700 vec![PortDef::required("m", Mat4), PortDef::required("p", Vec3)],
701 vec![PortDef::required("result", Vec3)],
702 ),
703 Self::TransformVector | Self::TransformNormal => (
704 vec![PortDef::required("m", Mat4), PortDef::required("v", Vec3)],
705 vec![PortDef::required("result", Vec3)],
706 ),
707
708 Self::SampleTexture2D => (
709 vec![PortDef::required("tex", Sampler2D), PortDef::required("uv", Vec2)],
710 vec![PortDef::required("rgba", Vec4),
711 PortDef::required("rgb", Vec3),
712 PortDef::required("r", Float)],
713 ),
714 Self::SampleTextureCube => (
715 vec![PortDef::required("tex", SamplerCube), PortDef::required("dir", Vec3)],
716 vec![PortDef::required("rgba", Vec4), PortDef::required("rgb", Vec3)],
717 ),
718 Self::SampleTexture2DLod => (
719 vec![PortDef::required("tex", Sampler2D),
720 PortDef::required("uv", Vec2),
721 PortDef::required("lod", Float)],
722 vec![PortDef::required("rgba", Vec4)],
723 ),
724 Self::SampleTexture2DGrad => (
725 vec![PortDef::required("tex", Sampler2D),
726 PortDef::required("uv", Vec2),
727 PortDef::required("dpdx", Vec2),
728 PortDef::required("dpdy", Vec2)],
729 vec![PortDef::required("rgba", Vec4)],
730 ),
731 Self::SampleNormalMap => (
732 vec![PortDef::required("tex", Sampler2D),
733 PortDef::required("uv", Vec2),
734 PortDef::optional("strength", Float, "1.0")],
735 vec![PortDef::required("normal", Vec3)],
736 ),
737 Self::TextureSize => (
738 vec![PortDef::required("tex", Sampler2D), PortDef::optional("lod", Int, "0")],
739 vec![PortDef::required("size", Vec2)],
740 ),
741
742 Self::ColorToLinear | Self::ColorToGamma => (
743 vec![PortDef::required("color", Vec3)],
744 vec![PortDef::required("result", Vec3)],
745 ),
746 Self::HsvToRgb | Self::RgbToHsv => (
747 vec![PortDef::required("color", Vec3)],
748 vec![PortDef::required("result", Vec3)],
749 ),
750 Self::Luminance => (
751 vec![PortDef::required("color", Vec3)],
752 vec![PortDef::required("luma", Float)],
753 ),
754 Self::ColorBalance => (
755 vec![PortDef::required("color", Vec3),
756 PortDef::optional("shadows", Vec3, "vec3(0.0)"),
757 PortDef::optional("midtones", Vec3, "vec3(0.0)"),
758 PortDef::optional("highlights", Vec3, "vec3(0.0)")],
759 vec![PortDef::required("result", Vec3)],
760 ),
761 Self::Hue | Self::Saturation | Self::Brightness => (
762 vec![PortDef::required("color", Vec3), PortDef::required("value", Float)],
763 vec![PortDef::required("result", Vec3)],
764 ),
765
766 Self::FresnelSchlick => (
767 vec![PortDef::required("F0", Vec3),
768 PortDef::required("cosTheta", Float)],
769 vec![PortDef::required("fresnel", Vec3)],
770 ),
771 Self::GGXDistribution => (
772 vec![PortDef::required("NdotH", Float), PortDef::required("roughness", Float)],
773 vec![PortDef::required("D", Float)],
774 ),
775 Self::SmithGeometry => (
776 vec![PortDef::required("NdotV", Float),
777 PortDef::required("NdotL", Float),
778 PortDef::required("roughness", Float)],
779 vec![PortDef::required("G", Float)],
780 ),
781 Self::BRDFSpecular => (
782 vec![PortDef::required("normal", Vec3),
783 PortDef::required("viewDir", Vec3),
784 PortDef::required("lightDir", Vec3),
785 PortDef::required("F0", Vec3),
786 PortDef::required("roughness", Float)],
787 vec![PortDef::required("specular", Vec3)],
788 ),
789 Self::BRDFDiffuse => (
790 vec![PortDef::required("albedo", Vec3),
791 PortDef::required("normal", Vec3),
792 PortDef::required("lightDir", Vec3)],
793 vec![PortDef::required("diffuse", Vec3)],
794 ),
795 Self::EnvBRDFApprox => (
796 vec![PortDef::required("F0", Vec3),
797 PortDef::required("roughness", Float),
798 PortDef::required("NdotV", Float)],
799 vec![PortDef::required("envBRDF", Vec3)],
800 ),
801 Self::SubsurfaceApprox => (
802 vec![PortDef::required("albedo", Vec3),
803 PortDef::required("thickness", Float),
804 PortDef::optional("scatter", Vec3, "vec3(1.0,0.3,0.1)")],
805 vec![PortDef::required("sss", Vec3)],
806 ),
807 Self::EmissionBlend => (
808 vec![PortDef::required("color", Vec3),
809 PortDef::required("emission", Vec3),
810 PortDef::optional("strength", Float, "1.0")],
811 vec![PortDef::required("result", Vec3)],
812 ),
813
814 Self::SdfSample { .. } => (
815 vec![PortDef::required("pos", Vec3)],
816 vec![PortDef::required("dist", Float)],
817 ),
818 Self::SdfNormal { .. } => (
819 vec![PortDef::required("pos", Vec3), PortDef::optional("eps", Float, "0.001")],
820 vec![PortDef::required("normal", Vec3)],
821 ),
822 Self::SdfAO { .. } => (
823 vec![PortDef::required("pos", Vec3), PortDef::required("normal", Vec3)],
824 vec![PortDef::required("ao", Float)],
825 ),
826
827 Self::IfElse => (
828 vec![PortDef::required("condition", Bool),
829 PortDef::required("ifTrue", Vec4),
830 PortDef::required("ifFalse", Vec4)],
831 vec![PortDef::required("result", Vec4)],
832 ),
833 Self::IsNaN | Self::IsInf => (
834 vec![PortDef::required("x", Float)],
835 vec![PortDef::required("result", Bool)],
836 ),
837 Self::Dpdx | Self::Dpdy | Self::FWidth => (
838 vec![PortDef::required("p", Float)],
839 vec![PortDef::required("result", Float)],
840 ),
841
842 Self::Noise2D | Self::GradientNoise => (
843 vec![PortDef::required("uv", Vec2), PortDef::optional("scale", Float, "1.0")],
844 vec![PortDef::required("noise", Float)],
845 ),
846 Self::Noise3D => (
847 vec![PortDef::required("pos", Vec3), PortDef::optional("scale", Float, "1.0")],
848 vec![PortDef::required("noise", Float)],
849 ),
850 Self::VoronoiNoise => (
851 vec![PortDef::required("uv", Vec2), PortDef::optional("scale", Float, "5.0")],
852 vec![PortDef::required("dist", Float),
853 PortDef::required("cell", Vec2)],
854 ),
855 Self::FbmNoise { .. } => (
856 vec![PortDef::required("pos", Vec3),
857 PortDef::optional("scale", Float, "1.0"),
858 PortDef::optional("gain", Float, "0.5"),
859 PortDef::optional("lacunarity", Float, "2.0")],
860 vec![PortDef::required("noise", Float)],
861 ),
862 Self::CellularNoise => (
863 vec![PortDef::required("pos", Vec3)],
864 vec![PortDef::required("f1", Float), PortDef::required("f2", Float)],
865 ),
866 Self::WhiteNoise => (
867 vec![PortDef::required("seed", Vec2)],
868 vec![PortDef::required("noise", Float)],
869 ),
870
871 Self::PbrOutput => (
872 vec![PortDef::required("albedo", Vec3),
873 PortDef::optional("normal", Vec3, "vec3(0,0,1)"),
874 PortDef::optional("metallic", Float, "0.0"),
875 PortDef::optional("roughness", Float, "0.5"),
876 PortDef::optional("ao", Float, "1.0"),
877 PortDef::optional("emission", Vec3, "vec3(0.0)"),
878 PortDef::optional("alpha", Float, "1.0"),
879 PortDef::optional("sss", Float, "0.0"),
880 PortDef::optional("ior", Float, "1.5")],
881 vec![],
882 ),
883 Self::UnlitOutput => (
884 vec![PortDef::required("color", Vec4)],
885 vec![],
886 ),
887 Self::PostProcessOutput => (
888 vec![PortDef::required("color", Vec4)],
889 vec![],
890 ),
891 Self::CustomOutput { value_type, .. } => (
892 vec![PortDef::required("value", *value_type)],
893 vec![],
894 ),
895 Self::VertexOffset => (
896 vec![PortDef::required("offset", Vec3)],
897 vec![],
898 ),
899 Self::DepthOutput => (
900 vec![PortDef::required("depth", Float)],
901 vec![],
902 ),
903 Self::CustomVarying { value_type, .. } => (
904 vec![PortDef::required("value", *value_type)],
905 vec![PortDef::required("out", *value_type)],
906 ),
907
908 Self::Reroute => (
909 vec![PortDef::required("in", Vec4)],
910 vec![PortDef::required("out", Vec4)],
911 ),
912 Self::Comment { .. } | Self::Group { .. } => (vec![], vec![]),
913 Self::SubGraph { .. } => (vec![], vec![]),
914 }
915 }
916
917 pub fn emit_glsl(&self, inputs: &[String], var: &str) -> String {
919 let i = |idx: usize| inputs.get(idx).map(|s| s.as_str()).unwrap_or("0.0");
920 match self {
921 Self::ConstFloat(v) => format!("float {} = {:.8};", var, v),
922 Self::ConstVec2(v) => format!("vec2 {} = vec2({:.8},{:.8});", var, v[0], v[1]),
923 Self::ConstVec3(v) => format!("vec3 {} = vec3({:.8},{:.8},{:.8});", var, v[0],v[1],v[2]),
924 Self::ConstVec4(v) => format!("vec4 {} = vec4({:.8},{:.8},{:.8},{:.8});", var, v[0],v[1],v[2],v[3]),
925 Self::ConstInt(v) => format!("int {} = {};", var, v),
926 Self::ConstBool(v) => format!("bool {} = {};", var, v),
927
928 Self::Time => format!("float {} = u_time;", var),
929 Self::Resolution => format!("vec2 {} = u_resolution;", var),
930 Self::FragCoord => format!("vec4 {} = gl_FragCoord;", var),
931 Self::VertexNormal => format!("vec3 {} = v_normal;", var),
932 Self::VertexTangent => format!("vec4 {} = v_tangent;", var),
933 Self::VertexUV => format!("vec2 {} = v_uv;", var),
934 Self::VertexUV2 => format!("vec2 {} = v_uv2;", var),
935 Self::VertexColor => format!("vec4 {} = v_color;", var),
936 Self::WorldPosition => format!("vec3 {} = v_world_pos;", var),
937 Self::ViewDirection => format!("vec3 {} = normalize(u_camera_pos - v_world_pos);", var),
938 Self::CameraPosition => format!("vec3 {} = u_camera_pos;", var),
939 Self::ModelMatrix => format!("mat4 {} = u_model;", var),
940 Self::ViewMatrix => format!("mat4 {} = u_view;", var),
941 Self::ProjectionMatrix => format!("mat4 {} = u_proj;", var),
942 Self::NormalMatrix => format!("mat3 {} = u_normal_mat;", var),
943 Self::CustomUniformFloat { name } => format!("float {} = {};", var, name),
944 Self::CustomUniformVec3 { name } => format!("vec3 {} = {};", var, name),
945 Self::CustomUniformVec4 { name } => format!("vec4 {} = {};", var, name),
946 Self::CustomUniformSampler2D { name } => format!("// texture {} bound as {}", name, var),
947
948 Self::Add => format!("vec4 {} = {} + {};", var, i(0), i(1)),
949 Self::Subtract => format!("vec4 {} = {} - {};", var, i(0), i(1)),
950 Self::Multiply => format!("vec4 {} = {} * {};", var, i(0), i(1)),
951 Self::Divide => format!("vec4 {} = {} / max({}, vec4(1e-7));", var, i(0), i(1)),
952 Self::Power => format!("float {} = pow({}, {});", var, i(0), i(1)),
953 Self::SquareRoot => format!("vec4 {} = sqrt({});", var, i(0)),
954 Self::AbsoluteValue => format!("vec4 {} = abs({});", var, i(0)),
955 Self::Negate => format!("vec4 {} = -{};", var, i(0)),
956 Self::OneMinus => format!("vec4 {} = 1.0 - {};", var, i(0)),
957 Self::Reciprocal => format!("vec4 {} = 1.0 / max({}, vec4(1e-7));", var, i(0)),
958 Self::Floor => format!("vec4 {} = floor({});", var, i(0)),
959 Self::Ceiling => format!("vec4 {} = ceil({});", var, i(0)),
960 Self::Round => format!("vec4 {} = round({});", var, i(0)),
961 Self::Fraction => format!("vec4 {} = fract({});", var, i(0)),
962 Self::Sign => format!("vec4 {} = sign({});", var, i(0)),
963 Self::Modulo => format!("vec4 {} = mod({}, {});", var, i(0), i(1)),
964 Self::Min => format!("vec4 {} = min({}, {});", var, i(0), i(1)),
965 Self::Max => format!("vec4 {} = max({}, {});", var, i(0), i(1)),
966 Self::Clamp => format!("vec4 {} = clamp({}, {}, {});", var, i(0), i(1), i(2)),
967 Self::Saturate => format!("vec4 {} = clamp({}, 0.0, 1.0);", var, i(0)),
968 Self::Lerp | Self::Mix
969 => format!("vec4 {} = mix({}, {}, {});", var, i(0), i(1), i(2)),
970 Self::SmoothStep => format!("float {} = smoothstep({}, {}, {});", var, i(0), i(1), i(2)),
971 Self::Step => format!("float {} = step({}, {});", var, i(0), i(1)),
972
973 Self::Sin => format!("vec4 {} = sin({});", var, i(0)),
974 Self::Cos => format!("vec4 {} = cos({});", var, i(0)),
975 Self::Tan => format!("vec4 {} = tan({});", var, i(0)),
976 Self::Asin => format!("vec4 {} = asin({});", var, i(0)),
977 Self::Acos => format!("vec4 {} = acos({});", var, i(0)),
978 Self::Atan => format!("vec4 {} = atan({});", var, i(0)),
979 Self::Atan2 => format!("float {} = atan({}, {});", var, i(0), i(1)),
980 Self::Degrees => format!("vec4 {} = degrees({});", var, i(0)),
981 Self::Radians => format!("vec4 {} = radians({});", var, i(0)),
982
983 Self::Exp => format!("float {} = exp({});", var, i(0)),
984 Self::Exp2 => format!("float {} = exp2({});", var, i(0)),
985 Self::Log => format!("float {} = log({});", var, i(0)),
986 Self::Log2 => format!("float {} = log2({});", var, i(0)),
987
988 Self::Dot => format!("float {} = dot({}, {});", var, i(0), i(1)),
989 Self::Cross => format!("vec3 {} = cross({}, {});", var, i(0), i(1)),
990 Self::Length => format!("float {} = length({});", var, i(0)),
991 Self::Normalize => format!("vec3 {} = normalize({});", var, i(0)),
992 Self::Reflect => format!("vec3 {} = reflect({}, {});", var, i(0), i(1)),
993 Self::Refract => format!("vec3 {} = refract({}, {}, {});", var, i(0), i(1), i(2)),
994 Self::FaceForward => format!("vec3 {} = faceforward({}, {}, {});", var, i(0), i(1), i(2)),
995 Self::Distance => format!("float {} = distance({}, {});", var, i(0), i(1)),
996 Self::VectorSplit => format!(
997 "float {}_x={}.x; float {}_y={}.y; float {}_z={}.z; float {}_w={}.w;",
998 var, i(0), var, i(0), var, i(0), var, i(0)
999 ),
1000 Self::VectorMerge => format!("vec4 {} = vec4({},{},{},{});", var, i(0), i(1), i(2), i(3)),
1001 Self::VectorSwizzle { swizzle, out_count } => {
1002 let channels = ['x','y','z','w'];
1003 let s: String = swizzle[..*out_count as usize].iter()
1004 .map(|&idx| channels[idx as usize % 4])
1005 .collect();
1006 let ty = match out_count {
1007 1 => "float",
1008 2 => "vec2",
1009 3 => "vec3",
1010 _ => "vec4",
1011 };
1012 format!("{} {} = {}.{};", ty, var, i(0), s)
1013 }
1014
1015 Self::MatrixMultiply => format!("mat4 {} = {} * {};", var, i(0), i(1)),
1016 Self::MatrixTranspose => format!("mat4 {} = transpose({});", var, i(0)),
1017 Self::MatrixInverse => format!("mat4 {} = inverse({});", var, i(0)),
1018 Self::TransformPoint => format!("vec3 {} = ({} * vec4({}, 1.0)).xyz;", var, i(0), i(1)),
1019 Self::TransformVector => format!("vec3 {} = ({} * vec4({}, 0.0)).xyz;", var, i(0), i(1)),
1020 Self::TransformNormal => format!("vec3 {} = normalize(mat3({}) * {});", var, i(0), i(1)),
1021
1022 Self::SampleTexture2D => format!(
1023 "vec4 {}_rgba = texture({}, {}); vec3 {}_rgb = {}_rgba.rgb; float {}_r = {}_rgba.r;",
1024 var, i(0), i(1), var, var, var, var
1025 ),
1026 Self::SampleTextureCube => format!(
1027 "vec4 {}_rgba = texture({}, {}); vec3 {}_rgb = {}_rgba.rgb;",
1028 var, i(0), i(1), var, var
1029 ),
1030 Self::SampleTexture2DLod => format!(
1031 "vec4 {} = textureLod({}, {}, {});", var, i(0), i(1), i(2)
1032 ),
1033 Self::SampleTexture2DGrad => format!(
1034 "vec4 {} = textureGrad({}, {}, {}, {});", var, i(0), i(1), i(2), i(3)
1035 ),
1036 Self::SampleNormalMap => format!(
1037 "vec3 {} = normalize(texture({},{}).rgb * 2.0 - 1.0) * vec3({},{},1.0);",
1038 var, i(0), i(1), i(2), i(2)
1039 ),
1040 Self::TextureSize => format!(
1041 "vec2 {} = vec2(textureSize({}, {}));", var, i(0), i(1)
1042 ),
1043
1044 Self::ColorToLinear => format!("vec3 {} = pow({}, vec3(2.2));", var, i(0)),
1045 Self::ColorToGamma => format!("vec3 {} = pow({}, vec3(1.0/2.2));", var, i(0)),
1046 Self::HsvToRgb => format!(
1047 "vec3 {} = clamp(abs(mod({}.x*6.0+vec3(0,4,2),6.0)-3.0)-1.0,0.0,1.0); {} = {}.z*mix(vec3(1.0),{},{}.y);",
1048 var, i(0), var, i(0), var, i(0)
1049 ),
1050 Self::RgbToHsv => format!(
1051 "vec4 _p{}=mix(vec4({}.bg,vec2(-1.0/3.0,2.0/3.0)),vec4({}.gb,vec2(0.0,-1.0/3.0)),step({}.b,{}.g));vec4 _q{}=mix(vec4(_p{}.xyw,{}.r),vec4({}.r,_p{}.yzx),step(_p{}.x,{}.r));float _d{}=_q{}.x-min(_q{}.w,_q{}.y);vec3 {}=vec3(abs(_q{}.z+((_q{}.w-_q{}.y)/(6.0*_d{}+1e-10))),_d{}/(_q{}.x+1e-10),_q{}.x);",
1052 var,i(0),i(0),i(0),i(0),
1053 var,var,i(0),i(0),var,var,i(0),
1054 var,var,var,var,
1055 var,var,var,var,var,var,var,var
1056 ),
1057 Self::Luminance => format!("float {} = dot({}, vec3(0.2126,0.7152,0.0722));", var, i(0)),
1058 Self::ColorBalance => format!(
1059 "vec3 {} = {} + {} * (1.0 - {}) + {} * clamp(length({} - 0.5)*2.0,0.0,1.0) + {} * {};",
1060 var, i(0), i(1), i(0), i(2), i(0), i(3), i(0)
1061 ),
1062 Self::Hue => format!(
1063 "vec3 _hsv{}=vec3(atan({}.g-{}.b,{}.r-{}.g)/(2.0*3.14159)+0.5,length({}.rgb),dot({}.rgb,vec3(0.333)));vec3 {}=vec3(mod(_hsv{}.x+{},1.0),_hsv{}.yz);",
1064 var,i(0),i(0),i(0),i(0),i(0),i(0),var,var,i(1),var
1065 ),
1066 Self::Saturation => format!("vec3 {} = mix(vec3(dot({},vec3(0.2126,0.7152,0.0722))),{},{});",var,i(0),i(0),i(1)),
1067 Self::Brightness => format!("vec3 {} = {} * {};", var, i(0), i(1)),
1068
1069 Self::FresnelSchlick => format!(
1070 "vec3 {} = {} + (1.0-{}) * pow(1.0-clamp({},0.0,1.0),5.0);",
1071 var, i(0), i(0), i(1)
1072 ),
1073 Self::GGXDistribution => format!(
1074 "float _a{}={}*{};float _a2{}=_a{}*_a{};float _denom{}=({}-1.0)*_a2{}+1.0;float {}=_a2{}/(3.14159*_denom{}*_denom{});",
1075 var,i(1),i(1),var,var,var,var,i(0),var,var,var,var,var
1076 ),
1077 Self::SmithGeometry => format!(
1078 "float _k{}={}*{}/8.0;float _gv{}={}/({}*(1.0-_k{})+_k{});float _gl{}={}/({}*(1.0-_k{})+_k{});float {}=_gv{}*_gl{};",
1079 var,i(2),i(2),var,i(0),i(0),var,var,var,i(1),i(1),var,var,var,var,var
1080 ),
1081 Self::BRDFSpecular => format!(
1082 "// BRDFSpecular: full Cook-Torrance stored in {}", var
1083 ),
1084 Self::BRDFDiffuse => format!(
1085 "vec3 {} = {} * max(dot({},{}),0.0) / 3.14159;", var, i(0), i(1), i(2)
1086 ),
1087 Self::EnvBRDFApprox => format!(
1088 "vec2 _env{}=vec2(clamp({},0.0,1.0),{}); vec3 {}={}*(_env{}.x+vec2(-0.0048,0.0,0.0).x)+_env{}.y;",
1089 var,i(2),i(1),var,i(0),var,var
1090 ),
1091 Self::SubsurfaceApprox => format!(
1092 "float _trans{}=exp(-{}/max({},0.001));vec3 {}={}*{}_trans;",
1093 var,i(1),i(1),var,i(0),var
1094 ),
1095 Self::EmissionBlend => format!(
1096 "vec3 {} = {} + {} * {};", var, i(0), i(1), i(2)
1097 ),
1098
1099 Self::SdfSample { sdf_graph_id } => format!(
1100 "float {} = sdf_graph_{}({});", var, sdf_graph_id, i(0)
1101 ),
1102 Self::SdfNormal { sdf_graph_id } => format!(
1103 "vec3 {} = sdf_normal_{}({}, {});", var, sdf_graph_id, i(0), i(1)
1104 ),
1105 Self::SdfAO { sdf_graph_id } => format!(
1106 "float {} = sdf_ao_{}({}, {});", var, sdf_graph_id, i(0), i(1)
1107 ),
1108
1109 Self::IfElse => format!("vec4 {} = {} ? {} : {};", var, i(0), i(1), i(2)),
1110 Self::IsNaN => format!("bool {} = isnan({});", var, i(0)),
1111 Self::IsInf => format!("bool {} = isinf({});", var, i(0)),
1112 Self::Dpdx => format!("float {} = dFdx({});", var, i(0)),
1113 Self::Dpdy => format!("float {} = dFdy({});", var, i(0)),
1114 Self::FWidth => format!("float {} = fwidth({});", var, i(0)),
1115
1116 Self::Noise2D | Self::GradientNoise => format!(
1117 "float {} = noise_grad2({} * {});", var, i(0), i(1)
1118 ),
1119 Self::Noise3D => format!(
1120 "float {} = noise_grad3({} * {});", var, i(0), i(1)
1121 ),
1122 Self::VoronoiNoise => format!(
1123 "vec3 _vor{}=voronoi({} * {}); float {}_dist=_vor{}.x; vec2 {}_cell=_vor{}.yz;",
1124 var, i(0), i(1), var, var, var, var
1125 ),
1126 Self::FbmNoise { octaves } => format!(
1127 "float {} = fbm({}, {}, {}, {}, {});", var, i(0), i(1), i(2), i(3), octaves
1128 ),
1129 Self::CellularNoise => format!(
1130 "vec2 _cell{}=cellular({});float {}_f1=_cell{}.x;float {}_f2=_cell{}.y;",
1131 var, i(0), var, var, var, var
1132 ),
1133 Self::WhiteNoise => format!(
1134 "float {} = fract(sin(dot({},vec2(127.1,311.7)))*43758.5453);", var, i(0)
1135 ),
1136
1137 Self::PbrOutput => format!(
1138 "// PBR output: albedo={} normal={} metallic={} roughness={} ao={} emission={} alpha={} sss={} ior={}",
1139 i(0), i(1), i(2), i(3), i(4), i(5), i(6), i(7), i(8)
1140 ),
1141 Self::UnlitOutput | Self::PostProcessOutput => format!(
1142 "// Output color: {}", i(0)
1143 ),
1144 Self::CustomOutput { name, .. } => format!("// {} = {};", name, i(0)),
1145 Self::VertexOffset => format!("// Vertex offset: {}", i(0)),
1146 Self::DepthOutput => format!("gl_FragDepth = {};", i(0)),
1147 Self::CustomVarying { name, value_type } => format!(
1148 "{} {} = {};", value_type.glsl_type(), name, i(0)
1149 ),
1150
1151 Self::Reroute => format!("vec4 {} = {};", var, i(0)),
1152 Self::Comment { .. } | Self::Group { .. } | Self::SubGraph { .. } => String::new(),
1153 }
1154 }
1155}
1156
1157#[derive(Debug, Clone)]
1160pub struct ShaderNode {
1161 pub id: ShaderNodeId,
1162 pub kind: ShaderNodeKind,
1163 pub position: [f32; 2],
1164 pub collapsed: bool,
1165 pub disabled: bool,
1166 pub comment: Option<String>,
1167 pub preview_enabled: bool,
1168}
1169
1170impl ShaderNode {
1171 pub fn new(id: ShaderNodeId, kind: ShaderNodeKind, x: f32, y: f32) -> Self {
1172 Self {
1173 id,
1174 kind,
1175 position: [x, y],
1176 collapsed: false,
1177 disabled: false,
1178 comment: None,
1179 preview_enabled: false,
1180 }
1181 }
1182
1183 pub fn width(&self) -> f32 {
1184 let (ins, outs) = self.kind.port_definitions();
1185 let max_ports = ins.len().max(outs.len());
1186 (160.0 + max_ports as f32 * 4.0).min(300.0)
1187 }
1188
1189 pub fn height(&self) -> f32 {
1190 if self.collapsed { return 28.0; }
1191 let (ins, outs) = self.kind.port_definitions();
1192 let rows = ins.len().max(outs.len()).max(1);
1193 28.0 + rows as f32 * 22.0
1194 }
1195
1196 pub fn port_position(&self, port: u16, is_output: bool) -> [f32; 2] {
1197 let y = self.position[1] + 28.0 + port as f32 * 22.0 + 11.0;
1198 let x = if is_output {
1199 self.position[0] + self.width()
1200 } else {
1201 self.position[0]
1202 };
1203 [x, y]
1204 }
1205}
1206
1207#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1210pub enum ShaderGraphType {
1211 Fragment,
1212 Vertex,
1213 PostProcess,
1214 Compute,
1215}
1216
1217#[derive(Debug, Clone)]
1218pub struct ShaderGraph {
1219 pub id: u32,
1220 pub name: String,
1221 pub graph_type: ShaderGraphType,
1222 nodes: HashMap<ShaderNodeId, ShaderNode>,
1223 connections: Vec<ShaderConnection>,
1224 next_id: u32,
1225 pub canvas_offset: [f32; 2],
1226 pub canvas_zoom: f32,
1227 pub glsl_version: u32,
1228}
1229
1230impl ShaderGraph {
1231 pub fn new(id: u32, name: String, graph_type: ShaderGraphType) -> Self {
1232 Self {
1233 id,
1234 name,
1235 graph_type,
1236 nodes: HashMap::new(),
1237 connections: Vec::new(),
1238 next_id: 1,
1239 canvas_offset: [0.0; 2],
1240 canvas_zoom: 1.0,
1241 glsl_version: 450,
1242 }
1243 }
1244
1245 pub fn add_node(&mut self, kind: ShaderNodeKind, x: f32, y: f32) -> ShaderNodeId {
1246 let id = ShaderNodeId(self.next_id);
1247 self.next_id += 1;
1248 self.nodes.insert(id, ShaderNode::new(id, kind, x, y));
1249 id
1250 }
1251
1252 pub fn remove_node(&mut self, id: ShaderNodeId) {
1253 self.nodes.remove(&id);
1254 self.connections.retain(|c| c.from.node != id && c.to.node != id);
1255 }
1256
1257 pub fn connect(&mut self, from: ShaderPortId, to: ShaderPortId) -> Result<(), &'static str> {
1258 if !self.nodes.contains_key(&from.node) { return Err("Source node not found"); }
1260 if !self.nodes.contains_key(&to.node) { return Err("Target node not found"); }
1261 if from.node == to.node { return Err("Cannot connect node to itself"); }
1262
1263 self.connections.retain(|c| !(c.to.node == to.node && c.to.port == to.port));
1265
1266 if self.would_create_cycle(from.node, to.node) {
1268 return Err("Connection would create a cycle");
1269 }
1270
1271 self.connections.push(ShaderConnection { from, to });
1272 Ok(())
1273 }
1274
1275 fn would_create_cycle(&self, from: ShaderNodeId, to: ShaderNodeId) -> bool {
1276 let mut visited = HashSet::new();
1278 let mut queue = VecDeque::new();
1279 queue.push_back(to);
1280 while let Some(node) = queue.pop_front() {
1281 if node == from { return true; }
1282 if visited.contains(&node) { continue; }
1283 visited.insert(node);
1284 for conn in &self.connections {
1285 if conn.from.node == node {
1286 queue.push_back(conn.to.node);
1287 }
1288 }
1289 }
1290 false
1291 }
1292
1293 pub fn disconnect(&mut self, to: ShaderPortId) {
1294 self.connections.retain(|c| !(c.to.node == to.node && c.to.port == to.port));
1295 }
1296
1297 pub fn topological_order(&self) -> Vec<ShaderNodeId> {
1298 let mut in_degree: HashMap<ShaderNodeId, usize> =
1299 self.nodes.keys().map(|&k| (k, 0)).collect();
1300 for conn in &self.connections {
1301 *in_degree.entry(conn.to.node).or_insert(0) += 1;
1302 }
1303 let mut queue: VecDeque<ShaderNodeId> =
1304 in_degree.iter().filter(|(_, &d)| d == 0).map(|(&k, _)| k).collect();
1305 let mut order = Vec::new();
1306 while let Some(n) = queue.pop_front() {
1307 order.push(n);
1308 for conn in &self.connections {
1309 if conn.from.node == n {
1310 let d = in_degree.entry(conn.to.node).or_insert(1);
1311 *d -= 1;
1312 if *d == 0 { queue.push_back(conn.to.node); }
1313 }
1314 }
1315 }
1316 order
1317 }
1318
1319 pub fn compile(&self) -> ShaderCompileResult {
1321 let order = self.topological_order();
1322 let mut lines: Vec<String> = Vec::new();
1323 let mut errors: Vec<String> = Vec::new();
1324 let mut uniforms: Vec<String> = Vec::new();
1325 let mut functions: Vec<String> = Vec::new();
1326
1327 lines.push(format!("#version {} core", self.glsl_version));
1329 lines.push(String::new());
1330
1331 for id in &order {
1333 if let Some(node) = self.nodes.get(id) {
1334 match &node.kind {
1335 ShaderNodeKind::CustomUniformFloat { name } =>
1336 uniforms.push(format!("uniform float {};", name)),
1337 ShaderNodeKind::CustomUniformVec3 { name } =>
1338 uniforms.push(format!("uniform vec3 {};", name)),
1339 ShaderNodeKind::CustomUniformVec4 { name } =>
1340 uniforms.push(format!("uniform vec4 {};", name)),
1341 ShaderNodeKind::CustomUniformSampler2D { name } =>
1342 uniforms.push(format!("uniform sampler2D {};", name)),
1343 _ => {}
1344 }
1345 }
1346 }
1347
1348 lines.push("uniform float u_time;".into());
1350 lines.push("uniform vec2 u_resolution;".into());
1351 lines.push("uniform mat4 u_model;".into());
1352 lines.push("uniform mat4 u_view;".into());
1353 lines.push("uniform mat4 u_proj;".into());
1354 lines.push("uniform mat3 u_normal_mat;".into());
1355 lines.push("uniform vec3 u_camera_pos;".into());
1356 for u in &uniforms { lines.push(u.clone()); }
1357 lines.push(String::new());
1358
1359 lines.push("in vec3 v_world_pos;".into());
1361 lines.push("in vec3 v_normal;".into());
1362 lines.push("in vec4 v_tangent;".into());
1363 lines.push("in vec2 v_uv;".into());
1364 lines.push("in vec2 v_uv2;".into());
1365 lines.push("in vec4 v_color;".into());
1366 lines.push(String::new());
1367
1368 lines.push("out vec4 frag_color;".into());
1370 lines.push(String::new());
1371
1372 functions.push("// ---- noise helpers ----".into());
1374 functions.push("float noise_grad2(vec2 p) { return fract(sin(dot(p,vec2(127.1,311.7)))*43758.5); }".into());
1375 functions.push("float noise_grad3(vec3 p) { return fract(sin(dot(p,vec3(127.1,311.7,74.7)))*43758.5); }".into());
1376 functions.push("vec3 voronoi(vec2 x) { vec2 p=floor(x),f=fract(x); float res=8.0; vec2 mr=vec2(0.0); for(int j=-1;j<=1;j++)for(int i=-1;i<=1;i++){vec2 b=vec2(float(i),float(j));vec2 r=b-f+fract(sin(vec2(dot(p+b,vec2(127.1,311.7)),dot(p+b,vec2(269.5,183.3))))*43758.5);float d=dot(r,r);if(d<res){res=d;mr=r;}}return vec3(sqrt(res),mr); }".into());
1377 functions.push("float fbm(vec3 p, float scale, float gain, float lac, int oct) { float s=0.0,a=0.5; p*=scale; for(int i=0;i<oct;i++){s+=a*noise_grad3(p);p*=lac;a*=gain;} return s; }".into());
1378 functions.push("vec2 cellular(vec3 p) { vec3 b=floor(p); float f1=9e9,f2=9e9; for(int z=-1;z<=1;z++)for(int y=-1;y<=1;y++)for(int x=-1;x<=1;x++){vec3 nb=b+vec3(x,y,z);vec3 c=nb+fract(sin(vec3(dot(nb,vec3(127.1,311.7,74.7)),dot(nb,vec3(269.5,183.3,246.1)),dot(nb,vec3(113.5,271.9,124.6))))*43758.5);float d=length(p-c);if(d<f1){f2=f1;f1=d;}else if(d<f2){f2=d;}}return vec2(f1,f2); }".into());
1379 functions.push(String::new());
1380
1381 for f in &functions { lines.push(f.clone()); }
1382 lines.push("void main() {".into());
1383
1384 for id in &order {
1386 if let Some(node) = self.nodes.get(id) {
1387 if node.disabled { continue; }
1388 let (in_defs, _) = node.kind.port_definitions();
1389 let mut input_exprs: Vec<String> = Vec::new();
1390 for (port_idx, _port_def) in in_defs.iter().enumerate() {
1391 let conn = self.connections.iter().find(|c| {
1392 c.to.node == *id && c.to.port == port_idx as u16
1393 });
1394 if let Some(c) = conn {
1395 input_exprs.push(format!("_n{}_{}", c.from.node.0, c.from.port));
1396 } else if let Some(default) = &_port_def.default_expr {
1397 input_exprs.push(default.clone());
1398 } else {
1399 input_exprs.push(_port_def.value_type.default_value().to_string());
1400 errors.push(format!("Node {} port {} has no connection and no default",
1401 node.id.0, port_idx));
1402 }
1403 }
1404 let var = format!("_n{}", id.0);
1405 let code = node.kind.emit_glsl(&input_exprs, &var);
1406 if !code.is_empty() {
1407 lines.push(format!(" {}", code));
1408 }
1409 }
1410 }
1411
1412 lines.push("}".into());
1413
1414 ShaderCompileResult {
1415 source: lines.join("\n"),
1416 errors,
1417 warnings: Vec::new(),
1418 uniform_names: uniforms,
1419 }
1420 }
1421
1422 pub fn node(&self, id: ShaderNodeId) -> Option<&ShaderNode> {
1423 self.nodes.get(&id)
1424 }
1425
1426 pub fn node_mut(&mut self, id: ShaderNodeId) -> Option<&mut ShaderNode> {
1427 self.nodes.get_mut(&id)
1428 }
1429
1430 pub fn nodes(&self) -> impl Iterator<Item = &ShaderNode> {
1431 self.nodes.values()
1432 }
1433
1434 pub fn connections(&self) -> &[ShaderConnection] {
1435 &self.connections
1436 }
1437
1438 pub fn node_count(&self) -> usize { self.nodes.len() }
1439 pub fn connection_count(&self) -> usize { self.connections.len() }
1440}
1441
1442#[derive(Debug, Clone)]
1443pub struct ShaderCompileResult {
1444 pub source: String,
1445 pub errors: Vec<String>,
1446 pub warnings: Vec<String>,
1447 pub uniform_names: Vec<String>,
1448}
1449
1450impl ShaderCompileResult {
1451 pub fn is_ok(&self) -> bool { self.errors.is_empty() }
1452}
1453
1454#[derive(Debug, Clone)]
1457pub struct ShaderGraphLibrary {
1458 graphs: HashMap<u32, ShaderGraph>,
1459 next_id: u32,
1460 pub active_graph: Option<u32>,
1461}
1462
1463impl ShaderGraphLibrary {
1464 pub fn new() -> Self {
1465 let mut lib = Self {
1466 graphs: HashMap::new(),
1467 next_id: 1,
1468 active_graph: None,
1469 };
1470 let id = lib.create_graph("Default PBR".into(), ShaderGraphType::Fragment);
1472 lib.build_default_pbr(id);
1473 lib.active_graph = Some(id);
1474 lib
1475 }
1476
1477 pub fn create_graph(&mut self, name: String, ty: ShaderGraphType) -> u32 {
1478 let id = self.next_id;
1479 self.next_id += 1;
1480 self.graphs.insert(id, ShaderGraph::new(id, name, ty));
1481 id
1482 }
1483
1484 pub fn remove_graph(&mut self, id: u32) {
1485 self.graphs.remove(&id);
1486 if self.active_graph == Some(id) {
1487 self.active_graph = self.graphs.keys().next().copied();
1488 }
1489 }
1490
1491 pub fn graph(&self, id: u32) -> Option<&ShaderGraph> {
1492 self.graphs.get(&id)
1493 }
1494
1495 pub fn graph_mut(&mut self, id: u32) -> Option<&mut ShaderGraph> {
1496 self.graphs.get_mut(&id)
1497 }
1498
1499 pub fn graphs(&self) -> impl Iterator<Item = &ShaderGraph> {
1500 self.graphs.values()
1501 }
1502
1503 pub fn active_graph_mut(&mut self) -> Option<&mut ShaderGraph> {
1504 let id = self.active_graph?;
1505 self.graphs.get_mut(&id)
1506 }
1507
1508 pub fn active_graph_ref(&self) -> Option<&ShaderGraph> {
1509 let id = self.active_graph?;
1510 self.graphs.get(&id)
1511 }
1512
1513 fn build_default_pbr(&mut self, graph_id: u32) {
1514 let g = match self.graphs.get_mut(&graph_id) {
1515 Some(g) => g,
1516 None => return,
1517 };
1518
1519 let albedo = g.add_node(ShaderNodeKind::ConstVec3([0.8, 0.6, 0.4]), 50.0, 50.0);
1521 let roughness = g.add_node(ShaderNodeKind::ConstFloat(0.4), 50.0, 150.0);
1522 let metallic = g.add_node(ShaderNodeKind::ConstFloat(0.0), 50.0, 250.0);
1523 let normal_uv = g.add_node(ShaderNodeKind::VertexUV, 50.0, 350.0);
1524 let normal_n = g.add_node(ShaderNodeKind::VertexNormal, 50.0, 430.0);
1525 let output = g.add_node(ShaderNodeKind::PbrOutput, 450.0, 100.0);
1526
1527 let _ = g.connect(
1528 ShaderPortId::output(albedo, 0),
1529 ShaderPortId::input(output, 0),
1530 );
1531 let _ = g.connect(
1532 ShaderPortId::output(normal_n, 0),
1533 ShaderPortId::input(output, 1),
1534 );
1535 let _ = g.connect(
1536 ShaderPortId::output(metallic, 0),
1537 ShaderPortId::input(output, 2),
1538 );
1539 let _ = g.connect(
1540 ShaderPortId::output(roughness, 0),
1541 ShaderPortId::input(output, 3),
1542 );
1543 let _ = normal_uv; }
1545}
1546
1547impl Default for ShaderGraphLibrary {
1548 fn default() -> Self { Self::new() }
1549}
1550
1551#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1554pub enum ShaderEditorTool {
1555 Select,
1556 Connect,
1557 Pan,
1558 Comment,
1559 Reroute,
1560}
1561
1562#[derive(Debug, Clone)]
1563pub enum ShaderEditorAction {
1564 AddNode { id: ShaderNodeId, kind_label: String, graph: u32 },
1565 RemoveNode { id: ShaderNodeId, graph: u32 },
1566 MoveNode { id: ShaderNodeId, from: [f32; 2], to: [f32; 2] },
1567 AddConnection { from: ShaderPortId, to: ShaderPortId, graph: u32 },
1568 RemoveConnection { from: ShaderPortId, to: ShaderPortId, graph: u32 },
1569 ChangeParam { node: ShaderNodeId, param: String, before: String, after: String },
1570 CreateGraph { id: u32, name: String },
1571 DeleteGraph { id: u32, name: String },
1572}
1573
1574#[derive(Debug, Clone)]
1575pub struct NodeSearchResult {
1576 pub kind_label: String,
1577 pub category: String,
1578 pub description: &'static str,
1579}
1580
1581#[derive(Debug)]
1582pub struct ShaderGraphEditor {
1583 pub library: ShaderGraphLibrary,
1584 pub active_tool: ShaderEditorTool,
1585 pub selected_nodes: HashSet<ShaderNodeId>,
1586 pub dragging_node: Option<(ShaderNodeId, [f32; 2])>,
1587 pub connecting_from: Option<ShaderPortId>,
1588 pub search_query: String,
1589 pub search_open: bool,
1590 pub search_results: Vec<NodeSearchResult>,
1591 pub compile_result: Option<ShaderCompileResult>,
1592 pub auto_compile: bool,
1593 pub show_node_previews: bool,
1594 pub grid_snap: bool,
1595 pub grid_size: f32,
1596 undo_stack: Vec<ShaderEditorAction>,
1597 redo_stack: Vec<ShaderEditorAction>,
1598}
1599
1600impl ShaderGraphEditor {
1601 pub fn new() -> Self {
1602 Self {
1603 library: ShaderGraphLibrary::new(),
1604 active_tool: ShaderEditorTool::Select,
1605 selected_nodes: HashSet::new(),
1606 dragging_node: None,
1607 connecting_from: None,
1608 search_query: String::new(),
1609 search_open: false,
1610 search_results: Vec::new(),
1611 compile_result: None,
1612 auto_compile: true,
1613 show_node_previews: false,
1614 grid_snap: true,
1615 grid_size: 16.0,
1616 undo_stack: Vec::new(),
1617 redo_stack: Vec::new(),
1618 }
1619 }
1620
1621 pub fn compile_active(&mut self) {
1622 if let Some(graph) = self.library.active_graph_ref() {
1623 self.compile_result = Some(graph.compile());
1624 }
1625 }
1626
1627 pub fn add_node_at(
1628 &mut self,
1629 kind: ShaderNodeKind,
1630 x: f32,
1631 y: f32,
1632 ) -> Option<ShaderNodeId> {
1633 let graph_id = self.library.active_graph?;
1634 let label = kind.label();
1635 let g = self.library.graph_mut(graph_id)?;
1636 let nx = if self.grid_snap { (x / self.grid_size).round() * self.grid_size } else { x };
1637 let ny = if self.grid_snap { (y / self.grid_size).round() * self.grid_size } else { y };
1638 let id = g.add_node(kind, nx, ny);
1639 self.undo_stack.push(ShaderEditorAction::AddNode {
1640 id, kind_label: label, graph: graph_id,
1641 });
1642 self.redo_stack.clear();
1643 if self.auto_compile { self.compile_active(); }
1644 Some(id)
1645 }
1646
1647 pub fn remove_selected(&mut self) {
1648 let ids: Vec<ShaderNodeId> = self.selected_nodes.drain().collect();
1649 if let Some(graph_id) = self.library.active_graph {
1650 if let Some(g) = self.library.graph_mut(graph_id) {
1651 for id in ids {
1652 g.remove_node(id);
1653 self.undo_stack.push(ShaderEditorAction::RemoveNode {
1654 id, graph: graph_id,
1655 });
1656 }
1657 }
1658 }
1659 self.redo_stack.clear();
1660 if self.auto_compile { self.compile_active(); }
1661 }
1662
1663 pub fn try_connect(&mut self, from: ShaderPortId, to: ShaderPortId) -> bool {
1664 let graph_id = match self.library.active_graph {
1665 Some(id) => id,
1666 None => return false,
1667 };
1668 let g = match self.library.graph_mut(graph_id) {
1669 Some(g) => g,
1670 None => return false,
1671 };
1672 if g.connect(from, to).is_ok() {
1673 self.undo_stack.push(ShaderEditorAction::AddConnection {
1674 from, to, graph: graph_id,
1675 });
1676 self.redo_stack.clear();
1677 if self.auto_compile { self.compile_active(); }
1678 true
1679 } else {
1680 false
1681 }
1682 }
1683
1684 pub fn move_node(&mut self, id: ShaderNodeId, dx: f32, dy: f32) {
1685 let graph_id = match self.library.active_graph {
1686 Some(gid) => gid,
1687 None => return,
1688 };
1689 if let Some(g) = self.library.graph_mut(graph_id) {
1690 if let Some(node) = g.node_mut(id) {
1691 let from = node.position;
1692 let nx = if self.grid_snap {
1693 ((from[0] + dx) / self.grid_size).round() * self.grid_size
1694 } else {
1695 from[0] + dx
1696 };
1697 let ny = if self.grid_snap {
1698 ((from[1] + dy) / self.grid_size).round() * self.grid_size
1699 } else {
1700 from[1] + dy
1701 };
1702 node.position = [nx, ny];
1703 self.undo_stack.push(ShaderEditorAction::MoveNode {
1704 id, from, to: [nx, ny],
1705 });
1706 }
1707 }
1708 }
1709
1710 pub fn select_all(&mut self) {
1711 if let Some(gid) = self.library.active_graph {
1712 if let Some(g) = self.library.graph(gid) {
1713 self.selected_nodes = g.nodes().map(|n| n.id).collect();
1714 }
1715 }
1716 }
1717
1718 pub fn deselect_all(&mut self) {
1719 self.selected_nodes.clear();
1720 }
1721
1722 pub fn search(&mut self, query: &str) {
1723 self.search_query = query.to_string();
1724 let q = query.to_lowercase();
1725 self.search_results.clear();
1726
1727 let candidates: &[(&str, &str, &'static str)] = &[
1728 ("Add", "Math", "Add two values component-wise"),
1729 ("Subtract", "Math", "Subtract B from A"),
1730 ("Multiply", "Math", "Multiply two values"),
1731 ("Divide", "Math", "Divide A by B (safe)"),
1732 ("Lerp", "Math", "Linear interpolation between A and B"),
1733 ("Clamp", "Math", "Clamp to [min, max]"),
1734 ("Smoothstep", "Math", "Hermite interpolation"),
1735 ("Power", "Math", "Raise base to exponent"),
1736 ("Sqrt", "Math", "Square root"),
1737 ("Abs", "Math", "Absolute value"),
1738 ("Sin", "Trig", "Sine of angle in radians"),
1739 ("Cos", "Trig", "Cosine of angle in radians"),
1740 ("Dot", "Vector", "Dot product of two vectors"),
1741 ("Cross", "Vector", "Cross product of two vec3"),
1742 ("Normalize", "Vector", "Normalize a vector to unit length"),
1743 ("Length", "Vector", "Length of a vector"),
1744 ("Reflect", "Vector", "Reflect a direction about a normal"),
1745 ("Split", "Vector", "Split vec4 into x, y, z, w"),
1746 ("Merge", "Vector", "Merge x, y, z, w into vec4"),
1747 ("Sample 2D", "Texture", "Sample a 2D texture at UV"),
1748 ("Normal Map", "Texture", "Decode a tangent-space normal map"),
1749 ("Fresnel", "PBR", "Schlick Fresnel approximation"),
1750 ("GGX NDF", "PBR", "GGX normal distribution function"),
1751 ("BRDF Specular", "PBR", "Full Cook-Torrance specular BRDF"),
1752 ("SSS Approx", "PBR", "Subsurface scattering approximation"),
1753 ("Noise 2D", "Noise", "2D gradient noise"),
1754 ("Noise 3D", "Noise", "3D gradient noise"),
1755 ("FBM", "Noise", "Fractal Brownian Motion (multi-octave noise)"),
1756 ("Voronoi", "Noise", "Voronoi/Worley cellular noise"),
1757 ("PBR Output", "Output", "PBR material output node"),
1758 ("Unlit Output", "Output", "Unlit color output node"),
1759 ("Time", "Input", "Current time in seconds"),
1760 ("UV0", "Input", "Primary texture coordinates"),
1761 ("World Position","Input", "World-space fragment position"),
1762 ("View Dir", "Input", "Normalized view direction"),
1763 ("Camera Position","Input", "World-space camera position"),
1764 ("Vertex Normal", "Input", "Interpolated vertex normal"),
1765 ];
1766
1767 for (label, cat, desc) in candidates {
1768 if label.to_lowercase().contains(&q) || cat.to_lowercase().contains(&q) {
1769 self.search_results.push(NodeSearchResult {
1770 kind_label: label.to_string(),
1771 category: cat.to_string(),
1772 description: desc,
1773 });
1774 }
1775 }
1776 }
1777
1778 pub fn undo(&mut self) {
1779 if let Some(action) = self.undo_stack.pop() {
1780 self.redo_stack.push(action);
1782 }
1783 }
1784
1785 pub fn redo(&mut self) {
1786 if let Some(action) = self.redo_stack.pop() {
1787 self.undo_stack.push(action);
1788 }
1789 }
1790
1791 pub fn glsl_source(&self) -> Option<String> {
1792 self.compile_result.as_ref().map(|r| r.source.clone())
1793 }
1794
1795 pub fn has_errors(&self) -> bool {
1796 self.compile_result.as_ref().map(|r| !r.errors.is_empty()).unwrap_or(false)
1797 }
1798
1799 pub fn graph_stats(&self) -> Option<(usize, usize)> {
1800 let g = self.library.active_graph_ref()?;
1801 Some((g.node_count(), g.connection_count()))
1802 }
1803}
1804
1805impl Default for ShaderGraphEditor {
1806 fn default() -> Self { Self::new() }
1807}
1808
1809#[cfg(test)]
1812mod tests {
1813 use super::*;
1814
1815 #[test]
1816 fn test_type_compat() {
1817 assert!(ShaderValueType::Float.can_connect_to(ShaderValueType::Vec3));
1818 assert!(!ShaderValueType::Vec2.can_connect_to(ShaderValueType::Vec3));
1819 assert!(ShaderValueType::Vec3.can_connect_to(ShaderValueType::Vec4));
1820 }
1821
1822 #[test]
1823 fn test_default_graph_compiles() {
1824 let lib = ShaderGraphLibrary::new();
1825 let gid = lib.active_graph.unwrap();
1826 let g = lib.graph(gid).unwrap();
1827 let result = g.compile();
1828 assert!(!result.source.is_empty());
1829 }
1830
1831 #[test]
1832 fn test_cycle_detection() {
1833 let mut g = ShaderGraph::new(1, "test".into(), ShaderGraphType::Fragment);
1834 let a = g.add_node(ShaderNodeKind::ConstFloat(1.0), 0.0, 0.0);
1835 let b = g.add_node(ShaderNodeKind::Sin, 100.0, 0.0);
1836 g.connect(ShaderPortId::output(a, 0), ShaderPortId::input(b, 0)).unwrap();
1837 let res = g.connect(ShaderPortId::output(b, 0), ShaderPortId::input(a, 0));
1838 assert!(res.is_err());
1839 }
1840
1841 #[test]
1842 fn test_topo_order() {
1843 let mut g = ShaderGraph::new(1, "test".into(), ShaderGraphType::Fragment);
1844 let a = g.add_node(ShaderNodeKind::ConstFloat(1.0), 0.0, 0.0);
1845 let b = g.add_node(ShaderNodeKind::Sin, 100.0, 0.0);
1846 let c = g.add_node(ShaderNodeKind::PbrOutput, 200.0, 0.0);
1847 g.connect(ShaderPortId::output(a, 0), ShaderPortId::input(b, 0)).unwrap();
1848 g.connect(ShaderPortId::output(b, 0), ShaderPortId::input(c, 0)).unwrap();
1849 let order = g.topological_order();
1850 let ai = order.iter().position(|&x| x == a).unwrap();
1851 let bi = order.iter().position(|&x| x == b).unwrap();
1852 let ci = order.iter().position(|&x| x == c).unwrap();
1853 assert!(ai < bi && bi < ci);
1854 }
1855}