chorama 0.0.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 (256) hide show
  1. package/.configs/tsconfig.lib.json +43 -0
  2. package/.configs/tsconfig.website.json +34 -0
  3. package/.github/workflows/static.yml +88 -0
  4. package/.vscode/launch.json +29 -0
  5. package/.vscode/tasks.json +19 -0
  6. package/README.md +127 -0
  7. package/assets/images/disappointed.jpg +0 -0
  8. package/assets/images/skybox/grimmnight_back.png +0 -0
  9. package/assets/images/skybox/grimmnight_bottom.png +0 -0
  10. package/assets/images/skybox/grimmnight_front.png +0 -0
  11. package/assets/images/skybox/grimmnight_left.png +0 -0
  12. package/assets/images/skybox/grimmnight_right.png +0 -0
  13. package/assets/images/skybox/grimmnight_top.png +0 -0
  14. package/assets/images/skybox/miramar_back.png +0 -0
  15. package/assets/images/skybox/miramar_bottom.png +0 -0
  16. package/assets/images/skybox/miramar_front.png +0 -0
  17. package/assets/images/skybox/miramar_left.png +0 -0
  18. package/assets/images/skybox/miramar_right.png +0 -0
  19. package/assets/images/skybox/miramar_top.png +0 -0
  20. package/assets/images/uv.jpg +0 -0
  21. package/assets/models/gltf/flight_helmet/FlightHelmet_Materials_GlassPlasticMat_BaseColor.png +0 -0
  22. package/assets/models/gltf/flight_helmet/FlightHelmet_Materials_GlassPlasticMat_Normal.png +0 -0
  23. package/assets/models/gltf/flight_helmet/FlightHelmet_Materials_GlassPlasticMat_OcclusionRoughMetal.png +0 -0
  24. package/assets/models/gltf/flight_helmet/FlightHelmet_Materials_LeatherPartsMat_BaseColor.png +0 -0
  25. package/assets/models/gltf/flight_helmet/FlightHelmet_Materials_LeatherPartsMat_Normal.png +0 -0
  26. package/assets/models/gltf/flight_helmet/FlightHelmet_Materials_LeatherPartsMat_OcclusionRoughMetal.png +0 -0
  27. package/assets/models/gltf/flight_helmet/FlightHelmet_Materials_LensesMat_BaseColor.png +0 -0
  28. package/assets/models/gltf/flight_helmet/FlightHelmet_Materials_LensesMat_Normal.png +0 -0
  29. package/assets/models/gltf/flight_helmet/FlightHelmet_Materials_LensesMat_OcclusionRoughMetal.png +0 -0
  30. package/assets/models/gltf/flight_helmet/FlightHelmet_Materials_MetalPartsMat_BaseColor.png +0 -0
  31. package/assets/models/gltf/flight_helmet/FlightHelmet_Materials_MetalPartsMat_Normal.png +0 -0
  32. package/assets/models/gltf/flight_helmet/FlightHelmet_Materials_MetalPartsMat_OcclusionRoughMetal.png +0 -0
  33. package/assets/models/gltf/flight_helmet/FlightHelmet_Materials_RubberWoodMat_BaseColor.png +0 -0
  34. package/assets/models/gltf/flight_helmet/FlightHelmet_Materials_RubberWoodMat_Normal.png +0 -0
  35. package/assets/models/gltf/flight_helmet/FlightHelmet_Materials_RubberWoodMat_OcclusionRoughMetal.png +0 -0
  36. package/assets/models/gltf/flight_helmet/index.bin +0 -0
  37. package/assets/models/gltf/flight_helmet/index.gltf +705 -0
  38. package/assets/models/gltf/object.gltf +23 -0
  39. package/assets/models/gltf/pirate_girl/index.bin +0 -0
  40. package/assets/models/gltf/pirate_girl/index.gltf +2082 -0
  41. package/assets/models/obj/pirate_girl/pirate_girl.obj +18459 -0
  42. package/assets/models/obj/pirate_girl/pirate_girl.png +0 -0
  43. package/astro.config.mjs +45 -0
  44. package/content/guide/api-map.md +89 -0
  45. package/content/guide/camera-and-controls.md +98 -0
  46. package/content/guide/first-scene.md +176 -0
  47. package/content/guide/index.md +72 -0
  48. package/content/guide/installation.md +179 -0
  49. package/content/guide/materials-and-lighting.md +138 -0
  50. package/content/guide/plugins-and-render-pipeline.md +124 -0
  51. package/content/guide/render-targets-and-views.md +147 -0
  52. package/content/guide/scene-graph-and-transforms.md +113 -0
  53. package/content/guide/textures-and-assets.md +120 -0
  54. package/content/guide/troubleshooting.md +49 -0
  55. package/env.d.ts +19 -0
  56. package/examples/addons/rendergraph_gui.js +580 -0
  57. package/examples/camera/orthographic.js +120 -0
  58. package/examples/camera/perspective.js +138 -0
  59. package/examples/lights/directional.js +397 -0
  60. package/examples/lights/multiple_spot_lights.js +304 -0
  61. package/examples/lights/point.js +337 -0
  62. package/examples/lights/spot.js +366 -0
  63. package/examples/loader/gltf_material.js +111 -0
  64. package/examples/loader/gltfloader.js +78 -0
  65. package/examples/loader/objloader.js +95 -0
  66. package/examples/material/cullface.js +111 -0
  67. package/examples/material/materials.js +126 -0
  68. package/examples/material/standard/basic.js +164 -0
  69. package/examples/mesh/circle.js +117 -0
  70. package/examples/mesh/cuboid.js +151 -0
  71. package/examples/mesh/cylinder.js +139 -0
  72. package/examples/mesh/geometries.js +108 -0
  73. package/examples/mesh/meshTopology.js +103 -0
  74. package/examples/mesh/plane.js +117 -0
  75. package/examples/mesh/skinning.js +136 -0
  76. package/examples/mesh/uvsphere.js +113 -0
  77. package/examples/other/rotatingCube.js +93 -0
  78. package/examples/other/rotatingSphere.js +96 -0
  79. package/examples/rendertarget/basic_canvas.js +130 -0
  80. package/examples/rendertarget/depth_texture.js +130 -0
  81. package/examples/rendertarget/image_target.js +140 -0
  82. package/examples/rendertarget/multiple_views.js +158 -0
  83. package/examples/rendertarget/render_masks.js +173 -0
  84. package/examples/rendertarget/split_screen.js +123 -0
  85. package/examples/rendertarget/split_view.js +137 -0
  86. package/examples/skybox/skybox.js +111 -0
  87. package/examples/texture/arrays.js +156 -0
  88. package/examples/texture/textureWrap.js +118 -0
  89. package/examples/transform/propagation.js +92 -0
  90. package/package.json +55 -0
  91. package/rollup.config.js +66 -0
  92. package/scripts/stage-chorama.mjs +29 -0
  93. package/src/caches/cache.js +420 -0
  94. package/src/caches/index.js +2 -0
  95. package/src/caches/uniformbuffers.js +104 -0
  96. package/src/cameracontrols/index.js +258 -0
  97. package/src/constants/index.js +3 -0
  98. package/src/constants/mesh.js +197 -0
  99. package/src/constants/others.js +218 -0
  100. package/src/constants/texture.js +183 -0
  101. package/src/core/constants.js +14 -0
  102. package/src/core/extensions.js +42 -0
  103. package/src/core/index.js +7 -0
  104. package/src/core/layouts/index.js +4 -0
  105. package/src/core/layouts/meshvertex.js +60 -0
  106. package/src/core/layouts/uniform.js +21 -0
  107. package/src/core/layouts/uniformbuffer.js +15 -0
  108. package/src/core/layouts/vertexbuffer.js +43 -0
  109. package/src/core/limits.js +247 -0
  110. package/src/core/resources/blendparams.js +89 -0
  111. package/src/core/resources/framebuffer.js +127 -0
  112. package/src/core/resources/gpubuffer.js +32 -0
  113. package/src/core/resources/gpumesh.js +43 -0
  114. package/src/core/resources/gputexture.js +73 -0
  115. package/src/core/resources/index.js +5 -0
  116. package/src/core/shader.js +62 -0
  117. package/src/core/webgl/bindgroup.js +89 -0
  118. package/src/core/webgl/descriptors.js +104 -0
  119. package/src/core/webgl/index.js +5 -0
  120. package/src/core/webgl/renderpassencoder.js +96 -0
  121. package/src/core/webgl/renderpipeline.js +54 -0
  122. package/src/core/webgl/utils.js +371 -0
  123. package/src/core/webgl/webglrenderdevice.js +235 -0
  124. package/src/function.js +358 -0
  125. package/src/index.js +15 -0
  126. package/src/loader/gltf.js +2172 -0
  127. package/src/loader/index.js +3 -0
  128. package/src/loader/loader.js +174 -0
  129. package/src/loader/obj.js +188 -0
  130. package/src/loader/texture.js +85 -0
  131. package/src/loader/utils.js +16 -0
  132. package/src/material/basic.js +75 -0
  133. package/src/material/depth.js +73 -0
  134. package/src/material/index.js +8 -0
  135. package/src/material/lambert.js +73 -0
  136. package/src/material/material.js +106 -0
  137. package/src/material/normal.js +30 -0
  138. package/src/material/phong.js +86 -0
  139. package/src/material/raw.js +52 -0
  140. package/src/material/standard.js +221 -0
  141. package/src/math/index.js +3 -0
  142. package/src/math/transform.js +38 -0
  143. package/src/mesh/attribute/attribute.js +79 -0
  144. package/src/mesh/attribute/index.js +1 -0
  145. package/src/mesh/attributedata/index.js +1 -0
  146. package/src/mesh/attributedata/separate.js +180 -0
  147. package/src/mesh/builders/base.js +41 -0
  148. package/src/mesh/builders/circle.js +63 -0
  149. package/src/mesh/builders/cuboid.js +135 -0
  150. package/src/mesh/builders/cylinder.js +131 -0
  151. package/src/mesh/builders/index.js +7 -0
  152. package/src/mesh/builders/plane.js +73 -0
  153. package/src/mesh/builders/utils.js +20 -0
  154. package/src/mesh/builders/uvsphere.js +80 -0
  155. package/src/mesh/builders/wireframe.js +62 -0
  156. package/src/mesh/index.js +4 -0
  157. package/src/mesh/mesh.js +149 -0
  158. package/src/objects/bone.js +17 -0
  159. package/src/objects/camera/camera.js +56 -0
  160. package/src/objects/camera/index.js +2 -0
  161. package/src/objects/camera/projection.js +203 -0
  162. package/src/objects/debug/index.js +1 -0
  163. package/src/objects/debug/skeleton.js +28 -0
  164. package/src/objects/index.js +7 -0
  165. package/src/objects/light/ambient.js +20 -0
  166. package/src/objects/light/directional.js +29 -0
  167. package/src/objects/light/index.js +5 -0
  168. package/src/objects/light/point.js +32 -0
  169. package/src/objects/light/shadow/index.js +1 -0
  170. package/src/objects/light/shadow/shadow.js +67 -0
  171. package/src/objects/light/spot.js +56 -0
  172. package/src/objects/mesh.js +141 -0
  173. package/src/objects/object3d.js +167 -0
  174. package/src/objects/skybox.js +38 -0
  175. package/src/plugins/camera/camera.js +19 -0
  176. package/src/plugins/camera/index.js +2 -0
  177. package/src/plugins/camera/nodes/cameraview.js +46 -0
  178. package/src/plugins/camera/nodes/index.js +2 -0
  179. package/src/plugins/camera/nodes/opaquepass.js +79 -0
  180. package/src/plugins/index.js +6 -0
  181. package/src/plugins/light/index.js +2 -0
  182. package/src/plugins/light/light.js +23 -0
  183. package/src/plugins/light/nodes/index.js +1 -0
  184. package/src/plugins/light/nodes/light.js +127 -0
  185. package/src/plugins/meshmaterial/index.js +3 -0
  186. package/src/plugins/meshmaterial/meshmaterial.js +381 -0
  187. package/src/plugins/meshmaterial/nodes/index.js +1 -0
  188. package/src/plugins/meshmaterial/nodes/meshmaterial.js +50 -0
  189. package/src/plugins/meshmaterial/resources/index.js +1 -0
  190. package/src/plugins/meshmaterial/resources/meshmaterialpipelines.js +50 -0
  191. package/src/plugins/shadow/index.js +3 -0
  192. package/src/plugins/shadow/nodes/index.js +3 -0
  193. package/src/plugins/shadow/nodes/shadow.js +272 -0
  194. package/src/plugins/shadow/nodes/shadowOccluder.js +112 -0
  195. package/src/plugins/shadow/nodes/shadowOpaquePass.js +73 -0
  196. package/src/plugins/shadow/resources/ShadowMap.js +99 -0
  197. package/src/plugins/shadow/resources/index.js +2 -0
  198. package/src/plugins/shadow/resources/shadowpipelines.js +25 -0
  199. package/src/plugins/shadow/shadow.js +31 -0
  200. package/src/plugins/skeletonhelper/index.js +1 -0
  201. package/src/plugins/skeletonhelper/skeletonhelper.js +160 -0
  202. package/src/plugins/skybox/index.js +3 -0
  203. package/src/plugins/skybox/nodes/index.js +1 -0
  204. package/src/plugins/skybox/nodes/skybox.js +143 -0
  205. package/src/plugins/skybox/resources/index.js +2 -0
  206. package/src/plugins/skybox/resources/skyboxmesh.js +14 -0
  207. package/src/plugins/skybox/resources/skyboxpipeline.js +6 -0
  208. package/src/plugins/skybox/skybox.js +137 -0
  209. package/src/renderer/core/index.js +179 -0
  210. package/src/renderer/graph/index.js +3 -0
  211. package/src/renderer/graph/nodes.js +34 -0
  212. package/src/renderer/graph/rendergraph.js +182 -0
  213. package/src/renderer/index.js +5 -0
  214. package/src/renderer/plugin.js +36 -0
  215. package/src/renderer/renderer.js +179 -0
  216. package/src/renderer/views.js +28 -0
  217. package/src/rendertarget/canvastarget.js +30 -0
  218. package/src/rendertarget/image.js +132 -0
  219. package/src/rendertarget/index.js +3 -0
  220. package/src/rendertarget/rendertarget.js +89 -0
  221. package/src/shader/basicFragment.glsl +30 -0
  222. package/src/shader/basicVertex.glsl +87 -0
  223. package/src/shader/common/color.glsl +7 -0
  224. package/src/shader/common/common.glsl +25 -0
  225. package/src/shader/common/index.js +4 -0
  226. package/src/shader/common/light.glsl +437 -0
  227. package/src/shader/common/math.glsl +12 -0
  228. package/src/shader/debug/index.js +2 -0
  229. package/src/shader/debug/skeletonFragment.glsl +8 -0
  230. package/src/shader/debug/skeletonVertex.glsl +27 -0
  231. package/src/shader/depthFragment.glsl +37 -0
  232. package/src/shader/index.js +11 -0
  233. package/src/shader/lambertFragment.glsl +126 -0
  234. package/src/shader/normalFragment.glsl +25 -0
  235. package/src/shader/phongFragment.glsl +140 -0
  236. package/src/shader/skyboxFragment.glsl +16 -0
  237. package/src/shader/skyboxVertex.glsl +20 -0
  238. package/src/shader/standardFragment.glsl +274 -0
  239. package/src/texture/index.js +2 -0
  240. package/src/texture/sampler.js +111 -0
  241. package/src/texture/texture.js +234 -0
  242. package/src/utils/index.js +115 -0
  243. package/tsconfig.json +11 -0
  244. package/website/config/index.ts +1 -0
  245. package/website/config/navigation.ts +53 -0
  246. package/website/content.config.ts +92 -0
  247. package/website/layouts/DocLayout.astro +501 -0
  248. package/website/layouts/Example.astro +91 -0
  249. package/website/pages/examples/[...slug].astro +77 -0
  250. package/website/pages/examples/index.astro +98 -0
  251. package/website/pages/examples/samples/[...slug].astro +17 -0
  252. package/website/pages/guide/[slug].astro +30 -0
  253. package/website/pages/guide/index.astro +21 -0
  254. package/website/pages/index.astro +9 -0
  255. package/website/plugins/remark-link-base.js +23 -0
  256. package/website/utils/url.ts +30 -0
@@ -0,0 +1,580 @@
1
+
2
+ // This entire thing was generated by ChatGPT 5.3 Codex
3
+ // SAFETY: Dont want to consume tokens fixing this mess
4
+ // @ts-nocheck
5
+ /**
6
+ * @typedef RenderGraphAddonOptions
7
+ * @property {import("dat.gui").GUI} gui
8
+ * @property {import("chorama").WebGLRenderer} renderer
9
+ * @property {string} [title]
10
+ * @property {{x:number;y:number}} [position]
11
+ * @property {number} [width]
12
+ */
13
+
14
+ /**
15
+ * Adds a draggable render graph panel next to dat.gui.
16
+ * @param {RenderGraphAddonOptions} options
17
+ */
18
+ export function addRenderGraphGuiAddon({
19
+ gui,
20
+ renderer,
21
+ title = "Render Graph",
22
+ position = { x: 24, y: 24 },
23
+ width = 320
24
+ }) {
25
+ const container = document.createElement("div")
26
+ const header = document.createElement("div")
27
+ const body = document.createElement("div")
28
+ const graphHeader = document.createElement("div")
29
+ const backButton = document.createElement("button")
30
+ const graphTitle = document.createElement("div")
31
+ const graphSvg = createSvgElement("svg")
32
+ const orderList = document.createElement("ol")
33
+ const markerId = `rg-arrow-${Math.random().toString(36).slice(2)}`
34
+ /** @type {{
35
+ * scale: number
36
+ * panX: number
37
+ * panY: number
38
+ * minScale: number
39
+ * maxScale: number
40
+ * markerId: string
41
+ * }} */
42
+ const graphViewState = {
43
+ scale: 1,
44
+ panX: 0,
45
+ panY: 0,
46
+ minScale: 0.35,
47
+ maxScale: 2.5,
48
+ markerId
49
+ }
50
+ /**
51
+ * @type {{
52
+ * nodes:{id:number;name:string;hasSubgraph:boolean;subgraph:any}[];
53
+ * edges:{from:number;to:number}[];
54
+ * executionOrderIds:number[];
55
+ * executionOrder:string[];
56
+ * }} */
57
+ let graphData = {
58
+ nodes: [],
59
+ edges: [],
60
+ executionOrderIds: [],
61
+ executionOrder: []
62
+ }
63
+ const panelState = {
64
+ visible: true,
65
+ refresh: () => renderSnapshot(),
66
+ back: () => {
67
+ if (graphStack.length <= 1) return
68
+ graphStack.pop()
69
+ graphViewState.scale = 1
70
+ graphViewState.panX = 0
71
+ graphViewState.panY = 0
72
+ renderSnapshot()
73
+ },
74
+ resetView: () => {
75
+ graphViewState.scale = 1
76
+ graphViewState.panX = 0
77
+ graphViewState.panY = 0
78
+ drawGraph(graphSvg, graphData, graphViewState, {
79
+ onSubgraphNodeClick: openSubgraph
80
+ })
81
+ }
82
+ }
83
+ /** @type {{name:string;graph:any}[]} */
84
+ const graphStack = [{
85
+ name: "Root",
86
+ graph: renderer.renderGraph
87
+ }]
88
+
89
+ container.style.position = "fixed"
90
+ container.style.left = `${position.x}px`
91
+ container.style.top = `${position.y}px`
92
+ container.style.width = `${width}px`
93
+ container.style.zIndex = "10000"
94
+ container.style.background = "#1a1a1a"
95
+ container.style.color = "#ddd"
96
+ container.style.font = "11px/1.4 Menlo, Monaco, Consolas, monospace"
97
+ container.style.border = "1px solid #2c2c2c"
98
+ container.style.boxShadow = "0 8px 24px rgba(0,0,0,0.35)"
99
+ container.style.userSelect = "none"
100
+
101
+ header.style.padding = "8px 10px"
102
+ header.style.cursor = "move"
103
+ header.style.background = "#111"
104
+ header.style.borderBottom = "1px solid #2c2c2c"
105
+ header.style.fontWeight = "bold"
106
+ header.textContent = title
107
+
108
+ body.style.padding = "8px 10px"
109
+ body.style.maxHeight = "40vh"
110
+ body.style.overflow = "auto"
111
+ body.style.display = "grid"
112
+ body.style.gap = "8px"
113
+
114
+ graphHeader.style.display = "flex"
115
+ graphHeader.style.alignItems = "center"
116
+ graphHeader.style.gap = "8px"
117
+
118
+ backButton.type = "button"
119
+ backButton.textContent = "Back"
120
+ backButton.style.font = "11px Menlo, Monaco, Consolas, monospace"
121
+ backButton.style.background = "#252525"
122
+ backButton.style.color = "#ddd"
123
+ backButton.style.border = "1px solid #3a3a3a"
124
+ backButton.style.padding = "2px 8px"
125
+ backButton.style.borderRadius = "4px"
126
+ backButton.style.cursor = "pointer"
127
+
128
+ graphTitle.style.opacity = "0.85"
129
+ graphTitle.textContent = "Graph (hover/click green nodes to open subgraph)"
130
+ graphSvg.style.width = "100%"
131
+ graphSvg.style.height = "240px"
132
+ graphSvg.style.background = "#101010"
133
+ graphSvg.style.border = "1px solid #2c2c2c"
134
+ graphSvg.style.borderRadius = "4px"
135
+
136
+ const orderTitle = document.createElement("div")
137
+ orderTitle.style.opacity = "0.85"
138
+ orderTitle.textContent = "Execution order"
139
+ orderList.style.margin = "0 0 0 20px"
140
+ orderList.style.padding = "0"
141
+ graphHeader.append(backButton, graphTitle)
142
+ body.append(graphHeader, graphSvg, orderTitle, orderList)
143
+ container.append(header, body)
144
+ document.body.append(container)
145
+
146
+ const folder = gui.addFolder("Render Graph")
147
+ folder.add(panelState, "visible").name("Show Panel").onChange((value) => {
148
+ container.style.display = value ? "block" : "none"
149
+ })
150
+ folder.add(panelState, "refresh").name("Refresh")
151
+ folder.add(panelState, "back").name("Back")
152
+ folder.add(panelState, "resetView").name("Reset View")
153
+
154
+ enableDrag(container, header)
155
+ backButton.addEventListener("click", panelState.back)
156
+ const disableGraphNavigation = enableGraphNavigation(graphSvg, graphViewState, () => {
157
+ drawGraph(graphSvg, graphData, graphViewState, {
158
+ onSubgraphNodeClick: openSubgraph
159
+ })
160
+ })
161
+ renderSnapshot()
162
+
163
+ return {
164
+ refresh: renderSnapshot,
165
+ dispose() {
166
+ disableGraphNavigation()
167
+ container.remove()
168
+ gui.removeFolder(folder)
169
+ }
170
+ }
171
+
172
+ function renderSnapshot() {
173
+ const current = graphStack[graphStack.length - 1]
174
+ const graph = readGraph(current.graph)
175
+ graphData = graph
176
+ graphTitle.textContent = `Graph: ${graphStack.map((entry) => entry.name).join(" / ")}`
177
+ const canGoBack = graphStack.length > 1
178
+ backButton.disabled = !canGoBack
179
+ backButton.style.opacity = canGoBack ? "1" : "0.5"
180
+ backButton.style.cursor = canGoBack ? "pointer" : "default"
181
+ orderList.innerHTML = ""
182
+ drawGraph(graphSvg, graphData, graphViewState, {
183
+ onSubgraphNodeClick: openSubgraph
184
+ })
185
+
186
+ for (let i = 0; i < graph.executionOrder.length; i++) {
187
+ const name = graph.executionOrder[i]
188
+ if (name === undefined) continue
189
+ const item = document.createElement("li")
190
+ item.textContent = name
191
+ orderList.append(item)
192
+ }
193
+ }
194
+
195
+ /**
196
+ * @param {{id:number;name:string;hasSubgraph:boolean;subgraph:any}} node
197
+ */
198
+ function openSubgraph(node) {
199
+ if (!node.hasSubgraph || !node.subgraph) return
200
+ graphStack.push({
201
+ name: node.name,
202
+ graph: node.subgraph
203
+ })
204
+ graphViewState.scale = 1
205
+ graphViewState.panX = 0
206
+ graphViewState.panY = 0
207
+ renderSnapshot()
208
+ }
209
+ }
210
+
211
+ /**
212
+ * @param {any} graph
213
+ */
214
+ function readGraph(graph) {
215
+ if (typeof graph.inspect === "function") {
216
+ const snapshot = graph.inspect()
217
+ const nodes = snapshot.nodes.map((node) => ({
218
+ id: node.id,
219
+ name: node.name,
220
+ subgraph: node.subgraph,
221
+ hasSubgraph: node.subgraph !== undefined
222
+ }))
223
+
224
+ return {
225
+ nodes,
226
+ executionOrderIds: snapshot.executionOrderIds ?? [],
227
+ executionOrder: snapshot.executionOrder,
228
+ edges: snapshot.edges
229
+ }
230
+ }
231
+
232
+ return {
233
+ nodes: [],
234
+ executionOrderIds: [],
235
+ executionOrder: [],
236
+ edges: []
237
+ }
238
+ }
239
+
240
+ /**
241
+ * @param {SVGSVGElement} svg
242
+ * @param {{
243
+ * nodes:{id:number;name:string;hasSubgraph:boolean;subgraph:any}[];
244
+ * edges:{from:number;to:number}[];
245
+ * executionOrderIds:number[];
246
+ * }} graph
247
+ * @param {{
248
+ * scale:number;
249
+ * panX:number;
250
+ * panY:number;
251
+ * markerId:string;
252
+ * }} view
253
+ * @param {{
254
+ * onSubgraphNodeClick: (node:{id:number;name:string;hasSubgraph:boolean;subgraph:any}) => void
255
+ * }} callbacks
256
+ */
257
+ function drawGraph(svg, graph, view, callbacks) {
258
+ while (svg.firstChild) {
259
+ svg.removeChild(svg.firstChild)
260
+ }
261
+
262
+ if (graph.nodes.length === 0) {
263
+ svg.setAttribute("viewBox", "0 0 320 80")
264
+ const empty = createSvgElement("text")
265
+ empty.setAttribute("x", "12")
266
+ empty.setAttribute("y", "42")
267
+ empty.setAttribute("fill", "#777")
268
+ empty.textContent = "No render graph nodes found"
269
+ svg.append(empty)
270
+ return
271
+ }
272
+
273
+ const nodeWidth = 130
274
+ const nodeHeight = 26
275
+ const levelGap = 46
276
+ const rowGap = 12
277
+ const padX = 14
278
+ const padY = 14
279
+ const nodeIds = graph.nodes.map((node) => node.id)
280
+ const incoming = new Map(nodeIds.map((id) => [id, []]))
281
+ const outgoing = new Map(nodeIds.map((id) => [id, []]))
282
+
283
+ for (let i = 0; i < graph.edges.length; i++) {
284
+ const edge = graph.edges[i]
285
+ if (!edge) continue
286
+ incoming.get(edge.to)?.push(edge.from)
287
+ outgoing.get(edge.from)?.push(edge.to)
288
+ }
289
+
290
+ const orderedIds = graph.executionOrderIds.length > 0 ? graph.executionOrderIds : nodeIds
291
+ const levelById = new Map(nodeIds.map((id) => [id, 0]))
292
+ for (let i = 0; i < orderedIds.length; i++) {
293
+ const id = orderedIds[i]
294
+ if (id === undefined) continue
295
+ const deps = incoming.get(id) ?? []
296
+ let level = 0
297
+ for (let j = 0; j < deps.length; j++) {
298
+ const dep = deps[j]
299
+ level = Math.max(level, (levelById.get(dep) ?? 0) + 1)
300
+ }
301
+ levelById.set(id, level)
302
+ }
303
+
304
+ const columns = new Map()
305
+ for (let i = 0; i < orderedIds.length; i++) {
306
+ const id = orderedIds[i]
307
+ if (id === undefined) continue
308
+ const level = levelById.get(id) ?? 0
309
+ if (!columns.has(level)) columns.set(level, [])
310
+ columns.get(level).push(id)
311
+ }
312
+
313
+ const levels = [...columns.keys()].sort((a, b) => a - b)
314
+ const maxRows = levels.reduce((count, level) => Math.max(count, columns.get(level)?.length ?? 0), 1)
315
+ const totalWidth = padX * 2 + levels.length * nodeWidth + Math.max(0, levels.length - 1) * levelGap
316
+ const totalHeight = padY * 2 + maxRows * nodeHeight + Math.max(0, maxRows - 1) * rowGap
317
+ svg.setAttribute("viewBox", `0 0 ${totalWidth} ${totalHeight}`)
318
+ svg.setAttribute("preserveAspectRatio", "xMidYMid meet")
319
+
320
+ const defs = createSvgElement("defs")
321
+ const marker = createSvgElement("marker")
322
+ marker.setAttribute("id", view.markerId)
323
+ marker.setAttribute("viewBox", "0 0 10 10")
324
+ marker.setAttribute("refX", "10")
325
+ marker.setAttribute("refY", "5")
326
+ marker.setAttribute("markerWidth", "6")
327
+ marker.setAttribute("markerHeight", "6")
328
+ marker.setAttribute("orient", "auto-start-reverse")
329
+ const arrowPath = createSvgElement("path")
330
+ arrowPath.setAttribute("d", "M 0 0 L 10 5 L 0 10 z")
331
+ arrowPath.setAttribute("fill", "#6aa4ff")
332
+ marker.append(arrowPath)
333
+ defs.append(marker)
334
+ svg.append(defs)
335
+ const scene = createSvgElement("g")
336
+ scene.setAttribute("transform", `translate(${view.panX} ${view.panY}) scale(${view.scale})`)
337
+ svg.append(scene)
338
+
339
+ const positionById = new Map()
340
+ for (let levelIndex = 0; levelIndex < levels.length; levelIndex++) {
341
+ const level = levels[levelIndex]
342
+ const ids = columns.get(level) ?? []
343
+ for (let row = 0; row < ids.length; row++) {
344
+ const id = ids[row]
345
+ const x = padX + levelIndex * (nodeWidth + levelGap)
346
+ const y = padY + row * (nodeHeight + rowGap)
347
+ positionById.set(id, {
348
+ x,
349
+ y,
350
+ cx: x + nodeWidth / 2,
351
+ cy: y + nodeHeight / 2
352
+ })
353
+ }
354
+ }
355
+
356
+ for (let i = 0; i < graph.edges.length; i++) {
357
+ const edge = graph.edges[i]
358
+ if (!edge) continue
359
+ const from = positionById.get(edge.from)
360
+ const to = positionById.get(edge.to)
361
+ if (!from || !to) continue
362
+
363
+ const path = createSvgElement("path")
364
+ const x1 = from.x + nodeWidth
365
+ const y1 = from.cy
366
+ const x2 = to.x
367
+ const y2 = to.cy
368
+ const bend = Math.max(18, (x2 - x1) * 0.45)
369
+ path.setAttribute("d", `M ${x1} ${y1} C ${x1 + bend} ${y1}, ${x2 - bend} ${y2}, ${x2} ${y2}`)
370
+ path.setAttribute("stroke", "#6aa4ff")
371
+ path.setAttribute("stroke-width", "1.4")
372
+ path.setAttribute("fill", "none")
373
+ path.setAttribute("opacity", "0.85")
374
+ path.setAttribute("marker-end", `url(#${view.markerId})`)
375
+ scene.append(path)
376
+ }
377
+
378
+ for (let i = 0; i < graph.nodes.length; i++) {
379
+ const node = graph.nodes[i]
380
+ const pos = positionById.get(node.id)
381
+ if (!pos) continue
382
+
383
+ const nodeGroup = createSvgElement("g")
384
+ const box = createSvgElement("rect")
385
+ box.setAttribute("x", `${pos.x}`)
386
+ box.setAttribute("y", `${pos.y}`)
387
+ box.setAttribute("width", `${nodeWidth}`)
388
+ box.setAttribute("height", `${nodeHeight}`)
389
+ box.setAttribute("rx", "4")
390
+ const baseFill = node.hasSubgraph ? "#243022" : "#222"
391
+ const baseStroke = node.hasSubgraph ? "#88c26f" : "#4b7ecc"
392
+ box.setAttribute("fill", baseFill)
393
+ box.setAttribute("stroke", baseStroke)
394
+ box.setAttribute("stroke-width", "1")
395
+ nodeGroup.append(box)
396
+
397
+ const label = createSvgElement("text")
398
+ label.setAttribute("x", `${pos.x + 8}`)
399
+ label.setAttribute("y", `${pos.y + 17}`)
400
+ label.setAttribute("fill", "#f1f1f1")
401
+ label.setAttribute("font-size", "11")
402
+ label.setAttribute("font-family", "Menlo, Monaco, Consolas, monospace")
403
+ label.textContent = node.hasSubgraph ? `${truncate(node.name, 18)} >` : truncate(node.name, 20)
404
+ nodeGroup.append(label)
405
+
406
+ if (node.hasSubgraph) {
407
+ nodeGroup.style.cursor = "pointer"
408
+ nodeGroup.addEventListener("mouseenter", () => {
409
+ box.setAttribute("fill", "#2e3c2b")
410
+ box.setAttribute("stroke", "#b7e391")
411
+ })
412
+ nodeGroup.addEventListener("mouseleave", () => {
413
+ box.setAttribute("fill", baseFill)
414
+ box.setAttribute("stroke", baseStroke)
415
+ })
416
+ nodeGroup.addEventListener("mousedown", (event) => {
417
+ event.stopPropagation()
418
+ })
419
+ nodeGroup.addEventListener("click", (event) => {
420
+ event.stopPropagation()
421
+ callbacks.onSubgraphNodeClick(node)
422
+ })
423
+ }
424
+
425
+ scene.append(nodeGroup)
426
+ }
427
+ }
428
+
429
+ /**
430
+ * @param {string} value
431
+ * @param {number} max
432
+ */
433
+ function truncate(value, max) {
434
+ if (value.length <= max) return value
435
+ return `${value.slice(0, max - 3)}...`
436
+ }
437
+
438
+ /**
439
+ * @param {keyof SVGElementTagNameMap} tag
440
+ */
441
+ function createSvgElement(tag) {
442
+ return document.createElementNS("http://www.w3.org/2000/svg", tag)
443
+ }
444
+
445
+ /**
446
+ * @param {SVGSVGElement} svg
447
+ * @param {{
448
+ * scale: number
449
+ * panX: number
450
+ * panY: number
451
+ * minScale: number
452
+ * maxScale: number
453
+ * }} view
454
+ * @param {() => void} redraw
455
+ */
456
+ function enableGraphNavigation(svg, view, redraw) {
457
+ let panning = false
458
+ let startClientX = 0
459
+ let startClientY = 0
460
+ let startPanX = 0
461
+ let startPanY = 0
462
+
463
+ svg.style.cursor = "grab"
464
+
465
+ /** @param {WheelEvent} event */
466
+ const onWheel = (event) => {
467
+ event.preventDefault()
468
+ const point = toSvgCoordinates(svg, event.clientX, event.clientY)
469
+ if (!point) return
470
+ const px = point.x
471
+ const py = point.y
472
+ const worldX = (px - view.panX) / view.scale
473
+ const worldY = (py - view.panY) / view.scale
474
+ const zoom = Math.exp(-event.deltaY * 0.0015)
475
+ const nextScale = clamp(view.scale * zoom, view.minScale, view.maxScale)
476
+
477
+ view.scale = nextScale
478
+ view.panX = px - worldX * nextScale
479
+ view.panY = py - worldY * nextScale
480
+ redraw()
481
+ }
482
+
483
+ /** @param {MouseEvent} event */
484
+ const onDown = (event) => {
485
+ if (event.button !== 0) return
486
+ panning = true
487
+ startClientX = event.clientX
488
+ startClientY = event.clientY
489
+ startPanX = view.panX
490
+ startPanY = view.panY
491
+ svg.style.cursor = "grabbing"
492
+ }
493
+
494
+ /** @param {MouseEvent} event */
495
+ const onMove = (event) => {
496
+ if (!panning) return
497
+ const start = toSvgCoordinates(svg, startClientX, startClientY)
498
+ const current = toSvgCoordinates(svg, event.clientX, event.clientY)
499
+ if (!start || !current) return
500
+
501
+ const dx = current.x - start.x
502
+ const dy = current.y - start.y
503
+ view.panX = startPanX + dx
504
+ view.panY = startPanY + dy
505
+ redraw()
506
+ }
507
+
508
+ const onUp = () => {
509
+ panning = false
510
+ svg.style.cursor = "grab"
511
+ }
512
+
513
+ svg.addEventListener("wheel", onWheel, { passive: false })
514
+ svg.addEventListener("mousedown", onDown)
515
+ window.addEventListener("mousemove", onMove)
516
+ window.addEventListener("mouseup", onUp)
517
+
518
+ return () => {
519
+ svg.removeEventListener("wheel", onWheel)
520
+ svg.removeEventListener("mousedown", onDown)
521
+ window.removeEventListener("mousemove", onMove)
522
+ window.removeEventListener("mouseup", onUp)
523
+ }
524
+ }
525
+
526
+ /**
527
+ * @param {SVGSVGElement} svg
528
+ * @param {number} clientX
529
+ * @param {number} clientY
530
+ */
531
+ function toSvgCoordinates(svg, clientX, clientY) {
532
+ const matrix = svg.getScreenCTM()
533
+ if (!matrix) return null
534
+
535
+ const point = svg.createSVGPoint()
536
+ point.x = clientX
537
+ point.y = clientY
538
+ return point.matrixTransform(matrix.inverse())
539
+ }
540
+
541
+ /**
542
+ * @param {number} value
543
+ * @param {number} min
544
+ * @param {number} max
545
+ */
546
+ function clamp(value, min, max) {
547
+ return Math.max(min, Math.min(max, value))
548
+ }
549
+
550
+ /**
551
+ * @param {HTMLDivElement} element
552
+ * @param {HTMLDivElement} handle
553
+ */
554
+ function enableDrag(element, handle) {
555
+ let dragging = false
556
+ let offsetX = 0
557
+ let offsetY = 0
558
+
559
+ /** @param {MouseEvent} event */
560
+ const onMove = (event) => {
561
+ if (!dragging) return
562
+ element.style.left = `${event.clientX - offsetX}px`
563
+ element.style.top = `${event.clientY - offsetY}px`
564
+ }
565
+
566
+ const onUp = () => {
567
+ dragging = false
568
+ document.removeEventListener("mousemove", onMove)
569
+ document.removeEventListener("mouseup", onUp)
570
+ }
571
+
572
+ handle.addEventListener("mousedown", /** @param {MouseEvent} event */ (event) => {
573
+ dragging = true
574
+ const rect = element.getBoundingClientRect()
575
+ offsetX = event.clientX - rect.left
576
+ offsetY = event.clientY - rect.top
577
+ document.addEventListener("mousemove", onMove)
578
+ document.addEventListener("mouseup", onUp)
579
+ })
580
+ }
@@ -0,0 +1,120 @@
1
+ import { GUI } from "dat.gui"
2
+ import { addRenderGraphGuiAddon } from "@examples/rendergraph_gui"
3
+ import {
4
+ MeshMaterial3D,
5
+ WebGLRenderer,
6
+ TextureLoader,
7
+ Camera,
8
+ WebGLRenderDevice,
9
+ PlaneMeshBuilder,
10
+ OrbitCameraControls,
11
+ MeshMaterialPlugin,
12
+ UVSphereMeshBuilder,
13
+ CanvasTarget,
14
+ BasicMaterial,
15
+ OrthographicProjection,
16
+ CameraPlugin
17
+ } from "chorama"
18
+
19
+ const canvas = document.createElement('canvas')
20
+ const renderTarget = new CanvasTarget(canvas)
21
+ const renderDevice = new WebGLRenderDevice(canvas,{
22
+ depth:true
23
+ })
24
+
25
+ const renderer = new WebGLRenderer({
26
+ plugins: [
27
+ new CameraPlugin(),
28
+ new MeshMaterialPlugin(),
29
+ ]
30
+ })
31
+ const projection = new OrthographicProjection()
32
+ const camera = new Camera(renderTarget)
33
+ const cameraControls = new OrbitCameraControls(camera, canvas)
34
+
35
+ // loaders
36
+ const textureLoader = new TextureLoader()
37
+ const texture = textureLoader.load({
38
+ paths: ["/images/uv.jpg"],
39
+ })
40
+
41
+ //create objects
42
+ const material = new BasicMaterial({
43
+ mainTexture: texture
44
+ })
45
+ const meshBuilder = new PlaneMeshBuilder()
46
+ meshBuilder.width = 10
47
+ meshBuilder.height = 10
48
+
49
+ const ground = new MeshMaterial3D(meshBuilder.build(), material)
50
+ const objects = createObjects()
51
+
52
+ ground.transform.orientation.rotateX(-Math.PI / 2)
53
+
54
+ //set up the camera
55
+ cameraControls.distance = 5
56
+ camera.projection = projection
57
+
58
+ document.body.append(canvas)
59
+ addEventListener("resize", updateView)
60
+ updateView()
61
+ requestAnimationFrame(update)
62
+
63
+ function createObjects() {
64
+ const results = []
65
+ const meshBuilder2 = new UVSphereMeshBuilder()
66
+ meshBuilder2.radius = 0.25
67
+ const sphereMesh = meshBuilder2.build()
68
+
69
+ for (let x = -5; x < 5; x++) {
70
+ for (let y = -5; y < 5; y++) {
71
+ const object = new MeshMaterial3D(sphereMesh, material)
72
+
73
+ object.transform.position.x = x
74
+ object.transform.position.y = 0.5
75
+ object.transform.position.z = y
76
+ results.push(object)
77
+ }
78
+ }
79
+
80
+ return results
81
+ }
82
+
83
+ function update() {
84
+ cameraControls.update()
85
+ renderer.render([ground, ...objects, camera], renderDevice)
86
+ requestAnimationFrame(update)
87
+ }
88
+
89
+ function updateView() {
90
+ canvas.style.width = innerWidth + "px"
91
+ canvas.style.height = innerHeight + "px"
92
+ canvas.width = innerWidth * devicePixelRatio
93
+ canvas.height = innerHeight * devicePixelRatio
94
+ }
95
+
96
+ const controls = new GUI()
97
+ const cameraFolder = controls.addFolder("Camera")
98
+
99
+ cameraFolder
100
+ .add(projection, 'left', -10, -1)
101
+ .name('Left')
102
+ cameraFolder
103
+ .add(projection, 'right', 1, 10)
104
+ .name('Right')
105
+ cameraFolder
106
+ .add(projection, 'top', 1, 10)
107
+ .name('Top')
108
+ cameraFolder
109
+ .add(projection, 'bottom', -10, -1)
110
+ .name('Bottom')
111
+ cameraFolder
112
+ .add(camera, 'near', 0.1, 10)
113
+ .name('Near Plane')
114
+ cameraFolder
115
+ .add(camera, 'far', 5, 20)
116
+ cameraFolder.open()
117
+ addRenderGraphGuiAddon({
118
+ gui: controls,
119
+ renderer
120
+ })