@basementstudio/shader-lab 0.1.0

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 (261) hide show
  1. package/.biome/plugins/README.md +21 -0
  2. package/.biome/plugins/no-anchor-element.grit +12 -0
  3. package/.biome/plugins/no-relative-parent-imports.grit +10 -0
  4. package/.biome/plugins/no-unnecessary-forwardref.grit +9 -0
  5. package/.changeset/README.md +17 -0
  6. package/.changeset/config.json +11 -0
  7. package/.editorconfig +40 -0
  8. package/.env.example +81 -0
  9. package/.gitattributes +19 -0
  10. package/.github/workflows/canary.yml +80 -0
  11. package/.github/workflows/ci.yml +37 -0
  12. package/.github/workflows/release.yml +56 -0
  13. package/.tldrignore +84 -0
  14. package/.vscode/extensions.json +20 -0
  15. package/.vscode/settings.json +105 -0
  16. package/README.md +119 -0
  17. package/biome.json +249 -0
  18. package/bun.lock +1224 -0
  19. package/next.config.ts +131 -0
  20. package/package.json +73 -0
  21. package/packages/shader-lab-react/CHANGELOG.md +9 -0
  22. package/packages/shader-lab-react/README.md +119 -0
  23. package/packages/shader-lab-react/assets/patterns/bars/1.svg +3 -0
  24. package/packages/shader-lab-react/assets/patterns/bars/2.svg +3 -0
  25. package/packages/shader-lab-react/assets/patterns/bars/3.svg +3 -0
  26. package/packages/shader-lab-react/assets/patterns/bars/4.svg +3 -0
  27. package/packages/shader-lab-react/assets/patterns/bars/5.svg +3 -0
  28. package/packages/shader-lab-react/assets/patterns/bars/6.svg +3 -0
  29. package/packages/shader-lab-react/assets/patterns/candles/1.svg +3 -0
  30. package/packages/shader-lab-react/assets/patterns/candles/2.svg +3 -0
  31. package/packages/shader-lab-react/assets/patterns/candles/3.svg +3 -0
  32. package/packages/shader-lab-react/assets/patterns/candles/4.svg +3 -0
  33. package/packages/shader-lab-react/assets/patterns/shapes/1.svg +3 -0
  34. package/packages/shader-lab-react/assets/patterns/shapes/2.svg +3 -0
  35. package/packages/shader-lab-react/assets/patterns/shapes/3.svg +3 -0
  36. package/packages/shader-lab-react/assets/patterns/shapes/4.svg +4 -0
  37. package/packages/shader-lab-react/assets/patterns/shapes/5.svg +3 -0
  38. package/packages/shader-lab-react/assets/patterns/shapes/6.svg +4 -0
  39. package/packages/shader-lab-react/assets/textures/blue-noise.png +0 -0
  40. package/packages/shader-lab-react/package.json +36 -0
  41. package/packages/shader-lab-react/scripts/fix-esm-specifiers.mjs +57 -0
  42. package/packages/shader-lab-react/scripts/prepare-dist.mjs +4 -0
  43. package/packages/shader-lab-react/src/ambient/three-tsl.d.ts +146 -0
  44. package/packages/shader-lab-react/src/ambient/three-webgpu.d.ts +51 -0
  45. package/packages/shader-lab-react/src/easings.ts +4 -0
  46. package/packages/shader-lab-react/src/index.ts +35 -0
  47. package/packages/shader-lab-react/src/lib/editor/custom-shader/shared.ts +2 -0
  48. package/packages/shader-lab-react/src/renderer/ascii-atlas.ts +83 -0
  49. package/packages/shader-lab-react/src/renderer/ascii-pass.ts +416 -0
  50. package/packages/shader-lab-react/src/renderer/asset-url.ts +3 -0
  51. package/packages/shader-lab-react/src/renderer/blend-modes.ts +229 -0
  52. package/packages/shader-lab-react/src/renderer/contracts.ts +54 -0
  53. package/packages/shader-lab-react/src/renderer/create-webgpu-renderer.ts +48 -0
  54. package/packages/shader-lab-react/src/renderer/crt-pass.ts +1040 -0
  55. package/packages/shader-lab-react/src/renderer/custom-shader-pass.ts +108 -0
  56. package/packages/shader-lab-react/src/renderer/custom-shader-runtime.ts +309 -0
  57. package/packages/shader-lab-react/src/renderer/dither-textures.ts +99 -0
  58. package/packages/shader-lab-react/src/renderer/dithering-pass.ts +322 -0
  59. package/packages/shader-lab-react/src/renderer/gradient-pass.ts +521 -0
  60. package/packages/shader-lab-react/src/renderer/halftone-pass.ts +932 -0
  61. package/packages/shader-lab-react/src/renderer/ink-pass.ts +802 -0
  62. package/packages/shader-lab-react/src/renderer/live-pass.ts +194 -0
  63. package/packages/shader-lab-react/src/renderer/media-pass.ts +187 -0
  64. package/packages/shader-lab-react/src/renderer/media-texture.ts +66 -0
  65. package/packages/shader-lab-react/src/renderer/particle-grid-pass.ts +389 -0
  66. package/packages/shader-lab-react/src/renderer/pass-node.ts +209 -0
  67. package/packages/shader-lab-react/src/renderer/pattern-atlas.ts +133 -0
  68. package/packages/shader-lab-react/src/renderer/pattern-pass.ts +552 -0
  69. package/packages/shader-lab-react/src/renderer/pipeline-manager.ts +369 -0
  70. package/packages/shader-lab-react/src/renderer/pixel-sorting-pass.ts +277 -0
  71. package/packages/shader-lab-react/src/renderer/shaders/tsl/color/tonemapping.ts +87 -0
  72. package/packages/shader-lab-react/src/renderer/shaders/tsl/cosine-palette.ts +9 -0
  73. package/packages/shader-lab-react/src/renderer/shaders/tsl/noise/common.ts +31 -0
  74. package/packages/shader-lab-react/src/renderer/shaders/tsl/noise/curl-noise-3d.ts +36 -0
  75. package/packages/shader-lab-react/src/renderer/shaders/tsl/noise/curl-noise-4d.ts +36 -0
  76. package/packages/shader-lab-react/src/renderer/shaders/tsl/noise/fbm.ts +13 -0
  77. package/packages/shader-lab-react/src/renderer/shaders/tsl/noise/perlin-noise-3d.ts +96 -0
  78. package/packages/shader-lab-react/src/renderer/shaders/tsl/noise/ridge-noise.ts +24 -0
  79. package/packages/shader-lab-react/src/renderer/shaders/tsl/noise/simplex-noise-3d.ts +79 -0
  80. package/packages/shader-lab-react/src/renderer/shaders/tsl/noise/simplex-noise-4d.ts +89 -0
  81. package/packages/shader-lab-react/src/renderer/shaders/tsl/noise/turbulence.ts +56 -0
  82. package/packages/shader-lab-react/src/renderer/shaders/tsl/noise/value-noise-3d.ts +32 -0
  83. package/packages/shader-lab-react/src/renderer/shaders/tsl/noise/voronoi-noise-3d.ts +60 -0
  84. package/packages/shader-lab-react/src/renderer/shaders/tsl/patterns/bloom-edge-pattern.ts +15 -0
  85. package/packages/shader-lab-react/src/renderer/shaders/tsl/patterns/bloom.ts +11 -0
  86. package/packages/shader-lab-react/src/renderer/shaders/tsl/patterns/canvas-weave-pattern.ts +24 -0
  87. package/packages/shader-lab-react/src/renderer/shaders/tsl/patterns/grain-texture-pattern.ts +9 -0
  88. package/packages/shader-lab-react/src/renderer/shaders/tsl/patterns/repeating-pattern.ts +11 -0
  89. package/packages/shader-lab-react/src/renderer/shaders/tsl/utils/atan2.ts +9 -0
  90. package/packages/shader-lab-react/src/renderer/shaders/tsl/utils/complex-conj.ts +9 -0
  91. package/packages/shader-lab-react/src/renderer/shaders/tsl/utils/complex-cos.ts +10 -0
  92. package/packages/shader-lab-react/src/renderer/shaders/tsl/utils/complex-div.ts +11 -0
  93. package/packages/shader-lab-react/src/renderer/shaders/tsl/utils/complex-log.ts +7 -0
  94. package/packages/shader-lab-react/src/renderer/shaders/tsl/utils/complex-mobius.ts +12 -0
  95. package/packages/shader-lab-react/src/renderer/shaders/tsl/utils/complex-mul.ts +9 -0
  96. package/packages/shader-lab-react/src/renderer/shaders/tsl/utils/complex-pow.ts +16 -0
  97. package/packages/shader-lab-react/src/renderer/shaders/tsl/utils/complex-sin.ts +10 -0
  98. package/packages/shader-lab-react/src/renderer/shaders/tsl/utils/complex-sqrt.ts +18 -0
  99. package/packages/shader-lab-react/src/renderer/shaders/tsl/utils/complex-tan.ts +12 -0
  100. package/packages/shader-lab-react/src/renderer/shaders/tsl/utils/complex-to-polar.ts +10 -0
  101. package/packages/shader-lab-react/src/renderer/shaders/tsl/utils/hyperbolic.ts +20 -0
  102. package/packages/shader-lab-react/src/renderer/shaders/tsl/utils/index.ts +48 -0
  103. package/packages/shader-lab-react/src/renderer/shaders/tsl/utils/rotate.ts +15 -0
  104. package/packages/shader-lab-react/src/renderer/shaders/tsl/utils/screen-aspect-uv.ts +15 -0
  105. package/packages/shader-lab-react/src/renderer/shaders/tsl/utils/sd-box-2d.ts +6 -0
  106. package/packages/shader-lab-react/src/renderer/shaders/tsl/utils/sd-diamond.ts +6 -0
  107. package/packages/shader-lab-react/src/renderer/shaders/tsl/utils/sd-rhombus.ts +27 -0
  108. package/packages/shader-lab-react/src/renderer/shaders/tsl/utils/sd-sphere.ts +6 -0
  109. package/packages/shader-lab-react/src/renderer/shaders/tsl/utils/smax.ts +7 -0
  110. package/packages/shader-lab-react/src/renderer/shaders/tsl/utils/smin.ts +7 -0
  111. package/packages/shader-lab-react/src/renderer/text-pass.ts +176 -0
  112. package/packages/shader-lab-react/src/runtime-clock.ts +42 -0
  113. package/packages/shader-lab-react/src/runtime-frame.ts +29 -0
  114. package/packages/shader-lab-react/src/shader-lab-composition.tsx +163 -0
  115. package/packages/shader-lab-react/src/timeline.ts +283 -0
  116. package/packages/shader-lab-react/src/types/editor.ts +5 -0
  117. package/packages/shader-lab-react/src/types.ts +141 -0
  118. package/packages/shader-lab-react/tsconfig.build.json +8 -0
  119. package/packages/shader-lab-react/tsconfig.json +21 -0
  120. package/postcss.config.mjs +5 -0
  121. package/public/assets/fonts/msdf/geist-mono/GeistMono-Regular-msdf-atlas.png +0 -0
  122. package/public/assets/fonts/msdf/geist-mono/GeistMono-Regular-msdf.json +1412 -0
  123. package/public/assets/patterns/bars/1.svg +3 -0
  124. package/public/assets/patterns/bars/2.svg +3 -0
  125. package/public/assets/patterns/bars/3.svg +3 -0
  126. package/public/assets/patterns/bars/4.svg +3 -0
  127. package/public/assets/patterns/bars/5.svg +3 -0
  128. package/public/assets/patterns/bars/6.svg +3 -0
  129. package/public/assets/patterns/candles/1.svg +3 -0
  130. package/public/assets/patterns/candles/2.svg +3 -0
  131. package/public/assets/patterns/candles/3.svg +3 -0
  132. package/public/assets/patterns/candles/4.svg +3 -0
  133. package/public/assets/patterns/shapes/1.svg +3 -0
  134. package/public/assets/patterns/shapes/2.svg +3 -0
  135. package/public/assets/patterns/shapes/3.svg +3 -0
  136. package/public/assets/patterns/shapes/4.svg +4 -0
  137. package/public/assets/patterns/shapes/5.svg +3 -0
  138. package/public/assets/patterns/shapes/6.svg +4 -0
  139. package/public/fonts/geist/Geist-Mono.woff2 +0 -0
  140. package/public/textures/blue-noise.png +0 -0
  141. package/public/textures/crt-mask.png +0 -0
  142. package/src/app/design/page.tsx +398 -0
  143. package/src/app/favicon.ico +0 -0
  144. package/src/app/globals.css +280 -0
  145. package/src/app/layout.tsx +89 -0
  146. package/src/app/page.tsx +20 -0
  147. package/src/app/robots.ts +13 -0
  148. package/src/app/sitemap.ts +13 -0
  149. package/src/components/editor/editor-canvas-viewport.tsx +116 -0
  150. package/src/components/editor/editor-export-dialog.tsx +1177 -0
  151. package/src/components/editor/editor-timeline-overlay.tsx +983 -0
  152. package/src/components/editor/editor-topbar.tsx +287 -0
  153. package/src/components/editor/layer-sidebar.tsx +738 -0
  154. package/src/components/editor/properties-sidebar-content.tsx +574 -0
  155. package/src/components/editor/properties-sidebar-fields.tsx +389 -0
  156. package/src/components/editor/properties-sidebar-utils.ts +178 -0
  157. package/src/components/editor/properties-sidebar.tsx +421 -0
  158. package/src/components/ui/button/index.tsx +57 -0
  159. package/src/components/ui/color-picker/index.tsx +358 -0
  160. package/src/components/ui/glass-panel/index.tsx +45 -0
  161. package/src/components/ui/icon-button/index.tsx +46 -0
  162. package/src/components/ui/select/index.tsx +136 -0
  163. package/src/components/ui/slider/index.tsx +192 -0
  164. package/src/components/ui/toggle/index.tsx +34 -0
  165. package/src/components/ui/typography/index.tsx +61 -0
  166. package/src/components/ui/xy-pad/index.tsx +160 -0
  167. package/src/features/editor/components/editor-export-dialog.module.css +271 -0
  168. package/src/hooks/use-editor-renderer.ts +182 -0
  169. package/src/lib/app.ts +6 -0
  170. package/src/lib/cn.ts +7 -0
  171. package/src/lib/easings.ts +240 -0
  172. package/src/lib/editor/config/layer-registry.ts +2434 -0
  173. package/src/lib/editor/custom-shader/shared.ts +28 -0
  174. package/src/lib/editor/export.ts +420 -0
  175. package/src/lib/editor/history.ts +71 -0
  176. package/src/lib/editor/layers.ts +76 -0
  177. package/src/lib/editor/parameter-schema.ts +75 -0
  178. package/src/lib/editor/project-file.ts +145 -0
  179. package/src/lib/editor/shader-export-snippet.ts +37 -0
  180. package/src/lib/editor/shader-export.ts +315 -0
  181. package/src/lib/editor/timeline/evaluate.ts +252 -0
  182. package/src/lib/editor/view-transform.ts +58 -0
  183. package/src/lib/fonts.ts +28 -0
  184. package/src/renderer/ascii-atlas.ts +83 -0
  185. package/src/renderer/ascii-pass.ts +416 -0
  186. package/src/renderer/blend-modes.ts +229 -0
  187. package/src/renderer/contracts.ts +161 -0
  188. package/src/renderer/create-webgpu-renderer.ts +48 -0
  189. package/src/renderer/crt-pass.ts +1040 -0
  190. package/src/renderer/custom-shader-pass.ts +117 -0
  191. package/src/renderer/custom-shader-runtime.ts +309 -0
  192. package/src/renderer/dither-textures.ts +99 -0
  193. package/src/renderer/dithering-pass.ts +322 -0
  194. package/src/renderer/gradient-pass.ts +520 -0
  195. package/src/renderer/halftone-pass.ts +932 -0
  196. package/src/renderer/ink-pass.ts +683 -0
  197. package/src/renderer/live-pass.ts +194 -0
  198. package/src/renderer/media-pass.ts +187 -0
  199. package/src/renderer/media-texture.ts +66 -0
  200. package/src/renderer/particle-grid-pass.ts +389 -0
  201. package/src/renderer/pass-node-factory.ts +33 -0
  202. package/src/renderer/pass-node.ts +209 -0
  203. package/src/renderer/pattern-atlas.ts +97 -0
  204. package/src/renderer/pattern-pass.ts +552 -0
  205. package/src/renderer/pipeline-manager.ts +343 -0
  206. package/src/renderer/pixel-sorting-pass.ts +277 -0
  207. package/src/renderer/project-clock.ts +57 -0
  208. package/src/renderer/shaders/tsl/color/tonemapping.ts +86 -0
  209. package/src/renderer/shaders/tsl/cosine-palette.ts +8 -0
  210. package/src/renderer/shaders/tsl/noise/common.ts +30 -0
  211. package/src/renderer/shaders/tsl/noise/curl-noise-3d.ts +35 -0
  212. package/src/renderer/shaders/tsl/noise/curl-noise-4d.ts +35 -0
  213. package/src/renderer/shaders/tsl/noise/fbm.ts +12 -0
  214. package/src/renderer/shaders/tsl/noise/perlin-noise-3d.ts +97 -0
  215. package/src/renderer/shaders/tsl/noise/ridge-noise.ts +23 -0
  216. package/src/renderer/shaders/tsl/noise/simplex-noise-3d.ts +78 -0
  217. package/src/renderer/shaders/tsl/noise/simplex-noise-4d.ts +88 -0
  218. package/src/renderer/shaders/tsl/noise/turbulence.ts +55 -0
  219. package/src/renderer/shaders/tsl/noise/value-noise-3d.ts +31 -0
  220. package/src/renderer/shaders/tsl/noise/voronoi-noise-3d.ts +59 -0
  221. package/src/renderer/shaders/tsl/patterns/bloom-edge-pattern.ts +14 -0
  222. package/src/renderer/shaders/tsl/patterns/bloom.ts +10 -0
  223. package/src/renderer/shaders/tsl/patterns/canvas-weave-pattern.ts +23 -0
  224. package/src/renderer/shaders/tsl/patterns/grain-texture-pattern.ts +8 -0
  225. package/src/renderer/shaders/tsl/patterns/repeating-pattern.ts +10 -0
  226. package/src/renderer/shaders/tsl/utils/atan2.ts +8 -0
  227. package/src/renderer/shaders/tsl/utils/complex-conj.ts +8 -0
  228. package/src/renderer/shaders/tsl/utils/complex-cos.ts +9 -0
  229. package/src/renderer/shaders/tsl/utils/complex-div.ts +10 -0
  230. package/src/renderer/shaders/tsl/utils/complex-log.ts +6 -0
  231. package/src/renderer/shaders/tsl/utils/complex-mobius.ts +11 -0
  232. package/src/renderer/shaders/tsl/utils/complex-mul.ts +8 -0
  233. package/src/renderer/shaders/tsl/utils/complex-pow.ts +15 -0
  234. package/src/renderer/shaders/tsl/utils/complex-sin.ts +9 -0
  235. package/src/renderer/shaders/tsl/utils/complex-sqrt.ts +17 -0
  236. package/src/renderer/shaders/tsl/utils/complex-tan.ts +11 -0
  237. package/src/renderer/shaders/tsl/utils/complex-to-polar.ts +9 -0
  238. package/src/renderer/shaders/tsl/utils/hyperbolic.ts +19 -0
  239. package/src/renderer/shaders/tsl/utils/index.ts +47 -0
  240. package/src/renderer/shaders/tsl/utils/rotate.ts +14 -0
  241. package/src/renderer/shaders/tsl/utils/screen-aspect-uv.ts +14 -0
  242. package/src/renderer/shaders/tsl/utils/sd-box-2d.ts +5 -0
  243. package/src/renderer/shaders/tsl/utils/sd-diamond.ts +5 -0
  244. package/src/renderer/shaders/tsl/utils/sd-rhombus.ts +26 -0
  245. package/src/renderer/shaders/tsl/utils/sd-sphere.ts +5 -0
  246. package/src/renderer/shaders/tsl/utils/smax.ts +7 -0
  247. package/src/renderer/shaders/tsl/utils/smin.ts +6 -0
  248. package/src/renderer/text-pass.ts +176 -0
  249. package/src/store/asset-store.ts +193 -0
  250. package/src/store/editor-store.ts +223 -0
  251. package/src/store/history-store.ts +172 -0
  252. package/src/store/index.ts +31 -0
  253. package/src/store/layer-store.ts +675 -0
  254. package/src/store/timeline-store.ts +572 -0
  255. package/src/types/assets.d.ts +6 -0
  256. package/src/types/css.d.ts +21 -0
  257. package/src/types/editor.ts +357 -0
  258. package/src/types/react.d.ts +15 -0
  259. package/src/types/three-tsl.d.ts +146 -0
  260. package/src/types/three-webgpu.d.ts +51 -0
  261. package/tsconfig.json +49 -0
@@ -0,0 +1,108 @@
1
+ import { clamp, float, type TSLNode, uniform, vec3, vec4 } from "three/tsl"
2
+ import { CUSTOM_SHADER_ENTRY_EXPORT } from "../lib/editor/custom-shader/shared"
3
+ import { compileCustomShaderModule } from "./custom-shader-runtime"
4
+ import { PassNode } from "./pass-node"
5
+ import type { LayerParameterValues } from "../types/editor"
6
+
7
+ type Node = TSLNode
8
+
9
+ export class CustomShaderPass extends PassNode {
10
+ private readonly onRuntimeError: ((message: string | null) => void) | undefined
11
+ private compiledSketch: (() => Node) | null = null
12
+ private compileRequestId = 0
13
+ private lastCompileSignature = ""
14
+ private readonly timeUniform: Node
15
+
16
+ constructor(layerId: string, onRuntimeError?: (message: string | null) => void) {
17
+ super(layerId)
18
+ this.onRuntimeError = onRuntimeError
19
+ this.timeUniform = uniform(0)
20
+ this.rebuildEffectNode()
21
+ }
22
+
23
+ override needsContinuousRender(): boolean {
24
+ return true
25
+ }
26
+
27
+ override updateParams(params: LayerParameterValues): void {
28
+ const sourceCode =
29
+ typeof params.sourceCode === "string" ? params.sourceCode : ""
30
+ const entryExport =
31
+ typeof params.entryExport === "string" && params.entryExport.trim()
32
+ ? params.entryExport.trim()
33
+ : CUSTOM_SHADER_ENTRY_EXPORT
34
+ const sourceFileName =
35
+ typeof params.sourceFileName === "string" ? params.sourceFileName : ""
36
+ const sourceRevision =
37
+ typeof params.sourceRevision === "number" ? params.sourceRevision : 0
38
+ const compileSignature = [
39
+ entryExport,
40
+ sourceCode,
41
+ sourceFileName,
42
+ sourceRevision,
43
+ ].join("\n")
44
+
45
+ if (compileSignature === this.lastCompileSignature) {
46
+ return
47
+ }
48
+
49
+ this.lastCompileSignature = compileSignature
50
+ this.compileRequestId += 1
51
+ const requestId = this.compileRequestId
52
+
53
+ void compileCustomShaderModule({
54
+ entryExport,
55
+ extraScope: {
56
+ time: this.timeUniform,
57
+ },
58
+ fileName: sourceFileName || "custom-shader.ts",
59
+ sourceCode,
60
+ })
61
+ .then((compiled) => {
62
+ if (requestId !== this.compileRequestId) {
63
+ return
64
+ }
65
+
66
+ this.compiledSketch = compiled.buildNode
67
+ this.onRuntimeError?.(null)
68
+ this.rebuildEffectNode()
69
+ })
70
+ .catch((error) => {
71
+ if (requestId !== this.compileRequestId) {
72
+ return
73
+ }
74
+
75
+ this.compiledSketch = null
76
+ this.onRuntimeError?.(
77
+ error instanceof Error ? error.message : "Custom shader compilation failed.",
78
+ )
79
+ this.rebuildEffectNode()
80
+ })
81
+ }
82
+
83
+ protected override beforeRender(time: number): void {
84
+ this.timeUniform.value = time
85
+ }
86
+
87
+ protected override buildEffectNode(): Node {
88
+ if (!this.compiledSketch) {
89
+ return vec4(vec3(float(0), float(0), float(0)), float(1))
90
+ }
91
+
92
+ try {
93
+ return vec4(
94
+ clamp(
95
+ this.compiledSketch(),
96
+ vec3(float(0), float(0), float(0)),
97
+ vec3(float(1), float(1), float(1))
98
+ ),
99
+ float(1)
100
+ )
101
+ } catch (error) {
102
+ this.onRuntimeError?.(
103
+ error instanceof Error ? error.message : "Custom shader execution failed.",
104
+ )
105
+ return vec4(vec3(float(0), float(0), float(0)), float(1))
106
+ }
107
+ }
108
+ }
@@ -0,0 +1,309 @@
1
+ import type { TSLNode } from "three/tsl"
2
+ import * as tsl from "three/tsl"
3
+ import * as shaderUtils from "./shaders/tsl/utils/index"
4
+
5
+ type CompiledShaderModule = {
6
+ buildNode: () => TSLNode
7
+ }
8
+
9
+ const PRELUDE = {
10
+ ...tsl,
11
+ ...shaderUtils,
12
+ }
13
+
14
+ const TRANSPILED_CACHE = new Map<string, string>()
15
+ let typescriptPromise: Promise<typeof import("typescript")> | null = null
16
+
17
+ function isNodeLike(value: unknown): value is TSLNode {
18
+ return Boolean(
19
+ value &&
20
+ typeof value === "object" &&
21
+ "mul" in value &&
22
+ "add" in value &&
23
+ "sub" in value
24
+ )
25
+ }
26
+
27
+ function formatDiagnostics(
28
+ compiler: typeof import("typescript"),
29
+ diagnostics: readonly import("typescript").Diagnostic[] | undefined
30
+ ): string | null {
31
+ if (!(diagnostics && diagnostics.length > 0)) {
32
+ return null
33
+ }
34
+
35
+ return diagnostics
36
+ .map((diagnostic) =>
37
+ compiler.flattenDiagnosticMessageText(diagnostic.messageText, "\n")
38
+ )
39
+ .join("\n\n")
40
+ }
41
+
42
+ function createSourceFile(
43
+ compiler: typeof import("typescript"),
44
+ fileName: string,
45
+ sourceCode: string
46
+ ) {
47
+ return compiler.createSourceFile(
48
+ fileName,
49
+ sourceCode,
50
+ compiler.ScriptTarget.ES2020,
51
+ true,
52
+ getScriptKind(compiler, fileName)
53
+ )
54
+ }
55
+
56
+ function getSourceFileDiagnostics(sourceFile: import("typescript").SourceFile) {
57
+ return (
58
+ sourceFile as import("typescript").SourceFile & {
59
+ parseDiagnostics?: readonly import("typescript").Diagnostic[]
60
+ }
61
+ ).parseDiagnostics
62
+ }
63
+
64
+ function isDirectiveStatement(
65
+ compiler: typeof import("typescript"),
66
+ statement: import("typescript").Statement
67
+ ) {
68
+ return (
69
+ compiler.isExpressionStatement(statement) &&
70
+ compiler.isStringLiteral(statement.expression) &&
71
+ (statement.expression.text === "use client" ||
72
+ statement.expression.text === "use server")
73
+ )
74
+ }
75
+
76
+ function statementContainsJsx(
77
+ compiler: typeof import("typescript"),
78
+ statement: import("typescript").Statement
79
+ ): boolean {
80
+ let containsJsx = false
81
+
82
+ const visit = (node: import("typescript").Node) => {
83
+ if (
84
+ compiler.isJsxElement(node) ||
85
+ compiler.isJsxSelfClosingElement(node) ||
86
+ compiler.isJsxFragment(node)
87
+ ) {
88
+ containsJsx = true
89
+ return
90
+ }
91
+
92
+ compiler.forEachChild(node, visit)
93
+ }
94
+
95
+ compiler.forEachChild(statement, visit)
96
+
97
+ return containsJsx
98
+ }
99
+
100
+ async function sanitizeCustomShaderSource({
101
+ fileName,
102
+ sourceCode,
103
+ }: {
104
+ fileName: string
105
+ sourceCode: string
106
+ }) {
107
+ const compiler = await getTypeScript()
108
+ const sourceFile = createSourceFile(compiler, fileName, sourceCode)
109
+ const diagnosticsMessage = formatDiagnostics(
110
+ compiler,
111
+ getSourceFileDiagnostics(sourceFile)
112
+ )
113
+
114
+ if (diagnosticsMessage) {
115
+ throw new Error(diagnosticsMessage)
116
+ }
117
+
118
+ const statements = sourceFile.statements.filter((statement) => {
119
+ if (isDirectiveStatement(compiler, statement)) {
120
+ return false
121
+ }
122
+
123
+ if (
124
+ compiler.isImportDeclaration(statement) ||
125
+ compiler.isImportEqualsDeclaration(statement) ||
126
+ compiler.isExportAssignment(statement)
127
+ ) {
128
+ return false
129
+ }
130
+
131
+ if (
132
+ compiler.isExportDeclaration(statement) &&
133
+ statement.moduleSpecifier !== undefined
134
+ ) {
135
+ return false
136
+ }
137
+
138
+ if (statementContainsJsx(compiler, statement)) {
139
+ return false
140
+ }
141
+
142
+ return true
143
+ })
144
+
145
+ const sanitizedFile = compiler.factory.updateSourceFile(
146
+ sourceFile,
147
+ statements
148
+ )
149
+ const printer = compiler.createPrinter({
150
+ newLine: compiler.NewLineKind.LineFeed,
151
+ })
152
+
153
+ return `${printer.printFile(sanitizedFile).trim()}\n`
154
+ }
155
+
156
+ function assertNoExplicitImports(sourceCode: string) {
157
+ if (/^\s*import[\s{*]/m.test(sourceCode)) {
158
+ throw new Error(
159
+ "Custom shader imports are resolved through the injected prelude. Remove the imports or paste the whole sketch file and let the custom shader layer strip them."
160
+ )
161
+ }
162
+ }
163
+
164
+ async function getTypeScript() {
165
+ if (!typescriptPromise) {
166
+ typescriptPromise = import("typescript")
167
+ }
168
+
169
+ return typescriptPromise
170
+ }
171
+
172
+ function getScriptKind(
173
+ compiler: typeof import("typescript"),
174
+ fileName: string
175
+ ): import("typescript").ScriptKind {
176
+ return fileName.endsWith(".tsx")
177
+ ? compiler.ScriptKind.TSX
178
+ : compiler.ScriptKind.TS
179
+ }
180
+
181
+ export async function formatCustomShaderSource({
182
+ fileName,
183
+ sourceCode,
184
+ }: {
185
+ fileName?: string
186
+ sourceCode: string
187
+ }): Promise<string> {
188
+ const compiler = await getTypeScript()
189
+ const resolvedFileName = fileName ?? "custom-shader.ts"
190
+ const sourceFile = createSourceFile(compiler, resolvedFileName, sourceCode)
191
+ const diagnosticsMessage = formatDiagnostics(
192
+ compiler,
193
+ getSourceFileDiagnostics(sourceFile)
194
+ )
195
+
196
+ if (diagnosticsMessage) {
197
+ throw new Error(diagnosticsMessage)
198
+ }
199
+
200
+ const printer = compiler.createPrinter({
201
+ newLine: compiler.NewLineKind.LineFeed,
202
+ })
203
+
204
+ return `${printer.printFile(sourceFile).trim()}\n`
205
+ }
206
+
207
+ export async function compileCustomShaderModule({
208
+ entryExport,
209
+ extraScope,
210
+ fileName,
211
+ force = false,
212
+ sourceCode,
213
+ }: {
214
+ entryExport: string
215
+ extraScope?: Record<string, unknown>
216
+ fileName?: string
217
+ force?: boolean
218
+ sourceCode: string
219
+ }): Promise<CompiledShaderModule> {
220
+ const resolvedFileName = fileName ?? "custom-shader.ts"
221
+ const sanitizedSourceCode = await sanitizeCustomShaderSource({
222
+ fileName: resolvedFileName,
223
+ sourceCode,
224
+ })
225
+ assertNoExplicitImports(sanitizedSourceCode)
226
+
227
+ const cacheKey = `${entryExport}\n${resolvedFileName}\n${sanitizedSourceCode}`
228
+ let outputText = !force ? (TRANSPILED_CACHE.get(cacheKey) ?? null) : null
229
+
230
+ const compiler = await getTypeScript()
231
+ if (!outputText) {
232
+ const transpiled = compiler.transpileModule(sanitizedSourceCode, {
233
+ compilerOptions: {
234
+ esModuleInterop: true,
235
+ jsx: compiler.JsxEmit.ReactJSX,
236
+ module: compiler.ModuleKind.CommonJS,
237
+ target: compiler.ScriptTarget.ES2020,
238
+ },
239
+ fileName: resolvedFileName,
240
+ reportDiagnostics: true,
241
+ })
242
+ const diagnosticsMessage = formatDiagnostics(
243
+ compiler,
244
+ transpiled.diagnostics
245
+ )
246
+
247
+ if (diagnosticsMessage) {
248
+ throw new Error(diagnosticsMessage)
249
+ }
250
+
251
+ outputText = transpiled.outputText
252
+ TRANSPILED_CACHE.set(cacheKey, outputText)
253
+ }
254
+
255
+ const runtimeScope = {
256
+ ...PRELUDE,
257
+ ...(extraScope ?? {}),
258
+ }
259
+ const scopeNames = Object.keys(runtimeScope)
260
+ const scopeValues = scopeNames.map(
261
+ (key) => runtimeScope[key as keyof typeof runtimeScope]
262
+ )
263
+ const module = { exports: {} as Record<string, unknown> }
264
+ const exportsObject = module.exports
265
+ const evaluator = new Function(
266
+ "exports",
267
+ "module",
268
+ ...scopeNames,
269
+ `${outputText}\nreturn module.exports;`
270
+ )
271
+
272
+ let resolvedExports: Record<string, unknown>
273
+
274
+ try {
275
+ resolvedExports = evaluator(
276
+ exportsObject,
277
+ module,
278
+ ...scopeValues
279
+ ) as Record<string, unknown>
280
+ } catch (error) {
281
+ throw new Error(
282
+ error instanceof Error
283
+ ? error.message
284
+ : "Custom shader evaluation failed."
285
+ )
286
+ }
287
+
288
+ const exported = resolvedExports[entryExport] ?? module.exports[entryExport]
289
+
290
+ if (typeof exported !== "function") {
291
+ throw new Error(
292
+ `Expected a named export \`${entryExport}\` that resolves to a TSL sketch function.`
293
+ )
294
+ }
295
+
296
+ return {
297
+ buildNode: () => {
298
+ const result = (exported as () => unknown)()
299
+
300
+ if (!isNodeLike(result)) {
301
+ throw new Error(
302
+ `The export \`${entryExport}\` did not return a valid TSL node.`
303
+ )
304
+ }
305
+
306
+ return result
307
+ },
308
+ }
309
+ }
@@ -0,0 +1,99 @@
1
+ import * as THREE from "three/webgpu"
2
+
3
+ const BAYER_2X2 = [0, 2, 3, 1] as const
4
+
5
+ const BAYER_4X4 = [
6
+ 0, 8, 2, 10,
7
+ 12, 4, 14, 6,
8
+ 3, 11, 1, 9,
9
+ 15, 7, 13, 5,
10
+ ] as const
11
+
12
+ const BAYER_8X8 = [
13
+ 0, 32, 8, 40, 2, 34, 10, 42,
14
+ 48, 16, 56, 24, 50, 18, 58, 26,
15
+ 12, 44, 4, 36, 14, 46, 6, 38,
16
+ 60, 28, 52, 20, 62, 30, 54, 22,
17
+ 3, 35, 11, 43, 1, 33, 9, 41,
18
+ 51, 19, 59, 27, 49, 17, 57, 25,
19
+ 15, 47, 7, 39, 13, 45, 5, 37,
20
+ 63, 31, 55, 23, 61, 29, 53, 21,
21
+ ] as const
22
+
23
+ function buildBayerTexture(values: readonly number[], size: number): THREE.DataTexture {
24
+ const normalizer = size * size
25
+ const data = new Uint8Array(size * size * 4)
26
+
27
+ for (let index = 0; index < size * size; index += 1) {
28
+ const channel = Math.round(((values[index] ?? 0) / normalizer) * 255)
29
+ const offset = index * 4
30
+ data[offset] = channel
31
+ data[offset + 1] = channel
32
+ data[offset + 2] = channel
33
+ data[offset + 3] = 255
34
+ }
35
+
36
+ const texture = new THREE.DataTexture(
37
+ data,
38
+ size,
39
+ size,
40
+ THREE.RGBAFormat,
41
+ THREE.UnsignedByteType,
42
+ )
43
+ texture.magFilter = THREE.NearestFilter
44
+ texture.minFilter = THREE.NearestFilter
45
+ texture.wrapS = THREE.RepeatWrapping
46
+ texture.wrapT = THREE.RepeatWrapping
47
+ texture.needsUpdate = true
48
+
49
+ return texture
50
+ }
51
+
52
+ function buildBlueNoiseTexture(size = 64): THREE.DataTexture {
53
+ const data = new Uint8Array(size * size * 4)
54
+ const fract = (value: number) => value - Math.floor(value)
55
+
56
+ for (let y = 0; y < size; y += 1) {
57
+ for (let x = 0; x < size; x += 1) {
58
+ const noise = Math.round(
59
+ fract(52.9829189 * fract(0.06711056 * x + 0.00583715 * y)) * 255,
60
+ )
61
+ const offset = (y * size + x) * 4
62
+ data[offset] = noise
63
+ data[offset + 1] = noise
64
+ data[offset + 2] = noise
65
+ data[offset + 3] = 255
66
+ }
67
+ }
68
+
69
+ const texture = new THREE.DataTexture(
70
+ data,
71
+ size,
72
+ size,
73
+ THREE.RGBAFormat,
74
+ THREE.UnsignedByteType,
75
+ )
76
+ texture.magFilter = THREE.NearestFilter
77
+ texture.minFilter = THREE.NearestFilter
78
+ texture.wrapS = THREE.RepeatWrapping
79
+ texture.wrapT = THREE.RepeatWrapping
80
+ texture.needsUpdate = true
81
+
82
+ return texture
83
+ }
84
+
85
+ export interface DitherTextures {
86
+ bayer2: THREE.DataTexture
87
+ bayer4: THREE.DataTexture
88
+ bayer8: THREE.DataTexture
89
+ noise: THREE.DataTexture
90
+ }
91
+
92
+ export function buildDitherTextures(): DitherTextures {
93
+ return {
94
+ bayer2: buildBayerTexture(BAYER_2X2, 2),
95
+ bayer4: buildBayerTexture(BAYER_4X4, 4),
96
+ bayer8: buildBayerTexture(BAYER_8X8, 8),
97
+ noise: buildBlueNoiseTexture(),
98
+ }
99
+ }