Skip to main content

proof_engine/shader_graph/
presets.rs

1//! Preset shader graphs: 15+ ready-made shader graphs built programmatically.
2//! Each preset constructs a complete node graph with all connections.
3
4use super::nodes::{NodeId, NodeType, ParamValue, ShaderGraph, ShaderNode};
5
6/// Factory for creating preset shader graphs.
7pub struct ShaderPresets;
8
9impl ShaderPresets {
10    /// List all available preset names.
11    pub fn list() -> Vec<&'static str> {
12        vec![
13            "void_protocol",
14            "blood_pact",
15            "emerald_engine",
16            "corruption_high",
17            "null_fight",
18            "paradox_invert",
19            "fire_shader",
20            "ice_crystal",
21            "electric_arc",
22            "hologram",
23            "stealth_cloak",
24            "shadow_form",
25            "divine_light",
26            "toxic_cloud",
27            "chaos_rift",
28        ]
29    }
30
31    /// Create a preset shader graph by name.
32    pub fn create(name: &str) -> Option<ShaderGraph> {
33        match name {
34            "void_protocol" => Some(Self::void_protocol()),
35            "blood_pact" => Some(Self::blood_pact()),
36            "emerald_engine" => Some(Self::emerald_engine()),
37            "corruption_high" => Some(Self::corruption_high()),
38            "null_fight" => Some(Self::null_fight()),
39            "paradox_invert" => Some(Self::paradox_invert()),
40            "fire_shader" => Some(Self::fire_shader()),
41            "ice_crystal" => Some(Self::ice_crystal()),
42            "electric_arc" => Some(Self::electric_arc()),
43            "hologram" => Some(Self::hologram()),
44            "stealth_cloak" => Some(Self::stealth_cloak()),
45            "shadow_form" => Some(Self::shadow_form()),
46            "divine_light" => Some(Self::divine_light()),
47            "toxic_cloud" => Some(Self::toxic_cloud()),
48            "chaos_rift" => Some(Self::chaos_rift()),
49            _ => None,
50        }
51    }
52
53    // -----------------------------------------------------------------------
54    // Void Protocol — black hole distortion effect
55    // -----------------------------------------------------------------------
56    pub fn void_protocol() -> ShaderGraph {
57        let mut g = ShaderGraph::new("void_protocol");
58
59        // Source: vertex position for distance calculation
60        let pos = g.add_node(NodeType::VertexPosition);
61        let time = g.add_node(NodeType::Time);
62        let cam = g.add_node(NodeType::CameraPos);
63
64        // Compute view direction
65        let sub_view = g.add_node(NodeType::Sub);
66        // Distance from camera
67        let length = g.add_node(NodeType::Length);
68
69        // Distortion based on distance and time
70        let sin_time = g.add_node(NodeType::Sin);
71        let mul_dist = g.add_node(NodeType::Mul);
72        let distortion_strength = g.add_node(NodeType::Mul);
73
74        // FBM noise for the swirling void
75        let fbm = g.add_node(NodeType::FBM);
76
77        // Dark color (near black with slight purple tint)
78        let void_color = g.add_node(NodeType::Color);
79        if let Some(n) = g.node_mut(void_color) {
80            n.inputs[0].default_value = Some(ParamValue::Vec4([0.02, 0.0, 0.05, 1.0]));
81        }
82
83        // Edge glow color (deep purple)
84        let edge_color = g.add_node(NodeType::Color);
85        if let Some(n) = g.node_mut(edge_color) {
86            n.inputs[0].default_value = Some(ParamValue::Vec4([0.3, 0.0, 0.8, 1.0]));
87        }
88
89        // Fresnel for edge glow
90        let normal = g.add_node(NodeType::VertexNormal);
91        let fresnel = g.add_node(NodeType::Fresnel);
92
93        // Lerp between void color and edge color
94        let lerp_color = g.add_node(NodeType::Lerp);
95
96        // Smoothstep for the distortion falloff
97        let smoothstep = g.add_node(NodeType::Smoothstep);
98
99        // Final color mix
100        let final_mul = g.add_node(NodeType::Mul);
101
102        // Output
103        let main_out = g.add_node(NodeType::MainColor);
104        let emission = g.add_node(NodeType::EmissionBuffer);
105        let bloom_out = g.add_node(NodeType::BloomBuffer);
106
107        // Connections: build the distortion pipeline
108        // view direction = cam_pos - vertex_pos (as floats for length)
109        g.connect(cam, 0, sub_view, 0);    // a = camera pos
110        g.connect(pos, 0, sub_view, 1);    // b = vertex pos
111
112        // length of view direction
113        g.connect(pos, 0, length, 0);
114
115        // sin(time) for animation
116        g.connect(time, 0, sin_time, 0);
117
118        // distortion = sin(time) * distance_factor
119        g.connect(sin_time, 0, mul_dist, 0);
120        g.connect(length, 0, mul_dist, 1);
121
122        // FBM noise using position and time-scaled offset
123        g.connect(pos, 0, fbm, 0);
124        g.connect(time, 0, fbm, 1);
125
126        // distortion_strength = mul_dist * fbm
127        g.connect(mul_dist, 0, distortion_strength, 0);
128        g.connect(fbm, 0, distortion_strength, 1);
129
130        // Fresnel
131        g.connect(normal, 0, fresnel, 0);
132        g.connect(sub_view, 0, fresnel, 1);
133
134        // smoothstep(0.2, 0.8, fresnel) for edge falloff
135        g.connect(fresnel, 0, smoothstep, 2);
136
137        // lerp(void_color, edge_color, smoothstep)
138        g.connect(void_color, 0, lerp_color, 0);
139        g.connect(edge_color, 0, lerp_color, 1);
140        g.connect(smoothstep, 0, lerp_color, 2);
141
142        // Final: lerp_result * (1 + distortion_strength)
143        g.connect(lerp_color, 0, final_mul, 0);
144        g.connect(distortion_strength, 0, final_mul, 1);
145
146        // Outputs
147        g.connect(final_mul, 0, main_out, 0);
148        g.connect(edge_color, 0, emission, 0);
149        g.connect(edge_color, 0, bloom_out, 0);
150
151        g
152    }
153
154    // -----------------------------------------------------------------------
155    // Blood Pact — red pulse vein effect
156    // -----------------------------------------------------------------------
157    pub fn blood_pact() -> ShaderGraph {
158        let mut g = ShaderGraph::new("blood_pact");
159
160        let pos = g.add_node(NodeType::VertexPosition);
161        let time = g.add_node(NodeType::Time);
162        let normal = g.add_node(NodeType::VertexNormal);
163
164        // Voronoi for vein pattern
165        let voronoi = g.add_node(NodeType::Voronoi);
166        if let Some(n) = g.node_mut(voronoi) {
167            n.inputs[1].default_value = Some(ParamValue::Float(5.0)); // scale
168            n.inputs[2].default_value = Some(ParamValue::Float(0.8)); // jitter
169        }
170
171        // Pulse effect: sin(time * speed) for pulsing
172        let pulse_speed = g.add_node(NodeType::Mul);
173        if let Some(n) = g.node_mut(pulse_speed) {
174            n.inputs[1].default_value = Some(ParamValue::Float(3.0));
175        }
176        let pulse_sin = g.add_node(NodeType::Sin);
177        let pulse_remap = g.add_node(NodeType::Remap);
178        if let Some(n) = g.node_mut(pulse_remap) {
179            n.inputs[1].default_value = Some(ParamValue::Float(-1.0)); // in_min
180            n.inputs[2].default_value = Some(ParamValue::Float(1.0));  // in_max
181            n.inputs[3].default_value = Some(ParamValue::Float(0.3));  // out_min
182            n.inputs[4].default_value = Some(ParamValue::Float(1.0));  // out_max
183        }
184
185        // Dark red base
186        let base_color = g.add_node(NodeType::Color);
187        if let Some(n) = g.node_mut(base_color) {
188            n.inputs[0].default_value = Some(ParamValue::Vec4([0.15, 0.0, 0.0, 1.0]));
189        }
190
191        // Bright red vein color
192        let vein_color = g.add_node(NodeType::Color);
193        if let Some(n) = g.node_mut(vein_color) {
194            n.inputs[0].default_value = Some(ParamValue::Vec4([0.9, 0.05, 0.05, 1.0]));
195        }
196
197        // Invert voronoi distance for vein brightness
198        let vein_width = g.add_node(NodeType::Smoothstep);
199        if let Some(n) = g.node_mut(vein_width) {
200            n.inputs[0].default_value = Some(ParamValue::Float(0.0));  // edge0
201            n.inputs[1].default_value = Some(ParamValue::Float(0.15)); // edge1
202        }
203
204        // Vein intensity = vein_width * pulse
205        let vein_intensity = g.add_node(NodeType::Mul);
206
207        // Lerp base/vein by intensity
208        let color_lerp = g.add_node(NodeType::Lerp);
209
210        // Fresnel for subsurface scattering look
211        let fresnel = g.add_node(NodeType::Fresnel);
212        if let Some(n) = g.node_mut(fresnel) {
213            n.inputs[2].default_value = Some(ParamValue::Float(3.0)); // power
214        }
215
216        // Final color modulation
217        let final_add = g.add_node(NodeType::Add);
218
219        let main_out = g.add_node(NodeType::MainColor);
220        let emission = g.add_node(NodeType::EmissionBuffer);
221
222        // Connections
223        g.connect(pos, 0, voronoi, 0);
224        g.connect(time, 0, pulse_speed, 0);
225        g.connect(pulse_speed, 0, pulse_sin, 0);
226        g.connect(pulse_sin, 0, pulse_remap, 0);
227
228        g.connect(voronoi, 0, vein_width, 2);  // x = voronoi distance
229        g.connect(vein_width, 0, vein_intensity, 0);
230        g.connect(pulse_remap, 0, vein_intensity, 1);
231
232        g.connect(base_color, 0, color_lerp, 0);
233        g.connect(vein_color, 0, color_lerp, 1);
234        g.connect(vein_intensity, 0, color_lerp, 2);
235
236        g.connect(normal, 0, fresnel, 0);
237        g.connect(color_lerp, 0, final_add, 0);
238        g.connect(fresnel, 0, final_add, 1);
239
240        g.connect(final_add, 0, main_out, 0);
241        g.connect(vein_color, 0, emission, 0);
242
243        g
244    }
245
246    // -----------------------------------------------------------------------
247    // Emerald Engine — green energy field effect
248    // -----------------------------------------------------------------------
249    pub fn emerald_engine() -> ShaderGraph {
250        let mut g = ShaderGraph::new("emerald_engine");
251
252        let pos = g.add_node(NodeType::VertexPosition);
253        let time = g.add_node(NodeType::Time);
254        let normal = g.add_node(NodeType::VertexNormal);
255
256        // FBM for energy turbulence
257        let fbm = g.add_node(NodeType::FBM);
258        if let Some(n) = g.node_mut(fbm) {
259            n.inputs[1].default_value = Some(ParamValue::Float(3.0));
260            n.inputs[3].default_value = Some(ParamValue::Float(2.5));
261            n.inputs[4].default_value = Some(ParamValue::Float(0.6));
262        }
263
264        // Animated position offset
265        let time_scale = g.add_node(NodeType::Mul);
266        if let Some(n) = g.node_mut(time_scale) {
267            n.inputs[1].default_value = Some(ParamValue::Float(0.5));
268        }
269        let pos_offset = g.add_node(NodeType::Add);
270
271        // Dark green base
272        let base = g.add_node(NodeType::Color);
273        if let Some(n) = g.node_mut(base) {
274            n.inputs[0].default_value = Some(ParamValue::Vec4([0.0, 0.15, 0.05, 1.0]));
275        }
276
277        // Bright emerald
278        let bright = g.add_node(NodeType::Color);
279        if let Some(n) = g.node_mut(bright) {
280            n.inputs[0].default_value = Some(ParamValue::Vec4([0.1, 0.95, 0.3, 1.0]));
281        }
282
283        // Fresnel
284        let fresnel = g.add_node(NodeType::Fresnel);
285        if let Some(n) = g.node_mut(fresnel) {
286            n.inputs[2].default_value = Some(ParamValue::Float(2.5));
287        }
288
289        // Energy intensity = fbm * fresnel
290        let energy = g.add_node(NodeType::Mul);
291
292        // Smoothstep for clean edges
293        let ss = g.add_node(NodeType::Smoothstep);
294        if let Some(n) = g.node_mut(ss) {
295            n.inputs[0].default_value = Some(ParamValue::Float(0.2));
296            n.inputs[1].default_value = Some(ParamValue::Float(0.7));
297        }
298
299        // Color lerp
300        let color_mix = g.add_node(NodeType::Lerp);
301
302        let main_out = g.add_node(NodeType::MainColor);
303        let emission = g.add_node(NodeType::EmissionBuffer);
304        let bloom_out = g.add_node(NodeType::BloomBuffer);
305
306        g.connect(time, 0, time_scale, 0);
307        g.connect(pos, 0, pos_offset, 0);
308        g.connect(time_scale, 0, pos_offset, 1);
309        g.connect(pos_offset, 0, fbm, 0);
310
311        g.connect(normal, 0, fresnel, 0);
312        g.connect(fbm, 0, energy, 0);
313        g.connect(fresnel, 0, energy, 1);
314
315        g.connect(energy, 0, ss, 2);
316        g.connect(base, 0, color_mix, 0);
317        g.connect(bright, 0, color_mix, 1);
318        g.connect(ss, 0, color_mix, 2);
319
320        g.connect(color_mix, 0, main_out, 0);
321        g.connect(bright, 0, emission, 0);
322        g.connect(bright, 0, bloom_out, 0);
323
324        g
325    }
326
327    // -----------------------------------------------------------------------
328    // Corruption High — purple decay noise
329    // -----------------------------------------------------------------------
330    pub fn corruption_high() -> ShaderGraph {
331        let mut g = ShaderGraph::new("corruption_high");
332
333        let pos = g.add_node(NodeType::VertexPosition);
334        let time = g.add_node(NodeType::Time);
335        let normal = g.add_node(NodeType::VertexNormal);
336
337        // Turbulence noise for corruption pattern
338        let turb = g.add_node(NodeType::Turbulence);
339        if let Some(n) = g.node_mut(turb) {
340            n.inputs[1].default_value = Some(ParamValue::Float(4.0));
341        }
342
343        // Perlin for secondary detail
344        let perlin = g.add_node(NodeType::Perlin);
345        if let Some(n) = g.node_mut(perlin) {
346            n.inputs[1].default_value = Some(ParamValue::Float(8.0));
347        }
348
349        // Combine noises
350        let noise_mix = g.add_node(NodeType::Mul);
351
352        // Dissolve effect
353        let dissolve = g.add_node(NodeType::Dissolve);
354        if let Some(n) = g.node_mut(dissolve) {
355            n.inputs[2].default_value = Some(ParamValue::Float(0.4)); // threshold
356            n.inputs[3].default_value = Some(ParamValue::Float(0.08)); // edge width
357            n.inputs[4].default_value = Some(ParamValue::Vec4([0.6, 0.0, 1.0, 1.0])); // purple edge
358        }
359
360        // Purple base color
361        let base = g.add_node(NodeType::Color);
362        if let Some(n) = g.node_mut(base) {
363            n.inputs[0].default_value = Some(ParamValue::Vec4([0.2, 0.0, 0.3, 1.0]));
364        }
365
366        // Animated threshold
367        let thresh_sin = g.add_node(NodeType::Sin);
368        let thresh_remap = g.add_node(NodeType::Remap);
369        if let Some(n) = g.node_mut(thresh_remap) {
370            n.inputs[1].default_value = Some(ParamValue::Float(-1.0));
371            n.inputs[2].default_value = Some(ParamValue::Float(1.0));
372            n.inputs[3].default_value = Some(ParamValue::Float(0.2));
373            n.inputs[4].default_value = Some(ParamValue::Float(0.7));
374        }
375
376        // Fresnel for edge highlight
377        let fresnel = g.add_node(NodeType::Fresnel);
378
379        let main_out = g.add_node(NodeType::MainColor);
380        let emission = g.add_node(NodeType::EmissionBuffer);
381
382        g.connect(pos, 0, turb, 0);
383        g.connect(pos, 0, perlin, 0);
384        g.connect(turb, 0, noise_mix, 0);
385        g.connect(perlin, 0, noise_mix, 1);
386
387        g.connect(base, 0, dissolve, 0);
388        g.connect(noise_mix, 0, dissolve, 1);
389
390        g.connect(time, 0, thresh_sin, 0);
391        g.connect(thresh_sin, 0, thresh_remap, 0);
392
393        g.connect(normal, 0, fresnel, 0);
394
395        g.connect(dissolve, 0, main_out, 0);
396        g.connect(dissolve, 0, emission, 0);
397
398        g
399    }
400
401    // -----------------------------------------------------------------------
402    // Null Fight — desaturated combat effect
403    // -----------------------------------------------------------------------
404    pub fn null_fight() -> ShaderGraph {
405        let mut g = ShaderGraph::new("null_fight");
406
407        let pos = g.add_node(NodeType::VertexPosition);
408        let time = g.add_node(NodeType::Time);
409        let normal = g.add_node(NodeType::VertexNormal);
410
411        // Game state variable for combat intensity
412        let combat_var = g.add_node(NodeType::GameStateVar);
413        if let Some(n) = g.node_mut(combat_var) {
414            n.inputs[0].default_value = Some(ParamValue::String("combat_intensity".to_string()));
415        }
416
417        // Base white-ish color
418        let base = g.add_node(NodeType::Color);
419        if let Some(n) = g.node_mut(base) {
420            n.inputs[0].default_value = Some(ParamValue::Vec4([0.8, 0.8, 0.85, 1.0]));
421        }
422
423        // Desaturation based on combat intensity
424        let desat = g.add_node(NodeType::Saturation);
425        if let Some(n) = g.node_mut(desat) {
426            n.inputs[1].default_value = Some(ParamValue::Float(0.1)); // nearly grayscale
427        }
428
429        // Contrast boost
430        let contrast = g.add_node(NodeType::Contrast);
431        if let Some(n) = g.node_mut(contrast) {
432            n.inputs[1].default_value = Some(ParamValue::Float(1.8));
433        }
434
435        // Sharp edge detection for combat outlines
436        let fresnel = g.add_node(NodeType::Fresnel);
437        if let Some(n) = g.node_mut(fresnel) {
438            n.inputs[2].default_value = Some(ParamValue::Float(4.0));
439        }
440
441        // Outline effect
442        let outline = g.add_node(NodeType::Outline);
443        if let Some(n) = g.node_mut(outline) {
444            n.inputs[3].default_value = Some(ParamValue::Float(2.0)); // width
445            n.inputs[4].default_value = Some(ParamValue::Vec4([0.1, 0.1, 0.15, 1.0])); // dark outline
446        }
447
448        // FBM for subtle noise
449        let fbm = g.add_node(NodeType::FBM);
450        if let Some(n) = g.node_mut(fbm) {
451            n.inputs[1].default_value = Some(ParamValue::Float(6.0));
452        }
453
454        // Subtle noise modulation
455        let noise_mul = g.add_node(NodeType::Mul);
456        if let Some(n) = g.node_mut(noise_mul) {
457            n.inputs[1].default_value = Some(ParamValue::Float(0.1));
458        }
459        let final_add = g.add_node(NodeType::Add);
460
461        let main_out = g.add_node(NodeType::MainColor);
462
463        // Wire it up
464        g.connect(base, 0, desat, 0);
465        g.connect(desat, 0, contrast, 0);
466        g.connect(contrast, 0, outline, 0);
467        g.connect(normal, 0, outline, 2);
468        g.connect(normal, 0, fresnel, 0);
469
470        g.connect(pos, 0, fbm, 0);
471        g.connect(fbm, 0, noise_mul, 0);
472        g.connect(outline, 0, final_add, 0);
473        g.connect(noise_mul, 0, final_add, 1);
474
475        g.connect(final_add, 0, main_out, 0);
476
477        g
478    }
479
480    // -----------------------------------------------------------------------
481    // Paradox Invert — inverted colors with time warp
482    // -----------------------------------------------------------------------
483    pub fn paradox_invert() -> ShaderGraph {
484        let mut g = ShaderGraph::new("paradox_invert");
485
486        let pos = g.add_node(NodeType::VertexPosition);
487        let time = g.add_node(NodeType::Time);
488
489        // Base color from vertex position (psychedelic)
490        let pos_fract = g.add_node(NodeType::Fract);
491        let color_from_pos = g.add_node(NodeType::Color);
492        if let Some(n) = g.node_mut(color_from_pos) {
493            n.inputs[0].default_value = Some(ParamValue::Vec4([0.5, 0.3, 0.8, 1.0]));
494        }
495
496        // Time warp: sin(time * varying_speed)
497        let time_mul = g.add_node(NodeType::Mul);
498        if let Some(n) = g.node_mut(time_mul) {
499            n.inputs[1].default_value = Some(ParamValue::Float(2.0));
500        }
501        let time_sin = g.add_node(NodeType::Sin);
502        let time_cos = g.add_node(NodeType::Cos);
503
504        // Hue shift driven by time
505        let hue = g.add_node(NodeType::Hue);
506
507        // Invert colors
508        let invert = g.add_node(NodeType::Invert);
509
510        // Lerp between normal and inverted based on sin(time)
511        let lerp_invert = g.add_node(NodeType::Lerp);
512        let abs_sin = g.add_node(NodeType::Abs);
513
514        // Posterize for glitch effect
515        let poster = g.add_node(NodeType::Posterize);
516        if let Some(n) = g.node_mut(poster) {
517            n.inputs[1].default_value = Some(ParamValue::Float(6.0));
518        }
519
520        // Chromatic aberration
521        let perlin = g.add_node(NodeType::Perlin);
522
523        let main_out = g.add_node(NodeType::MainColor);
524        let emission = g.add_node(NodeType::EmissionBuffer);
525
526        g.connect(time, 0, time_mul, 0);
527        g.connect(time_mul, 0, time_sin, 0);
528        g.connect(time_mul, 0, time_cos, 0);
529
530        g.connect(color_from_pos, 0, hue, 0);
531        g.connect(time_sin, 0, hue, 1);
532
533        g.connect(hue, 0, invert, 0);
534
535        g.connect(time_sin, 0, abs_sin, 0);
536        g.connect(hue, 0, lerp_invert, 0);
537        g.connect(invert, 0, lerp_invert, 1);
538        g.connect(abs_sin, 0, lerp_invert, 2);
539
540        g.connect(lerp_invert, 0, poster, 0);
541
542        g.connect(poster, 0, main_out, 0);
543        g.connect(poster, 0, emission, 0);
544
545        g
546    }
547
548    // -----------------------------------------------------------------------
549    // Fire Shader — realistic fire effect
550    // -----------------------------------------------------------------------
551    pub fn fire_shader() -> ShaderGraph {
552        let mut g = ShaderGraph::new("fire_shader");
553
554        let pos = g.add_node(NodeType::VertexPosition);
555        let time = g.add_node(NodeType::Time);
556
557        // Scrolling noise for fire movement
558        let scroll = g.add_node(NodeType::Mul);
559        if let Some(n) = g.node_mut(scroll) {
560            n.inputs[1].default_value = Some(ParamValue::Float(2.0));
561        }
562        let scroll_offset = g.add_node(NodeType::Add);
563
564        // Turbulence for fire shape
565        let turb = g.add_node(NodeType::Turbulence);
566        if let Some(n) = g.node_mut(turb) {
567            n.inputs[1].default_value = Some(ParamValue::Float(3.0));
568            n.inputs[3].default_value = Some(ParamValue::Float(2.5));
569            n.inputs[4].default_value = Some(ParamValue::Float(0.5));
570        }
571
572        // FBM for detail
573        let fbm = g.add_node(NodeType::FBM);
574        if let Some(n) = g.node_mut(fbm) {
575            n.inputs[1].default_value = Some(ParamValue::Float(6.0));
576        }
577
578        // Combine noises
579        let noise_add = g.add_node(NodeType::Add);
580        let noise_clamp = g.add_node(NodeType::Clamp);
581
582        // Gradient map: dark red -> orange -> yellow -> white
583        let grad_low = g.add_node(NodeType::GradientMap);
584        if let Some(n) = g.node_mut(grad_low) {
585            n.inputs[1].default_value = Some(ParamValue::Vec3([0.1, 0.0, 0.0]));  // dark red
586            n.inputs[2].default_value = Some(ParamValue::Vec3([1.0, 0.3, 0.0]));  // orange
587        }
588        let grad_high = g.add_node(NodeType::GradientMap);
589        if let Some(n) = g.node_mut(grad_high) {
590            n.inputs[1].default_value = Some(ParamValue::Vec3([1.0, 0.6, 0.0]));  // yellow-orange
591            n.inputs[2].default_value = Some(ParamValue::Vec3([1.0, 1.0, 0.8]));  // white-yellow
592        }
593
594        // Lerp between gradients
595        let fire_lerp = g.add_node(NodeType::Lerp);
596
597        // Height-based falloff (fire fades at top)
598        let height = g.add_node(NodeType::Fract);
599
600        let main_out = g.add_node(NodeType::MainColor);
601        let emission = g.add_node(NodeType::EmissionBuffer);
602        let bloom_out = g.add_node(NodeType::BloomBuffer);
603
604        g.connect(time, 0, scroll, 0);
605        g.connect(pos, 0, scroll_offset, 0);
606        g.connect(scroll, 0, scroll_offset, 1);
607        g.connect(scroll_offset, 0, turb, 0);
608        g.connect(scroll_offset, 0, fbm, 0);
609
610        g.connect(turb, 0, noise_add, 0);
611        g.connect(fbm, 0, noise_add, 1);
612        g.connect(noise_add, 0, noise_clamp, 0);
613
614        g.connect(noise_clamp, 0, grad_low, 0);
615        g.connect(noise_clamp, 0, grad_high, 0);
616        g.connect(grad_low, 0, fire_lerp, 0);
617        g.connect(grad_high, 0, fire_lerp, 1);
618        g.connect(noise_clamp, 0, fire_lerp, 2);
619
620        g.connect(fire_lerp, 0, main_out, 0);
621        g.connect(fire_lerp, 0, emission, 0);
622        g.connect(fire_lerp, 0, bloom_out, 0);
623
624        g
625    }
626
627    // -----------------------------------------------------------------------
628    // Ice Crystal — frozen crystalline effect
629    // -----------------------------------------------------------------------
630    pub fn ice_crystal() -> ShaderGraph {
631        let mut g = ShaderGraph::new("ice_crystal");
632
633        let pos = g.add_node(NodeType::VertexPosition);
634        let normal = g.add_node(NodeType::VertexNormal);
635        let time = g.add_node(NodeType::Time);
636
637        // Voronoi for crystal facets
638        let voronoi = g.add_node(NodeType::Voronoi);
639        if let Some(n) = g.node_mut(voronoi) {
640            n.inputs[1].default_value = Some(ParamValue::Float(8.0));
641            n.inputs[2].default_value = Some(ParamValue::Float(0.9));
642        }
643
644        // Ice blue base
645        let base = g.add_node(NodeType::Color);
646        if let Some(n) = g.node_mut(base) {
647            n.inputs[0].default_value = Some(ParamValue::Vec4([0.7, 0.85, 0.95, 0.8]));
648        }
649
650        // Deep blue
651        let deep = g.add_node(NodeType::Color);
652        if let Some(n) = g.node_mut(deep) {
653            n.inputs[0].default_value = Some(ParamValue::Vec4([0.1, 0.2, 0.5, 1.0]));
654        }
655
656        // Fresnel for ice rim
657        let fresnel = g.add_node(NodeType::Fresnel);
658        if let Some(n) = g.node_mut(fresnel) {
659            n.inputs[2].default_value = Some(ParamValue::Float(3.0));
660        }
661
662        // Crystal edges from voronoi
663        let edge_step = g.add_node(NodeType::Smoothstep);
664        if let Some(n) = g.node_mut(edge_step) {
665            n.inputs[0].default_value = Some(ParamValue::Float(0.02));
666            n.inputs[1].default_value = Some(ParamValue::Float(0.08));
667        }
668
669        // Color mixing
670        let color_lerp = g.add_node(NodeType::Lerp);
671        let edge_add = g.add_node(NodeType::Add);
672
673        // Subtle sparkle from noise
674        let sparkle = g.add_node(NodeType::Perlin);
675        if let Some(n) = g.node_mut(sparkle) {
676            n.inputs[1].default_value = Some(ParamValue::Float(20.0));
677        }
678        let sparkle_step = g.add_node(NodeType::Step);
679        if let Some(n) = g.node_mut(sparkle_step) {
680            n.inputs[0].default_value = Some(ParamValue::Float(0.9));
681        }
682
683        let main_out = g.add_node(NodeType::MainColor);
684        let bloom_out = g.add_node(NodeType::BloomBuffer);
685        let normal_out = g.add_node(NodeType::NormalOutput);
686
687        g.connect(pos, 0, voronoi, 0);
688        g.connect(normal, 0, fresnel, 0);
689        g.connect(voronoi, 0, edge_step, 2);
690
691        g.connect(base, 0, color_lerp, 0);
692        g.connect(deep, 0, color_lerp, 1);
693        g.connect(fresnel, 0, color_lerp, 2);
694
695        g.connect(color_lerp, 0, edge_add, 0);
696        g.connect(edge_step, 0, edge_add, 1);
697
698        g.connect(pos, 0, sparkle, 0);
699        g.connect(time, 0, sparkle, 2);
700        g.connect(sparkle, 0, sparkle_step, 1);
701
702        g.connect(edge_add, 0, main_out, 0);
703        g.connect(base, 0, bloom_out, 0);
704        g.connect(normal, 0, normal_out, 0);
705
706        g
707    }
708
709    // -----------------------------------------------------------------------
710    // Electric Arc — lightning/electricity effect
711    // -----------------------------------------------------------------------
712    pub fn electric_arc() -> ShaderGraph {
713        let mut g = ShaderGraph::new("electric_arc");
714
715        let pos = g.add_node(NodeType::VertexPosition);
716        let time = g.add_node(NodeType::Time);
717
718        // Fast-moving noise for lightning
719        let time_fast = g.add_node(NodeType::Mul);
720        if let Some(n) = g.node_mut(time_fast) {
721            n.inputs[1].default_value = Some(ParamValue::Float(8.0));
722        }
723        let noise_pos = g.add_node(NodeType::Add);
724
725        let perlin1 = g.add_node(NodeType::Perlin);
726        if let Some(n) = g.node_mut(perlin1) {
727            n.inputs[1].default_value = Some(ParamValue::Float(10.0));
728        }
729        let perlin2 = g.add_node(NodeType::Perlin);
730        if let Some(n) = g.node_mut(perlin2) {
731            n.inputs[1].default_value = Some(ParamValue::Float(20.0));
732        }
733
734        // Sharp threshold for lightning bolts
735        let abs_noise = g.add_node(NodeType::Abs);
736        let bolt_step = g.add_node(NodeType::Smoothstep);
737        if let Some(n) = g.node_mut(bolt_step) {
738            n.inputs[0].default_value = Some(ParamValue::Float(0.85));
739            n.inputs[1].default_value = Some(ParamValue::Float(0.95));
740        }
741
742        // Electric blue
743        let electric_color = g.add_node(NodeType::Color);
744        if let Some(n) = g.node_mut(electric_color) {
745            n.inputs[0].default_value = Some(ParamValue::Vec4([0.3, 0.5, 1.0, 1.0]));
746        }
747
748        // White core
749        let core_color = g.add_node(NodeType::Color);
750        if let Some(n) = g.node_mut(core_color) {
751            n.inputs[0].default_value = Some(ParamValue::Vec4([0.9, 0.95, 1.0, 1.0]));
752        }
753
754        // Lerp colors
755        let color_lerp = g.add_node(NodeType::Lerp);
756        let intensity = g.add_node(NodeType::Mul);
757
758        let main_out = g.add_node(NodeType::MainColor);
759        let emission = g.add_node(NodeType::EmissionBuffer);
760        let bloom_out = g.add_node(NodeType::BloomBuffer);
761
762        g.connect(time, 0, time_fast, 0);
763        g.connect(pos, 0, noise_pos, 0);
764        g.connect(time_fast, 0, noise_pos, 1);
765        g.connect(noise_pos, 0, perlin1, 0);
766        g.connect(noise_pos, 0, perlin2, 0);
767
768        g.connect(perlin1, 0, abs_noise, 0);
769        g.connect(abs_noise, 0, bolt_step, 2);
770
771        g.connect(electric_color, 0, color_lerp, 0);
772        g.connect(core_color, 0, color_lerp, 1);
773        g.connect(bolt_step, 0, color_lerp, 2);
774
775        g.connect(color_lerp, 0, intensity, 0);
776        g.connect(bolt_step, 0, intensity, 1);
777
778        g.connect(intensity, 0, main_out, 0);
779        g.connect(intensity, 0, emission, 0);
780        g.connect(core_color, 0, bloom_out, 0);
781
782        g
783    }
784
785    // -----------------------------------------------------------------------
786    // Hologram — holographic display effect
787    // -----------------------------------------------------------------------
788    pub fn hologram() -> ShaderGraph {
789        let mut g = ShaderGraph::new("hologram");
790
791        let pos = g.add_node(NodeType::VertexPosition);
792        let normal = g.add_node(NodeType::VertexNormal);
793        let time = g.add_node(NodeType::Time);
794
795        // Scanlines
796        let scanline_scale = g.add_node(NodeType::Mul);
797        if let Some(n) = g.node_mut(scanline_scale) {
798            n.inputs[1].default_value = Some(ParamValue::Float(50.0));
799        }
800        let scanline_sin = g.add_node(NodeType::Sin);
801        let scanline_step = g.add_node(NodeType::Step);
802        if let Some(n) = g.node_mut(scanline_step) {
803            n.inputs[0].default_value = Some(ParamValue::Float(0.0));
804        }
805
806        // Hologram blue-cyan color
807        let holo_color = g.add_node(NodeType::Color);
808        if let Some(n) = g.node_mut(holo_color) {
809            n.inputs[0].default_value = Some(ParamValue::Vec4([0.0, 0.7, 1.0, 0.5]));
810        }
811
812        // Fresnel for edge glow
813        let fresnel = g.add_node(NodeType::Fresnel);
814        if let Some(n) = g.node_mut(fresnel) {
815            n.inputs[2].default_value = Some(ParamValue::Float(2.0));
816        }
817
818        // Flicker from noise
819        let flicker = g.add_node(NodeType::Perlin);
820        if let Some(n) = g.node_mut(flicker) {
821            n.inputs[1].default_value = Some(ParamValue::Float(1.0));
822        }
823        let flicker_remap = g.add_node(NodeType::Remap);
824        if let Some(n) = g.node_mut(flicker_remap) {
825            n.inputs[1].default_value = Some(ParamValue::Float(0.0));
826            n.inputs[2].default_value = Some(ParamValue::Float(1.0));
827            n.inputs[3].default_value = Some(ParamValue::Float(0.6));
828            n.inputs[4].default_value = Some(ParamValue::Float(1.0));
829        }
830
831        // Combine: color * scanlines * fresnel * flicker
832        let mul1 = g.add_node(NodeType::Mul);
833        let mul2 = g.add_node(NodeType::Mul);
834        let mul3 = g.add_node(NodeType::Mul);
835
836        // Glitch offset
837        let glitch_noise = g.add_node(NodeType::Perlin);
838        if let Some(n) = g.node_mut(glitch_noise) {
839            n.inputs[1].default_value = Some(ParamValue::Float(100.0));
840        }
841
842        let main_out = g.add_node(NodeType::MainColor);
843        let emission = g.add_node(NodeType::EmissionBuffer);
844
845        g.connect(pos, 0, scanline_scale, 0);
846        g.connect(scanline_scale, 0, scanline_sin, 0);
847        g.connect(scanline_sin, 0, scanline_step, 1);
848
849        g.connect(normal, 0, fresnel, 0);
850
851        g.connect(pos, 0, flicker, 0);
852        g.connect(time, 0, flicker, 2);
853        g.connect(flicker, 0, flicker_remap, 0);
854
855        g.connect(holo_color, 0, mul1, 0);
856        g.connect(scanline_step, 0, mul1, 1);
857        g.connect(mul1, 0, mul2, 0);
858        g.connect(fresnel, 0, mul2, 1);
859        g.connect(mul2, 0, mul3, 0);
860        g.connect(flicker_remap, 0, mul3, 1);
861
862        g.connect(mul3, 0, main_out, 0);
863        g.connect(holo_color, 0, emission, 0);
864
865        g
866    }
867
868    // -----------------------------------------------------------------------
869    // Stealth Cloak — invisibility/refraction effect
870    // -----------------------------------------------------------------------
871    pub fn stealth_cloak() -> ShaderGraph {
872        let mut g = ShaderGraph::new("stealth_cloak");
873
874        let pos = g.add_node(NodeType::VertexPosition);
875        let normal = g.add_node(NodeType::VertexNormal);
876        let time = g.add_node(NodeType::Time);
877
878        // Distortion based on normal and noise
879        let perlin = g.add_node(NodeType::Perlin);
880        if let Some(n) = g.node_mut(perlin) {
881            n.inputs[1].default_value = Some(ParamValue::Float(5.0));
882        }
883
884        // Fresnel for edge visibility
885        let fresnel = g.add_node(NodeType::Fresnel);
886        if let Some(n) = g.node_mut(fresnel) {
887            n.inputs[2].default_value = Some(ParamValue::Float(5.0)); // high power = thin edge
888            n.inputs[3].default_value = Some(ParamValue::Float(0.02)); // small bias
889        }
890
891        // Nearly transparent base
892        let base = g.add_node(NodeType::Color);
893        if let Some(n) = g.node_mut(base) {
894            n.inputs[0].default_value = Some(ParamValue::Vec4([0.1, 0.1, 0.15, 0.05]));
895        }
896
897        // Edge shimmer color
898        let edge = g.add_node(NodeType::Color);
899        if let Some(n) = g.node_mut(edge) {
900            n.inputs[0].default_value = Some(ParamValue::Vec4([0.3, 0.5, 0.8, 0.3]));
901        }
902
903        // Noise modulation for shimmer
904        let noise_mul = g.add_node(NodeType::Mul);
905        let time_noise_offset = g.add_node(NodeType::Add);
906
907        // Lerp between transparent and edge
908        let color_lerp = g.add_node(NodeType::Lerp);
909
910        let main_out = g.add_node(NodeType::MainColor);
911
912        g.connect(time, 0, time_noise_offset, 0);
913        g.connect(pos, 0, time_noise_offset, 1);
914        g.connect(time_noise_offset, 0, perlin, 0);
915
916        g.connect(normal, 0, fresnel, 0);
917        g.connect(perlin, 0, noise_mul, 0);
918        g.connect(fresnel, 0, noise_mul, 1);
919
920        g.connect(base, 0, color_lerp, 0);
921        g.connect(edge, 0, color_lerp, 1);
922        g.connect(noise_mul, 0, color_lerp, 2);
923
924        g.connect(color_lerp, 0, main_out, 0);
925
926        g
927    }
928
929    // -----------------------------------------------------------------------
930    // Shadow Form — dark shadow entity effect
931    // -----------------------------------------------------------------------
932    pub fn shadow_form() -> ShaderGraph {
933        let mut g = ShaderGraph::new("shadow_form");
934
935        let pos = g.add_node(NodeType::VertexPosition);
936        let normal = g.add_node(NodeType::VertexNormal);
937        let time = g.add_node(NodeType::Time);
938
939        // Dark base
940        let base = g.add_node(NodeType::Color);
941        if let Some(n) = g.node_mut(base) {
942            n.inputs[0].default_value = Some(ParamValue::Vec4([0.02, 0.02, 0.03, 0.9]));
943        }
944
945        // Shadow purple accent
946        let accent = g.add_node(NodeType::Color);
947        if let Some(n) = g.node_mut(accent) {
948            n.inputs[0].default_value = Some(ParamValue::Vec4([0.15, 0.0, 0.25, 1.0]));
949        }
950
951        // Wispy noise
952        let turb = g.add_node(NodeType::Turbulence);
953        if let Some(n) = g.node_mut(turb) {
954            n.inputs[1].default_value = Some(ParamValue::Float(2.0));
955        }
956
957        // Animate the wisps
958        let scroll = g.add_node(NodeType::Mul);
959        if let Some(n) = g.node_mut(scroll) {
960            n.inputs[1].default_value = Some(ParamValue::Float(0.3));
961        }
962        let scroll_pos = g.add_node(NodeType::Add);
963
964        // Fresnel for ethereal edges
965        let fresnel = g.add_node(NodeType::Fresnel);
966        if let Some(n) = g.node_mut(fresnel) {
967            n.inputs[2].default_value = Some(ParamValue::Float(1.5));
968        }
969
970        // Combine
971        let wisp_lerp = g.add_node(NodeType::Lerp);
972        let edge_add = g.add_node(NodeType::Add);
973        let edge_mul = g.add_node(NodeType::Mul);
974
975        let main_out = g.add_node(NodeType::MainColor);
976        let emission = g.add_node(NodeType::EmissionBuffer);
977
978        g.connect(time, 0, scroll, 0);
979        g.connect(pos, 0, scroll_pos, 0);
980        g.connect(scroll, 0, scroll_pos, 1);
981        g.connect(scroll_pos, 0, turb, 0);
982
983        g.connect(normal, 0, fresnel, 0);
984
985        g.connect(base, 0, wisp_lerp, 0);
986        g.connect(accent, 0, wisp_lerp, 1);
987        g.connect(turb, 0, wisp_lerp, 2);
988
989        g.connect(accent, 0, edge_mul, 0);
990        g.connect(fresnel, 0, edge_mul, 1);
991
992        g.connect(wisp_lerp, 0, edge_add, 0);
993        g.connect(edge_mul, 0, edge_add, 1);
994
995        g.connect(edge_add, 0, main_out, 0);
996        g.connect(accent, 0, emission, 0);
997
998        g
999    }
1000
1001    // -----------------------------------------------------------------------
1002    // Divine Light — holy/radiant light effect
1003    // -----------------------------------------------------------------------
1004    pub fn divine_light() -> ShaderGraph {
1005        let mut g = ShaderGraph::new("divine_light");
1006
1007        let pos = g.add_node(NodeType::VertexPosition);
1008        let normal = g.add_node(NodeType::VertexNormal);
1009        let time = g.add_node(NodeType::Time);
1010
1011        // Golden base
1012        let gold = g.add_node(NodeType::Color);
1013        if let Some(n) = g.node_mut(gold) {
1014            n.inputs[0].default_value = Some(ParamValue::Vec4([1.0, 0.85, 0.4, 1.0]));
1015        }
1016
1017        // White core
1018        let white = g.add_node(NodeType::Color);
1019        if let Some(n) = g.node_mut(white) {
1020            n.inputs[0].default_value = Some(ParamValue::Vec4([1.0, 1.0, 0.95, 1.0]));
1021        }
1022
1023        // Pulsing glow
1024        let pulse = g.add_node(NodeType::Sin);
1025        let pulse_remap = g.add_node(NodeType::Remap);
1026        if let Some(n) = g.node_mut(pulse_remap) {
1027            n.inputs[1].default_value = Some(ParamValue::Float(-1.0));
1028            n.inputs[2].default_value = Some(ParamValue::Float(1.0));
1029            n.inputs[3].default_value = Some(ParamValue::Float(0.7));
1030            n.inputs[4].default_value = Some(ParamValue::Float(1.0));
1031        }
1032
1033        // Strong fresnel for divine aura
1034        let fresnel = g.add_node(NodeType::Fresnel);
1035        if let Some(n) = g.node_mut(fresnel) {
1036            n.inputs[2].default_value = Some(ParamValue::Float(1.5));
1037            n.inputs[3].default_value = Some(ParamValue::Float(0.3));
1038        }
1039
1040        // Bloom effect
1041        let bloom = g.add_node(NodeType::Bloom);
1042        if let Some(n) = g.node_mut(bloom) {
1043            n.inputs[1].default_value = Some(ParamValue::Float(0.3)); // low threshold
1044            n.inputs[2].default_value = Some(ParamValue::Float(2.5)); // high intensity
1045        }
1046
1047        // Color mix
1048        let color_lerp = g.add_node(NodeType::Lerp);
1049        let glow_mul = g.add_node(NodeType::Mul);
1050
1051        let main_out = g.add_node(NodeType::MainColor);
1052        let emission = g.add_node(NodeType::EmissionBuffer);
1053        let bloom_out = g.add_node(NodeType::BloomBuffer);
1054
1055        g.connect(time, 0, pulse, 0);
1056        g.connect(pulse, 0, pulse_remap, 0);
1057        g.connect(normal, 0, fresnel, 0);
1058
1059        g.connect(gold, 0, color_lerp, 0);
1060        g.connect(white, 0, color_lerp, 1);
1061        g.connect(fresnel, 0, color_lerp, 2);
1062
1063        g.connect(color_lerp, 0, glow_mul, 0);
1064        g.connect(pulse_remap, 0, glow_mul, 1);
1065
1066        g.connect(glow_mul, 0, bloom, 0);
1067
1068        g.connect(bloom, 0, main_out, 0);
1069        g.connect(gold, 0, emission, 0);
1070        g.connect(bloom, 0, bloom_out, 0);
1071
1072        g
1073    }
1074
1075    // -----------------------------------------------------------------------
1076    // Toxic Cloud — poisonous gas effect
1077    // -----------------------------------------------------------------------
1078    pub fn toxic_cloud() -> ShaderGraph {
1079        let mut g = ShaderGraph::new("toxic_cloud");
1080
1081        let pos = g.add_node(NodeType::VertexPosition);
1082        let time = g.add_node(NodeType::Time);
1083
1084        // Slow scrolling
1085        let scroll = g.add_node(NodeType::Mul);
1086        if let Some(n) = g.node_mut(scroll) {
1087            n.inputs[1].default_value = Some(ParamValue::Float(0.4));
1088        }
1089        let scroll_pos = g.add_node(NodeType::Add);
1090
1091        // Multi-octave noise
1092        let fbm = g.add_node(NodeType::FBM);
1093        if let Some(n) = g.node_mut(fbm) {
1094            n.inputs[1].default_value = Some(ParamValue::Float(2.0));
1095            n.inputs[3].default_value = Some(ParamValue::Float(2.0));
1096            n.inputs[4].default_value = Some(ParamValue::Float(0.5));
1097        }
1098
1099        // Toxic green
1100        let green = g.add_node(NodeType::Color);
1101        if let Some(n) = g.node_mut(green) {
1102            n.inputs[0].default_value = Some(ParamValue::Vec4([0.2, 0.8, 0.1, 0.7]));
1103        }
1104
1105        // Dark murky
1106        let dark = g.add_node(NodeType::Color);
1107        if let Some(n) = g.node_mut(dark) {
1108            n.inputs[0].default_value = Some(ParamValue::Vec4([0.05, 0.15, 0.0, 0.5]));
1109        }
1110
1111        // Yellow highlight
1112        let yellow = g.add_node(NodeType::Color);
1113        if let Some(n) = g.node_mut(yellow) {
1114            n.inputs[0].default_value = Some(ParamValue::Vec4([0.7, 0.9, 0.1, 0.6]));
1115        }
1116
1117        // Gradient maps for color variation
1118        let grad = g.add_node(NodeType::GradientMap);
1119        let color_lerp = g.add_node(NodeType::Lerp);
1120
1121        // Density modulation
1122        let density = g.add_node(NodeType::Smoothstep);
1123        if let Some(n) = g.node_mut(density) {
1124            n.inputs[0].default_value = Some(ParamValue::Float(0.2));
1125            n.inputs[1].default_value = Some(ParamValue::Float(0.6));
1126        }
1127
1128        let main_out = g.add_node(NodeType::MainColor);
1129        let emission = g.add_node(NodeType::EmissionBuffer);
1130
1131        g.connect(time, 0, scroll, 0);
1132        g.connect(pos, 0, scroll_pos, 0);
1133        g.connect(scroll, 0, scroll_pos, 1);
1134        g.connect(scroll_pos, 0, fbm, 0);
1135
1136        g.connect(fbm, 0, grad, 0);
1137        g.connect(dark, 0, color_lerp, 0);
1138        g.connect(green, 0, color_lerp, 1);
1139        g.connect(fbm, 0, color_lerp, 2);
1140
1141        g.connect(fbm, 0, density, 2);
1142
1143        g.connect(color_lerp, 0, main_out, 0);
1144        g.connect(green, 0, emission, 0);
1145
1146        g
1147    }
1148
1149    // -----------------------------------------------------------------------
1150    // Chaos Rift — reality-tearing dimensional rift
1151    // -----------------------------------------------------------------------
1152    pub fn chaos_rift() -> ShaderGraph {
1153        let mut g = ShaderGraph::new("chaos_rift");
1154
1155        let pos = g.add_node(NodeType::VertexPosition);
1156        let normal = g.add_node(NodeType::VertexNormal);
1157        let time = g.add_node(NodeType::Time);
1158
1159        // Multi-layer noise
1160        let turb = g.add_node(NodeType::Turbulence);
1161        if let Some(n) = g.node_mut(turb) {
1162            n.inputs[1].default_value = Some(ParamValue::Float(3.0));
1163        }
1164        let voronoi = g.add_node(NodeType::Voronoi);
1165        if let Some(n) = g.node_mut(voronoi) {
1166            n.inputs[1].default_value = Some(ParamValue::Float(4.0));
1167        }
1168        let simplex = g.add_node(NodeType::Simplex);
1169        if let Some(n) = g.node_mut(simplex) {
1170            n.inputs[1].default_value = Some(ParamValue::Float(6.0));
1171        }
1172
1173        // Combine noises for chaos
1174        let noise_mul = g.add_node(NodeType::Mul);
1175        let noise_add = g.add_node(NodeType::Add);
1176        let noise_fract = g.add_node(NodeType::Fract);
1177
1178        // Rapidly shifting hue
1179        let time_fast = g.add_node(NodeType::Mul);
1180        if let Some(n) = g.node_mut(time_fast) {
1181            n.inputs[1].default_value = Some(ParamValue::Float(5.0));
1182        }
1183
1184        // HSV color generation
1185        let hsv = g.add_node(NodeType::HSVToRGB);
1186
1187        // Distortion color (deep red-purple)
1188        let rift_base = g.add_node(NodeType::Color);
1189        if let Some(n) = g.node_mut(rift_base) {
1190            n.inputs[0].default_value = Some(ParamValue::Vec4([0.4, 0.0, 0.1, 1.0]));
1191        }
1192
1193        // Bright energy color
1194        let energy = g.add_node(NodeType::Color);
1195        if let Some(n) = g.node_mut(energy) {
1196            n.inputs[0].default_value = Some(ParamValue::Vec4([0.8, 0.2, 1.0, 1.0]));
1197        }
1198
1199        // Fresnel
1200        let fresnel = g.add_node(NodeType::Fresnel);
1201        if let Some(n) = g.node_mut(fresnel) {
1202            n.inputs[2].default_value = Some(ParamValue::Float(2.0));
1203        }
1204
1205        // Color mixing
1206        let color_lerp1 = g.add_node(NodeType::Lerp);
1207        let color_lerp2 = g.add_node(NodeType::Lerp);
1208
1209        let main_out = g.add_node(NodeType::MainColor);
1210        let emission = g.add_node(NodeType::EmissionBuffer);
1211        let bloom_out = g.add_node(NodeType::BloomBuffer);
1212
1213        // Wire the chaos
1214        g.connect(pos, 0, turb, 0);
1215        g.connect(pos, 0, voronoi, 0);
1216        g.connect(pos, 0, simplex, 0);
1217
1218        g.connect(turb, 0, noise_mul, 0);
1219        g.connect(voronoi, 0, noise_mul, 1);
1220        g.connect(noise_mul, 0, noise_add, 0);
1221        g.connect(simplex, 0, noise_add, 1);
1222        g.connect(noise_add, 0, noise_fract, 0);
1223
1224        g.connect(time, 0, time_fast, 0);
1225        g.connect(noise_fract, 0, hsv, 0); // hue from noise
1226        g.connect(time_fast, 0, hsv, 1);   // saturation (will be clamped by GLSL)
1227
1228        g.connect(normal, 0, fresnel, 0);
1229
1230        g.connect(rift_base, 0, color_lerp1, 0);
1231        g.connect(energy, 0, color_lerp1, 1);
1232        g.connect(noise_fract, 0, color_lerp1, 2);
1233
1234        g.connect(color_lerp1, 0, color_lerp2, 0);
1235        g.connect(hsv, 0, color_lerp2, 1);
1236        g.connect(fresnel, 0, color_lerp2, 2);
1237
1238        g.connect(color_lerp2, 0, main_out, 0);
1239        g.connect(energy, 0, emission, 0);
1240        g.connect(energy, 0, bloom_out, 0);
1241
1242        g
1243    }
1244}
1245
1246/// Helper: create all presets and return them as a vec.
1247pub fn all_presets() -> Vec<ShaderGraph> {
1248    ShaderPresets::list().iter()
1249        .filter_map(|name| ShaderPresets::create(name))
1250        .collect()
1251}