Skip to main content

proof_engine/editor/
shader_graph.rs

1// shader_graph.rs — Visual shader graph editor for proof-engine
2// Builds GLSL fragment/vertex shaders from connected node networks.
3// Supports PBR, unlit, post-processing, and custom compute passes.
4
5use std::collections::{HashMap, HashSet, VecDeque};
6use std::fmt;
7
8// ─── Value types ────────────────────────────────────────────────────────────
9
10#[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        // Allow float → vecN promotion
82        if *self == Self::Float && matches!(target, Self::Vec2 | Self::Vec3 | Self::Vec4) {
83            return true;
84        }
85        // Allow vec3 → vec4 (appends 1.0)
86        if *self == Self::Vec3 && target == Self::Vec4 { return true; }
87        // Allow int → float
88        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// ─── Port definitions ────────────────────────────────────────────────────────
124
125#[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// ─── Node kinds ─────────────────────────────────────────────────────────────
168
169#[derive(Debug, Clone)]
170pub enum ShaderNodeKind {
171    // Inputs
172    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    // Math — arithmetic
199    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    // Math — trig
224    Sin,
225    Cos,
226    Tan,
227    Asin,
228    Acos,
229    Atan,
230    Atan2,
231    Degrees,
232    Radians,
233
234    // Math — exponential / log
235    Exp,
236    Exp2,
237    Log,
238    Log2,
239
240    // Vector ops
241    Dot,
242    Cross,
243    Length,
244    Normalize,
245    Reflect,
246    Refract,
247    FaceForward,
248    Distance,
249    Mix,
250    VectorSplit,   // vec3 → x, y, z
251    VectorMerge,   // x, y, z → vec3
252    VectorSwizzle { swizzle: [u8; 4], out_count: u8 },
253
254    // Matrix ops
255    MatrixMultiply,
256    MatrixTranspose,
257    MatrixInverse,
258    TransformPoint,
259    TransformVector,
260    TransformNormal,
261
262    // Texture sampling
263    SampleTexture2D,
264    SampleTextureCube,
265    SampleTexture2DLod,
266    SampleTexture2DGrad,
267    SampleNormalMap,
268    TextureSize,
269
270    // Color ops
271    ColorToLinear,
272    ColorToGamma,
273    HsvToRgb,
274    RgbToHsv,
275    Luminance,
276    ColorBalance,
277    Hue,
278    Saturation,
279    Brightness,
280
281    // PBR nodes
282    FresnelSchlick,
283    GGXDistribution,
284    SmithGeometry,
285    BRDFSpecular,
286    BRDFDiffuse,
287    EnvBRDFApprox,
288    SubsurfaceApprox,
289    EmissionBlend,
290
291    // SDF integration
292    SdfSample { sdf_graph_id: u32 },
293    SdfNormal { sdf_graph_id: u32 },
294    SdfAO { sdf_graph_id: u32 },
295
296    // Control flow / utility
297    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    // Output
312    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    // Grouping / organization
321    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    /// Returns (input_ports, output_ports)
535    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    /// Emit GLSL expression for this node given named expressions for each input.
918    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// ─── Node struct ─────────────────────────────────────────────────────────────
1158
1159#[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// ─── Shader graph ─────────────────────────────────────────────────────────────
1208
1209#[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        // Validate ports exist
1259        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        // Remove existing connection to same input
1264        self.connections.retain(|c| !(c.to.node == to.node && c.to.port == to.port));
1265
1266        // Check for cycles
1267        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        // BFS from `to` — if we can reach `from`, it's a cycle
1277        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    /// Compile the full GLSL shader source for this graph
1320    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        // Header
1328        lines.push(format!("#version {} core", self.glsl_version));
1329        lines.push(String::new());
1330
1331        // Collect uniforms from input nodes
1332        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        // Built-in uniforms
1349        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        // Varyings from vertex shader
1360        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        // Out
1369        lines.push("out vec4 frag_color;".into());
1370        lines.push(String::new());
1371
1372        // Helper function stubs
1373        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        // Emit node code in topological order
1385        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// ─── Graph library ───────────────────────────────────────────────────────────
1455
1456#[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        // Create default PBR graph
1471        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        // Position nodes in a sensible layout
1520        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; // reserve for later use
1544    }
1545}
1546
1547impl Default for ShaderGraphLibrary {
1548    fn default() -> Self { Self::new() }
1549}
1550
1551// ─── Editor state ────────────────────────────────────────────────────────────
1552
1553#[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            // Simplified: just record it
1781            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// ─── Tests ───────────────────────────────────────────────────────────────────
1810
1811#[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}