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