@vib3code/sdk 2.0.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 (258) hide show
  1. package/CHANGELOG.md +118 -0
  2. package/DOCS/BLUEPRINT_EXECUTION_PLAN_2026-01-07.md +34 -0
  3. package/DOCS/CI_TESTING.md +38 -0
  4. package/DOCS/CLI_ONBOARDING.md +75 -0
  5. package/DOCS/CONTROL_REFERENCE.md +64 -0
  6. package/DOCS/DEV_TRACK_ANALYSIS.md +77 -0
  7. package/DOCS/DEV_TRACK_PLAN_2026-01-07.md +42 -0
  8. package/DOCS/DEV_TRACK_SESSION_2026-01-31.md +220 -0
  9. package/DOCS/ENV_SETUP.md +189 -0
  10. package/DOCS/EXPORT_FORMATS.md +417 -0
  11. package/DOCS/GPU_DISPOSAL_GUIDE.md +21 -0
  12. package/DOCS/LICENSING_TIERS.md +275 -0
  13. package/DOCS/MASTER_PLAN_2026-01-31.md +570 -0
  14. package/DOCS/OBS_SETUP_GUIDE.md +98 -0
  15. package/DOCS/PROJECT_SETUP.md +66 -0
  16. package/DOCS/RENDERER_LIFECYCLE.md +40 -0
  17. package/DOCS/REPO_MANIFEST.md +121 -0
  18. package/DOCS/SESSION_014_PLAN.md +195 -0
  19. package/DOCS/SESSION_LOG_2026-01-07.md +56 -0
  20. package/DOCS/STRATEGIC_BLUEPRINT_2026-01-07.md +72 -0
  21. package/DOCS/SYSTEM_AUDIT_2026-01-30.md +738 -0
  22. package/DOCS/SYSTEM_INVENTORY.md +520 -0
  23. package/DOCS/TELEMETRY_EXPORTS.md +34 -0
  24. package/DOCS/WEBGPU_STATUS.md +38 -0
  25. package/DOCS/XR_BENCHMARKS.md +608 -0
  26. package/LICENSE +21 -0
  27. package/README.md +426 -0
  28. package/docs/.nojekyll +0 -0
  29. package/docs/01-dissolution_of_euclidean_hegemony.html +346 -0
  30. package/docs/02-hyperspatial_ego_death.html +346 -0
  31. package/docs/03-post_cartesian_sublime.html +346 -0
  32. package/docs/04-crystalline_void_meditation.html +346 -0
  33. package/docs/05-quantum_decoherence_ballet.html +346 -0
  34. package/docs/06-dissolution_of_euclidean_hegemony.html +346 -0
  35. package/docs/07-hyperspatial_ego_death.html +346 -0
  36. package/docs/08-post_cartesian_sublime.html +346 -0
  37. package/docs/09-crystalline_void_meditation.html +346 -0
  38. package/docs/10-quantum_decoherence_ballet.html +346 -0
  39. package/docs/11-dissolution_of_euclidean_hegemony.html +346 -0
  40. package/docs/12-hyperspatial_ego_death.html +346 -0
  41. package/docs/13-post_cartesian_sublime.html +346 -0
  42. package/docs/index.html +794 -0
  43. package/docs/test-hub.html +441 -0
  44. package/docs/url-state.js +102 -0
  45. package/docs/vib3-exports/01-quantum-quantum-tetrahedron-lattice.html +489 -0
  46. package/docs/vib3-exports/02-quantum-quantum-hypersphere-matrix.html +489 -0
  47. package/docs/vib3-exports/03-quantum-quantum-hypertetra-fractal.html +489 -0
  48. package/docs/vib3-exports/04-faceted-faceted-crystal-structure.html +407 -0
  49. package/docs/vib3-exports/05-faceted-faceted-klein-bottle.html +407 -0
  50. package/docs/vib3-exports/06-faceted-faceted-hypertetra-torus.html +407 -0
  51. package/docs/vib3-exports/07-holographic-holographic-wave-field.html +457 -0
  52. package/docs/vib3-exports/08-holographic-holographic-hypersphere-sphere.html +457 -0
  53. package/docs/vib3-exports/09-holographic-holographic-hypertetra-crystal.html +457 -0
  54. package/docs/vib3-exports/index.html +238 -0
  55. package/docs/webgpu-live.html +702 -0
  56. package/package.json +367 -0
  57. package/src/advanced/AIPresetGenerator.js +777 -0
  58. package/src/advanced/MIDIController.js +703 -0
  59. package/src/advanced/OffscreenWorker.js +1051 -0
  60. package/src/advanced/WebGPUCompute.js +1051 -0
  61. package/src/advanced/WebXRRenderer.js +680 -0
  62. package/src/agent/cli/AgentCLI.js +615 -0
  63. package/src/agent/cli/index.js +14 -0
  64. package/src/agent/index.js +73 -0
  65. package/src/agent/mcp/MCPServer.js +950 -0
  66. package/src/agent/mcp/index.js +9 -0
  67. package/src/agent/mcp/tools.js +548 -0
  68. package/src/agent/telemetry/EventStream.js +669 -0
  69. package/src/agent/telemetry/Instrumentation.js +618 -0
  70. package/src/agent/telemetry/TelemetryExporters.js +427 -0
  71. package/src/agent/telemetry/TelemetryService.js +464 -0
  72. package/src/agent/telemetry/index.js +52 -0
  73. package/src/benchmarks/BenchmarkRunner.js +381 -0
  74. package/src/benchmarks/MetricsCollector.js +299 -0
  75. package/src/benchmarks/index.js +9 -0
  76. package/src/benchmarks/scenes.js +259 -0
  77. package/src/cli/index.js +675 -0
  78. package/src/config/ApiConfig.js +88 -0
  79. package/src/core/CanvasManager.js +217 -0
  80. package/src/core/ErrorReporter.js +117 -0
  81. package/src/core/ParameterMapper.js +333 -0
  82. package/src/core/Parameters.js +396 -0
  83. package/src/core/RendererContracts.js +200 -0
  84. package/src/core/UnifiedResourceManager.js +370 -0
  85. package/src/core/VIB3Engine.js +636 -0
  86. package/src/core/renderers/FacetedRendererAdapter.js +32 -0
  87. package/src/core/renderers/HolographicRendererAdapter.js +29 -0
  88. package/src/core/renderers/QuantumRendererAdapter.js +29 -0
  89. package/src/core/renderers/RendererLifecycleManager.js +63 -0
  90. package/src/creative/ColorPresetsSystem.js +980 -0
  91. package/src/creative/ParameterTimeline.js +1061 -0
  92. package/src/creative/PostProcessingPipeline.js +1113 -0
  93. package/src/creative/TransitionAnimator.js +683 -0
  94. package/src/export/CSSExporter.js +226 -0
  95. package/src/export/CardGeneratorBase.js +279 -0
  96. package/src/export/ExportManager.js +580 -0
  97. package/src/export/FacetedCardGenerator.js +279 -0
  98. package/src/export/HolographicCardGenerator.js +543 -0
  99. package/src/export/LottieExporter.js +552 -0
  100. package/src/export/QuantumCardGenerator.js +315 -0
  101. package/src/export/SVGExporter.js +519 -0
  102. package/src/export/ShaderExporter.js +903 -0
  103. package/src/export/TradingCardGenerator.js +3055 -0
  104. package/src/export/TradingCardManager.js +181 -0
  105. package/src/export/VIB3PackageExporter.js +559 -0
  106. package/src/export/index.js +14 -0
  107. package/src/export/systems/TradingCardSystemFaceted.js +494 -0
  108. package/src/export/systems/TradingCardSystemHolographic.js +452 -0
  109. package/src/export/systems/TradingCardSystemQuantum.js +411 -0
  110. package/src/faceted/FacetedSystem.js +963 -0
  111. package/src/features/CollectionManager.js +433 -0
  112. package/src/gallery/CollectionManager.js +240 -0
  113. package/src/gallery/GallerySystem.js +485 -0
  114. package/src/geometry/GeometryFactory.js +314 -0
  115. package/src/geometry/GeometryLibrary.js +72 -0
  116. package/src/geometry/buffers/BufferBuilder.js +338 -0
  117. package/src/geometry/buffers/index.js +18 -0
  118. package/src/geometry/generators/Crystal.js +420 -0
  119. package/src/geometry/generators/Fractal.js +298 -0
  120. package/src/geometry/generators/KleinBottle.js +197 -0
  121. package/src/geometry/generators/Sphere.js +192 -0
  122. package/src/geometry/generators/Tesseract.js +160 -0
  123. package/src/geometry/generators/Tetrahedron.js +225 -0
  124. package/src/geometry/generators/Torus.js +304 -0
  125. package/src/geometry/generators/Wave.js +341 -0
  126. package/src/geometry/index.js +142 -0
  127. package/src/geometry/warp/HypersphereCore.js +211 -0
  128. package/src/geometry/warp/HypertetraCore.js +386 -0
  129. package/src/geometry/warp/index.js +57 -0
  130. package/src/holograms/HolographicVisualizer.js +1073 -0
  131. package/src/holograms/RealHolographicSystem.js +966 -0
  132. package/src/holograms/variantRegistry.js +69 -0
  133. package/src/integrations/FigmaPlugin.js +854 -0
  134. package/src/integrations/OBSMode.js +754 -0
  135. package/src/integrations/ThreeJsPackage.js +660 -0
  136. package/src/integrations/TouchDesignerExport.js +552 -0
  137. package/src/integrations/frameworks/Vib3React.js +591 -0
  138. package/src/integrations/frameworks/Vib3Svelte.js +654 -0
  139. package/src/integrations/frameworks/Vib3Vue.js +628 -0
  140. package/src/llm/LLMParameterInterface.js +240 -0
  141. package/src/llm/LLMParameterUI.js +577 -0
  142. package/src/math/Mat4x4.js +708 -0
  143. package/src/math/Projection.js +341 -0
  144. package/src/math/Rotor4D.js +637 -0
  145. package/src/math/Vec4.js +476 -0
  146. package/src/math/constants.js +164 -0
  147. package/src/math/index.js +68 -0
  148. package/src/math/projections.js +54 -0
  149. package/src/math/rotations.js +196 -0
  150. package/src/quantum/QuantumEngine.js +906 -0
  151. package/src/quantum/QuantumVisualizer.js +1103 -0
  152. package/src/reactivity/ReactivityConfig.js +499 -0
  153. package/src/reactivity/ReactivityManager.js +586 -0
  154. package/src/reactivity/SpatialInputSystem.js +1783 -0
  155. package/src/reactivity/index.js +93 -0
  156. package/src/render/CommandBuffer.js +465 -0
  157. package/src/render/MultiCanvasBridge.js +340 -0
  158. package/src/render/RenderCommand.js +514 -0
  159. package/src/render/RenderResourceRegistry.js +523 -0
  160. package/src/render/RenderState.js +552 -0
  161. package/src/render/RenderTarget.js +512 -0
  162. package/src/render/ShaderLoader.js +253 -0
  163. package/src/render/ShaderProgram.js +599 -0
  164. package/src/render/UnifiedRenderBridge.js +496 -0
  165. package/src/render/backends/WebGLBackend.js +1108 -0
  166. package/src/render/backends/WebGPUBackend.js +1409 -0
  167. package/src/render/commands/CommandBufferExecutor.js +607 -0
  168. package/src/render/commands/RenderCommandBuffer.js +661 -0
  169. package/src/render/commands/index.js +17 -0
  170. package/src/render/index.js +367 -0
  171. package/src/scene/Disposable.js +498 -0
  172. package/src/scene/MemoryPool.js +618 -0
  173. package/src/scene/Node4D.js +697 -0
  174. package/src/scene/ResourceManager.js +599 -0
  175. package/src/scene/Scene4D.js +540 -0
  176. package/src/scene/index.js +98 -0
  177. package/src/schemas/error.schema.json +84 -0
  178. package/src/schemas/extension.schema.json +88 -0
  179. package/src/schemas/index.js +214 -0
  180. package/src/schemas/parameters.schema.json +142 -0
  181. package/src/schemas/tool-pack.schema.json +44 -0
  182. package/src/schemas/tool-response.schema.json +127 -0
  183. package/src/shaders/common/fullscreen.vert.glsl +5 -0
  184. package/src/shaders/common/fullscreen.vert.wgsl +17 -0
  185. package/src/shaders/common/geometry24.glsl +65 -0
  186. package/src/shaders/common/geometry24.wgsl +54 -0
  187. package/src/shaders/common/rotation4d.glsl +85 -0
  188. package/src/shaders/common/rotation4d.wgsl +86 -0
  189. package/src/shaders/common/uniforms.glsl +44 -0
  190. package/src/shaders/common/uniforms.wgsl +48 -0
  191. package/src/shaders/faceted/faceted.frag.glsl +129 -0
  192. package/src/shaders/faceted/faceted.frag.wgsl +164 -0
  193. package/src/shaders/holographic/holographic.frag.glsl +406 -0
  194. package/src/shaders/holographic/holographic.frag.wgsl +185 -0
  195. package/src/shaders/quantum/quantum.frag.glsl +513 -0
  196. package/src/shaders/quantum/quantum.frag.wgsl +361 -0
  197. package/src/testing/ParallelTestFramework.js +519 -0
  198. package/src/testing/__snapshots__/exportFormats.test.js.snap +24 -0
  199. package/src/testing/exportFormats.test.js +8 -0
  200. package/src/testing/projections.test.js +14 -0
  201. package/src/testing/rotations.test.js +37 -0
  202. package/src/ui/InteractivityMenu.js +516 -0
  203. package/src/ui/StatusManager.js +96 -0
  204. package/src/ui/adaptive/renderers/webgpu/BufferLayout.ts +252 -0
  205. package/src/ui/adaptive/renderers/webgpu/PolytopeInstanceBuffer.ts +144 -0
  206. package/src/ui/adaptive/renderers/webgpu/TripleBufferedUniform.ts +170 -0
  207. package/src/ui/adaptive/renderers/webgpu/WebGPURenderer.ts +735 -0
  208. package/src/ui/adaptive/renderers/webgpu/index.ts +112 -0
  209. package/src/variations/VariationManager.js +431 -0
  210. package/src/viewer/AudioReactivity.js +505 -0
  211. package/src/viewer/CardBending.js +481 -0
  212. package/src/viewer/GalleryUI.js +832 -0
  213. package/src/viewer/ReactivityManager.js +590 -0
  214. package/src/viewer/TradingCardExporter.js +600 -0
  215. package/src/viewer/ViewerPortal.js +374 -0
  216. package/src/viewer/index.js +12 -0
  217. package/src/wasm/WasmLoader.js +296 -0
  218. package/src/wasm/index.js +132 -0
  219. package/tools/agentic/mcpTools.js +88 -0
  220. package/tools/cli/agent-cli.js +92 -0
  221. package/tools/export/formats.js +24 -0
  222. package/tools/math/rotation-baseline.mjs +64 -0
  223. package/tools/shader-sync-verify.js +937 -0
  224. package/tools/telemetry/manifestPipeline.js +141 -0
  225. package/tools/telemetry/telemetryEvents.js +35 -0
  226. package/types/adaptive-sdk.d.ts +185 -0
  227. package/types/advanced/AIPresetGenerator.d.ts +81 -0
  228. package/types/advanced/MIDIController.d.ts +100 -0
  229. package/types/advanced/OffscreenWorker.d.ts +82 -0
  230. package/types/advanced/WebGPUCompute.d.ts +52 -0
  231. package/types/advanced/WebXRRenderer.d.ts +77 -0
  232. package/types/advanced/index.d.ts +46 -0
  233. package/types/core/ErrorReporter.d.ts +50 -0
  234. package/types/core/VIB3Engine.d.ts +204 -0
  235. package/types/creative/ColorPresetsSystem.d.ts +91 -0
  236. package/types/creative/ParameterTimeline.d.ts +74 -0
  237. package/types/creative/PostProcessingPipeline.d.ts +109 -0
  238. package/types/creative/TransitionAnimator.d.ts +71 -0
  239. package/types/creative/index.d.ts +35 -0
  240. package/types/integrations/FigmaPlugin.d.ts +46 -0
  241. package/types/integrations/OBSMode.d.ts +74 -0
  242. package/types/integrations/ThreeJsPackage.d.ts +62 -0
  243. package/types/integrations/TouchDesignerExport.d.ts +36 -0
  244. package/types/integrations/Vib3React.d.ts +74 -0
  245. package/types/integrations/Vib3Svelte.d.ts +63 -0
  246. package/types/integrations/Vib3Vue.d.ts +55 -0
  247. package/types/integrations/index.d.ts +52 -0
  248. package/types/reactivity/SpatialInputSystem.d.ts +173 -0
  249. package/types/reactivity/index.d.ts +394 -0
  250. package/types/render/CommandBuffer.d.ts +169 -0
  251. package/types/render/RenderCommand.d.ts +312 -0
  252. package/types/render/RenderState.d.ts +279 -0
  253. package/types/render/RenderTarget.d.ts +254 -0
  254. package/types/render/ShaderProgram.d.ts +277 -0
  255. package/types/render/UnifiedRenderBridge.d.ts +143 -0
  256. package/types/render/WebGLBackend.d.ts +168 -0
  257. package/types/render/WebGPUBackend.d.ts +186 -0
  258. package/types/render/index.d.ts +141 -0
@@ -0,0 +1,252 @@
1
+ export type LayoutMode = 'std140' | 'std430';
2
+
3
+ export type LayoutFieldType = 'f32' | 'vec2' | 'vec3' | 'vec4' | 'mat4x4';
4
+
5
+ export interface LayoutFieldDefinition {
6
+ readonly name: string;
7
+ readonly type: LayoutFieldType;
8
+ /** Number of elements for arrays. Defaults to 1. */
9
+ readonly count?: number;
10
+ }
11
+
12
+ export interface LayoutField {
13
+ readonly name: string;
14
+ readonly type: LayoutFieldType;
15
+ readonly count: number;
16
+ /** Byte offset from the start of the struct. */
17
+ readonly offset: number;
18
+ /** Total byte span occupied by the field including padding. */
19
+ readonly size: number;
20
+ /** Byte distance between array elements (equals size when count === 1). */
21
+ readonly stride: number;
22
+ /** Number of numeric components per element (e.g. vec3 → 3). */
23
+ readonly componentCount: number;
24
+ }
25
+
26
+ export interface BufferLayout {
27
+ readonly mode: LayoutMode;
28
+ readonly byteSize: number;
29
+ readonly fields: Readonly<Record<string, LayoutField>>;
30
+ }
31
+
32
+ export interface WriteFieldOptions {
33
+ /** Index of the array element to write (defaults to 0). */
34
+ readonly elementIndex?: number;
35
+ /** Number of array elements that will be written from the provided values. */
36
+ readonly elementCount?: number;
37
+ }
38
+
39
+ const COMPONENT_COUNT: Record<LayoutFieldType, number> = {
40
+ f32: 1,
41
+ vec2: 2,
42
+ vec3: 3,
43
+ vec4: 4,
44
+ mat4x4: 16,
45
+ };
46
+
47
+ const NATURAL_SIZE: Record<LayoutFieldType, number> = {
48
+ f32: 4,
49
+ vec2: 8,
50
+ vec3: 12,
51
+ vec4: 16,
52
+ mat4x4: 64,
53
+ };
54
+
55
+ const BASE_ALIGNMENT: Record<LayoutFieldType, number> = {
56
+ f32: 4,
57
+ vec2: 8,
58
+ vec3: 16,
59
+ vec4: 16,
60
+ mat4x4: 16,
61
+ };
62
+
63
+ function align(value: number, alignment: number): number {
64
+ return Math.ceil(value / alignment) * alignment;
65
+ }
66
+
67
+ function resolveCount(def: LayoutFieldDefinition): number {
68
+ const count = Math.floor(def.count ?? 1);
69
+ if (count <= 0) {
70
+ throw new Error(`Field "${def.name}" requires a positive element count.`);
71
+ }
72
+ return count;
73
+ }
74
+
75
+ function computeArrayAlignment(mode: LayoutMode, baseAlignment: number): number {
76
+ if (mode === 'std140') {
77
+ return Math.max(baseAlignment, 16);
78
+ }
79
+ return baseAlignment;
80
+ }
81
+
82
+ function computeStride(mode: LayoutMode, type: LayoutFieldType, arrayAlignment: number): number {
83
+ const natural = NATURAL_SIZE[type];
84
+ if (mode === 'std140') {
85
+ return align(natural, arrayAlignment);
86
+ }
87
+ return align(natural, arrayAlignment);
88
+ }
89
+
90
+ function computeElementSize(type: LayoutFieldType): number {
91
+ const baseAlignment = BASE_ALIGNMENT[type];
92
+ return align(NATURAL_SIZE[type], baseAlignment);
93
+ }
94
+
95
+ function createLayout(mode: LayoutMode, definitions: readonly LayoutFieldDefinition[]): BufferLayout {
96
+ let size = 0;
97
+ const fields: Record<string, LayoutField> = {};
98
+
99
+ for (const def of definitions) {
100
+ if (!def?.name) {
101
+ throw new Error('Layout fields require a name.');
102
+ }
103
+ if (!COMPONENT_COUNT[def.type]) {
104
+ throw new Error(`Unsupported field type "${String(def.type)}" for ${def.name}.`);
105
+ }
106
+
107
+ const baseAlignment = BASE_ALIGNMENT[def.type];
108
+ const count = resolveCount(def);
109
+ const componentCount = COMPONENT_COUNT[def.type];
110
+
111
+ if (count > 1) {
112
+ const arrayAlignment = computeArrayAlignment(mode, baseAlignment);
113
+ size = align(size, arrayAlignment);
114
+ const stride = computeStride(mode, def.type, arrayAlignment);
115
+ const fieldSize = stride * count;
116
+ fields[def.name] = {
117
+ name: def.name,
118
+ type: def.type,
119
+ count,
120
+ offset: size,
121
+ size: fieldSize,
122
+ stride,
123
+ componentCount,
124
+ };
125
+ size += fieldSize;
126
+ } else {
127
+ size = align(size, baseAlignment);
128
+ const elementSize = computeElementSize(def.type);
129
+ fields[def.name] = {
130
+ name: def.name,
131
+ type: def.type,
132
+ count,
133
+ offset: size,
134
+ size: elementSize,
135
+ stride: elementSize,
136
+ componentCount,
137
+ };
138
+ size += elementSize;
139
+ }
140
+ }
141
+
142
+ const byteSize = align(size, 16);
143
+ return Object.freeze({
144
+ mode,
145
+ byteSize,
146
+ fields: Object.freeze(fields),
147
+ });
148
+ }
149
+
150
+ export function createStd140Layout(definitions: readonly LayoutFieldDefinition[]): BufferLayout {
151
+ return createLayout('std140', definitions);
152
+ }
153
+
154
+ export function createStd430Layout(definitions: readonly LayoutFieldDefinition[]): BufferLayout {
155
+ return createLayout('std430', definitions);
156
+ }
157
+
158
+ export function createFloat32ArrayForLayout(layout: BufferLayout): Float32Array {
159
+ return new Float32Array(layout.byteSize / Float32Array.BYTES_PER_ELEMENT);
160
+ }
161
+
162
+ function ensureTargetSize(layout: BufferLayout, target: Float32Array): void {
163
+ if (target.length * Float32Array.BYTES_PER_ELEMENT < layout.byteSize) {
164
+ throw new Error('Target buffer is too small for the provided layout.');
165
+ }
166
+ }
167
+
168
+ export function writeField(
169
+ layout: BufferLayout,
170
+ target: Float32Array,
171
+ fieldName: string,
172
+ values: ArrayLike<number>,
173
+ options: WriteFieldOptions = {}
174
+ ): void {
175
+ ensureTargetSize(layout, target);
176
+ const field = layout.fields[fieldName];
177
+ if (!field) {
178
+ throw new Error(`Unknown field "${fieldName}" in layout.`);
179
+ }
180
+
181
+ const elementCount = Math.min(field.count, Math.floor(options.elementCount ?? field.count));
182
+ if (elementCount <= 0) {
183
+ return;
184
+ }
185
+ const elementIndex = Math.min(field.count - 1, Math.max(0, Math.floor(options.elementIndex ?? 0)));
186
+ const components = field.componentCount;
187
+ const strideFloats = field.stride / Float32Array.BYTES_PER_ELEMENT;
188
+ const offsetFloats = field.offset / Float32Array.BYTES_PER_ELEMENT + elementIndex * strideFloats;
189
+
190
+ const requiredValues = components * elementCount;
191
+ if (values.length < requiredValues) {
192
+ throw new Error(`Field "${fieldName}" requires at least ${requiredValues} components.`);
193
+ }
194
+
195
+ let cursor = 0;
196
+ for (let element = 0; element < elementCount; element += 1) {
197
+ const base = offsetFloats + element * strideFloats;
198
+ for (let i = 0; i < components; i += 1) {
199
+ target[base + i] = values[cursor + i];
200
+ }
201
+ for (let i = components; i < strideFloats; i += 1) {
202
+ target[base + i] = 0;
203
+ }
204
+ cursor += components;
205
+ }
206
+
207
+ const remaining = field.count - (elementIndex + elementCount);
208
+ if (remaining > 0) {
209
+ const base = offsetFloats + elementCount * strideFloats;
210
+ const total = remaining * strideFloats;
211
+ for (let i = 0; i < total; i += 1) {
212
+ target[base + i] = 0;
213
+ }
214
+ }
215
+ }
216
+
217
+ export function readField(
218
+ layout: BufferLayout,
219
+ source: Float32Array,
220
+ fieldName: string,
221
+ options: WriteFieldOptions = {}
222
+ ): Float32Array {
223
+ ensureTargetSize(layout, source);
224
+ const field = layout.fields[fieldName];
225
+ if (!field) {
226
+ throw new Error(`Unknown field "${fieldName}" in layout.`);
227
+ }
228
+ const elementIndex = Math.min(field.count - 1, Math.max(0, Math.floor(options.elementIndex ?? 0)));
229
+ const elementCount = Math.min(field.count - elementIndex, Math.floor(options.elementCount ?? 1));
230
+ const strideFloats = field.stride / Float32Array.BYTES_PER_ELEMENT;
231
+ const offsetFloats = field.offset / Float32Array.BYTES_PER_ELEMENT + elementIndex * strideFloats;
232
+ const result = new Float32Array(field.componentCount * elementCount);
233
+ for (let element = 0; element < elementCount; element += 1) {
234
+ const base = offsetFloats + element * strideFloats;
235
+ for (let i = 0; i < field.componentCount; i += 1) {
236
+ result[element * field.componentCount + i] = source[base + i];
237
+ }
238
+ }
239
+ return result;
240
+ }
241
+
242
+ export const GlassUniformLayout = createStd140Layout([
243
+ { name: 'leftViewProj', type: 'mat4x4' },
244
+ { name: 'rightViewProj', type: 'mat4x4' },
245
+ { name: 'headMatrix', type: 'mat4x4' },
246
+ { name: 'rotor4d', type: 'vec4' },
247
+ { name: 'euler', type: 'vec4' },
248
+ { name: 'metrics', type: 'vec4' },
249
+ { name: 'audio', type: 'vec4' },
250
+ { name: 'localization', type: 'vec4' },
251
+ { name: 'visual', type: 'vec4' },
252
+ ]);
@@ -0,0 +1,144 @@
1
+ import { createStd430Layout, createFloat32ArrayForLayout, writeField, readField, type BufferLayout } from './BufferLayout.ts';
2
+ import type { GPUBufferLike, GPUDeviceLike, GPUQueueLike } from './TripleBufferedUniform.ts';
3
+
4
+ const GPU_BUFFER_USAGE_STORAGE = 0x20;
5
+ const GPU_BUFFER_USAGE_COPY_DST = 0x8;
6
+
7
+ export interface PolytopeInstance {
8
+ readonly modelMatrix: ArrayLike<number>;
9
+ readonly rotor: ArrayLike<number>;
10
+ readonly color: ArrayLike<number>;
11
+ readonly misc?: {
12
+ readonly scale?: number;
13
+ readonly audioEnergy?: number;
14
+ readonly glitch?: number;
15
+ readonly id?: number;
16
+ };
17
+ }
18
+
19
+ export interface PolytopeInstanceBufferOptions {
20
+ readonly device: GPUDeviceLike;
21
+ readonly maxInstances: number;
22
+ readonly label?: string;
23
+ readonly usage?: number;
24
+ }
25
+
26
+ export interface InstanceWriteOptions {
27
+ readonly index: number;
28
+ }
29
+
30
+ export class PolytopeInstanceBuffer {
31
+ readonly device: GPUDeviceLike;
32
+ readonly layout: BufferLayout;
33
+ readonly maxInstances: number;
34
+ readonly label: string;
35
+ readonly buffer: GPUBufferLike;
36
+ readonly data: Float32Array;
37
+
38
+ private instanceCount = 0;
39
+
40
+ constructor(options: PolytopeInstanceBufferOptions) {
41
+ if (!options?.device) {
42
+ throw new Error('PolytopeInstanceBuffer requires a WebGPU-compatible device.');
43
+ }
44
+ if (!Number.isFinite(options.maxInstances) || options.maxInstances <= 0) {
45
+ throw new Error('PolytopeInstanceBuffer maxInstances must be a positive integer.');
46
+ }
47
+
48
+ this.device = options.device;
49
+ this.maxInstances = Math.floor(options.maxInstances);
50
+ this.label = options.label ?? 'PolytopeInstanceBuffer';
51
+
52
+ this.layout = createStd430Layout([
53
+ { name: 'modelMatrices', type: 'mat4x4', count: this.maxInstances },
54
+ { name: 'rotors', type: 'vec4', count: this.maxInstances },
55
+ { name: 'colors', type: 'vec4', count: this.maxInstances },
56
+ { name: 'misc', type: 'vec4', count: this.maxInstances },
57
+ ]);
58
+
59
+ this.data = createFloat32ArrayForLayout(this.layout);
60
+
61
+ const usage = options.usage ?? (GPU_BUFFER_USAGE_STORAGE | GPU_BUFFER_USAGE_COPY_DST);
62
+ this.buffer = this.device.createBuffer({
63
+ size: this.layout.byteSize,
64
+ usage,
65
+ label: `${this.label}-storage`,
66
+ });
67
+ }
68
+
69
+ get count(): number {
70
+ return this.instanceCount;
71
+ }
72
+
73
+ reset(): void {
74
+ this.instanceCount = 0;
75
+ this.data.fill(0);
76
+ }
77
+
78
+ writeInstance(instance: PolytopeInstance, options: InstanceWriteOptions): void {
79
+ const index = Math.floor(options.index);
80
+ if (index < 0 || index >= this.maxInstances) {
81
+ throw new Error(`Instance index ${index} is out of bounds for maxInstances=${this.maxInstances}.`);
82
+ }
83
+
84
+ writeField(this.layout, this.data, 'modelMatrices', ensureLength(instance.modelMatrix, 16), {
85
+ elementIndex: index,
86
+ elementCount: 1,
87
+ });
88
+
89
+ const rotor = ensureLength(instance.rotor, 4);
90
+ writeField(this.layout, this.data, 'rotors', rotor, { elementIndex: index, elementCount: 1 });
91
+
92
+ const color = ensureLength(instance.color, 4);
93
+ writeField(this.layout, this.data, 'colors', color, { elementIndex: index, elementCount: 1 });
94
+
95
+ const misc = buildMisc(instance.misc);
96
+ writeField(this.layout, this.data, 'misc', misc, { elementIndex: index, elementCount: 1 });
97
+
98
+ this.instanceCount = Math.max(this.instanceCount, index + 1);
99
+ }
100
+
101
+ readInstance(index: number): { modelMatrix: Float32Array; rotor: Float32Array; color: Float32Array; misc: Float32Array } {
102
+ if (index < 0 || index >= this.maxInstances) {
103
+ throw new Error(`Instance index ${index} is out of bounds for maxInstances=${this.maxInstances}.`);
104
+ }
105
+ return {
106
+ modelMatrix: readField(this.layout, this.data, 'modelMatrices', { elementIndex: index }),
107
+ rotor: readField(this.layout, this.data, 'rotors', { elementIndex: index }),
108
+ color: readField(this.layout, this.data, 'colors', { elementIndex: index }),
109
+ misc: readField(this.layout, this.data, 'misc', { elementIndex: index }),
110
+ };
111
+ }
112
+
113
+ upload(queue: GPUQueueLike = this.device.queue): void {
114
+ if (!queue || typeof queue.writeBuffer !== 'function') {
115
+ throw new Error('PolytopeInstanceBuffer.upload requires a valid GPU queue.');
116
+ }
117
+ queue.writeBuffer(this.buffer, 0, this.data);
118
+ }
119
+
120
+ bindGroupEntry(binding = 0): { binding: number; resource: { buffer: GPUBufferLike } } {
121
+ return { binding, resource: { buffer: this.buffer } };
122
+ }
123
+ }
124
+
125
+ function ensureLength(values: ArrayLike<number>, expected: number): Float32Array {
126
+ const result = new Float32Array(expected);
127
+ const length = Math.min(values.length, expected);
128
+ for (let i = 0; i < length; i += 1) {
129
+ result[i] = Number(values[i]) || 0;
130
+ }
131
+ return result;
132
+ }
133
+
134
+ function buildMisc(misc: PolytopeInstance['misc']): Float32Array {
135
+ const result = new Float32Array(4);
136
+ if (!misc) {
137
+ return result;
138
+ }
139
+ result[0] = Number(misc.scale) || 0;
140
+ result[1] = Number(misc.audioEnergy) || 0;
141
+ result[2] = Number(misc.glitch) || 0;
142
+ result[3] = Number(misc.id) || 0;
143
+ return result;
144
+ }
@@ -0,0 +1,170 @@
1
+ /**
2
+ * Triple-buffered uniform buffer for WebGPU
3
+ * Provides smooth animation by maintaining 3 buffers for CPU write, GPU read, and swap
4
+ */
5
+
6
+ // Type definitions for WebGPU-like interfaces (compatible with actual WebGPU or mocks)
7
+ export interface GPUBufferLike {
8
+ readonly size: number;
9
+ readonly usage: number;
10
+ readonly label?: string;
11
+ mapAsync?: (mode: number) => Promise<void>;
12
+ getMappedRange?: () => ArrayBuffer;
13
+ unmap?: () => void;
14
+ destroy?: () => void;
15
+ }
16
+
17
+ export interface GPUDeviceLike {
18
+ readonly queue: GPUQueueLike;
19
+ createBuffer(descriptor: {
20
+ size: number;
21
+ usage: number;
22
+ label?: string;
23
+ mappedAtCreation?: boolean;
24
+ }): GPUBufferLike;
25
+ createBindGroup?(descriptor: unknown): unknown;
26
+ createBindGroupLayout?(descriptor: unknown): unknown;
27
+ }
28
+
29
+ export interface GPUQueueLike {
30
+ writeBuffer(buffer: GPUBufferLike, offset: number, data: ArrayBufferView | ArrayBuffer): void;
31
+ submit?(commandBuffers: unknown[]): void;
32
+ }
33
+
34
+ export interface TripleBufferedUniformOptions {
35
+ readonly device: GPUDeviceLike;
36
+ readonly byteSize: number;
37
+ readonly label?: string;
38
+ }
39
+
40
+ const GPU_BUFFER_USAGE_UNIFORM = 0x40;
41
+ const GPU_BUFFER_USAGE_COPY_DST = 0x8;
42
+
43
+ /**
44
+ * Triple-buffered uniform buffer for smooth animations
45
+ * - Buffer 0: Currently being written by CPU
46
+ * - Buffer 1: Staged for next GPU read
47
+ * - Buffer 2: Currently being read by GPU
48
+ */
49
+ export class TripleBufferedUniform {
50
+ readonly device: GPUDeviceLike;
51
+ readonly byteSize: number;
52
+ readonly label: string;
53
+ readonly buffers: [GPUBufferLike, GPUBufferLike, GPUBufferLike];
54
+ readonly data: Float32Array;
55
+
56
+ private writeIndex = 0;
57
+ private readIndex = 2;
58
+
59
+ constructor(options: TripleBufferedUniformOptions) {
60
+ if (!options?.device) {
61
+ throw new Error('TripleBufferedUniform requires a WebGPU-compatible device.');
62
+ }
63
+ if (!Number.isFinite(options.byteSize) || options.byteSize <= 0) {
64
+ throw new Error('TripleBufferedUniform byteSize must be a positive number.');
65
+ }
66
+
67
+ this.device = options.device;
68
+ // Align to 256 bytes (WebGPU requirement for uniform buffers)
69
+ this.byteSize = Math.ceil(options.byteSize / 256) * 256;
70
+ this.label = options.label ?? 'TripleBufferedUniform';
71
+
72
+ // Create CPU-side data buffer
73
+ this.data = new Float32Array(this.byteSize / Float32Array.BYTES_PER_ELEMENT);
74
+
75
+ // Create three GPU buffers
76
+ this.buffers = [
77
+ this.createBuffer(0),
78
+ this.createBuffer(1),
79
+ this.createBuffer(2)
80
+ ];
81
+ }
82
+
83
+ private createBuffer(index: number): GPUBufferLike {
84
+ return this.device.createBuffer({
85
+ size: this.byteSize,
86
+ usage: GPU_BUFFER_USAGE_UNIFORM | GPU_BUFFER_USAGE_COPY_DST,
87
+ label: `${this.label}-${index}`
88
+ });
89
+ }
90
+
91
+ /**
92
+ * Get the buffer currently available for GPU reading
93
+ */
94
+ get currentBuffer(): GPUBufferLike {
95
+ return this.buffers[this.readIndex];
96
+ }
97
+
98
+ /**
99
+ * Write data to the current write buffer and rotate
100
+ * @param queue - GPU queue for buffer upload
101
+ */
102
+ upload(queue: GPUQueueLike = this.device.queue): void {
103
+ if (!queue || typeof queue.writeBuffer !== 'function') {
104
+ throw new Error('TripleBufferedUniform.upload requires a valid GPU queue.');
105
+ }
106
+
107
+ // Write to current write buffer
108
+ queue.writeBuffer(this.buffers[this.writeIndex], 0, this.data);
109
+
110
+ // Rotate indices
111
+ const oldWrite = this.writeIndex;
112
+ this.writeIndex = (this.writeIndex + 1) % 3;
113
+ this.readIndex = oldWrite;
114
+ }
115
+
116
+ /**
117
+ * Write a float value at the specified offset
118
+ */
119
+ writeFloat(offset: number, value: number): void {
120
+ const index = Math.floor(offset / Float32Array.BYTES_PER_ELEMENT);
121
+ if (index >= 0 && index < this.data.length) {
122
+ this.data[index] = value;
123
+ }
124
+ }
125
+
126
+ /**
127
+ * Write a vec4 at the specified offset
128
+ */
129
+ writeVec4(offset: number, values: ArrayLike<number>): void {
130
+ const startIndex = Math.floor(offset / Float32Array.BYTES_PER_ELEMENT);
131
+ for (let i = 0; i < 4 && i < values.length; i++) {
132
+ if (startIndex + i < this.data.length) {
133
+ this.data[startIndex + i] = values[i];
134
+ }
135
+ }
136
+ }
137
+
138
+ /**
139
+ * Write a mat4x4 at the specified offset
140
+ */
141
+ writeMat4(offset: number, values: ArrayLike<number>): void {
142
+ const startIndex = Math.floor(offset / Float32Array.BYTES_PER_ELEMENT);
143
+ for (let i = 0; i < 16 && i < values.length; i++) {
144
+ if (startIndex + i < this.data.length) {
145
+ this.data[startIndex + i] = values[i];
146
+ }
147
+ }
148
+ }
149
+
150
+ /**
151
+ * Create a bind group entry for this buffer
152
+ */
153
+ bindGroupEntry(binding = 0): { binding: number; resource: { buffer: GPUBufferLike } } {
154
+ return {
155
+ binding,
156
+ resource: { buffer: this.currentBuffer }
157
+ };
158
+ }
159
+
160
+ /**
161
+ * Destroy all buffers
162
+ */
163
+ destroy(): void {
164
+ for (const buffer of this.buffers) {
165
+ if (buffer.destroy) {
166
+ buffer.destroy();
167
+ }
168
+ }
169
+ }
170
+ }