@plastic-software/three 0.183.4 → 0.184.1

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 (277) hide show
  1. package/build/three.cjs +775 -287
  2. package/build/three.core.js +372 -110
  3. package/build/three.core.min.js +1 -1
  4. package/build/three.module.js +428 -181
  5. package/build/three.module.min.js +1 -1
  6. package/build/three.tsl.js +7 -1
  7. package/build/three.tsl.min.js +1 -1
  8. package/build/three.webgpu.js +2979 -1281
  9. package/build/three.webgpu.min.js +1 -1
  10. package/build/three.webgpu.nodes.js +2942 -1281
  11. package/build/three.webgpu.nodes.min.js +1 -1
  12. package/examples/jsm/Addons.js +11 -0
  13. package/examples/jsm/animation/CCDIKSolver.js +5 -1
  14. package/examples/jsm/controls/ArcballControls.js +4 -1
  15. package/examples/jsm/controls/DragControls.js +2 -2
  16. package/examples/jsm/controls/FirstPersonControls.js +58 -54
  17. package/examples/jsm/controls/FlyControls.js +4 -0
  18. package/examples/jsm/controls/OrbitControls.js +2 -2
  19. package/examples/jsm/controls/TrackballControls.js +2 -2
  20. package/examples/jsm/controls/TransformControls.js +34 -2
  21. package/examples/jsm/csm/CSMShadowNode.js +6 -2
  22. package/examples/jsm/exporters/GLTFExporter.js +21 -5
  23. package/examples/jsm/geometries/TextGeometry.js +18 -0
  24. package/examples/jsm/helpers/LightProbeGridHelper.js +221 -0
  25. package/examples/jsm/inspector/Extension.js +13 -0
  26. package/examples/jsm/inspector/Inspector.js +169 -114
  27. package/examples/jsm/inspector/RendererInspector.js +2 -2
  28. package/examples/jsm/inspector/extensions/extensions.json +6 -0
  29. package/examples/jsm/inspector/extensions/tsl-graph/TSLGraphEditor.js +916 -0
  30. package/examples/jsm/inspector/extensions/tsl-graph/TSLGraphLoader.js +281 -0
  31. package/examples/jsm/inspector/tabs/Memory.js +128 -0
  32. package/examples/jsm/inspector/tabs/Parameters.js +34 -2
  33. package/examples/jsm/inspector/tabs/Performance.js +2 -2
  34. package/examples/jsm/inspector/tabs/Settings.js +264 -0
  35. package/examples/jsm/inspector/tabs/Timeline.js +1611 -0
  36. package/examples/jsm/inspector/tabs/Viewer.js +105 -3
  37. package/examples/jsm/inspector/ui/Graph.js +2 -2
  38. package/examples/jsm/inspector/ui/List.js +1 -1
  39. package/examples/jsm/inspector/ui/Profiler.js +273 -176
  40. package/examples/jsm/inspector/ui/Style.js +64 -10
  41. package/examples/jsm/inspector/ui/Tab.js +39 -7
  42. package/examples/jsm/inspector/ui/Values.js +39 -2
  43. package/examples/jsm/inspector/ui/utils.js +13 -0
  44. package/examples/jsm/interaction/InteractionManager.js +226 -0
  45. package/examples/jsm/libs/meshopt_decoder.module.js +8 -8
  46. package/examples/jsm/lighting/DynamicLighting.js +82 -0
  47. package/examples/jsm/lighting/LightProbeGrid.js +651 -0
  48. package/examples/jsm/lines/LineMaterial.js +1 -1
  49. package/examples/jsm/loaders/EXRLoader.js +682 -43
  50. package/examples/jsm/loaders/FBXLoader.js +233 -33
  51. package/examples/jsm/loaders/GLTFLoader.js +24 -7
  52. package/examples/jsm/loaders/HDRLoader.js +1 -1
  53. package/examples/jsm/loaders/KTX2Loader.js +8 -2
  54. package/examples/jsm/loaders/LDrawLoader.js +39 -47
  55. package/examples/jsm/loaders/SVGLoader.js +1 -1
  56. package/examples/jsm/loaders/VTKLoader.js +5 -1
  57. package/examples/jsm/loaders/collada/ColladaComposer.js +101 -7
  58. package/examples/jsm/loaders/collada/ColladaParser.js +19 -4
  59. package/examples/jsm/loaders/usd/USDAParser.js +6 -0
  60. package/examples/jsm/loaders/usd/USDCParser.js +26 -0
  61. package/examples/jsm/loaders/usd/USDComposer.js +656 -103
  62. package/examples/jsm/misc/GPUComputationRenderer.js +2 -0
  63. package/examples/jsm/misc/RollerCoaster.js +42 -4
  64. package/examples/jsm/modifiers/TessellateModifier.js +1 -1
  65. package/examples/jsm/objects/Reflector.js +73 -25
  66. package/examples/jsm/objects/Sky.js +14 -2
  67. package/examples/jsm/objects/SkyMesh.js +23 -6
  68. package/examples/jsm/renderers/Projector.js +18 -38
  69. package/examples/jsm/renderers/SVGRenderer.js +6 -25
  70. package/examples/jsm/transpiler/GLSLDecoder.js +2 -2
  71. package/examples/jsm/tsl/WebGLNodesHandler.js +605 -0
  72. package/examples/jsm/tsl/display/AfterImageNode.js +10 -0
  73. package/examples/jsm/tsl/display/AnamorphicNode.js +11 -0
  74. package/examples/jsm/tsl/display/BilateralBlurNode.js +10 -0
  75. package/examples/jsm/tsl/display/ChromaticAberrationNode.js +3 -36
  76. package/examples/jsm/tsl/display/FSR1Node.js +477 -0
  77. package/examples/jsm/tsl/display/GTAONode.js +2 -1
  78. package/examples/jsm/tsl/display/GaussianBlurNode.js +10 -0
  79. package/examples/jsm/tsl/display/GodraysNode.js +2 -11
  80. package/examples/jsm/tsl/display/OutlineNode.js +66 -16
  81. package/examples/jsm/tsl/display/SSGINode.js +0 -4
  82. package/examples/jsm/tsl/display/SharpenNode.js +283 -0
  83. package/examples/jsm/tsl/display/TAAUNode.js +835 -0
  84. package/examples/jsm/tsl/display/TRAANode.js +48 -7
  85. package/examples/jsm/tsl/lighting/DynamicLightsNode.js +300 -0
  86. package/examples/jsm/tsl/lighting/data/AmbientLightDataNode.js +61 -0
  87. package/examples/jsm/tsl/lighting/data/DirectionalLightDataNode.js +111 -0
  88. package/examples/jsm/tsl/lighting/data/HemisphereLightDataNode.js +99 -0
  89. package/examples/jsm/tsl/lighting/data/PointLightDataNode.js +134 -0
  90. package/examples/jsm/tsl/lighting/data/SpotLightDataNode.js +161 -0
  91. package/examples/jsm/tsl/math/Bayer.js +13 -2
  92. package/examples/jsm/utils/BufferGeometryUtils.js +2 -3
  93. package/examples/jsm/utils/ColorUtils.js +76 -0
  94. package/examples/jsm/utils/SkeletonUtils.js +14 -8
  95. package/examples/jsm/webxr/XRHandMeshModel.js +36 -10
  96. package/examples/jsm/webxr/XRHandModelFactory.js +2 -1
  97. package/package.json +4 -4
  98. package/src/Three.Core.js +1 -0
  99. package/src/Three.TSL.js +6 -0
  100. package/src/Three.WebGPU.Nodes.js +3 -0
  101. package/src/Three.WebGPU.js +6 -0
  102. package/src/animation/AnimationAction.js +11 -1
  103. package/src/audio/AudioContext.js +2 -2
  104. package/src/constants.js +1 -1
  105. package/src/core/BufferAttribute.js +13 -1
  106. package/src/core/Clock.js +1 -1
  107. package/src/core/Object3D.js +1 -5
  108. package/src/core/RenderTarget.js +1 -0
  109. package/src/extras/curves/CatmullRomCurve3.js +3 -2
  110. package/src/loaders/AudioLoader.js +11 -1
  111. package/src/loaders/DataTextureLoader.js +6 -4
  112. package/src/loaders/FileLoader.js +1 -2
  113. package/src/loaders/ImageBitmapLoader.js +4 -6
  114. package/src/loaders/MaterialLoader.js +1 -1
  115. package/src/loaders/ObjectLoader.js +25 -4
  116. package/src/loaders/nodes/NodeObjectLoader.js +18 -0
  117. package/src/materials/MeshToonMaterial.js +1 -1
  118. package/src/materials/nodes/Line2NodeMaterial.js +27 -0
  119. package/src/materials/nodes/NodeMaterial.js +0 -27
  120. package/src/materials/nodes/manager/NodeMaterialObserver.js +188 -89
  121. package/src/math/Line3.js +3 -0
  122. package/src/math/Matrix2.js +13 -9
  123. package/src/math/Matrix3.js +13 -9
  124. package/src/math/Matrix4.js +13 -9
  125. package/src/math/Plane.js +4 -3
  126. package/src/math/Triangle.js +1 -1
  127. package/src/math/Vector2.js +11 -7
  128. package/src/math/Vector3.js +12 -8
  129. package/src/math/Vector4.js +13 -9
  130. package/src/nodes/Nodes.js +0 -1
  131. package/src/nodes/TSL.js +1 -1
  132. package/src/nodes/accessors/BufferAttributeNode.js +9 -3
  133. package/src/nodes/accessors/CubeTextureNode.js +7 -1
  134. package/src/nodes/accessors/MaterialProperties.js +2 -5
  135. package/src/nodes/accessors/Object3DNode.js +1 -1
  136. package/src/nodes/accessors/ReferenceBaseNode.js +2 -2
  137. package/src/nodes/accessors/ReferenceNode.js +4 -4
  138. package/src/nodes/accessors/SceneProperties.js +2 -8
  139. package/src/nodes/accessors/StorageBufferNode.js +10 -4
  140. package/src/nodes/accessors/StorageTextureNode.js +4 -9
  141. package/src/nodes/accessors/TextureNode.js +10 -2
  142. package/src/nodes/accessors/UniformArrayNode.js +2 -2
  143. package/src/nodes/code/FunctionCallNode.js +1 -1
  144. package/src/nodes/code/FunctionNode.js +1 -1
  145. package/src/nodes/core/ArrayNode.js +1 -1
  146. package/src/nodes/core/AssignNode.js +1 -1
  147. package/src/nodes/core/AttributeNode.js +1 -1
  148. package/src/nodes/core/BypassNode.js +1 -1
  149. package/src/nodes/core/ContextNode.js +1 -1
  150. package/src/nodes/core/IndexNode.js +2 -1
  151. package/src/nodes/core/InputNode.js +1 -1
  152. package/src/nodes/core/InspectorNode.js +1 -1
  153. package/src/nodes/core/IsolateNode.js +1 -1
  154. package/src/nodes/core/Node.js +83 -12
  155. package/src/nodes/core/NodeBuilder.js +117 -16
  156. package/src/nodes/core/NodeUtils.js +1 -1
  157. package/src/nodes/core/OutputStructNode.js +1 -1
  158. package/src/nodes/core/ParameterNode.js +1 -1
  159. package/src/nodes/core/StackNode.js +1 -1
  160. package/src/nodes/core/StructNode.js +1 -1
  161. package/src/nodes/core/StructTypeNode.js +1 -1
  162. package/src/nodes/core/SubBuildNode.js +1 -1
  163. package/src/nodes/core/UniformGroupNode.js +36 -6
  164. package/src/nodes/core/VarNode.js +1 -1
  165. package/src/nodes/core/VaryingNode.js +1 -1
  166. package/src/nodes/display/NormalMapNode.js +2 -2
  167. package/src/nodes/display/PassNode.js +27 -7
  168. package/src/nodes/display/RenderOutputNode.js +4 -4
  169. package/src/nodes/display/ScreenNode.js +1 -1
  170. package/src/nodes/display/ViewportDepthTextureNode.js +11 -15
  171. package/src/nodes/display/ViewportTextureNode.js +18 -7
  172. package/src/nodes/functions/BSDF/V_GGX_SmithCorrelated_Anisotropic.js +2 -2
  173. package/src/nodes/geometry/RangeNode.js +1 -1
  174. package/src/nodes/gpgpu/AtomicFunctionNode.js +1 -1
  175. package/src/nodes/gpgpu/BarrierNode.js +9 -0
  176. package/src/nodes/gpgpu/ComputeBuiltinNode.js +1 -1
  177. package/src/nodes/gpgpu/ComputeNode.js +69 -44
  178. package/src/nodes/gpgpu/SubgroupFunctionNode.js +1 -1
  179. package/src/nodes/lighting/LightsNode.js +6 -27
  180. package/src/nodes/lighting/ShadowNode.js +24 -2
  181. package/src/nodes/math/BitcastNode.js +1 -1
  182. package/src/nodes/math/ConditionalNode.js +1 -1
  183. package/src/nodes/math/MathNode.js +73 -1
  184. package/src/nodes/math/OperatorNode.js +1 -1
  185. package/src/nodes/math/PackFloatNode.js +1 -1
  186. package/src/nodes/math/UnpackFloatNode.js +1 -1
  187. package/src/nodes/tsl/TSLBase.js +1 -1
  188. package/src/nodes/tsl/TSLCore.js +21 -3
  189. package/src/nodes/utils/ArrayElementNode.js +1 -1
  190. package/src/nodes/utils/ConvertNode.js +1 -1
  191. package/src/nodes/utils/DebugNode.js +1 -1
  192. package/src/nodes/utils/EventNode.js +30 -0
  193. package/src/nodes/utils/FlipNode.js +1 -1
  194. package/src/nodes/utils/FunctionOverloadingNode.js +1 -1
  195. package/src/nodes/utils/JoinNode.js +1 -1
  196. package/src/nodes/utils/MemberNode.js +1 -1
  197. package/src/nodes/utils/Remap.js +48 -0
  198. package/src/nodes/utils/RotateNode.js +1 -1
  199. package/src/nodes/utils/SetNode.js +1 -1
  200. package/src/nodes/utils/SplitNode.js +1 -1
  201. package/src/objects/BatchedMesh.js +17 -2
  202. package/src/objects/InstancedMesh.js +19 -3
  203. package/src/objects/SkinnedMesh.js +26 -9
  204. package/src/renderers/WebGLRenderer.js +147 -48
  205. package/src/renderers/common/Animation.js +3 -3
  206. package/src/renderers/common/Attributes.js +15 -1
  207. package/src/renderers/common/Backend.js +0 -8
  208. package/src/renderers/common/Background.js +2 -2
  209. package/src/renderers/common/BindGroup.js +1 -8
  210. package/src/renderers/common/Bindings.js +2 -2
  211. package/src/renderers/common/ComputePipeline.js +1 -1
  212. package/src/renderers/common/CubeRenderTarget.js +1 -1
  213. package/src/renderers/common/Info.js +333 -4
  214. package/src/renderers/common/InspectorBase.js +6 -1
  215. package/src/renderers/common/Pipelines.js +36 -3
  216. package/src/renderers/common/ReadbackBuffer.js +78 -0
  217. package/src/renderers/common/RenderBundle.js +3 -1
  218. package/src/renderers/common/RenderBundles.js +5 -2
  219. package/src/renderers/common/RenderObject.js +2 -2
  220. package/src/renderers/common/RenderObjects.js +3 -3
  221. package/src/renderers/common/RenderPipeline.js +35 -6
  222. package/src/renderers/common/Renderer.js +232 -53
  223. package/src/renderers/common/Textures.js +72 -3
  224. package/src/renderers/common/UniformsGroup.js +1 -1
  225. package/src/renderers/common/XRManager.js +34 -27
  226. package/src/renderers/common/extras/PMREMGenerator.js +23 -15
  227. package/src/renderers/common/nodes/NodeBuilderState.js +1 -1
  228. package/src/renderers/common/nodes/NodeManager.js +230 -99
  229. package/src/renderers/shaders/ShaderChunk/envmap_common_pars_fragment.glsl.js +0 -1
  230. package/src/renderers/shaders/ShaderChunk/envmap_fragment.glsl.js +1 -1
  231. package/src/renderers/shaders/ShaderChunk/lightprobes_pars_fragment.glsl.js +80 -0
  232. package/src/renderers/shaders/ShaderChunk/lights_fragment_begin.glsl.js +8 -0
  233. package/src/renderers/shaders/ShaderChunk/lights_pars_begin.glsl.js +2 -0
  234. package/src/renderers/shaders/ShaderChunk/lights_physical_pars_fragment.glsl.js +1 -3
  235. package/src/renderers/shaders/ShaderChunk/normal_fragment_maps.glsl.js +7 -0
  236. package/src/renderers/shaders/ShaderChunk/premultiplied_alpha_fragment.glsl.js +0 -1
  237. package/src/renderers/shaders/ShaderChunk/shadowmap_vertex.glsl.js +12 -2
  238. package/src/renderers/shaders/ShaderChunk.js +2 -0
  239. package/src/renderers/shaders/ShaderLib/backgroundCube.glsl.js +1 -2
  240. package/src/renderers/shaders/ShaderLib.js +0 -1
  241. package/src/renderers/shaders/UniformsLib.js +7 -2
  242. package/src/renderers/shaders/UniformsUtils.js +27 -5
  243. package/src/renderers/webgl/WebGLAnimation.js +2 -1
  244. package/src/renderers/webgl/WebGLBackground.js +13 -13
  245. package/src/renderers/webgl/WebGLBufferRenderer.js +0 -32
  246. package/src/renderers/webgl/WebGLCapabilities.js +6 -0
  247. package/src/renderers/webgl/WebGLIndexedBufferRenderer.js +0 -32
  248. package/src/renderers/webgl/WebGLMaterials.js +12 -13
  249. package/src/renderers/webgl/WebGLOutput.js +4 -1
  250. package/src/renderers/webgl/WebGLProgram.js +4 -0
  251. package/src/renderers/webgl/WebGLPrograms.js +21 -4
  252. package/src/renderers/webgl/WebGLRenderStates.js +13 -2
  253. package/src/renderers/webgl/WebGLState.js +43 -0
  254. package/src/renderers/webgl/WebGLTextures.js +129 -26
  255. package/src/renderers/webgl/WebGLUniformsGroups.js +19 -0
  256. package/src/renderers/webgl-fallback/WebGLBackend.js +106 -65
  257. package/src/renderers/webgl-fallback/WebGLBufferRenderer.js +0 -41
  258. package/src/renderers/webgl-fallback/nodes/GLSLNodeBuilder.js +29 -51
  259. package/src/renderers/webgl-fallback/utils/WebGLAttributeUtils.js +53 -19
  260. package/src/renderers/webgl-fallback/utils/WebGLCapabilities.js +25 -0
  261. package/src/renderers/webgl-fallback/utils/WebGLState.js +42 -1
  262. package/src/renderers/webgl-fallback/utils/WebGLTextureUtils.js +63 -50
  263. package/src/renderers/webgl-fallback/utils/WebGLTimestampQueryPool.js +1 -1
  264. package/src/renderers/webgpu/WebGPUBackend.js +160 -146
  265. package/src/renderers/webgpu/nodes/WGSLNodeBuilder.js +55 -33
  266. package/src/renderers/webgpu/utils/WebGPUAttributeUtils.js +103 -17
  267. package/src/renderers/webgpu/utils/WebGPUBindingUtils.js +1 -1
  268. package/src/renderers/webgpu/utils/WebGPUCapabilities.js +48 -0
  269. package/src/renderers/webgpu/utils/WebGPUConstants.js +8 -0
  270. package/src/renderers/webgpu/utils/WebGPUTextureUtils.js +91 -17
  271. package/src/renderers/webgpu/utils/WebGPUUtils.js +18 -2
  272. package/src/renderers/webxr/WebXRController.js +12 -0
  273. package/src/textures/HTMLTexture.js +74 -0
  274. package/src/textures/Source.js +1 -1
  275. package/src/textures/Texture.js +13 -2
  276. package/src/utils.js +23 -1
  277. package/src/nodes/utils/RemapNode.js +0 -125
@@ -0,0 +1,916 @@
1
+ import { Raycaster, Vector2, BoxHelper, error, warn } from 'three/webgpu';
2
+ import { Extension } from 'three/addons/inspector/Extension.js';
3
+ import { TSLGraphLoader } from './TSLGraphLoader.js';
4
+
5
+ const HOST_SOURCE = 'tsl-graph-host';
6
+ const EDITOR_SOURCE = 'tsl-graph-editor';
7
+
8
+ const _resposeByCommand = {
9
+ 'tsl:command:get-code': 'tsl:response:get-code',
10
+ 'tsl:command:set-root-material': 'tsl:response:set-root-material',
11
+ 'tsl:command:get-graph': 'tsl:response:get-graph',
12
+ 'tsl:command:load': 'tsl:response:load',
13
+ 'tsl:command:clear-graph': 'tsl:response:clear-graph'
14
+ };
15
+
16
+ const _refMaterials = new WeakMap();
17
+
18
+ class TSLGraphEditor extends Extension {
19
+
20
+ constructor( options = {} ) {
21
+
22
+ super( 'TSL Graph', options );
23
+
24
+ const editorUrl = new URL( 'https://www.tsl-graph.xyz/editor/standalone' );
25
+ editorUrl.searchParams.set( 'graphs', 'material' );
26
+ editorUrl.searchParams.set( 'targetOrigin', '*' );
27
+
28
+ // UI Setup
29
+ this.content.style.display = 'flex';
30
+ this.content.style.flexDirection = 'column';
31
+ this.content.style.position = 'relative';
32
+
33
+ const headerDiv = document.createElement( 'div' );
34
+ headerDiv.style.padding = '4px';
35
+ headerDiv.style.backgroundColor = 'var(--profiler-header-bg, #2a2a33aa)';
36
+ headerDiv.style.borderBottom = '1px solid var(--profiler-border, #4a4a5a)';
37
+ headerDiv.style.display = 'flex';
38
+ headerDiv.style.justifyContent = 'center';
39
+ headerDiv.style.gap = '4px';
40
+ headerDiv.style.position = 'relative';
41
+
42
+ const importBtn = document.createElement( 'button' );
43
+ importBtn.innerHTML = '<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"></path><polyline points="17 8 12 3 7 8"></polyline><line x1="12" y1="3" x2="12" y2="15"></line></svg>';
44
+ importBtn.className = 'panel-action-btn';
45
+ importBtn.title = 'Import';
46
+ importBtn.style.padding = '5px 8px';
47
+ importBtn.onclick = () => this._importData();
48
+
49
+ const exportBtn = document.createElement( 'button' );
50
+ exportBtn.innerHTML = '<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"></path><polyline points="7 10 12 15 17 10"></polyline><line x1="12" y1="15" x2="12" y2="3"></line></svg>';
51
+ exportBtn.className = 'panel-action-btn';
52
+ exportBtn.title = 'Export';
53
+ exportBtn.style.padding = '5px 8px';
54
+ exportBtn.onclick = () => this._exportData();
55
+
56
+ const manageBtn = document.createElement( 'button' );
57
+ manageBtn.innerHTML = '<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="14" width="7" height="7" rx="1"></rect><rect x="3" y="3" width="7" height="7" rx="1"></rect><path d="M14 4h7"></path><path d="M14 9h7"></path><path d="M14 15h7"></path><path d="M14 20h7"></path></svg>';
58
+ manageBtn.className = 'panel-action-btn';
59
+ manageBtn.title = 'Saved Materials';
60
+ manageBtn.style.padding = '5px 8px';
61
+ manageBtn.onclick = () => this._showManagerModal();
62
+
63
+ const autoIdBtn = document.createElement( 'button' );
64
+ autoIdBtn.innerHTML = '<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 3c.132 5.466 2.534 7.868 8 8-5.466.132-7.868 2.534-8 8-.132-5.466-2.534-7.868-8-8 5.466-.132 7.868-2.534 8-8z"></path></svg>';
65
+ autoIdBtn.className = 'panel-action-btn';
66
+ autoIdBtn.title = 'Auto-Generate Graph ID';
67
+ autoIdBtn.style.padding = '5px 8px';
68
+ autoIdBtn.style.position = 'absolute';
69
+ autoIdBtn.style.right = '4px';
70
+ autoIdBtn.style.top = '4px';
71
+
72
+ this.autoGraphId = false;
73
+
74
+ autoIdBtn.onclick = () => {
75
+
76
+ this.autoGraphId = ! this.autoGraphId;
77
+
78
+ if ( this.autoGraphId ) {
79
+
80
+ autoIdBtn.style.backgroundColor = 'rgba(255, 255, 255, 0.1)';
81
+ autoIdBtn.style.color = '#fff';
82
+
83
+ } else {
84
+
85
+ autoIdBtn.style.backgroundColor = '';
86
+ autoIdBtn.style.color = '';
87
+
88
+ }
89
+
90
+ };
91
+
92
+ headerDiv.appendChild( importBtn );
93
+ headerDiv.appendChild( exportBtn );
94
+ headerDiv.appendChild( manageBtn );
95
+ headerDiv.appendChild( autoIdBtn );
96
+
97
+ this.content.appendChild( headerDiv );
98
+
99
+ this.iframe = document.createElement( 'iframe' );
100
+ this.iframe.style.width = '100%';
101
+ this.iframe.style.height = '100%';
102
+ this.iframe.style.border = 'none';
103
+ this.iframe.src = editorUrl.toString();
104
+ this.editorOrigin = new URL( this.iframe.src ).origin;
105
+
106
+ this.content.appendChild( this.iframe );
107
+
108
+ this.material = null;
109
+ this.uniforms = null;
110
+
111
+ this.isReady = false;
112
+
113
+ this._codeData = null;
114
+ this._codeSaveTimeout = null;
115
+
116
+ this._pending = new Map();
117
+
118
+ this._resolveReady = null;
119
+ this._editorReady = new Promise( ( resolve ) => {
120
+
121
+ this._resolveReady = resolve;
122
+
123
+ } );
124
+
125
+ window.addEventListener( 'message', this.onMessage.bind( this ) );
126
+
127
+ }
128
+
129
+ get hasGraphs() {
130
+
131
+ return TSLGraphLoader.hasGraphs;
132
+
133
+ }
134
+
135
+ _initPicker( inspector ) {
136
+
137
+ const renderer = inspector.getRenderer();
138
+
139
+ let boundingBox = null;
140
+
141
+ const raycaster = new Raycaster();
142
+ const pointer = new Vector2();
143
+
144
+ const removeBoundingBox = () => {
145
+
146
+ if ( boundingBox ) {
147
+
148
+ boundingBox.removeFromParent();
149
+ boundingBox.dispose();
150
+ boundingBox = null;
151
+
152
+ }
153
+
154
+ };
155
+
156
+ this.addEventListener( 'change', ( { material } ) => {
157
+
158
+ if ( material === null ) {
159
+
160
+ removeBoundingBox();
161
+
162
+ }
163
+
164
+ } );
165
+
166
+ this.addEventListener( 'remove', ( { graphId } ) => {
167
+
168
+ const frame = inspector.getFrame();
169
+ const scene = frame && frame.renders.length > 0 ? frame.renders[ 0 ].scene : null;
170
+
171
+ if ( scene ) {
172
+
173
+ scene.traverse( ( object ) => {
174
+
175
+ if ( object.material && object.material.userData && object.material.userData.graphId === graphId ) {
176
+
177
+ this.restoreMaterial( object.material );
178
+
179
+ }
180
+
181
+ } );
182
+
183
+ }
184
+
185
+ } );
186
+
187
+ const pointerDownPosition = new Vector2();
188
+
189
+ renderer.domElement.addEventListener( 'pointerdown', ( e ) => {
190
+
191
+ pointerDownPosition.set( e.clientX, e.clientY );
192
+
193
+ } );
194
+
195
+ renderer.domElement.addEventListener( 'pointerup', ( e ) => {
196
+
197
+ const frame = inspector.getFrame();
198
+
199
+ for ( const render of frame.renders ) {
200
+
201
+ const scene = render.scene;
202
+
203
+ if ( scene.isScene !== true ) continue;
204
+
205
+ const camera = render.camera;
206
+
207
+ if ( pointerDownPosition.distanceTo( pointer.set( e.clientX, e.clientY ) ) > 2 ) return;
208
+
209
+ const rect = renderer.domElement.getBoundingClientRect();
210
+ pointer.x = ( ( e.clientX - rect.left ) / rect.width ) * 2 - 1;
211
+ pointer.y = - ( ( e.clientY - rect.top ) / rect.height ) * 2 + 1;
212
+
213
+ raycaster.setFromCamera( pointer, camera );
214
+
215
+ const intersects = raycaster.intersectObjects( scene.children, true );
216
+
217
+ let graphMaterial = null;
218
+
219
+ if ( intersects.length > 0 ) {
220
+
221
+ for ( const intersect of intersects ) {
222
+
223
+ const object = intersect.object;
224
+ const material = object.material;
225
+
226
+ if ( material && material.isNodeMaterial ) {
227
+
228
+ removeBoundingBox();
229
+
230
+ boundingBox = new BoxHelper( object, 0xffff00 );
231
+ scene.add( boundingBox );
232
+
233
+ graphMaterial = material;
234
+
235
+ }
236
+
237
+ if ( object.isMesh || object.isSprite ) {
238
+
239
+ break;
240
+
241
+ }
242
+
243
+ }
244
+
245
+ }
246
+
247
+ this.setMaterial( graphMaterial );
248
+
249
+ }
250
+
251
+ } );
252
+
253
+ }
254
+
255
+ apply( scene ) {
256
+
257
+ const loader = new TSLGraphLoader();
258
+ const applier = loader.parse( TSLGraphLoader.getCodes() );
259
+ applier.apply( scene );
260
+
261
+ return this;
262
+
263
+ }
264
+
265
+ restoreMaterial( material ) {
266
+
267
+ material.copy( new material.constructor() );
268
+ material.needsUpdate = true;
269
+
270
+ }
271
+
272
+ init( inspector ) {
273
+
274
+ this._initPicker( inspector );
275
+
276
+ }
277
+
278
+ async setMaterial( material ) {
279
+
280
+ if ( this.material === material ) return;
281
+
282
+ await this._setMaterial( material );
283
+
284
+ this.dispatchEvent( { type: 'change', material } );
285
+
286
+ }
287
+
288
+ async loadGraph( graphData ) {
289
+
290
+ await this.command( 'load', { graphData } );
291
+
292
+ }
293
+
294
+ async command( type, payload ) {
295
+
296
+ type = 'tsl:command:' + type;
297
+
298
+ await this._editorReady;
299
+
300
+ const requestId = this._makeRequestId();
301
+ const expectedType = _resposeByCommand[ type ];
302
+
303
+ return new Promise( ( resolve, reject ) => {
304
+
305
+ const timer = window.setTimeout( () => {
306
+
307
+ if ( ! this._pending.has( requestId ) ) return;
308
+ this._pending.delete( requestId );
309
+ reject( new Error( `Timeout for ${type}` ) );
310
+
311
+ }, 5000 );
312
+
313
+ this._pending.set( requestId, { expectedType, resolve, reject, timer } );
314
+
315
+ const message = { source: HOST_SOURCE, type, requestId };
316
+ if ( payload !== undefined ) message.payload = payload;
317
+
318
+ this._post( message );
319
+
320
+ } );
321
+
322
+ }
323
+
324
+ async getCode() {
325
+
326
+ return this.command( 'get-code' );
327
+
328
+ }
329
+
330
+ async getTSLFunction() {
331
+
332
+ const graphLoader = new TSLGraphLoader();
333
+ const applier = graphLoader.parse( await this.getCode() );
334
+
335
+ return applier.tslGraphFns[ 'tslGraph' ];
336
+
337
+ }
338
+
339
+ async getGraph() {
340
+
341
+ return ( await this.command( 'get-graph' ) ).graphData;
342
+
343
+ }
344
+
345
+ async onResponse( /*type, payload*/ ) {
346
+
347
+
348
+
349
+ }
350
+
351
+ async onEvent( type, payload ) {
352
+
353
+ if ( type === 'ready' ) {
354
+
355
+ if ( ! this.isReady ) {
356
+
357
+ this.isReady = true;
358
+
359
+ this._resolveReady();
360
+
361
+ }
362
+
363
+ } else if ( type === 'graph-changed' ) {
364
+
365
+ if ( this.material === null ) return;
366
+
367
+ await this._updateMaterial();
368
+
369
+ const graphData = await this.getGraph();
370
+
371
+ const graphId = this.material.userData.graphId;
372
+
373
+ TSLGraphLoader.setGraph( graphId, graphData );
374
+
375
+ } else if ( type === 'uniforms-changed' ) {
376
+
377
+ this._updateUniforms( payload.uniforms );
378
+
379
+ }
380
+
381
+ }
382
+
383
+ async onMessage( event ) {
384
+
385
+ if ( event.origin !== this.editorOrigin ) return;
386
+ if ( ! this._isEditorMessage( event.data ) ) return;
387
+
388
+ const msg = event.data;
389
+
390
+ if ( msg.requestId && msg.type.startsWith( 'tsl:response:' ) ) {
391
+
392
+ const waiter = this._pending.get( msg.requestId );
393
+ if ( ! waiter ) return;
394
+ if ( msg.type !== waiter.expectedType ) return;
395
+
396
+ this._pending.delete( msg.requestId );
397
+ window.clearTimeout( waiter.timer );
398
+
399
+ if ( msg.error ) waiter.reject( new Error( msg.error ) );
400
+ else waiter.resolve( msg.payload );
401
+
402
+ this.onResponse( msg.type.substring( 'tsl:response:'.length ), msg.payload );
403
+
404
+ } else if ( msg.type.startsWith( 'tsl:event:' ) ) {
405
+
406
+ this.onEvent( msg.type.substring( 'tsl:event:'.length ), msg.payload );
407
+
408
+ }
409
+
410
+ }
411
+
412
+ async _setMaterial( material ) {
413
+
414
+ if ( ! material ) {
415
+
416
+ this.material = null;
417
+ this.materialDefault = null;
418
+ this.uniforms = null;
419
+
420
+ await this.command( 'clear-graph' );
421
+
422
+ return;
423
+
424
+ }
425
+
426
+ if ( material.isNodeMaterial !== true ) {
427
+
428
+ error( 'TSLGraphEditor: "Material" needs be a "NodeMaterial".' );
429
+
430
+ return;
431
+
432
+ }
433
+
434
+ if ( material.userData.graphId === undefined ) {
435
+
436
+ if ( this.autoGraphId ) {
437
+
438
+ material.userData.graphId = material.name || 'id:' + material.id;
439
+
440
+ } else {
441
+
442
+ warn( 'TSLGraphEditor: "NodeMaterial" has no graphId. Set a "graphId" for the material in "material.userData.graphId".' );
443
+
444
+ return;
445
+
446
+ }
447
+
448
+ }
449
+
450
+ let materialDefault = _refMaterials.get( material );
451
+
452
+ if ( materialDefault === undefined ) {
453
+
454
+ //materialDefault = material.clone();
455
+ materialDefault = new material.constructor();
456
+ materialDefault.userData = material.userData;
457
+
458
+ _refMaterials.set( material, materialDefault );
459
+
460
+ }
461
+
462
+ this.material = material;
463
+ this.materialDefault = materialDefault;
464
+ this.uniforms = null;
465
+
466
+ const graphData = TSLGraphLoader.getGraph( this.material.userData.graphId );
467
+
468
+ if ( graphData ) {
469
+
470
+ await this.loadGraph( graphData );
471
+
472
+ } else {
473
+
474
+ await this.command( 'clear-graph' );
475
+
476
+ await this.command( 'set-root-material', { materialType: this._getGraphType( this.material ) } );
477
+
478
+ }
479
+
480
+ }
481
+
482
+ _getGraphType( material ) {
483
+
484
+ if ( material.isMeshPhysicalNodeMaterial ) return 'material/physical';
485
+ if ( material.isMeshStandardNodeMaterial ) return 'material/standard';
486
+ if ( material.isMeshPhongNodeMaterial ) return 'material/phong';
487
+ if ( material.isMeshBasicNodeMaterial ) return 'material/basic';
488
+ if ( material.isSpriteNodeMaterial ) return 'material/sprite';
489
+
490
+ return 'material/node';
491
+
492
+ }
493
+
494
+ _showManagerModal() {
495
+
496
+ const overlay = document.createElement( 'div' );
497
+ overlay.style.position = 'absolute';
498
+ overlay.style.top = '0';
499
+ overlay.style.left = '0';
500
+ overlay.style.width = '100%';
501
+ overlay.style.height = '100%';
502
+ overlay.style.backgroundColor = 'rgba(0, 0, 0, 0.7)';
503
+ overlay.style.zIndex = '100';
504
+ overlay.style.display = 'flex';
505
+ overlay.style.justifyContent = 'center';
506
+ overlay.style.alignItems = 'center';
507
+ overlay.onclick = ( e ) => {
508
+
509
+ if ( e.target === overlay ) {
510
+
511
+ this.content.removeChild( overlay );
512
+
513
+ }
514
+
515
+ };
516
+
517
+ const modal = document.createElement( 'div' );
518
+ modal.style.width = '80%';
519
+ modal.style.maxWidth = '500px';
520
+ modal.style.height = '400px';
521
+ modal.style.backgroundColor = 'var(--profiler-bg, #1e1e24f5)';
522
+ modal.style.border = '1px solid var(--profiler-border, #4a4a5a)';
523
+ modal.style.borderRadius = '8px';
524
+ modal.style.display = 'flex';
525
+ modal.style.flexDirection = 'column';
526
+
527
+ const header = document.createElement( 'div' );
528
+ header.style.padding = '15px';
529
+ header.style.borderBottom = '1px solid var(--profiler-border, #4a4a5a)';
530
+ header.style.display = 'flex';
531
+ header.style.justifyContent = 'space-between';
532
+ header.style.alignItems = 'center';
533
+ header.style.gap = '15px';
534
+
535
+ const filterInput = document.createElement( 'input' );
536
+ filterInput.type = 'text';
537
+ filterInput.className = 'console-filter-input';
538
+ filterInput.placeholder = 'Filter...';
539
+ filterInput.style.flex = '1';
540
+
541
+ const closeBtn = document.createElement( 'button' );
542
+ closeBtn.innerHTML = '&#x2715;';
543
+ closeBtn.style.background = 'transparent';
544
+ closeBtn.style.border = 'none';
545
+ closeBtn.style.color = 'var(--text-secondary, #9a9aab)';
546
+ closeBtn.style.cursor = 'pointer';
547
+ closeBtn.style.fontSize = '16px';
548
+ closeBtn.onmouseover = () => closeBtn.style.color = 'var(--text-primary, #e0e0e0)';
549
+ closeBtn.onmouseout = () => closeBtn.style.color = 'var(--text-secondary, #9a9aab)';
550
+ closeBtn.onclick = () => this.content.removeChild( overlay );
551
+
552
+ header.appendChild( filterInput );
553
+ header.appendChild( closeBtn );
554
+
555
+ const codes = this.getCodes();
556
+ const materialIds = Object.keys( codes.materials || {} );
557
+
558
+ if ( materialIds.length === 0 ) {
559
+
560
+ const listContainer = document.createElement( 'div' );
561
+ listContainer.style.padding = '10px';
562
+ listContainer.style.flex = '1';
563
+
564
+ const emptyMsg = document.createElement( 'div' );
565
+ emptyMsg.textContent = 'No saved materials found.';
566
+ emptyMsg.style.color = 'var(--text-secondary, #9a9aab)';
567
+ emptyMsg.style.padding = '10px';
568
+ emptyMsg.style.textAlign = 'center';
569
+ emptyMsg.style.fontFamily = 'var(--font-family, sans-serif)';
570
+ emptyMsg.style.fontSize = '12px';
571
+ listContainer.appendChild( emptyMsg );
572
+
573
+ modal.appendChild( header );
574
+ modal.appendChild( listContainer );
575
+
576
+ } else {
577
+
578
+ const listHeaderContainer = document.createElement( 'div' );
579
+ listHeaderContainer.style.display = 'grid';
580
+ listHeaderContainer.style.gridTemplateColumns = '1fr 80px';
581
+ listHeaderContainer.style.gap = '10px';
582
+ listHeaderContainer.style.padding = '10px 15px 8px 15px';
583
+ listHeaderContainer.style.borderBottom = '1px solid var(--profiler-border, #4a4a5a)';
584
+ listHeaderContainer.style.backgroundColor = 'var(--profiler-bg, #1e1e24f5)';
585
+ listHeaderContainer.style.fontFamily = 'var(--font-family, sans-serif)';
586
+ listHeaderContainer.style.fontSize = '11px';
587
+ listHeaderContainer.style.fontWeight = 'bold';
588
+ listHeaderContainer.style.textTransform = 'uppercase';
589
+ listHeaderContainer.style.letterSpacing = '0.5px';
590
+ listHeaderContainer.style.color = 'var(--text-secondary, #9a9aab)';
591
+ listHeaderContainer.style.boxShadow = '0 2px 4px rgba(0,0,0,0.1)';
592
+ listHeaderContainer.style.zIndex = '1';
593
+
594
+ const col1 = document.createElement( 'div' );
595
+ col1.textContent = 'Material Name / ID';
596
+ const col2 = document.createElement( 'div' );
597
+ col2.textContent = 'Action';
598
+ col2.style.textAlign = 'right';
599
+
600
+ listHeaderContainer.appendChild( col1 );
601
+ listHeaderContainer.appendChild( col2 );
602
+
603
+ const scrollWrapper = document.createElement( 'div' );
604
+ scrollWrapper.style.flex = '1';
605
+ scrollWrapper.style.overflowY = 'auto';
606
+ scrollWrapper.style.padding = '0';
607
+
608
+ const rows = [];
609
+
610
+ for ( const id of materialIds ) {
611
+
612
+ const itemRow = document.createElement( 'div' );
613
+ itemRow.style.display = 'grid';
614
+ itemRow.style.gridTemplateColumns = '1fr 80px';
615
+ itemRow.style.gap = '10px';
616
+ itemRow.style.alignItems = 'center';
617
+ itemRow.style.padding = '8px 15px';
618
+ itemRow.style.borderBottom = '1px solid rgba(74, 74, 90, 0.4)';
619
+ itemRow.onmouseover = () => itemRow.style.backgroundColor = 'rgba(255, 255, 255, 0.04)';
620
+ itemRow.onmouseout = () => itemRow.style.backgroundColor = 'transparent';
621
+
622
+ const nameSpan = document.createElement( 'span' );
623
+ const materialData = codes.materials[ id ];
624
+ const materialName = materialData.name || id;
625
+ nameSpan.textContent = materialName;
626
+ nameSpan.style.fontFamily = 'var(--font-mono, monospace)';
627
+ nameSpan.style.fontSize = '12px';
628
+ nameSpan.style.color = 'var(--text-primary, #e0e0e0)';
629
+ nameSpan.style.userSelect = 'all';
630
+ nameSpan.style.overflow = 'hidden';
631
+ nameSpan.style.textOverflow = 'ellipsis';
632
+ nameSpan.style.whiteSpace = 'nowrap';
633
+
634
+ const actionContainer = document.createElement( 'div' );
635
+ actionContainer.style.textAlign = 'right';
636
+
637
+ const removeBtn = document.createElement( 'button' );
638
+ removeBtn.textContent = 'Remove';
639
+ removeBtn.style.background = 'rgba(244, 67, 54, 0.1)';
640
+ removeBtn.style.border = '1px solid var(--color-red, #f44336)';
641
+ removeBtn.style.color = 'var(--color-red, #f44336)';
642
+ removeBtn.style.borderRadius = '4px';
643
+ removeBtn.style.padding = '4px 8px';
644
+ removeBtn.style.cursor = 'pointer';
645
+ removeBtn.style.fontSize = '11px';
646
+ removeBtn.onmouseover = () => removeBtn.style.background = 'rgba(244, 67, 54, 0.2)';
647
+ removeBtn.onmouseout = () => removeBtn.style.background = 'rgba(244, 67, 54, 0.1)';
648
+
649
+ actionContainer.appendChild( removeBtn );
650
+
651
+ itemRow.appendChild( nameSpan );
652
+ itemRow.appendChild( actionContainer );
653
+
654
+ scrollWrapper.appendChild( itemRow );
655
+
656
+ rows.push( { element: itemRow, text: materialName.toLowerCase() } );
657
+
658
+ removeBtn.onclick = async () => {
659
+
660
+ delete codes.materials[ id ];
661
+ TSLGraphLoader.setCodes( codes );
662
+ TSLGraphLoader.deleteGraph( id );
663
+ scrollWrapper.removeChild( itemRow );
664
+
665
+ const index = rows.findIndex( r => r.element === itemRow );
666
+ if ( index > - 1 ) rows.splice( index, 1 );
667
+
668
+ if ( rows.length === 0 ) {
669
+
670
+ modal.removeChild( listHeaderContainer );
671
+ modal.removeChild( scrollWrapper );
672
+
673
+ const listContainer = document.createElement( 'div' );
674
+ listContainer.style.padding = '10px';
675
+ listContainer.style.flex = '1';
676
+
677
+ const emptyMsg = document.createElement( 'div' );
678
+ emptyMsg.textContent = 'No saved materials found.';
679
+ emptyMsg.style.color = 'var(--text-secondary, #9a9aab)';
680
+ emptyMsg.style.padding = '10px';
681
+ emptyMsg.style.textAlign = 'center';
682
+ emptyMsg.style.fontFamily = 'var(--font-family, sans-serif)';
683
+ emptyMsg.style.fontSize = '12px';
684
+
685
+ listContainer.appendChild( emptyMsg );
686
+ modal.appendChild( listContainer );
687
+
688
+ }
689
+
690
+ _refMaterials.delete( this.material );
691
+
692
+ if ( this.material && this.material.userData.graphId === id ) {
693
+
694
+ this.restoreMaterial( this.material );
695
+
696
+ await this.setMaterial( null );
697
+
698
+ }
699
+
700
+ this.dispatchEvent( { type: 'remove', graphId: id } );
701
+
702
+ };
703
+
704
+ }
705
+
706
+ filterInput.addEventListener( 'input', ( e ) => {
707
+
708
+ const term = e.target.value.toLowerCase();
709
+ for ( const row of rows ) {
710
+
711
+ row.element.style.display = row.text.includes( term ) ? 'grid' : 'none';
712
+
713
+ }
714
+
715
+ } );
716
+
717
+ modal.appendChild( header );
718
+ modal.appendChild( listHeaderContainer );
719
+ modal.appendChild( scrollWrapper );
720
+
721
+ }
722
+
723
+ overlay.appendChild( modal );
724
+
725
+ this.content.appendChild( overlay );
726
+
727
+ }
728
+
729
+ _exportData() {
730
+
731
+ const codes = this.getCodes();
732
+ const materialIds = Object.keys( codes.materials || {} );
733
+
734
+ const exportPayload = {
735
+ codes: codes,
736
+ graphs: {}
737
+ };
738
+
739
+ for ( const id of materialIds ) {
740
+
741
+ const graphData = TSLGraphLoader.getGraph( id );
742
+
743
+ if ( graphData ) {
744
+
745
+ exportPayload.graphs[ id ] = graphData;
746
+
747
+ }
748
+
749
+ }
750
+
751
+ const dataStr = 'data:text/json;charset=utf-8,' + encodeURIComponent( JSON.stringify( exportPayload, null, '\t' ) );
752
+ const downloadAnchorNode = document.createElement( 'a' );
753
+ downloadAnchorNode.setAttribute( 'href', dataStr );
754
+ downloadAnchorNode.setAttribute( 'download', 'tsl-graphs.json' );
755
+ document.body.appendChild( downloadAnchorNode );
756
+ downloadAnchorNode.click();
757
+ downloadAnchorNode.remove();
758
+
759
+ }
760
+
761
+ _importData() {
762
+
763
+ const fileInput = document.createElement( 'input' );
764
+ fileInput.type = 'file';
765
+ fileInput.accept = '.json';
766
+
767
+ fileInput.onchange = e => {
768
+
769
+ const file = e.target.files[ 0 ];
770
+
771
+ if ( ! file ) return;
772
+
773
+ const reader = new FileReader();
774
+ reader.onload = async ( event ) => {
775
+
776
+ try {
777
+
778
+ const importedData = TSLGraphLoader.setGraphs( JSON.parse( event.target.result ) );
779
+
780
+ this._codeData = importedData.codes;
781
+
782
+ // Reload visual state if we have a material open
783
+ if ( this.material ) {
784
+
785
+ // refresh material
786
+ await this._setMaterial( this.material );
787
+
788
+ }
789
+
790
+ } catch ( err ) {
791
+
792
+ error( 'TSLGraphEditor: Failed to parse or load imported JSON.', err );
793
+
794
+ }
795
+
796
+ };
797
+
798
+ reader.readAsText( file );
799
+
800
+ };
801
+
802
+ fileInput.click();
803
+
804
+ }
805
+
806
+ getCodes() {
807
+
808
+ if ( this._codeData === null ) {
809
+
810
+ this._codeData = TSLGraphLoader.getCodes();
811
+
812
+ }
813
+
814
+ return this._codeData;
815
+
816
+ }
817
+
818
+ _saveCode() {
819
+
820
+ const graphId = this.material.userData.graphId;
821
+
822
+ clearTimeout( this._codeSaveTimeout );
823
+
824
+ this._codeSaveTimeout = setTimeout( async () => {
825
+
826
+ if ( this.material === null || graphId !== this.material.userData.graphId ) return;
827
+
828
+ const codes = this.getCodes();
829
+ const codeData = await this.getCode();
830
+
831
+ codes.materials[ graphId ] = codeData.material;
832
+
833
+ TSLGraphLoader.setCodes( codes );
834
+
835
+ }, 1000 );
836
+
837
+ }
838
+
839
+ _restoreMaterial() {
840
+
841
+ this.material.copy( this.materialDefault );
842
+
843
+ }
844
+
845
+ async _updateMaterial() {
846
+
847
+ this._restoreMaterial();
848
+
849
+ const applyNodes = await this.getTSLFunction();
850
+
851
+ const { uniforms } = applyNodes( this.material );
852
+
853
+ this.uniforms = uniforms;
854
+ this.material.needsUpdate = true;
855
+
856
+ this._saveCode();
857
+
858
+ }
859
+
860
+ _updateUniforms( uniforms ) {
861
+
862
+ if ( this.uniforms === null ) return;
863
+
864
+ for ( const uniform of uniforms ) {
865
+
866
+ const uniformNode = this.uniforms[ uniform.name ];
867
+ const uniformType = uniform.uniformType;
868
+
869
+ const value = uniform.value;
870
+
871
+ if ( uniformType.startsWith( 'vec' ) ) {
872
+
873
+ uniformNode.value.fromArray( value );
874
+
875
+ } else if ( uniformType.startsWith( 'color' ) ) {
876
+
877
+ uniformNode.value.setHex( parseInt( value.slice( 1 ), 16 ) );
878
+
879
+ } else {
880
+
881
+ uniformNode.value = value;
882
+
883
+ }
884
+
885
+ }
886
+
887
+ this._saveCode();
888
+
889
+ }
890
+
891
+ _isEditorMessage( value ) {
892
+
893
+ if ( ! value || typeof value !== 'object' ) return false;
894
+ return value.source === EDITOR_SOURCE && typeof value.type === 'string';
895
+
896
+ }
897
+
898
+ _makeRequestId() {
899
+
900
+ return `${Date.now()}-${Math.random().toString( 36 ).slice( 2, 10 )}`;
901
+
902
+ }
903
+
904
+ _post( message ) {
905
+
906
+ if ( this.iframe.contentWindow ) {
907
+
908
+ this.iframe.contentWindow.postMessage( message, this.editorOrigin );
909
+
910
+ }
911
+
912
+ }
913
+
914
+ }
915
+
916
+ export default TSLGraphEditor;