@nice2dev/game-engine 1.0.2 → 1.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (188) hide show
  1. package/dist/cjs/ai/BehaviorTree.js +1215 -0
  2. package/dist/cjs/ai/BehaviorTree.js.map +1 -0
  3. package/dist/cjs/ai/StateMachine.js +783 -0
  4. package/dist/cjs/ai/StateMachine.js.map +1 -0
  5. package/dist/cjs/editor/ShaderGraph.js +1616 -0
  6. package/dist/cjs/editor/ShaderGraph.js.map +1 -0
  7. package/dist/cjs/editor/TimelineEditor.js +819 -0
  8. package/dist/cjs/editor/TimelineEditor.js.map +1 -0
  9. package/dist/cjs/export/GodotExporter.js +1102 -0
  10. package/dist/cjs/export/GodotExporter.js.map +1 -0
  11. package/dist/cjs/export/PlatformExporter.js +236 -0
  12. package/dist/cjs/export/PlatformExporter.js.map +1 -0
  13. package/dist/cjs/export/ThreeJSExporter.js +1116 -0
  14. package/dist/cjs/export/ThreeJSExporter.js.map +1 -0
  15. package/dist/cjs/export/UnityExporter.js +1193 -0
  16. package/dist/cjs/export/UnityExporter.js.map +1 -0
  17. package/dist/cjs/export/WebExporter.js +1036 -0
  18. package/dist/cjs/export/WebExporter.js.map +1 -0
  19. package/dist/cjs/export/index.js +58 -0
  20. package/dist/cjs/export/index.js.map +1 -0
  21. package/dist/cjs/import/AsepriteImporter.js +761 -0
  22. package/dist/cjs/import/AsepriteImporter.js.map +1 -0
  23. package/dist/cjs/import/DragonBonesImporter.js +499 -0
  24. package/dist/cjs/import/DragonBonesImporter.js.map +1 -0
  25. package/dist/cjs/import/GameMakerImporter.js +559 -0
  26. package/dist/cjs/import/GameMakerImporter.js.map +1 -0
  27. package/dist/cjs/import/GodotSceneImporter.js +824 -0
  28. package/dist/cjs/import/GodotSceneImporter.js.map +1 -0
  29. package/dist/cjs/import/LDtkImporter.js +481 -0
  30. package/dist/cjs/import/LDtkImporter.js.map +1 -0
  31. package/dist/cjs/import/Live2DImporter.js +553 -0
  32. package/dist/cjs/import/Live2DImporter.js.map +1 -0
  33. package/dist/cjs/import/NdgFormat.js +499 -0
  34. package/dist/cjs/import/NdgFormat.js.map +1 -0
  35. package/dist/cjs/import/OgmoImporter.js +529 -0
  36. package/dist/cjs/import/OgmoImporter.js.map +1 -0
  37. package/dist/cjs/import/RPGMakerImporter.js +520 -0
  38. package/dist/cjs/import/RPGMakerImporter.js.map +1 -0
  39. package/dist/cjs/import/SceneImporter.js +449 -0
  40. package/dist/cjs/import/SceneImporter.js.map +1 -0
  41. package/dist/cjs/import/SpineImporter.js +583 -0
  42. package/dist/cjs/import/SpineImporter.js.map +1 -0
  43. package/dist/cjs/import/SpriterImporter.js +652 -0
  44. package/dist/cjs/import/SpriterImporter.js.map +1 -0
  45. package/dist/cjs/import/TiledMapImporter.js +859 -0
  46. package/dist/cjs/import/TiledMapImporter.js.map +1 -0
  47. package/dist/cjs/import/UnitySceneImporter.js +732 -0
  48. package/dist/cjs/import/UnitySceneImporter.js.map +1 -0
  49. package/dist/cjs/import/index.js +305 -0
  50. package/dist/cjs/import/index.js.map +1 -0
  51. package/dist/cjs/index.js +201 -0
  52. package/dist/cjs/index.js.map +1 -1
  53. package/dist/cjs/scripting/GraphToAST.js +567 -0
  54. package/dist/cjs/scripting/GraphToAST.js.map +1 -0
  55. package/dist/cjs/scripting/LanguageExporter.js +321 -0
  56. package/dist/cjs/scripting/LanguageExporter.js.map +1 -0
  57. package/dist/cjs/scripting/ScriptAST.js +67 -0
  58. package/dist/cjs/scripting/ScriptAST.js.map +1 -0
  59. package/dist/cjs/scripting/VisualScripting2.js +1140 -0
  60. package/dist/cjs/scripting/VisualScripting2.js.map +1 -0
  61. package/dist/cjs/scripting/exporters/CSharpExporter.js +503 -0
  62. package/dist/cjs/scripting/exporters/CSharpExporter.js.map +1 -0
  63. package/dist/cjs/scripting/exporters/GDScriptExporter.js +452 -0
  64. package/dist/cjs/scripting/exporters/GDScriptExporter.js.map +1 -0
  65. package/dist/cjs/scripting/exporters/LuaExporter.js +457 -0
  66. package/dist/cjs/scripting/exporters/LuaExporter.js.map +1 -0
  67. package/dist/cjs/scripting/exporters/PythonExporter.js +565 -0
  68. package/dist/cjs/scripting/exporters/PythonExporter.js.map +1 -0
  69. package/dist/cjs/scripting/exporters/RustExporter.js +525 -0
  70. package/dist/cjs/scripting/exporters/RustExporter.js.map +1 -0
  71. package/dist/cjs/scripting/exporters/TypeScriptExporter.js +570 -0
  72. package/dist/cjs/scripting/exporters/TypeScriptExporter.js.map +1 -0
  73. package/dist/cjs/systems/ParticleSystem2.js +1478 -0
  74. package/dist/cjs/systems/ParticleSystem2.js.map +1 -0
  75. package/dist/esm/ai/BehaviorTree.js +1186 -0
  76. package/dist/esm/ai/BehaviorTree.js.map +1 -0
  77. package/dist/esm/ai/StateMachine.js +767 -0
  78. package/dist/esm/ai/StateMachine.js.map +1 -0
  79. package/dist/esm/editor/ShaderGraph.js +1606 -0
  80. package/dist/esm/editor/ShaderGraph.js.map +1 -0
  81. package/dist/esm/editor/TimelineEditor.js +800 -0
  82. package/dist/esm/editor/TimelineEditor.js.map +1 -0
  83. package/dist/esm/export/GodotExporter.js +1100 -0
  84. package/dist/esm/export/GodotExporter.js.map +1 -0
  85. package/dist/esm/export/PlatformExporter.js +230 -0
  86. package/dist/esm/export/PlatformExporter.js.map +1 -0
  87. package/dist/esm/export/ThreeJSExporter.js +1114 -0
  88. package/dist/esm/export/ThreeJSExporter.js.map +1 -0
  89. package/dist/esm/export/UnityExporter.js +1191 -0
  90. package/dist/esm/export/UnityExporter.js.map +1 -0
  91. package/dist/esm/export/WebExporter.js +1033 -0
  92. package/dist/esm/export/WebExporter.js.map +1 -0
  93. package/dist/esm/export/index.js +44 -0
  94. package/dist/esm/export/index.js.map +1 -0
  95. package/dist/esm/import/AsepriteImporter.js +759 -0
  96. package/dist/esm/import/AsepriteImporter.js.map +1 -0
  97. package/dist/esm/import/DragonBonesImporter.js +496 -0
  98. package/dist/esm/import/DragonBonesImporter.js.map +1 -0
  99. package/dist/esm/import/GameMakerImporter.js +556 -0
  100. package/dist/esm/import/GameMakerImporter.js.map +1 -0
  101. package/dist/esm/import/GodotSceneImporter.js +822 -0
  102. package/dist/esm/import/GodotSceneImporter.js.map +1 -0
  103. package/dist/esm/import/LDtkImporter.js +479 -0
  104. package/dist/esm/import/LDtkImporter.js.map +1 -0
  105. package/dist/esm/import/Live2DImporter.js +550 -0
  106. package/dist/esm/import/Live2DImporter.js.map +1 -0
  107. package/dist/esm/import/NdgFormat.js +490 -0
  108. package/dist/esm/import/NdgFormat.js.map +1 -0
  109. package/dist/esm/import/OgmoImporter.js +526 -0
  110. package/dist/esm/import/OgmoImporter.js.map +1 -0
  111. package/dist/esm/import/RPGMakerImporter.js +517 -0
  112. package/dist/esm/import/RPGMakerImporter.js.map +1 -0
  113. package/dist/esm/import/SceneImporter.js +441 -0
  114. package/dist/esm/import/SceneImporter.js.map +1 -0
  115. package/dist/esm/import/SpineImporter.js +580 -0
  116. package/dist/esm/import/SpineImporter.js.map +1 -0
  117. package/dist/esm/import/SpriterImporter.js +649 -0
  118. package/dist/esm/import/SpriterImporter.js.map +1 -0
  119. package/dist/esm/import/TiledMapImporter.js +857 -0
  120. package/dist/esm/import/TiledMapImporter.js.map +1 -0
  121. package/dist/esm/import/UnitySceneImporter.js +730 -0
  122. package/dist/esm/import/UnitySceneImporter.js.map +1 -0
  123. package/dist/esm/import/index.js +279 -0
  124. package/dist/esm/import/index.js.map +1 -0
  125. package/dist/esm/index.js +36 -0
  126. package/dist/esm/index.js.map +1 -1
  127. package/dist/esm/scripting/GraphToAST.js +564 -0
  128. package/dist/esm/scripting/GraphToAST.js.map +1 -0
  129. package/dist/esm/scripting/LanguageExporter.js +311 -0
  130. package/dist/esm/scripting/LanguageExporter.js.map +1 -0
  131. package/dist/esm/scripting/ScriptAST.js +52 -0
  132. package/dist/esm/scripting/ScriptAST.js.map +1 -0
  133. package/dist/esm/scripting/VisualScripting2.js +1130 -0
  134. package/dist/esm/scripting/VisualScripting2.js.map +1 -0
  135. package/dist/esm/scripting/exporters/CSharpExporter.js +501 -0
  136. package/dist/esm/scripting/exporters/CSharpExporter.js.map +1 -0
  137. package/dist/esm/scripting/exporters/GDScriptExporter.js +450 -0
  138. package/dist/esm/scripting/exporters/GDScriptExporter.js.map +1 -0
  139. package/dist/esm/scripting/exporters/LuaExporter.js +455 -0
  140. package/dist/esm/scripting/exporters/LuaExporter.js.map +1 -0
  141. package/dist/esm/scripting/exporters/PythonExporter.js +563 -0
  142. package/dist/esm/scripting/exporters/PythonExporter.js.map +1 -0
  143. package/dist/esm/scripting/exporters/RustExporter.js +523 -0
  144. package/dist/esm/scripting/exporters/RustExporter.js.map +1 -0
  145. package/dist/esm/scripting/exporters/TypeScriptExporter.js +568 -0
  146. package/dist/esm/scripting/exporters/TypeScriptExporter.js.map +1 -0
  147. package/dist/esm/systems/ParticleSystem2.js +1471 -0
  148. package/dist/esm/systems/ParticleSystem2.js.map +1 -0
  149. package/dist/types/ai/BehaviorTree.d.ts +375 -0
  150. package/dist/types/ai/StateMachine.d.ts +296 -0
  151. package/dist/types/editor/ShaderGraph.d.ts +207 -0
  152. package/dist/types/editor/TimelineEditor.d.ts +393 -0
  153. package/dist/types/export/GodotExporter.d.ts +56 -0
  154. package/dist/types/export/PlatformExporter.d.ts +201 -0
  155. package/dist/types/export/ThreeJSExporter.d.ts +40 -0
  156. package/dist/types/export/UnityExporter.d.ts +69 -0
  157. package/dist/types/export/WebExporter.d.ts +58 -0
  158. package/dist/types/export/index.d.ts +19 -0
  159. package/dist/types/import/AsepriteImporter.d.ts +46 -0
  160. package/dist/types/import/DragonBonesImporter.d.ts +331 -0
  161. package/dist/types/import/GameMakerImporter.d.ts +375 -0
  162. package/dist/types/import/GodotSceneImporter.d.ts +34 -0
  163. package/dist/types/import/LDtkImporter.d.ts +177 -0
  164. package/dist/types/import/Live2DImporter.d.ts +237 -0
  165. package/dist/types/import/NdgFormat.d.ts +387 -0
  166. package/dist/types/import/OgmoImporter.d.ts +237 -0
  167. package/dist/types/import/RPGMakerImporter.d.ts +186 -0
  168. package/dist/types/import/SceneImporter.d.ts +276 -0
  169. package/dist/types/import/SpineImporter.d.ts +372 -0
  170. package/dist/types/import/SpriterImporter.d.ts +230 -0
  171. package/dist/types/import/TiledMapImporter.d.ts +57 -0
  172. package/dist/types/import/UnitySceneImporter.d.ts +87 -0
  173. package/dist/types/import/index.d.ts +59 -0
  174. package/dist/types/index.d.ts +29 -17
  175. package/dist/types/scripting/GraphToAST.d.ts +55 -0
  176. package/dist/types/scripting/LanguageExporter.d.ts +136 -0
  177. package/dist/types/scripting/ScriptAST.d.ts +312 -0
  178. package/dist/types/scripting/VisualScripting2.d.ts +353 -0
  179. package/dist/types/scripting/exporters/CSharpExporter.d.ts +44 -0
  180. package/dist/types/scripting/exporters/GDScriptExporter.d.ts +46 -0
  181. package/dist/types/scripting/exporters/LuaExporter.d.ts +46 -0
  182. package/dist/types/scripting/exporters/PythonExporter.d.ts +49 -0
  183. package/dist/types/scripting/exporters/RustExporter.d.ts +46 -0
  184. package/dist/types/scripting/exporters/TypeScriptExporter.d.ts +48 -0
  185. package/dist/types/scripting/exporters/index.d.ts +8 -0
  186. package/dist/types/scripting/index.d.ts +11 -0
  187. package/dist/types/systems/ParticleSystem2.d.ts +646 -0
  188. package/package.json +2 -2
@@ -0,0 +1,1616 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * @file ShaderGraph.ts
5
+ * @description Visual Shader Graph Editor System
6
+ * PRO-1.4: Professional Editor Features
7
+ *
8
+ * Features:
9
+ * - Node-based shader editing
10
+ * - GLSL/WGSL/HLSL generation
11
+ * - Real-time preview
12
+ * - Material templates
13
+ * - Custom node creation
14
+ * - Subgraph support
15
+ * - PBR and unlit workflows
16
+ */
17
+ // ============================================================
18
+ // Built-in Node Library
19
+ // ============================================================
20
+ const SHADER_NODE_LIBRARY = [
21
+ // ─── Input Nodes ─────────────────────────────────────────
22
+ {
23
+ id: 'position',
24
+ name: 'Position',
25
+ category: 'input',
26
+ description: 'World/Object/View position',
27
+ inputs: [],
28
+ outputs: [
29
+ { name: 'World', type: 'vec3' },
30
+ { name: 'Object', type: 'vec3' },
31
+ { name: 'View', type: 'vec3' },
32
+ { name: 'Tangent', type: 'vec3' },
33
+ ],
34
+ glsl: `
35
+ vec3 {{output.World}} = v_worldPosition;
36
+ vec3 {{output.Object}} = v_objectPosition;
37
+ vec3 {{output.View}} = v_viewPosition;
38
+ vec3 {{output.Tangent}} = v_tangent;
39
+ `,
40
+ },
41
+ {
42
+ id: 'normal',
43
+ name: 'Normal',
44
+ category: 'input',
45
+ description: 'Surface normal vectors',
46
+ inputs: [],
47
+ outputs: [
48
+ { name: 'World', type: 'vec3' },
49
+ { name: 'Object', type: 'vec3' },
50
+ { name: 'View', type: 'vec3' },
51
+ { name: 'Tangent', type: 'vec3' },
52
+ ],
53
+ glsl: `
54
+ vec3 {{output.World}} = normalize(v_worldNormal);
55
+ vec3 {{output.Object}} = normalize(v_objectNormal);
56
+ vec3 {{output.View}} = normalize(v_viewNormal);
57
+ vec3 {{output.Tangent}} = normalize(v_tangentNormal);
58
+ `,
59
+ },
60
+ {
61
+ id: 'uv',
62
+ name: 'UV',
63
+ category: 'input',
64
+ description: 'Texture coordinates',
65
+ inputs: [],
66
+ outputs: [
67
+ { name: 'UV0', type: 'vec2' },
68
+ { name: 'UV1', type: 'vec2' },
69
+ { name: 'UV2', type: 'vec2' },
70
+ { name: 'UV3', type: 'vec2' },
71
+ ],
72
+ glsl: `
73
+ vec2 {{output.UV0}} = v_uv0;
74
+ vec2 {{output.UV1}} = v_uv1;
75
+ vec2 {{output.UV2}} = v_uv2;
76
+ vec2 {{output.UV3}} = v_uv3;
77
+ `,
78
+ },
79
+ {
80
+ id: 'vertexColor',
81
+ name: 'Vertex Color',
82
+ category: 'input',
83
+ description: 'Per-vertex color',
84
+ inputs: [],
85
+ outputs: [
86
+ { name: 'Color', type: 'vec4' },
87
+ { name: 'R', type: 'float' },
88
+ { name: 'G', type: 'float' },
89
+ { name: 'B', type: 'float' },
90
+ { name: 'A', type: 'float' },
91
+ ],
92
+ glsl: `
93
+ vec4 {{output.Color}} = v_color;
94
+ float {{output.R}} = v_color.r;
95
+ float {{output.G}} = v_color.g;
96
+ float {{output.B}} = v_color.b;
97
+ float {{output.A}} = v_color.a;
98
+ `,
99
+ },
100
+ {
101
+ id: 'time',
102
+ name: 'Time',
103
+ category: 'input',
104
+ description: 'Time values for animation',
105
+ inputs: [],
106
+ outputs: [
107
+ { name: 'Time', type: 'float' },
108
+ { name: 'SinTime', type: 'float' },
109
+ { name: 'CosTime', type: 'float' },
110
+ { name: 'DeltaTime', type: 'float' },
111
+ { name: 'SmoothDelta', type: 'float' },
112
+ ],
113
+ glsl: `
114
+ float {{output.Time}} = u_time;
115
+ float {{output.SinTime}} = sin(u_time);
116
+ float {{output.CosTime}} = cos(u_time);
117
+ float {{output.DeltaTime}} = u_deltaTime;
118
+ float {{output.SmoothDelta}} = u_smoothDeltaTime;
119
+ `,
120
+ },
121
+ {
122
+ id: 'viewDirection',
123
+ name: 'View Direction',
124
+ category: 'input',
125
+ description: 'Camera view direction',
126
+ inputs: [],
127
+ outputs: [
128
+ { name: 'World', type: 'vec3' },
129
+ { name: 'Object', type: 'vec3' },
130
+ { name: 'View', type: 'vec3' },
131
+ ],
132
+ glsl: `
133
+ vec3 {{output.World}} = normalize(u_cameraPosition - v_worldPosition);
134
+ vec3 {{output.Object}} = (u_worldToObject * vec4({{output.World}}, 0.0)).xyz;
135
+ vec3 {{output.View}} = (u_viewMatrix * vec4({{output.World}}, 0.0)).xyz;
136
+ `,
137
+ },
138
+ {
139
+ id: 'cameraPosition',
140
+ name: 'Camera Position',
141
+ category: 'input',
142
+ inputs: [],
143
+ outputs: [{ name: 'Position', type: 'vec3' }],
144
+ glsl: `vec3 {{output.Position}} = u_cameraPosition;`,
145
+ },
146
+ {
147
+ id: 'screenPosition',
148
+ name: 'Screen Position',
149
+ category: 'input',
150
+ inputs: [],
151
+ outputs: [
152
+ { name: 'Default', type: 'vec4' },
153
+ { name: 'Raw', type: 'vec4' },
154
+ { name: 'Center', type: 'vec2' },
155
+ { name: 'Tiled', type: 'vec2' },
156
+ ],
157
+ glsl: `
158
+ vec4 {{output.Default}} = gl_FragCoord;
159
+ vec4 {{output.Raw}} = gl_FragCoord;
160
+ vec2 {{output.Center}} = gl_FragCoord.xy / u_resolution - 0.5;
161
+ vec2 {{output.Tiled}} = fract(gl_FragCoord.xy / 256.0);
162
+ `,
163
+ },
164
+ {
165
+ id: 'constant',
166
+ name: 'Constant',
167
+ category: 'input',
168
+ description: 'Constant value',
169
+ inputs: [],
170
+ outputs: [{ name: 'Out', type: 'float' }],
171
+ properties: [{ name: 'Value', type: 'float', default: 0 }],
172
+ glsl: `float {{output.Out}} = {{prop.Value}};`,
173
+ },
174
+ {
175
+ id: 'color',
176
+ name: 'Color',
177
+ category: 'input',
178
+ description: 'Color constant',
179
+ inputs: [],
180
+ outputs: [
181
+ { name: 'Out', type: 'vec4' },
182
+ { name: 'RGB', type: 'vec3' },
183
+ { name: 'R', type: 'float' },
184
+ { name: 'G', type: 'float' },
185
+ { name: 'B', type: 'float' },
186
+ { name: 'A', type: 'float' },
187
+ ],
188
+ properties: [{ name: 'Color', type: 'color', default: [1, 1, 1, 1] }],
189
+ glsl: `
190
+ vec4 {{output.Out}} = vec4({{prop.Color}});
191
+ vec3 {{output.RGB}} = {{output.Out}}.rgb;
192
+ float {{output.R}} = {{output.Out}}.r;
193
+ float {{output.G}} = {{output.Out}}.g;
194
+ float {{output.B}} = {{output.Out}}.b;
195
+ float {{output.A}} = {{output.Out}}.a;
196
+ `,
197
+ },
198
+ {
199
+ id: 'vector2',
200
+ name: 'Vector 2',
201
+ category: 'input',
202
+ inputs: [],
203
+ outputs: [{ name: 'Out', type: 'vec2' }],
204
+ properties: [{ name: 'Value', type: 'vec2', default: [0, 0] }],
205
+ glsl: `vec2 {{output.Out}} = vec2({{prop.Value}});`,
206
+ },
207
+ {
208
+ id: 'vector3',
209
+ name: 'Vector 3',
210
+ category: 'input',
211
+ inputs: [],
212
+ outputs: [{ name: 'Out', type: 'vec3' }],
213
+ properties: [{ name: 'Value', type: 'vec3', default: [0, 0, 0] }],
214
+ glsl: `vec3 {{output.Out}} = vec3({{prop.Value}});`,
215
+ },
216
+ {
217
+ id: 'vector4',
218
+ name: 'Vector 4',
219
+ category: 'input',
220
+ inputs: [],
221
+ outputs: [{ name: 'Out', type: 'vec4' }],
222
+ properties: [{ name: 'Value', type: 'vec4', default: [0, 0, 0, 0] }],
223
+ glsl: `vec4 {{output.Out}} = vec4({{prop.Value}});`,
224
+ },
225
+ // ─── Math Nodes ──────────────────────────────────────────
226
+ {
227
+ id: 'add',
228
+ name: 'Add',
229
+ category: 'math',
230
+ inputs: [
231
+ { name: 'A', type: 'float', default: 0 },
232
+ { name: 'B', type: 'float', default: 0 },
233
+ ],
234
+ outputs: [{ name: 'Out', type: 'float' }],
235
+ glsl: `float {{output.Out}} = {{input.A}} + {{input.B}};`,
236
+ },
237
+ {
238
+ id: 'subtract',
239
+ name: 'Subtract',
240
+ category: 'math',
241
+ inputs: [
242
+ { name: 'A', type: 'float', default: 0 },
243
+ { name: 'B', type: 'float', default: 0 },
244
+ ],
245
+ outputs: [{ name: 'Out', type: 'float' }],
246
+ glsl: `float {{output.Out}} = {{input.A}} - {{input.B}};`,
247
+ },
248
+ {
249
+ id: 'multiply',
250
+ name: 'Multiply',
251
+ category: 'math',
252
+ inputs: [
253
+ { name: 'A', type: 'float', default: 1 },
254
+ { name: 'B', type: 'float', default: 1 },
255
+ ],
256
+ outputs: [{ name: 'Out', type: 'float' }],
257
+ glsl: `float {{output.Out}} = {{input.A}} * {{input.B}};`,
258
+ },
259
+ {
260
+ id: 'divide',
261
+ name: 'Divide',
262
+ category: 'math',
263
+ inputs: [
264
+ { name: 'A', type: 'float', default: 1 },
265
+ { name: 'B', type: 'float', default: 1 },
266
+ ],
267
+ outputs: [{ name: 'Out', type: 'float' }],
268
+ glsl: `float {{output.Out}} = {{input.A}} / max({{input.B}}, 0.0001);`,
269
+ },
270
+ {
271
+ id: 'power',
272
+ name: 'Power',
273
+ category: 'math',
274
+ inputs: [
275
+ { name: 'Base', type: 'float', default: 2 },
276
+ { name: 'Exp', type: 'float', default: 2 },
277
+ ],
278
+ outputs: [{ name: 'Out', type: 'float' }],
279
+ glsl: `float {{output.Out}} = pow({{input.Base}}, {{input.Exp}});`,
280
+ },
281
+ {
282
+ id: 'sqrt',
283
+ name: 'Square Root',
284
+ category: 'math',
285
+ inputs: [{ name: 'In', type: 'float', default: 1 }],
286
+ outputs: [{ name: 'Out', type: 'float' }],
287
+ glsl: `float {{output.Out}} = sqrt(max({{input.In}}, 0.0));`,
288
+ },
289
+ {
290
+ id: 'abs',
291
+ name: 'Absolute',
292
+ category: 'math',
293
+ inputs: [{ name: 'In', type: 'float', default: 0 }],
294
+ outputs: [{ name: 'Out', type: 'float' }],
295
+ glsl: `float {{output.Out}} = abs({{input.In}});`,
296
+ },
297
+ {
298
+ id: 'negate',
299
+ name: 'Negate',
300
+ category: 'math',
301
+ inputs: [{ name: 'In', type: 'float', default: 0 }],
302
+ outputs: [{ name: 'Out', type: 'float' }],
303
+ glsl: `float {{output.Out}} = -{{input.In}};`,
304
+ },
305
+ {
306
+ id: 'min',
307
+ name: 'Minimum',
308
+ category: 'math',
309
+ inputs: [
310
+ { name: 'A', type: 'float', default: 0 },
311
+ { name: 'B', type: 'float', default: 0 },
312
+ ],
313
+ outputs: [{ name: 'Out', type: 'float' }],
314
+ glsl: `float {{output.Out}} = min({{input.A}}, {{input.B}});`,
315
+ },
316
+ {
317
+ id: 'max',
318
+ name: 'Maximum',
319
+ category: 'math',
320
+ inputs: [
321
+ { name: 'A', type: 'float', default: 0 },
322
+ { name: 'B', type: 'float', default: 0 },
323
+ ],
324
+ outputs: [{ name: 'Out', type: 'float' }],
325
+ glsl: `float {{output.Out}} = max({{input.A}}, {{input.B}});`,
326
+ },
327
+ {
328
+ id: 'clamp',
329
+ name: 'Clamp',
330
+ category: 'math',
331
+ inputs: [
332
+ { name: 'In', type: 'float', default: 0 },
333
+ { name: 'Min', type: 'float', default: 0 },
334
+ { name: 'Max', type: 'float', default: 1 },
335
+ ],
336
+ outputs: [{ name: 'Out', type: 'float' }],
337
+ glsl: `float {{output.Out}} = clamp({{input.In}}, {{input.Min}}, {{input.Max}});`,
338
+ },
339
+ {
340
+ id: 'saturate',
341
+ name: 'Saturate',
342
+ category: 'math',
343
+ description: 'Clamp to 0-1 range',
344
+ inputs: [{ name: 'In', type: 'float', default: 0 }],
345
+ outputs: [{ name: 'Out', type: 'float' }],
346
+ glsl: `float {{output.Out}} = clamp({{input.In}}, 0.0, 1.0);`,
347
+ },
348
+ {
349
+ id: 'lerp',
350
+ name: 'Lerp',
351
+ category: 'math',
352
+ description: 'Linear interpolation',
353
+ inputs: [
354
+ { name: 'A', type: 'float', default: 0 },
355
+ { name: 'B', type: 'float', default: 1 },
356
+ { name: 'T', type: 'float', default: 0.5 },
357
+ ],
358
+ outputs: [{ name: 'Out', type: 'float' }],
359
+ glsl: `float {{output.Out}} = mix({{input.A}}, {{input.B}}, {{input.T}});`,
360
+ },
361
+ {
362
+ id: 'smoothstep',
363
+ name: 'Smoothstep',
364
+ category: 'math',
365
+ inputs: [
366
+ { name: 'Edge0', type: 'float', default: 0 },
367
+ { name: 'Edge1', type: 'float', default: 1 },
368
+ { name: 'In', type: 'float', default: 0.5 },
369
+ ],
370
+ outputs: [{ name: 'Out', type: 'float' }],
371
+ glsl: `float {{output.Out}} = smoothstep({{input.Edge0}}, {{input.Edge1}}, {{input.In}});`,
372
+ },
373
+ {
374
+ id: 'step',
375
+ name: 'Step',
376
+ category: 'math',
377
+ inputs: [
378
+ { name: 'Edge', type: 'float', default: 0.5 },
379
+ { name: 'In', type: 'float', default: 0 },
380
+ ],
381
+ outputs: [{ name: 'Out', type: 'float' }],
382
+ glsl: `float {{output.Out}} = step({{input.Edge}}, {{input.In}});`,
383
+ },
384
+ {
385
+ id: 'floor',
386
+ name: 'Floor',
387
+ category: 'math',
388
+ inputs: [{ name: 'In', type: 'float', default: 0 }],
389
+ outputs: [{ name: 'Out', type: 'float' }],
390
+ glsl: `float {{output.Out}} = floor({{input.In}});`,
391
+ },
392
+ {
393
+ id: 'ceil',
394
+ name: 'Ceiling',
395
+ category: 'math',
396
+ inputs: [{ name: 'In', type: 'float', default: 0 }],
397
+ outputs: [{ name: 'Out', type: 'float' }],
398
+ glsl: `float {{output.Out}} = ceil({{input.In}});`,
399
+ },
400
+ {
401
+ id: 'round',
402
+ name: 'Round',
403
+ category: 'math',
404
+ inputs: [{ name: 'In', type: 'float', default: 0 }],
405
+ outputs: [{ name: 'Out', type: 'float' }],
406
+ glsl: `float {{output.Out}} = floor({{input.In}} + 0.5);`,
407
+ },
408
+ {
409
+ id: 'frac',
410
+ name: 'Fraction',
411
+ category: 'math',
412
+ inputs: [{ name: 'In', type: 'float', default: 0 }],
413
+ outputs: [{ name: 'Out', type: 'float' }],
414
+ glsl: `float {{output.Out}} = fract({{input.In}});`,
415
+ },
416
+ {
417
+ id: 'mod',
418
+ name: 'Modulo',
419
+ category: 'math',
420
+ inputs: [
421
+ { name: 'A', type: 'float', default: 0 },
422
+ { name: 'B', type: 'float', default: 1 },
423
+ ],
424
+ outputs: [{ name: 'Out', type: 'float' }],
425
+ glsl: `float {{output.Out}} = mod({{input.A}}, {{input.B}});`,
426
+ },
427
+ {
428
+ id: 'sin',
429
+ name: 'Sine',
430
+ category: 'math',
431
+ inputs: [{ name: 'In', type: 'float', default: 0 }],
432
+ outputs: [{ name: 'Out', type: 'float' }],
433
+ glsl: `float {{output.Out}} = sin({{input.In}});`,
434
+ },
435
+ {
436
+ id: 'cos',
437
+ name: 'Cosine',
438
+ category: 'math',
439
+ inputs: [{ name: 'In', type: 'float', default: 0 }],
440
+ outputs: [{ name: 'Out', type: 'float' }],
441
+ glsl: `float {{output.Out}} = cos({{input.In}});`,
442
+ },
443
+ {
444
+ id: 'tan',
445
+ name: 'Tangent',
446
+ category: 'math',
447
+ inputs: [{ name: 'In', type: 'float', default: 0 }],
448
+ outputs: [{ name: 'Out', type: 'float' }],
449
+ glsl: `float {{output.Out}} = tan({{input.In}});`,
450
+ },
451
+ {
452
+ id: 'asin',
453
+ name: 'Arcsine',
454
+ category: 'math',
455
+ inputs: [{ name: 'In', type: 'float', default: 0 }],
456
+ outputs: [{ name: 'Out', type: 'float' }],
457
+ glsl: `float {{output.Out}} = asin(clamp({{input.In}}, -1.0, 1.0));`,
458
+ },
459
+ {
460
+ id: 'acos',
461
+ name: 'Arccosine',
462
+ category: 'math',
463
+ inputs: [{ name: 'In', type: 'float', default: 0 }],
464
+ outputs: [{ name: 'Out', type: 'float' }],
465
+ glsl: `float {{output.Out}} = acos(clamp({{input.In}}, -1.0, 1.0));`,
466
+ },
467
+ {
468
+ id: 'atan',
469
+ name: 'Arctangent',
470
+ category: 'math',
471
+ inputs: [{ name: 'In', type: 'float', default: 0 }],
472
+ outputs: [{ name: 'Out', type: 'float' }],
473
+ glsl: `float {{output.Out}} = atan({{input.In}});`,
474
+ },
475
+ {
476
+ id: 'atan2',
477
+ name: 'Arctangent2',
478
+ category: 'math',
479
+ inputs: [
480
+ { name: 'Y', type: 'float', default: 0 },
481
+ { name: 'X', type: 'float', default: 1 },
482
+ ],
483
+ outputs: [{ name: 'Out', type: 'float' }],
484
+ glsl: `float {{output.Out}} = atan({{input.Y}}, {{input.X}});`,
485
+ },
486
+ {
487
+ id: 'exp',
488
+ name: 'Exponential',
489
+ category: 'math',
490
+ inputs: [{ name: 'In', type: 'float', default: 0 }],
491
+ outputs: [{ name: 'Out', type: 'float' }],
492
+ glsl: `float {{output.Out}} = exp({{input.In}});`,
493
+ },
494
+ {
495
+ id: 'exp2',
496
+ name: 'Exponential 2',
497
+ category: 'math',
498
+ inputs: [{ name: 'In', type: 'float', default: 0 }],
499
+ outputs: [{ name: 'Out', type: 'float' }],
500
+ glsl: `float {{output.Out}} = exp2({{input.In}});`,
501
+ },
502
+ {
503
+ id: 'log',
504
+ name: 'Logarithm',
505
+ category: 'math',
506
+ inputs: [{ name: 'In', type: 'float', default: 1 }],
507
+ outputs: [{ name: 'Out', type: 'float' }],
508
+ glsl: `float {{output.Out}} = log(max({{input.In}}, 0.0001));`,
509
+ },
510
+ {
511
+ id: 'log2',
512
+ name: 'Logarithm 2',
513
+ category: 'math',
514
+ inputs: [{ name: 'In', type: 'float', default: 1 }],
515
+ outputs: [{ name: 'Out', type: 'float' }],
516
+ glsl: `float {{output.Out}} = log2(max({{input.In}}, 0.0001));`,
517
+ },
518
+ {
519
+ id: 'oneMinus',
520
+ name: 'One Minus',
521
+ category: 'math',
522
+ inputs: [{ name: 'In', type: 'float', default: 0 }],
523
+ outputs: [{ name: 'Out', type: 'float' }],
524
+ glsl: `float {{output.Out}} = 1.0 - {{input.In}};`,
525
+ },
526
+ {
527
+ id: 'remap',
528
+ name: 'Remap',
529
+ category: 'math',
530
+ description: 'Remap value from one range to another',
531
+ inputs: [
532
+ { name: 'In', type: 'float', default: 0 },
533
+ { name: 'InMin', type: 'float', default: 0 },
534
+ { name: 'InMax', type: 'float', default: 1 },
535
+ { name: 'OutMin', type: 'float', default: 0 },
536
+ { name: 'OutMax', type: 'float', default: 1 },
537
+ ],
538
+ outputs: [{ name: 'Out', type: 'float' }],
539
+ glsl: `
540
+ float {{output.Out}} = {{input.OutMin}} + ({{input.In}} - {{input.InMin}}) * ({{input.OutMax}} - {{input.OutMin}}) / ({{input.InMax}} - {{input.InMin}});
541
+ `,
542
+ },
543
+ // ─── Vector Nodes ────────────────────────────────────────
544
+ {
545
+ id: 'vectorSplit',
546
+ name: 'Split',
547
+ category: 'vector',
548
+ description: 'Split vector into components',
549
+ inputs: [{ name: 'In', type: 'vec4', default: [0, 0, 0, 0] }],
550
+ outputs: [
551
+ { name: 'X', type: 'float' },
552
+ { name: 'Y', type: 'float' },
553
+ { name: 'Z', type: 'float' },
554
+ { name: 'W', type: 'float' },
555
+ ],
556
+ glsl: `
557
+ float {{output.X}} = {{input.In}}.x;
558
+ float {{output.Y}} = {{input.In}}.y;
559
+ float {{output.Z}} = {{input.In}}.z;
560
+ float {{output.W}} = {{input.In}}.w;
561
+ `,
562
+ },
563
+ {
564
+ id: 'vectorCombine',
565
+ name: 'Combine',
566
+ category: 'vector',
567
+ description: 'Combine components into vector',
568
+ inputs: [
569
+ { name: 'X', type: 'float', default: 0 },
570
+ { name: 'Y', type: 'float', default: 0 },
571
+ { name: 'Z', type: 'float', default: 0 },
572
+ { name: 'W', type: 'float', default: 0 },
573
+ ],
574
+ outputs: [
575
+ { name: 'XYZW', type: 'vec4' },
576
+ { name: 'XYZ', type: 'vec3' },
577
+ { name: 'XY', type: 'vec2' },
578
+ ],
579
+ glsl: `
580
+ vec4 {{output.XYZW}} = vec4({{input.X}}, {{input.Y}}, {{input.Z}}, {{input.W}});
581
+ vec3 {{output.XYZ}} = vec3({{input.X}}, {{input.Y}}, {{input.Z}});
582
+ vec2 {{output.XY}} = vec2({{input.X}}, {{input.Y}});
583
+ `,
584
+ },
585
+ {
586
+ id: 'dot',
587
+ name: 'Dot Product',
588
+ category: 'vector',
589
+ inputs: [
590
+ { name: 'A', type: 'vec3', default: [1, 0, 0] },
591
+ { name: 'B', type: 'vec3', default: [0, 1, 0] },
592
+ ],
593
+ outputs: [{ name: 'Out', type: 'float' }],
594
+ glsl: `float {{output.Out}} = dot({{input.A}}, {{input.B}});`,
595
+ },
596
+ {
597
+ id: 'cross',
598
+ name: 'Cross Product',
599
+ category: 'vector',
600
+ inputs: [
601
+ { name: 'A', type: 'vec3', default: [1, 0, 0] },
602
+ { name: 'B', type: 'vec3', default: [0, 1, 0] },
603
+ ],
604
+ outputs: [{ name: 'Out', type: 'vec3' }],
605
+ glsl: `vec3 {{output.Out}} = cross({{input.A}}, {{input.B}});`,
606
+ },
607
+ {
608
+ id: 'normalize',
609
+ name: 'Normalize',
610
+ category: 'vector',
611
+ inputs: [{ name: 'In', type: 'vec3', default: [1, 0, 0] }],
612
+ outputs: [{ name: 'Out', type: 'vec3' }],
613
+ glsl: `vec3 {{output.Out}} = normalize({{input.In}});`,
614
+ },
615
+ {
616
+ id: 'length',
617
+ name: 'Length',
618
+ category: 'vector',
619
+ inputs: [{ name: 'In', type: 'vec3', default: [1, 0, 0] }],
620
+ outputs: [{ name: 'Out', type: 'float' }],
621
+ glsl: `float {{output.Out}} = length({{input.In}});`,
622
+ },
623
+ {
624
+ id: 'distance',
625
+ name: 'Distance',
626
+ category: 'vector',
627
+ inputs: [
628
+ { name: 'A', type: 'vec3', default: [0, 0, 0] },
629
+ { name: 'B', type: 'vec3', default: [1, 0, 0] },
630
+ ],
631
+ outputs: [{ name: 'Out', type: 'float' }],
632
+ glsl: `float {{output.Out}} = distance({{input.A}}, {{input.B}});`,
633
+ },
634
+ {
635
+ id: 'reflect',
636
+ name: 'Reflect',
637
+ category: 'vector',
638
+ inputs: [
639
+ { name: 'In', type: 'vec3', default: [1, 0, 0] },
640
+ { name: 'Normal', type: 'vec3', default: [0, 1, 0] },
641
+ ],
642
+ outputs: [{ name: 'Out', type: 'vec3' }],
643
+ glsl: `vec3 {{output.Out}} = reflect({{input.In}}, {{input.Normal}});`,
644
+ },
645
+ {
646
+ id: 'refract',
647
+ name: 'Refract',
648
+ category: 'vector',
649
+ inputs: [
650
+ { name: 'In', type: 'vec3', default: [1, 0, 0] },
651
+ { name: 'Normal', type: 'vec3', default: [0, 1, 0] },
652
+ { name: 'IOR', type: 'float', default: 1.5 },
653
+ ],
654
+ outputs: [{ name: 'Out', type: 'vec3' }],
655
+ glsl: `vec3 {{output.Out}} = refract({{input.In}}, {{input.Normal}}, 1.0 / {{input.IOR}});`,
656
+ },
657
+ // ─── Texture Nodes ───────────────────────────────────────
658
+ {
659
+ id: 'sampleTexture2D',
660
+ name: 'Sample Texture 2D',
661
+ category: 'texture',
662
+ inputs: [{ name: 'UV', type: 'vec2', default: [0, 0] }],
663
+ outputs: [
664
+ { name: 'RGBA', type: 'vec4' },
665
+ { name: 'RGB', type: 'vec3' },
666
+ { name: 'R', type: 'float' },
667
+ { name: 'G', type: 'float' },
668
+ { name: 'B', type: 'float' },
669
+ { name: 'A', type: 'float' },
670
+ ],
671
+ properties: [{ name: 'Texture', type: 'texture', default: null }],
672
+ glsl: `
673
+ vec4 {{output.RGBA}} = texture({{prop.Texture}}, {{input.UV}});
674
+ vec3 {{output.RGB}} = {{output.RGBA}}.rgb;
675
+ float {{output.R}} = {{output.RGBA}}.r;
676
+ float {{output.G}} = {{output.RGBA}}.g;
677
+ float {{output.B}} = {{output.RGBA}}.b;
678
+ float {{output.A}} = {{output.RGBA}}.a;
679
+ `,
680
+ },
681
+ {
682
+ id: 'sampleNormalMap',
683
+ name: 'Sample Normal Map',
684
+ category: 'texture',
685
+ inputs: [
686
+ { name: 'UV', type: 'vec2', default: [0, 0] },
687
+ { name: 'Strength', type: 'float', default: 1 },
688
+ ],
689
+ outputs: [{ name: 'Normal', type: 'vec3' }],
690
+ properties: [{ name: 'Texture', type: 'texture', default: null }],
691
+ glsl: `
692
+ vec3 normalSample = texture({{prop.Texture}}, {{input.UV}}).rgb * 2.0 - 1.0;
693
+ normalSample.xy *= {{input.Strength}};
694
+ vec3 {{output.Normal}} = normalize(normalSample);
695
+ `,
696
+ },
697
+ {
698
+ id: 'sampleCubemap',
699
+ name: 'Sample Cubemap',
700
+ category: 'texture',
701
+ inputs: [{ name: 'Direction', type: 'vec3', default: [0, 1, 0] }],
702
+ outputs: [{ name: 'RGBA', type: 'vec4' }],
703
+ properties: [{ name: 'Cubemap', type: 'texture', default: null }],
704
+ glsl: `vec4 {{output.RGBA}} = textureCube({{prop.Cubemap}}, {{input.Direction}});`,
705
+ },
706
+ {
707
+ id: 'triplanar',
708
+ name: 'Triplanar Mapping',
709
+ category: 'texture',
710
+ description: 'Sample texture using world position and normal',
711
+ inputs: [
712
+ { name: 'Position', type: 'vec3' },
713
+ { name: 'Normal', type: 'vec3' },
714
+ { name: 'Tile', type: 'float', default: 1 },
715
+ { name: 'Blend', type: 'float', default: 1 },
716
+ ],
717
+ outputs: [{ name: 'RGBA', type: 'vec4' }],
718
+ properties: [{ name: 'Texture', type: 'texture', default: null }],
719
+ glsl: `
720
+ vec3 blending = pow(abs({{input.Normal}}), vec3({{input.Blend}}));
721
+ blending /= dot(blending, vec3(1.0));
722
+
723
+ vec4 xProject = texture({{prop.Texture}}, {{input.Position}}.yz * {{input.Tile}});
724
+ vec4 yProject = texture({{prop.Texture}}, {{input.Position}}.xz * {{input.Tile}});
725
+ vec4 zProject = texture({{prop.Texture}}, {{input.Position}}.xy * {{input.Tile}});
726
+
727
+ vec4 {{output.RGBA}} = xProject * blending.x + yProject * blending.y + zProject * blending.z;
728
+ `,
729
+ },
730
+ // ─── UV Nodes ────────────────────────────────────────────
731
+ {
732
+ id: 'tiling',
733
+ name: 'Tiling and Offset',
734
+ category: 'uv',
735
+ inputs: [
736
+ { name: 'UV', type: 'vec2', default: [0, 0] },
737
+ { name: 'Tiling', type: 'vec2', default: [1, 1] },
738
+ { name: 'Offset', type: 'vec2', default: [0, 0] },
739
+ ],
740
+ outputs: [{ name: 'Out', type: 'vec2' }],
741
+ glsl: `vec2 {{output.Out}} = {{input.UV}} * {{input.Tiling}} + {{input.Offset}};`,
742
+ },
743
+ {
744
+ id: 'rotate',
745
+ name: 'Rotate',
746
+ category: 'uv',
747
+ inputs: [
748
+ { name: 'UV', type: 'vec2', default: [0, 0] },
749
+ { name: 'Center', type: 'vec2', default: [0.5, 0.5] },
750
+ { name: 'Rotation', type: 'float', default: 0 },
751
+ ],
752
+ outputs: [{ name: 'Out', type: 'vec2' }],
753
+ glsl: `
754
+ vec2 uvCentered = {{input.UV}} - {{input.Center}};
755
+ float c = cos({{input.Rotation}});
756
+ float s = sin({{input.Rotation}});
757
+ vec2 {{output.Out}} = vec2(uvCentered.x * c - uvCentered.y * s, uvCentered.x * s + uvCentered.y * c) + {{input.Center}};
758
+ `,
759
+ },
760
+ {
761
+ id: 'flipbook',
762
+ name: 'Flipbook',
763
+ category: 'uv',
764
+ description: 'Animated sprite sheet UV',
765
+ inputs: [
766
+ { name: 'UV', type: 'vec2', default: [0, 0] },
767
+ { name: 'Width', type: 'float', default: 4 },
768
+ { name: 'Height', type: 'float', default: 4 },
769
+ { name: 'Tile', type: 'float', default: 0 },
770
+ ],
771
+ outputs: [{ name: 'Out', type: 'vec2' }],
772
+ glsl: `
773
+ float tile = floor({{input.Tile}});
774
+ float invWidth = 1.0 / {{input.Width}};
775
+ float invHeight = 1.0 / {{input.Height}};
776
+ float tileX = mod(tile, {{input.Width}});
777
+ float tileY = floor(tile * invWidth);
778
+ vec2 {{output.Out}} = ({{input.UV}} + vec2(tileX, tileY)) * vec2(invWidth, invHeight);
779
+ `,
780
+ },
781
+ {
782
+ id: 'polar',
783
+ name: 'Polar Coordinates',
784
+ category: 'uv',
785
+ inputs: [
786
+ { name: 'UV', type: 'vec2', default: [0, 0] },
787
+ { name: 'Center', type: 'vec2', default: [0.5, 0.5] },
788
+ { name: 'RadialScale', type: 'float', default: 1 },
789
+ { name: 'LengthScale', type: 'float', default: 1 },
790
+ ],
791
+ outputs: [{ name: 'Out', type: 'vec2' }],
792
+ glsl: `
793
+ vec2 delta = {{input.UV}} - {{input.Center}};
794
+ float radius = length(delta) * 2.0 * {{input.LengthScale}};
795
+ float angle = atan(delta.y, delta.x) / 6.283185 + 0.5;
796
+ vec2 {{output.Out}} = vec2(radius, angle * {{input.RadialScale}});
797
+ `,
798
+ },
799
+ {
800
+ id: 'spherize',
801
+ name: 'Spherize',
802
+ category: 'uv',
803
+ inputs: [
804
+ { name: 'UV', type: 'vec2', default: [0, 0] },
805
+ { name: 'Center', type: 'vec2', default: [0.5, 0.5] },
806
+ { name: 'Strength', type: 'float', default: 0.5 },
807
+ { name: 'Offset', type: 'vec2', default: [0, 0] },
808
+ ],
809
+ outputs: [{ name: 'Out', type: 'vec2' }],
810
+ glsl: `
811
+ vec2 delta = {{input.UV}} - {{input.Center}};
812
+ float dist = length(delta);
813
+ float factor = 1.0 + dist * dist * {{input.Strength}};
814
+ vec2 {{output.Out}} = {{input.Center}} + delta / factor + {{input.Offset}};
815
+ `,
816
+ },
817
+ {
818
+ id: 'twirl',
819
+ name: 'Twirl',
820
+ category: 'uv',
821
+ inputs: [
822
+ { name: 'UV', type: 'vec2', default: [0, 0] },
823
+ { name: 'Center', type: 'vec2', default: [0.5, 0.5] },
824
+ { name: 'Strength', type: 'float', default: 1 },
825
+ { name: 'Offset', type: 'vec2', default: [0, 0] },
826
+ ],
827
+ outputs: [{ name: 'Out', type: 'vec2' }],
828
+ glsl: `
829
+ vec2 delta = {{input.UV}} - {{input.Center}};
830
+ float dist = length(delta);
831
+ float angle = {{input.Strength}} * dist;
832
+ float c = cos(angle);
833
+ float s = sin(angle);
834
+ vec2 {{output.Out}} = vec2(delta.x * c - delta.y * s, delta.x * s + delta.y * c) + {{input.Center}} + {{input.Offset}};
835
+ `,
836
+ },
837
+ {
838
+ id: 'parallax',
839
+ name: 'Parallax Mapping',
840
+ category: 'uv',
841
+ inputs: [
842
+ { name: 'UV', type: 'vec2', default: [0, 0] },
843
+ { name: 'Height', type: 'float', default: 0.5 },
844
+ { name: 'ViewDir', type: 'vec3' },
845
+ { name: 'Amplitude', type: 'float', default: 0.05 },
846
+ ],
847
+ outputs: [{ name: 'Out', type: 'vec2' }],
848
+ glsl: `
849
+ float h = {{input.Height}};
850
+ vec2 {{output.Out}} = {{input.UV}} - {{input.ViewDir}}.xy * (h * {{input.Amplitude}});
851
+ `,
852
+ },
853
+ // ─── Color Nodes ─────────────────────────────────────────
854
+ {
855
+ id: 'blend',
856
+ name: 'Blend',
857
+ category: 'color',
858
+ description: 'Blend two colors',
859
+ inputs: [
860
+ { name: 'Base', type: 'vec4', default: [0, 0, 0, 1] },
861
+ { name: 'Blend', type: 'vec4', default: [1, 1, 1, 1] },
862
+ { name: 'Opacity', type: 'float', default: 1 },
863
+ ],
864
+ outputs: [{ name: 'Out', type: 'vec4' }],
865
+ properties: [
866
+ {
867
+ name: 'Mode',
868
+ type: 'dropdown',
869
+ default: 'normal',
870
+ options: [
871
+ 'normal',
872
+ 'multiply',
873
+ 'screen',
874
+ 'overlay',
875
+ 'add',
876
+ 'subtract',
877
+ 'difference',
878
+ 'darken',
879
+ 'lighten',
880
+ ],
881
+ },
882
+ ],
883
+ glsl: `
884
+ vec4 {{output.Out}};
885
+ // Blend mode logic here based on prop.Mode
886
+ {{output.Out}} = mix({{input.Base}}, {{input.Blend}}, {{input.Opacity}});
887
+ `,
888
+ },
889
+ {
890
+ id: 'contrast',
891
+ name: 'Contrast',
892
+ category: 'color',
893
+ inputs: [
894
+ { name: 'In', type: 'vec3', default: [0.5, 0.5, 0.5] },
895
+ { name: 'Contrast', type: 'float', default: 1 },
896
+ ],
897
+ outputs: [{ name: 'Out', type: 'vec3' }],
898
+ glsl: `vec3 {{output.Out}} = ({{input.In}} - 0.5) * {{input.Contrast}} + 0.5;`,
899
+ },
900
+ {
901
+ id: 'saturation',
902
+ name: 'Saturation',
903
+ category: 'color',
904
+ inputs: [
905
+ { name: 'In', type: 'vec3', default: [0.5, 0.5, 0.5] },
906
+ { name: 'Saturation', type: 'float', default: 1 },
907
+ ],
908
+ outputs: [{ name: 'Out', type: 'vec3' }],
909
+ glsl: `
910
+ float luma = dot({{input.In}}, vec3(0.2126, 0.7152, 0.0722));
911
+ vec3 {{output.Out}} = mix(vec3(luma), {{input.In}}, {{input.Saturation}});
912
+ `,
913
+ },
914
+ {
915
+ id: 'hue',
916
+ name: 'Hue',
917
+ category: 'color',
918
+ inputs: [
919
+ { name: 'In', type: 'vec3', default: [1, 0, 0] },
920
+ { name: 'Offset', type: 'float', default: 0 },
921
+ ],
922
+ outputs: [{ name: 'Out', type: 'vec3' }],
923
+ glsl: `
924
+ // RGB to HSV
925
+ vec3 hsvIn = {{input.In}};
926
+ float maxC = max(max(hsvIn.r, hsvIn.g), hsvIn.b);
927
+ float minC = min(min(hsvIn.r, hsvIn.g), hsvIn.b);
928
+ float delta = maxC - minC;
929
+ float h = 0.0;
930
+ if (delta > 0.0) {
931
+ if (maxC == hsvIn.r) h = mod((hsvIn.g - hsvIn.b) / delta, 6.0);
932
+ else if (maxC == hsvIn.g) h = (hsvIn.b - hsvIn.r) / delta + 2.0;
933
+ else h = (hsvIn.r - hsvIn.g) / delta + 4.0;
934
+ h /= 6.0;
935
+ }
936
+ float s = maxC > 0.0 ? delta / maxC : 0.0;
937
+ float v = maxC;
938
+
939
+ // Shift hue
940
+ h = fract(h + {{input.Offset}});
941
+
942
+ // HSV to RGB
943
+ float i = floor(h * 6.0);
944
+ float f = h * 6.0 - i;
945
+ float p = v * (1.0 - s);
946
+ float q = v * (1.0 - f * s);
947
+ float t = v * (1.0 - (1.0 - f) * s);
948
+
949
+ vec3 {{output.Out}};
950
+ int im = int(mod(i, 6.0));
951
+ if (im == 0) {{output.Out}} = vec3(v, t, p);
952
+ else if (im == 1) {{output.Out}} = vec3(q, v, p);
953
+ else if (im == 2) {{output.Out}} = vec3(p, v, t);
954
+ else if (im == 3) {{output.Out}} = vec3(p, q, v);
955
+ else if (im == 4) {{output.Out}} = vec3(t, p, v);
956
+ else {{output.Out}} = vec3(v, p, q);
957
+ `,
958
+ },
959
+ {
960
+ id: 'brightness',
961
+ name: 'Brightness',
962
+ category: 'color',
963
+ inputs: [
964
+ { name: 'In', type: 'vec3', default: [0.5, 0.5, 0.5] },
965
+ { name: 'Brightness', type: 'float', default: 0 },
966
+ ],
967
+ outputs: [{ name: 'Out', type: 'vec3' }],
968
+ glsl: `vec3 {{output.Out}} = {{input.In}} + {{input.Brightness}};`,
969
+ },
970
+ {
971
+ id: 'whiteBalance',
972
+ name: 'White Balance',
973
+ category: 'color',
974
+ inputs: [
975
+ { name: 'In', type: 'vec3', default: [1, 1, 1] },
976
+ { name: 'Temperature', type: 'float', default: 0 },
977
+ { name: 'Tint', type: 'float', default: 0 },
978
+ ],
979
+ outputs: [{ name: 'Out', type: 'vec3' }],
980
+ glsl: `
981
+ float t = {{input.Temperature}};
982
+ float ti = {{input.Tint}};
983
+ vec3 {{output.Out}} = {{input.In}} + vec3(t, ti, -t);
984
+ `,
985
+ },
986
+ {
987
+ id: 'channelMixer',
988
+ name: 'Channel Mixer',
989
+ category: 'color',
990
+ inputs: [{ name: 'In', type: 'vec3', default: [1, 1, 1] }],
991
+ outputs: [{ name: 'Out', type: 'vec3' }],
992
+ properties: [
993
+ { name: 'Red', type: 'vec3', default: [1, 0, 0] },
994
+ { name: 'Green', type: 'vec3', default: [0, 1, 0] },
995
+ { name: 'Blue', type: 'vec3', default: [0, 0, 1] },
996
+ ],
997
+ glsl: `
998
+ vec3 {{output.Out}} = vec3(
999
+ dot({{input.In}}, vec3({{prop.Red}})),
1000
+ dot({{input.In}}, vec3({{prop.Green}})),
1001
+ dot({{input.In}}, vec3({{prop.Blue}}))
1002
+ );
1003
+ `,
1004
+ },
1005
+ {
1006
+ id: 'colorGrade',
1007
+ name: 'Color Grade',
1008
+ category: 'color',
1009
+ description: 'Lift-Gamma-Gain color grading',
1010
+ inputs: [{ name: 'In', type: 'vec3', default: [0.5, 0.5, 0.5] }],
1011
+ outputs: [{ name: 'Out', type: 'vec3' }],
1012
+ properties: [
1013
+ { name: 'Lift', type: 'color', default: [0, 0, 0, 1] },
1014
+ { name: 'Gamma', type: 'color', default: [0.5, 0.5, 0.5, 1] },
1015
+ { name: 'Gain', type: 'color', default: [1, 1, 1, 1] },
1016
+ ],
1017
+ glsl: `
1018
+ vec3 lift = vec3({{prop.Lift}}.rgb);
1019
+ vec3 gamma = vec3({{prop.Gamma}}.rgb);
1020
+ vec3 gain = vec3({{prop.Gain}}.rgb);
1021
+ vec3 col = {{input.In}} * gain + lift;
1022
+ vec3 {{output.Out}} = pow(col, 1.0 / (gamma * 2.0));
1023
+ `,
1024
+ },
1025
+ // ─── Lighting Nodes ──────────────────────────────────────
1026
+ {
1027
+ id: 'lambertDiffuse',
1028
+ name: 'Lambert Diffuse',
1029
+ category: 'lighting',
1030
+ inputs: [
1031
+ { name: 'Normal', type: 'vec3' },
1032
+ { name: 'LightDir', type: 'vec3' },
1033
+ { name: 'LightColor', type: 'vec3', default: [1, 1, 1] },
1034
+ ],
1035
+ outputs: [{ name: 'Out', type: 'vec3' }],
1036
+ glsl: `
1037
+ float ndotl = max(dot({{input.Normal}}, {{input.LightDir}}), 0.0);
1038
+ vec3 {{output.Out}} = {{input.LightColor}} * ndotl;
1039
+ `,
1040
+ },
1041
+ {
1042
+ id: 'blinnPhongSpecular',
1043
+ name: 'Blinn-Phong Specular',
1044
+ category: 'lighting',
1045
+ inputs: [
1046
+ { name: 'Normal', type: 'vec3' },
1047
+ { name: 'ViewDir', type: 'vec3' },
1048
+ { name: 'LightDir', type: 'vec3' },
1049
+ { name: 'Smoothness', type: 'float', default: 0.5 },
1050
+ ],
1051
+ outputs: [{ name: 'Out', type: 'float' }],
1052
+ glsl: `
1053
+ vec3 halfDir = normalize({{input.LightDir}} + {{input.ViewDir}});
1054
+ float ndoth = max(dot({{input.Normal}}, halfDir), 0.0);
1055
+ float spec = pow(ndoth, exp2(10.0 * {{input.Smoothness}} + 1.0));
1056
+ float {{output.Out}} = spec;
1057
+ `,
1058
+ },
1059
+ {
1060
+ id: 'fresnel',
1061
+ name: 'Fresnel',
1062
+ category: 'lighting',
1063
+ inputs: [
1064
+ { name: 'Normal', type: 'vec3' },
1065
+ { name: 'ViewDir', type: 'vec3' },
1066
+ { name: 'Power', type: 'float', default: 5 },
1067
+ ],
1068
+ outputs: [{ name: 'Out', type: 'float' }],
1069
+ glsl: `
1070
+ float ndotv = max(dot({{input.Normal}}, {{input.ViewDir}}), 0.0);
1071
+ float {{output.Out}} = pow(1.0 - ndotv, {{input.Power}});
1072
+ `,
1073
+ },
1074
+ {
1075
+ id: 'ambientOcclusion',
1076
+ name: 'Ambient Occlusion',
1077
+ category: 'lighting',
1078
+ inputs: [
1079
+ { name: 'Color', type: 'vec3' },
1080
+ { name: 'AO', type: 'float', default: 1 },
1081
+ { name: 'Intensity', type: 'float', default: 1 },
1082
+ ],
1083
+ outputs: [{ name: 'Out', type: 'vec3' }],
1084
+ glsl: `vec3 {{output.Out}} = {{input.Color}} * mix(1.0, {{input.AO}}, {{input.Intensity}});`,
1085
+ },
1086
+ // ─── Procedural Nodes ────────────────────────────────────
1087
+ {
1088
+ id: 'noise',
1089
+ name: 'Simple Noise',
1090
+ category: 'procedural',
1091
+ inputs: [
1092
+ { name: 'UV', type: 'vec2', default: [0, 0] },
1093
+ { name: 'Scale', type: 'float', default: 10 },
1094
+ ],
1095
+ outputs: [{ name: 'Out', type: 'float' }],
1096
+ glsl: `
1097
+ vec2 p = {{input.UV}} * {{input.Scale}};
1098
+ float {{output.Out}} = fract(sin(dot(p, vec2(12.9898, 78.233))) * 43758.5453);
1099
+ `,
1100
+ },
1101
+ {
1102
+ id: 'gradient',
1103
+ name: 'Gradient Noise',
1104
+ category: 'procedural',
1105
+ inputs: [
1106
+ { name: 'UV', type: 'vec2', default: [0, 0] },
1107
+ { name: 'Scale', type: 'float', default: 5 },
1108
+ ],
1109
+ outputs: [{ name: 'Out', type: 'float' }],
1110
+ glsl: `
1111
+ // Simple gradient noise approximation
1112
+ vec2 p = {{input.UV}} * {{input.Scale}};
1113
+ vec2 i = floor(p);
1114
+ vec2 f = fract(p);
1115
+ f = f * f * (3.0 - 2.0 * f);
1116
+
1117
+ float a = fract(sin(dot(i, vec2(12.9898, 78.233))) * 43758.5453);
1118
+ float b = fract(sin(dot(i + vec2(1.0, 0.0), vec2(12.9898, 78.233))) * 43758.5453);
1119
+ float c = fract(sin(dot(i + vec2(0.0, 1.0), vec2(12.9898, 78.233))) * 43758.5453);
1120
+ float d = fract(sin(dot(i + vec2(1.0, 1.0), vec2(12.9898, 78.233))) * 43758.5453);
1121
+
1122
+ float {{output.Out}} = mix(mix(a, b, f.x), mix(c, d, f.x), f.y);
1123
+ `,
1124
+ },
1125
+ {
1126
+ id: 'voronoi',
1127
+ name: 'Voronoi',
1128
+ category: 'procedural',
1129
+ inputs: [
1130
+ { name: 'UV', type: 'vec2', default: [0, 0] },
1131
+ { name: 'Scale', type: 'float', default: 5 },
1132
+ { name: 'Angle', type: 'float', default: 0 },
1133
+ ],
1134
+ outputs: [
1135
+ { name: 'Out', type: 'float' },
1136
+ { name: 'Cells', type: 'float' },
1137
+ ],
1138
+ glsl: `
1139
+ vec2 p = {{input.UV}} * {{input.Scale}};
1140
+ vec2 n = floor(p);
1141
+ vec2 f = fract(p);
1142
+
1143
+ float minDist = 1.0;
1144
+ vec2 minCell = vec2(0.0);
1145
+
1146
+ for (int j = -1; j <= 1; j++) {
1147
+ for (int i = -1; i <= 1; i++) {
1148
+ vec2 g = vec2(float(i), float(j));
1149
+ vec2 o = fract(sin(vec2(dot(n + g, vec2(12.9898, 78.233)), dot(n + g, vec2(23.4321, 14.5678)))) * 43758.5453);
1150
+ vec2 r = g + o - f;
1151
+ float d = dot(r, r);
1152
+ if (d < minDist) {
1153
+ minDist = d;
1154
+ minCell = n + g + o;
1155
+ }
1156
+ }
1157
+ }
1158
+
1159
+ float {{output.Out}} = minDist;
1160
+ float {{output.Cells}} = fract(sin(dot(minCell, vec2(12.9898, 78.233))) * 43758.5453);
1161
+ `,
1162
+ },
1163
+ {
1164
+ id: 'checkerboard',
1165
+ name: 'Checkerboard',
1166
+ category: 'procedural',
1167
+ inputs: [
1168
+ { name: 'UV', type: 'vec2', default: [0, 0] },
1169
+ { name: 'Frequency', type: 'vec2', default: [4, 4] },
1170
+ ],
1171
+ outputs: [{ name: 'Out', type: 'float' }],
1172
+ properties: [
1173
+ { name: 'ColorA', type: 'color', default: [1, 1, 1, 1] },
1174
+ { name: 'ColorB', type: 'color', default: [0, 0, 0, 1] },
1175
+ ],
1176
+ glsl: `
1177
+ vec2 uv = {{input.UV}} * {{input.Frequency}};
1178
+ float c = mod(floor(uv.x) + floor(uv.y), 2.0);
1179
+ float {{output.Out}} = c;
1180
+ `,
1181
+ },
1182
+ {
1183
+ id: 'ellipse',
1184
+ name: 'Ellipse',
1185
+ category: 'procedural',
1186
+ inputs: [
1187
+ { name: 'UV', type: 'vec2', default: [0, 0] },
1188
+ { name: 'Width', type: 'float', default: 0.5 },
1189
+ { name: 'Height', type: 'float', default: 0.5 },
1190
+ ],
1191
+ outputs: [{ name: 'Out', type: 'float' }],
1192
+ glsl: `
1193
+ vec2 d = {{input.UV}} - 0.5;
1194
+ d.x /= {{input.Width}};
1195
+ d.y /= {{input.Height}};
1196
+ float {{output.Out}} = 1.0 - smoothstep(0.5, 0.51, length(d));
1197
+ `,
1198
+ },
1199
+ {
1200
+ id: 'rectangle',
1201
+ name: 'Rectangle',
1202
+ category: 'procedural',
1203
+ inputs: [
1204
+ { name: 'UV', type: 'vec2', default: [0, 0] },
1205
+ { name: 'Width', type: 'float', default: 0.5 },
1206
+ { name: 'Height', type: 'float', default: 0.5 },
1207
+ ],
1208
+ outputs: [{ name: 'Out', type: 'float' }],
1209
+ glsl: `
1210
+ vec2 d = abs({{input.UV}} - 0.5);
1211
+ float {{output.Out}} = 1.0 - smoothstep(0.0, 0.01, max(d.x - {{input.Width}} * 0.5, d.y - {{input.Height}} * 0.5));
1212
+ `,
1213
+ },
1214
+ {
1215
+ id: 'roundedRectangle',
1216
+ name: 'Rounded Rectangle',
1217
+ category: 'procedural',
1218
+ inputs: [
1219
+ { name: 'UV', type: 'vec2', default: [0, 0] },
1220
+ { name: 'Width', type: 'float', default: 0.5 },
1221
+ { name: 'Height', type: 'float', default: 0.5 },
1222
+ { name: 'Radius', type: 'float', default: 0.1 },
1223
+ ],
1224
+ outputs: [{ name: 'Out', type: 'float' }],
1225
+ glsl: `
1226
+ vec2 d = abs({{input.UV}} - 0.5) - vec2({{input.Width}}, {{input.Height}}) * 0.5 + {{input.Radius}};
1227
+ float {{output.Out}} = 1.0 - smoothstep(0.0, 0.01, length(max(d, 0.0)) - {{input.Radius}});
1228
+ `,
1229
+ },
1230
+ {
1231
+ id: 'polygon',
1232
+ name: 'Polygon',
1233
+ category: 'procedural',
1234
+ inputs: [
1235
+ { name: 'UV', type: 'vec2', default: [0, 0] },
1236
+ { name: 'Sides', type: 'float', default: 6 },
1237
+ { name: 'Width', type: 'float', default: 0.5 },
1238
+ { name: 'Height', type: 'float', default: 0.5 },
1239
+ ],
1240
+ outputs: [{ name: 'Out', type: 'float' }],
1241
+ glsl: `
1242
+ vec2 d = {{input.UV}} - 0.5;
1243
+ float angle = atan(d.y, d.x);
1244
+ float slice = 6.283185 / {{input.Sides}};
1245
+ float dist = cos(floor(0.5 + angle / slice) * slice - angle) * length(d * 2.0 / vec2({{input.Width}}, {{input.Height}}));
1246
+ float {{output.Out}} = 1.0 - smoothstep(1.0, 1.01, dist);
1247
+ `,
1248
+ },
1249
+ // ─── Output Nodes ────────────────────────────────────────
1250
+ {
1251
+ id: 'pbrMasterNode',
1252
+ name: 'PBR Master',
1253
+ category: 'output',
1254
+ description: 'Physically Based Rendering output',
1255
+ isMaster: true,
1256
+ inputs: [
1257
+ { name: 'Albedo', type: 'vec3', default: [1, 1, 1] },
1258
+ { name: 'Normal', type: 'vec3', default: [0, 0, 1] },
1259
+ { name: 'Metallic', type: 'float', default: 0 },
1260
+ { name: 'Smoothness', type: 'float', default: 0.5 },
1261
+ { name: 'AO', type: 'float', default: 1 },
1262
+ { name: 'Emission', type: 'vec3', default: [0, 0, 0] },
1263
+ { name: 'Alpha', type: 'float', default: 1 },
1264
+ { name: 'AlphaClip', type: 'float', default: 0 },
1265
+ ],
1266
+ outputs: [],
1267
+ glsl: `
1268
+ // PBR output
1269
+ gl_FragColor = vec4({{input.Albedo}}, {{input.Alpha}});
1270
+
1271
+ // Normal output
1272
+ normalOutput = {{input.Normal}};
1273
+
1274
+ // Material properties
1275
+ metallicOutput = {{input.Metallic}};
1276
+ smoothnessOutput = {{input.Smoothness}};
1277
+ aoOutput = {{input.AO}};
1278
+ emissionOutput = {{input.Emission}};
1279
+
1280
+ // Alpha clip
1281
+ if ({{input.Alpha}} < {{input.AlphaClip}}) discard;
1282
+ `,
1283
+ },
1284
+ {
1285
+ id: 'unlitMasterNode',
1286
+ name: 'Unlit Master',
1287
+ category: 'output',
1288
+ description: 'Unlit/emissive output',
1289
+ isMaster: true,
1290
+ inputs: [
1291
+ { name: 'Color', type: 'vec3', default: [1, 1, 1] },
1292
+ { name: 'Alpha', type: 'float', default: 1 },
1293
+ { name: 'AlphaClip', type: 'float', default: 0 },
1294
+ ],
1295
+ outputs: [],
1296
+ glsl: `
1297
+ gl_FragColor = vec4({{input.Color}}, {{input.Alpha}});
1298
+ if ({{input.Alpha}} < {{input.AlphaClip}}) discard;
1299
+ `,
1300
+ },
1301
+ {
1302
+ id: 'vertexMasterNode',
1303
+ name: 'Vertex Master',
1304
+ category: 'output',
1305
+ description: 'Vertex shader output',
1306
+ isMaster: true,
1307
+ stage: 'vertex',
1308
+ inputs: [
1309
+ { name: 'Position', type: 'vec3' },
1310
+ { name: 'Normal', type: 'vec3' },
1311
+ { name: 'Tangent', type: 'vec3' },
1312
+ ],
1313
+ outputs: [],
1314
+ glsl: `
1315
+ gl_Position = u_projectionMatrix * u_viewMatrix * u_modelMatrix * vec4({{input.Position}}, 1.0);
1316
+ `,
1317
+ },
1318
+ ];
1319
+ class ShaderCodeGenerator {
1320
+ constructor(graph) {
1321
+ this.nodeLibrary = new Map();
1322
+ this.generatedCode = new Map();
1323
+ this.uniforms = [];
1324
+ this.defines = [];
1325
+ this.nodeCounter = 0;
1326
+ this.graph = graph;
1327
+ // Build node library lookup
1328
+ for (const nodeDef of SHADER_NODE_LIBRARY) {
1329
+ this.nodeLibrary.set(nodeDef.id, nodeDef);
1330
+ }
1331
+ }
1332
+ generate() {
1333
+ this.generatedCode.clear();
1334
+ this.uniforms = [];
1335
+ this.defines = [];
1336
+ this.nodeCounter = 0;
1337
+ // Find master node
1338
+ const masterNode = this.graph.nodes.find((n) => {
1339
+ const def = this.nodeLibrary.get(n.type);
1340
+ return def === null || def === void 0 ? void 0 : def.isMaster;
1341
+ });
1342
+ if (!masterNode) {
1343
+ throw new Error('No master node found in shader graph');
1344
+ }
1345
+ // Generate from master node backwards
1346
+ const fragmentCode = this.generateNode(masterNode.id);
1347
+ // Build complete shaders
1348
+ const vertex = this.buildVertexShader();
1349
+ const fragment = this.buildFragmentShader(fragmentCode);
1350
+ return {
1351
+ vertex,
1352
+ fragment,
1353
+ uniforms: this.uniforms,
1354
+ defines: this.defines,
1355
+ };
1356
+ }
1357
+ generateNode(nodeId) {
1358
+ var _a;
1359
+ // Check if already generated
1360
+ if (this.generatedCode.has(nodeId)) {
1361
+ return this.generatedCode.get(nodeId);
1362
+ }
1363
+ const node = this.graph.nodes.find((n) => n.id === nodeId);
1364
+ if (!node)
1365
+ return '';
1366
+ const nodeDef = this.nodeLibrary.get(node.type);
1367
+ if (!nodeDef)
1368
+ return '';
1369
+ // Generate dependencies first
1370
+ const inputConnections = this.graph.connections.filter((c) => c.to.nodeId === nodeId);
1371
+ const inputCode = [];
1372
+ const inputVars = {};
1373
+ for (const conn of inputConnections) {
1374
+ const sourceCode = this.generateNode(conn.from.nodeId);
1375
+ inputCode.push(sourceCode);
1376
+ inputVars[conn.to.portName] = `node_${conn.from.nodeId}_${conn.from.portName}`;
1377
+ }
1378
+ // Generate this node's code
1379
+ let code = nodeDef.glsl || '';
1380
+ const varPrefix = `node_${nodeId}`;
1381
+ // Replace input placeholders
1382
+ for (const input of nodeDef.inputs) {
1383
+ const varName = inputVars[input.name] || this.defaultValue(input.type, input.default);
1384
+ code = code.replace(new RegExp(`\\{\\{input\\.${input.name}\\}\\}`, 'g'), varName);
1385
+ }
1386
+ // Replace output placeholders
1387
+ for (const output of nodeDef.outputs) {
1388
+ code = code.replace(new RegExp(`\\{\\{output\\.${output.name}\\}\\}`, 'g'), `${varPrefix}_${output.name}`);
1389
+ }
1390
+ // Replace property placeholders
1391
+ for (const prop of nodeDef.properties || []) {
1392
+ const value = (_a = node.properties[prop.name]) !== null && _a !== void 0 ? _a : prop.default;
1393
+ code = code.replace(new RegExp(`\\{\\{prop\\.${prop.name}\\}\\}`, 'g'), this.formatValue(value, prop.type));
1394
+ // Add uniform if it's a texture
1395
+ if (prop.type === 'texture') {
1396
+ this.uniforms.push({
1397
+ name: `${varPrefix}_${prop.name}`,
1398
+ type: 'sampler2D',
1399
+ });
1400
+ }
1401
+ }
1402
+ const fullCode = inputCode.join('\n') + '\n' + code;
1403
+ this.generatedCode.set(nodeId, fullCode);
1404
+ return fullCode;
1405
+ }
1406
+ defaultValue(type, defaultVal) {
1407
+ if (defaultVal !== undefined) {
1408
+ return this.formatValue(defaultVal, type);
1409
+ }
1410
+ switch (type) {
1411
+ case 'float':
1412
+ return '0.0';
1413
+ case 'vec2':
1414
+ return 'vec2(0.0)';
1415
+ case 'vec3':
1416
+ return 'vec3(0.0)';
1417
+ case 'vec4':
1418
+ return 'vec4(0.0)';
1419
+ case 'int':
1420
+ return '0';
1421
+ case 'bool':
1422
+ return 'false';
1423
+ default:
1424
+ return '0.0';
1425
+ }
1426
+ }
1427
+ formatValue(value, type) {
1428
+ if (Array.isArray(value)) {
1429
+ return `vec${value.length}(${value.map((v) => v.toFixed(6)).join(', ')})`;
1430
+ }
1431
+ if (typeof value === 'number') {
1432
+ return value.toFixed(6);
1433
+ }
1434
+ if (typeof value === 'boolean') {
1435
+ return value ? 'true' : 'false';
1436
+ }
1437
+ return String(value);
1438
+ }
1439
+ buildVertexShader() {
1440
+ const precision = this.graph.settings.precision;
1441
+ return `#version 300 es
1442
+ precision ${precision} float;
1443
+
1444
+ // Attributes
1445
+ in vec3 a_position;
1446
+ in vec3 a_normal;
1447
+ in vec2 a_uv0;
1448
+ in vec2 a_uv1;
1449
+ in vec4 a_color;
1450
+ in vec3 a_tangent;
1451
+
1452
+ // Uniforms
1453
+ uniform mat4 u_modelMatrix;
1454
+ uniform mat4 u_viewMatrix;
1455
+ uniform mat4 u_projectionMatrix;
1456
+ uniform mat4 u_normalMatrix;
1457
+ uniform mat4 u_worldToObject;
1458
+
1459
+ // Varyings
1460
+ out vec3 v_worldPosition;
1461
+ out vec3 v_objectPosition;
1462
+ out vec3 v_viewPosition;
1463
+ out vec3 v_worldNormal;
1464
+ out vec3 v_objectNormal;
1465
+ out vec3 v_viewNormal;
1466
+ out vec3 v_tangent;
1467
+ out vec3 v_tangentNormal;
1468
+ out vec2 v_uv0;
1469
+ out vec2 v_uv1;
1470
+ out vec2 v_uv2;
1471
+ out vec2 v_uv3;
1472
+ out vec4 v_color;
1473
+
1474
+ void main() {
1475
+ vec4 worldPos = u_modelMatrix * vec4(a_position, 1.0);
1476
+ vec4 viewPos = u_viewMatrix * worldPos;
1477
+
1478
+ v_worldPosition = worldPos.xyz;
1479
+ v_objectPosition = a_position;
1480
+ v_viewPosition = viewPos.xyz;
1481
+
1482
+ v_worldNormal = mat3(u_normalMatrix) * a_normal;
1483
+ v_objectNormal = a_normal;
1484
+ v_viewNormal = mat3(u_viewMatrix) * v_worldNormal;
1485
+
1486
+ v_tangent = mat3(u_normalMatrix) * a_tangent;
1487
+ v_tangentNormal = vec3(0.0, 0.0, 1.0);
1488
+
1489
+ v_uv0 = a_uv0;
1490
+ v_uv1 = a_uv1;
1491
+ v_uv2 = a_uv0;
1492
+ v_uv3 = a_uv0;
1493
+ v_color = a_color;
1494
+
1495
+ gl_Position = u_projectionMatrix * viewPos;
1496
+ }
1497
+ `;
1498
+ }
1499
+ buildFragmentShader(mainCode) {
1500
+ const precision = this.graph.settings.precision;
1501
+ const uniformDecls = this.uniforms.map((u) => `uniform ${u.type} ${u.name};`).join('\n');
1502
+ return `#version 300 es
1503
+ precision ${precision} float;
1504
+
1505
+ // Varyings
1506
+ in vec3 v_worldPosition;
1507
+ in vec3 v_objectPosition;
1508
+ in vec3 v_viewPosition;
1509
+ in vec3 v_worldNormal;
1510
+ in vec3 v_objectNormal;
1511
+ in vec3 v_viewNormal;
1512
+ in vec3 v_tangent;
1513
+ in vec3 v_tangentNormal;
1514
+ in vec2 v_uv0;
1515
+ in vec2 v_uv1;
1516
+ in vec2 v_uv2;
1517
+ in vec2 v_uv3;
1518
+ in vec4 v_color;
1519
+
1520
+ // Global uniforms
1521
+ uniform float u_time;
1522
+ uniform float u_deltaTime;
1523
+ uniform float u_smoothDeltaTime;
1524
+ uniform vec3 u_cameraPosition;
1525
+ uniform vec2 u_resolution;
1526
+
1527
+ // Material uniforms
1528
+ ${uniformDecls}
1529
+
1530
+ // Outputs
1531
+ out vec4 fragColor;
1532
+
1533
+ void main() {
1534
+ ${mainCode}
1535
+ fragColor = gl_FragColor;
1536
+ }
1537
+ `;
1538
+ }
1539
+ }
1540
+ function createShaderGraphEditorState(graph) {
1541
+ return {
1542
+ graph: graph !== null && graph !== void 0 ? graph : createEmptyShaderGraph(),
1543
+ selectedNodes: [],
1544
+ hoveredNode: null,
1545
+ hoveredPort: null,
1546
+ draggingConnection: null,
1547
+ viewOffset: { x: 0, y: 0 },
1548
+ zoom: 1,
1549
+ previewEnabled: true,
1550
+ generatedShader: null,
1551
+ compileErrors: [],
1552
+ };
1553
+ }
1554
+ function createEmptyShaderGraph() {
1555
+ return {
1556
+ id: `shader_${Date.now()}`,
1557
+ name: 'New Shader',
1558
+ target: 'glsl',
1559
+ stage: 'fragment',
1560
+ nodes: [
1561
+ {
1562
+ id: 'master',
1563
+ type: 'pbrMasterNode',
1564
+ position: { x: 600, y: 200 },
1565
+ properties: {},
1566
+ },
1567
+ ],
1568
+ connections: [],
1569
+ properties: [],
1570
+ settings: {
1571
+ precision: 'highp',
1572
+ blendMode: 'opaque',
1573
+ cullMode: 'back',
1574
+ depthWrite: true,
1575
+ depthTest: 'lequal',
1576
+ castShadows: true,
1577
+ receiveShadows: true,
1578
+ doubleSided: false,
1579
+ surfaceType: 'opaque',
1580
+ renderQueue: 2000,
1581
+ },
1582
+ };
1583
+ }
1584
+ // ============================================================
1585
+ // Factory Functions
1586
+ // ============================================================
1587
+ function createShaderGraph(name, target = 'glsl') {
1588
+ const graph = createEmptyShaderGraph();
1589
+ graph.name = name;
1590
+ graph.target = target;
1591
+ return graph;
1592
+ }
1593
+ function generateShader(graph) {
1594
+ const generator = new ShaderCodeGenerator(graph);
1595
+ return generator.generate();
1596
+ }
1597
+ function getNodeLibrary() {
1598
+ return SHADER_NODE_LIBRARY;
1599
+ }
1600
+ function getNodesByCategory(category) {
1601
+ return SHADER_NODE_LIBRARY.filter((n) => n.category === category);
1602
+ }
1603
+ function findNodeDef(nodeType) {
1604
+ return SHADER_NODE_LIBRARY.find((n) => n.id === nodeType);
1605
+ }
1606
+
1607
+ exports.SHADER_NODE_LIBRARY = SHADER_NODE_LIBRARY;
1608
+ exports.ShaderCodeGenerator = ShaderCodeGenerator;
1609
+ exports.createEmptyShaderGraph = createEmptyShaderGraph;
1610
+ exports.createShaderGraph = createShaderGraph;
1611
+ exports.createShaderGraphEditorState = createShaderGraphEditorState;
1612
+ exports.findNodeDef = findNodeDef;
1613
+ exports.generateShader = generateShader;
1614
+ exports.getNodeLibrary = getNodeLibrary;
1615
+ exports.getNodesByCategory = getNodesByCategory;
1616
+ //# sourceMappingURL=ShaderGraph.js.map