@octanejs/drei 0.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 (152) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +11 -0
  3. package/UPSTREAM.md +92 -0
  4. package/package.json +96 -0
  5. package/src/config.ts +14 -0
  6. package/src/core/AccumulativeShadows.three.tsrx +313 -0
  7. package/src/core/AdaptivePerformance.three.tsrx +49 -0
  8. package/src/core/AsciiRenderer.three.tsrx +61 -0
  9. package/src/core/Bounds.three.tsrx +323 -0
  10. package/src/core/Bvh.three.tsrx +116 -0
  11. package/src/core/CameraControls.three.tsrx +181 -0
  12. package/src/core/CameraShake.three.tsrx +104 -0
  13. package/src/core/Caustics.three.tsrx +107 -0
  14. package/src/core/Center.three.tsrx +145 -0
  15. package/src/core/Clone.three.tsrx +167 -0
  16. package/src/core/Cloud.three.tsrx +314 -0
  17. package/src/core/ComputedAttribute.three.tsrx +27 -0
  18. package/src/core/ContactShadows.three.tsrx +106 -0
  19. package/src/core/CubeTexture.three.tsrx +32 -0
  20. package/src/core/CurveModifier.three.tsrx +37 -0
  21. package/src/core/Decal.three.tsrx +132 -0
  22. package/src/core/Detailed.three.tsrx +28 -0
  23. package/src/core/DetectGPU.three.tsrx +22 -0
  24. package/src/core/Effects.three.tsrx +123 -0
  25. package/src/core/Environment.three.tsrx +220 -0
  26. package/src/core/Example.three.tsrx +56 -0
  27. package/src/core/Fbo.three.tsrx +70 -0
  28. package/src/core/Fbx.three.tsrx +25 -0
  29. package/src/core/Fisheye.three.tsrx +82 -0
  30. package/src/core/GizmoHelper.three.tsrx +115 -0
  31. package/src/core/GizmoViewcube.three.tsrx +82 -0
  32. package/src/core/GizmoViewport.three.tsrx +122 -0
  33. package/src/core/Gltf.three.tsrx +62 -0
  34. package/src/core/GradientTexture.three.tsrx +66 -0
  35. package/src/core/Grid.three.tsrx +170 -0
  36. package/src/core/Helper.three.tsrx +56 -0
  37. package/src/core/Hud.three.tsrx +52 -0
  38. package/src/core/Image.three.tsrx +146 -0
  39. package/src/core/Instances.three.tsrx +332 -0
  40. package/src/core/Ktx2.three.tsrx +59 -0
  41. package/src/core/Lightformer.three.tsrx +75 -0
  42. package/src/core/MarchingCubes.three.tsrx +125 -0
  43. package/src/core/MeshPortalMaterial.three.tsrx +103 -0
  44. package/src/core/MeshReflectorMaterial.three.tsrx +67 -0
  45. package/src/core/MeshRefractionMaterial.three.tsrx +70 -0
  46. package/src/core/MeshTransmissionMaterial.three.tsrx +71 -0
  47. package/src/core/MotionPathControls.three.tsrx +139 -0
  48. package/src/core/MultiMaterial.three.tsrx +32 -0
  49. package/src/core/Outlines.three.tsrx +162 -0
  50. package/src/core/PerformanceMonitor.three.tsrx +144 -0
  51. package/src/core/PointerLockControls.three.tsrx +84 -0
  52. package/src/core/Points.three.tsrx +242 -0
  53. package/src/core/PositionalAudio.three.tsrx +76 -0
  54. package/src/core/Preload.three.tsrx +40 -0
  55. package/src/core/Progress.three.tsrx +47 -0
  56. package/src/core/RenderCubeTexture.three.tsrx +111 -0
  57. package/src/core/RenderTexture.three.tsrx +102 -0
  58. package/src/core/Resize.three.tsrx +59 -0
  59. package/src/core/RoundedBox.three.tsrx +106 -0
  60. package/src/core/Sampler.three.tsrx +116 -0
  61. package/src/core/ScreenSizer.three.tsrx +43 -0
  62. package/src/core/ScreenSpace.three.tsrx +21 -0
  63. package/src/core/Segments.three.tsrx +119 -0
  64. package/src/core/Sky.three.tsrx +63 -0
  65. package/src/core/Sparkles.three.tsrx +145 -0
  66. package/src/core/Splat.three.tsrx +52 -0
  67. package/src/core/SpotLight.three.tsrx +350 -0
  68. package/src/core/SpriteAnimator.three.tsrx +338 -0
  69. package/src/core/Stage.three.tsrx +112 -0
  70. package/src/core/Stars.three.tsrx +103 -0
  71. package/src/core/Stats.three.tsrx +35 -0
  72. package/src/core/StatsGl.three.tsrx +60 -0
  73. package/src/core/Svg.three.tsrx +82 -0
  74. package/src/core/Text.three.tsrx +96 -0
  75. package/src/core/Text3D.three.tsrx +90 -0
  76. package/src/core/Texture.three.tsrx +72 -0
  77. package/src/core/Trail.three.tsrx +167 -0
  78. package/src/core/TrailTexture.three.tsrx +171 -0
  79. package/src/core/TransformControls.three.tsrx +155 -0
  80. package/src/core/VideoTexture.three.tsrx +116 -0
  81. package/src/core/Wireframe.three.tsrx +92 -0
  82. package/src/core/basic-controls.three.tsrx +353 -0
  83. package/src/core/calculateScaleFactor.ts +42 -0
  84. package/src/core/cameras.three.tsrx +230 -0
  85. package/src/core/catalog-resource.ts +12 -0
  86. package/src/core/catalog-textures.three.tsrx +105 -0
  87. package/src/core/compiler-slot.ts +9 -0
  88. package/src/core/effects-utils.ts +8 -0
  89. package/src/core/environment-loader.ts +87 -0
  90. package/src/core/font-resource.ts +31 -0
  91. package/src/core/gltf-loader.ts +33 -0
  92. package/src/core/gradient-types.ts +4 -0
  93. package/src/core/index.ts +295 -0
  94. package/src/core/lines.three.tsrx +329 -0
  95. package/src/core/meshBounds.ts +26 -0
  96. package/src/core/motion-materials.three.tsrx +93 -0
  97. package/src/core/octane-children.ts +18 -0
  98. package/src/core/octane-element.ts +16 -0
  99. package/src/core/resource.ts +29 -0
  100. package/src/core/scene-motion.three.tsrx +183 -0
  101. package/src/core/shaderMaterial.ts +50 -0
  102. package/src/core/shadows.three.tsrx +164 -0
  103. package/src/core/shapes.three.tsrx +170 -0
  104. package/src/core/simple-materials.three.tsrx +124 -0
  105. package/src/core/softShadows.three.tsrx +151 -0
  106. package/src/core/splat-resource.ts +598 -0
  107. package/src/core/useAnimations.three.tsrx +57 -0
  108. package/src/core/useAspect.three.tsrx +10 -0
  109. package/src/core/useBoxProjectedEnv.three.tsrx +113 -0
  110. package/src/core/useCamera.three.tsrx +26 -0
  111. package/src/core/useContextBridge.three.tsrx +25 -0
  112. package/src/core/useDepthBuffer.three.tsrx +35 -0
  113. package/src/core/useEnvironment.three.tsrx +81 -0
  114. package/src/core/useFont.three.tsrx +12 -0
  115. package/src/core/useIntersect.three.tsrx +38 -0
  116. package/src/core/useSpriteLoader.three.tsrx +249 -0
  117. package/src/core/video-texture-resource.ts +14 -0
  118. package/src/index.ts +2 -0
  119. package/src/materials/BlurPass.ts +94 -0
  120. package/src/materials/CausticsMaterials.ts +197 -0
  121. package/src/materials/ConvolutionMaterial.ts +65 -0
  122. package/src/materials/MeshReflectorMaterial.ts +180 -0
  123. package/src/materials/MeshRefractionMaterial.ts +131 -0
  124. package/src/materials/MeshTransmissionMaterial.ts +182 -0
  125. package/src/materials/PortalMaterial.ts +38 -0
  126. package/src/materials/WireframeMaterial.ts +241 -0
  127. package/src/materials/makeSDFGenerator.ts +155 -0
  128. package/src/troika-three-text.d.ts +19 -0
  129. package/src/web/CycleRaycast.three.tsrx +81 -0
  130. package/src/web/DragControls.three.tsrx +82 -0
  131. package/src/web/FaceControls.three.tsrx +178 -0
  132. package/src/web/FaceLandmarker.three.tsrx +48 -0
  133. package/src/web/Facemesh.three.tsrx +464 -0
  134. package/src/web/Html.three.tsrx +390 -0
  135. package/src/web/KeyboardControls.three.tsrx +98 -0
  136. package/src/web/Loader.tsrx +61 -0
  137. package/src/web/PresentationControls.three.tsrx +63 -0
  138. package/src/web/ScrollControls.three.tsrx +120 -0
  139. package/src/web/Select.three.tsrx +155 -0
  140. package/src/web/View.three.tsrx +46 -0
  141. package/src/web/face-landmarker-resource.ts +18 -0
  142. package/src/web/facemesh-data.ts +54 -0
  143. package/src/web/index.ts +41 -0
  144. package/src/web/keyboard-controls-types.ts +33 -0
  145. package/src/web/media-textures.three.tsrx +50 -0
  146. package/src/web/pivotControls/annotation.tsrx +17 -0
  147. package/src/web/pivotControls/context.ts +38 -0
  148. package/src/web/pivotControls/index.three.tsrx +877 -0
  149. package/src/web/scroll-html-root.tsrx +27 -0
  150. package/src/web/useCursor.three.tsrx +19 -0
  151. package/src/web/useGesture.ts +27 -0
  152. package/status.json +14 -0
@@ -0,0 +1,598 @@
1
+ import type { ThreeElements } from '@octanejs/three';
2
+ import * as THREE from 'three';
3
+ import { shaderMaterial } from './shaderMaterial.js';
4
+
5
+ export type SplatMaterialType = {
6
+ alphaTest?: number;
7
+ alphaHash?: boolean;
8
+ centerAndScaleTexture?: THREE.DataTexture;
9
+ covAndColorTexture?: THREE.DataTexture;
10
+ viewport?: THREE.Vector2;
11
+ focal?: number;
12
+ };
13
+
14
+ export type TargetMesh = THREE.Mesh<
15
+ THREE.InstancedBufferGeometry,
16
+ THREE.ShaderMaterial & SplatMaterialType
17
+ > & {
18
+ ready: boolean;
19
+ sorted: boolean;
20
+ pm: THREE.Matrix4;
21
+ vm1: THREE.Matrix4;
22
+ vm2: THREE.Matrix4;
23
+ viewport: THREE.Vector4;
24
+ };
25
+
26
+ export type SharedState = {
27
+ url: string;
28
+ gl: THREE.WebGLRenderer;
29
+ worker: Worker;
30
+ manager: THREE.LoadingManager;
31
+ stream: ReadableStreamDefaultReader<Uint8Array>;
32
+ loading: boolean;
33
+ loaded: boolean;
34
+ loadedVertexCount: number;
35
+ rowLength: number;
36
+ maxVertexes: number;
37
+ chunkSize: number;
38
+ totalDownloadBytes: number;
39
+ numVertices: number;
40
+ bufferTextureWidth: number;
41
+ bufferTextureHeight: number;
42
+ centerAndScaleData: Float32Array;
43
+ covAndColorData: Uint32Array;
44
+ covAndColorTexture: THREE.DataTexture;
45
+ centerAndScaleTexture: THREE.DataTexture;
46
+ connect(target: TargetMesh): () => void;
47
+ update(target: TargetMesh, camera: THREE.Camera, hashed: boolean): void;
48
+ onProgress?: (event: ProgressEvent) => void;
49
+ };
50
+
51
+ export type SplatProps = {
52
+ src: string;
53
+ toneMapped?: boolean;
54
+ alphaTest?: number;
55
+ alphaHash?: boolean;
56
+ chunkSize?: number;
57
+ } & Omit<ThreeElements['mesh'], 'ref'>;
58
+
59
+ export const SplatMaterial = /* @__PURE__ */ shaderMaterial(
60
+ {
61
+ alphaTest: 0,
62
+ viewport: new THREE.Vector2(1980, 1080),
63
+ focal: 1000,
64
+ centerAndScaleTexture: null,
65
+ covAndColorTexture: null,
66
+ },
67
+ /* glsl */ `
68
+ precision highp sampler2D;
69
+ precision highp usampler2D;
70
+ out vec4 vColor;
71
+ out vec3 vPosition;
72
+ uniform vec2 resolution;
73
+ uniform vec2 viewport;
74
+ uniform float focal;
75
+ attribute uint splatIndex;
76
+ uniform sampler2D centerAndScaleTexture;
77
+ uniform usampler2D covAndColorTexture;
78
+
79
+ vec2 unpackInt16(in uint value) {
80
+ int v = int(value);
81
+ int v0 = v >> 16;
82
+ int v1 = (v & 0xFFFF);
83
+ if((v & 0x8000) != 0) v1 |= 0xFFFF0000;
84
+ return vec2(float(v1), float(v0));
85
+ }
86
+
87
+ void main () {
88
+ ivec2 texSize = textureSize(centerAndScaleTexture, 0);
89
+ ivec2 texPos = ivec2(splatIndex%uint(texSize.x), splatIndex/uint(texSize.x));
90
+ vec4 centerAndScaleData = texelFetch(centerAndScaleTexture, texPos, 0);
91
+ vec4 center = vec4(centerAndScaleData.xyz, 1);
92
+ vec4 camspace = modelViewMatrix * center;
93
+ vec4 pos2d = projectionMatrix * camspace;
94
+ float bounds = 1.2 * pos2d.w;
95
+ if (pos2d.z < -pos2d.w || pos2d.x < -bounds || pos2d.x > bounds || pos2d.y < -bounds || pos2d.y > bounds) {
96
+ gl_Position = vec4(0.0, 0.0, 2.0, 1.0);
97
+ return;
98
+ }
99
+ uvec4 covAndColorData = texelFetch(covAndColorTexture, texPos, 0);
100
+ vec2 cov3D_M11_M12 = unpackInt16(covAndColorData.x) * centerAndScaleData.w;
101
+ vec2 cov3D_M13_M22 = unpackInt16(covAndColorData.y) * centerAndScaleData.w;
102
+ vec2 cov3D_M23_M33 = unpackInt16(covAndColorData.z) * centerAndScaleData.w;
103
+ mat3 Vrk = mat3(
104
+ cov3D_M11_M12.x, cov3D_M11_M12.y, cov3D_M13_M22.x,
105
+ cov3D_M11_M12.y, cov3D_M13_M22.y, cov3D_M23_M33.x,
106
+ cov3D_M13_M22.x, cov3D_M23_M33.x, cov3D_M23_M33.y
107
+ );
108
+ mat3 J = mat3(
109
+ focal / camspace.z, 0., -(focal * camspace.x) / (camspace.z * camspace.z),
110
+ 0., focal / camspace.z, -(focal * camspace.y) / (camspace.z * camspace.z),
111
+ 0., 0., 0.
112
+ );
113
+ mat3 W = transpose(mat3(modelViewMatrix));
114
+ mat3 T = W * J;
115
+ mat3 cov = transpose(T) * Vrk * T;
116
+ vec2 vCenter = vec2(pos2d) / pos2d.w;
117
+ float diagonal1 = cov[0][0] + 0.3;
118
+ float offDiagonal = cov[0][1];
119
+ float diagonal2 = cov[1][1] + 0.3;
120
+ float mid = 0.5 * (diagonal1 + diagonal2);
121
+ float radius = length(vec2((diagonal1 - diagonal2) / 2.0, offDiagonal));
122
+ float lambda1 = mid + radius;
123
+ float lambda2 = max(mid - radius, 0.1);
124
+ vec2 diagonalVector = normalize(vec2(offDiagonal, lambda1 - diagonal1));
125
+ vec2 v1 = min(sqrt(2.0 * lambda1), 1024.0) * diagonalVector;
126
+ vec2 v2 = min(sqrt(2.0 * lambda2), 1024.0) * vec2(diagonalVector.y, -diagonalVector.x);
127
+ uint colorUint = covAndColorData.w;
128
+ vColor = vec4(
129
+ float(colorUint & uint(0xFF)) / 255.0,
130
+ float((colorUint >> uint(8)) & uint(0xFF)) / 255.0,
131
+ float((colorUint >> uint(16)) & uint(0xFF)) / 255.0,
132
+ float(colorUint >> uint(24)) / 255.0
133
+ );
134
+ vPosition = position;
135
+ gl_Position = vec4(vCenter + position.x * v2 / viewport * 2.0 + position.y * v1 / viewport * 2.0, pos2d.z / pos2d.w, 1.0);
136
+ }
137
+ `,
138
+ /* glsl */ `
139
+ #include <alphatest_pars_fragment>
140
+ #include <alphahash_pars_fragment>
141
+ in vec4 vColor;
142
+ in vec3 vPosition;
143
+ void main () {
144
+ float A = -dot(vPosition.xy, vPosition.xy);
145
+ if (A < -4.0) discard;
146
+ float B = exp(A) * vColor.a;
147
+ vec4 diffuseColor = vec4(vColor.rgb, B);
148
+ #include <alphatest_fragment>
149
+ #include <alphahash_fragment>
150
+ gl_FragColor = diffuseColor;
151
+ #include <tonemapping_fragment>
152
+ #include <colorspace_fragment>
153
+ }
154
+ `,
155
+ );
156
+
157
+ function createWorker(self: any) {
158
+ let matrices: Float32Array = null!;
159
+ let offset = 0;
160
+ function sortSplats(view: Float32Array, hashed = false) {
161
+ const vertexCount = matrices.length / 16;
162
+ const threshold = -0.0001;
163
+ let maxDepth = -Infinity;
164
+ let minDepth = Infinity;
165
+ const depthList = new Float32Array(vertexCount);
166
+ const sizeList = new Int32Array(depthList.buffer);
167
+ const validIndexList = new Int32Array(vertexCount);
168
+ let validCount = 0;
169
+ for (let index = 0; index < vertexCount; index++) {
170
+ const depth =
171
+ view[0] * matrices[index * 16 + 12] +
172
+ view[1] * matrices[index * 16 + 13] +
173
+ view[2] * matrices[index * 16 + 14] +
174
+ view[3];
175
+ if (hashed || (depth < 0 && matrices[index * 16 + 15] > threshold * depth)) {
176
+ depthList[validCount] = depth;
177
+ validIndexList[validCount] = index;
178
+ validCount++;
179
+ if (depth > maxDepth) maxDepth = depth;
180
+ if (depth < minDepth) minDepth = depth;
181
+ }
182
+ }
183
+ const depthInverse = (256 * 256 - 1) / (maxDepth - minDepth);
184
+ const counts = new Uint32Array(256 * 256);
185
+ for (let index = 0; index < validCount; index++) {
186
+ sizeList[index] = ((depthList[index] - minDepth) * depthInverse) | 0;
187
+ counts[sizeList[index]]++;
188
+ }
189
+ const starts = new Uint32Array(256 * 256);
190
+ for (let index = 1; index < 256 * 256; index++)
191
+ starts[index] = starts[index - 1] + counts[index - 1];
192
+ const depthIndex = new Uint32Array(validCount);
193
+ for (let index = 0; index < validCount; index++)
194
+ depthIndex[starts[sizeList[index]]++] = validIndexList[index];
195
+ return depthIndex;
196
+ }
197
+ self.onmessage = (event: {
198
+ data: {
199
+ method: string;
200
+ length: number;
201
+ key: string;
202
+ view: ArrayBuffer;
203
+ matrices: ArrayBuffer;
204
+ hashed: boolean;
205
+ };
206
+ }) => {
207
+ if (event.data.method === 'push') {
208
+ if (offset === 0) matrices = new Float32Array(event.data.length);
209
+ const newMatrices = new Float32Array(event.data.matrices);
210
+ matrices.set(newMatrices, offset);
211
+ offset += newMatrices.length;
212
+ } else if (event.data.method === 'sort' && matrices !== null) {
213
+ const indices = sortSplats(new Float32Array(event.data.view), event.data.hashed);
214
+ self.postMessage({ indices, key: event.data.key }, [indices.buffer]);
215
+ }
216
+ };
217
+ }
218
+
219
+ export class SplatLoader extends THREE.Loader<SharedState> {
220
+ gl: THREE.WebGLRenderer = null!;
221
+ chunkSize = 25000;
222
+ override load(
223
+ url: string,
224
+ onLoad: (data: SharedState) => void,
225
+ onProgress?: (event: ProgressEvent) => void,
226
+ onError?: (event: unknown) => void,
227
+ ): void {
228
+ const shared: SharedState = {
229
+ gl: this.gl,
230
+ url: this.manager.resolveURL(url),
231
+ worker: new Worker(
232
+ URL.createObjectURL(
233
+ new Blob(['(', createWorker.toString(), ')(self)'], { type: 'application/javascript' }),
234
+ ),
235
+ ),
236
+ manager: this.manager,
237
+ update: (target: TargetMesh, camera: THREE.Camera, hashed: boolean) =>
238
+ update(camera, shared, target, hashed),
239
+ connect: (target: TargetMesh) => connect(shared, target),
240
+ loading: false,
241
+ loaded: false,
242
+ loadedVertexCount: 0,
243
+ chunkSize: this.chunkSize,
244
+ totalDownloadBytes: 0,
245
+ numVertices: 0,
246
+ rowLength: 32,
247
+ maxVertexes: 0,
248
+ bufferTextureWidth: 0,
249
+ bufferTextureHeight: 0,
250
+ stream: null!,
251
+ centerAndScaleData: null!,
252
+ covAndColorData: null!,
253
+ covAndColorTexture: null!,
254
+ centerAndScaleTexture: null!,
255
+ onProgress,
256
+ };
257
+ load(shared)
258
+ .then(onLoad)
259
+ .catch((error) => {
260
+ onError?.(error);
261
+ shared.manager.itemError(shared.url);
262
+ });
263
+ }
264
+ }
265
+
266
+ async function load(shared: SharedState): Promise<SharedState> {
267
+ shared.manager.itemStart(shared.url);
268
+ const response = await fetch(shared.url);
269
+ if (response.body === null) throw 'Failed to fetch file';
270
+ const contentLength = response.headers.get('Content-Length');
271
+ const totalDownloadBytes = contentLength ? Number.parseInt(contentLength) : undefined;
272
+ if (totalDownloadBytes === undefined) throw 'Failed to get content length';
273
+ shared.stream = response.body.getReader();
274
+ shared.totalDownloadBytes = totalDownloadBytes;
275
+ shared.numVertices = Math.floor(shared.totalDownloadBytes / shared.rowLength);
276
+ const context = shared.gl.getContext();
277
+ const maxTextureSize = context.getParameter(context.MAX_TEXTURE_SIZE);
278
+ shared.maxVertexes = maxTextureSize * maxTextureSize;
279
+ if (shared.numVertices > shared.maxVertexes) shared.numVertices = shared.maxVertexes;
280
+ shared.bufferTextureWidth = maxTextureSize;
281
+ shared.bufferTextureHeight = Math.floor((shared.numVertices - 1) / maxTextureSize) + 1;
282
+ shared.centerAndScaleData = new Float32Array(
283
+ shared.bufferTextureWidth * shared.bufferTextureHeight * 4,
284
+ );
285
+ shared.covAndColorData = new Uint32Array(
286
+ shared.bufferTextureWidth * shared.bufferTextureHeight * 4,
287
+ );
288
+ shared.centerAndScaleTexture = new THREE.DataTexture(
289
+ shared.centerAndScaleData,
290
+ shared.bufferTextureWidth,
291
+ shared.bufferTextureHeight,
292
+ THREE.RGBAFormat,
293
+ THREE.FloatType,
294
+ );
295
+ shared.centerAndScaleTexture.needsUpdate = true;
296
+ shared.covAndColorTexture = new THREE.DataTexture(
297
+ shared.covAndColorData,
298
+ shared.bufferTextureWidth,
299
+ shared.bufferTextureHeight,
300
+ THREE.RGBAIntegerFormat,
301
+ THREE.UnsignedIntType,
302
+ );
303
+ shared.covAndColorTexture.internalFormat = 'RGBA32UI';
304
+ shared.covAndColorTexture.needsUpdate = true;
305
+ return shared;
306
+ }
307
+
308
+ async function lazyLoad(shared: SharedState): Promise<void> {
309
+ shared.loading = true;
310
+ let bytesDownloaded = 0;
311
+ let bytesProcessed = 0;
312
+ const chunks: Uint8Array[] = [];
313
+ let lastReportedProgress = 0;
314
+ const lengthComputable = shared.totalDownloadBytes !== 0;
315
+ while (true) {
316
+ try {
317
+ const { value, done } = await shared.stream.read();
318
+ if (done) break;
319
+ bytesDownloaded += value.length;
320
+ if (shared.totalDownloadBytes !== undefined) {
321
+ const percent = (bytesDownloaded / shared.totalDownloadBytes) * 100;
322
+ if (shared.onProgress && percent - lastReportedProgress > 1) {
323
+ shared.onProgress(
324
+ new ProgressEvent('progress', {
325
+ lengthComputable,
326
+ loaded: bytesDownloaded,
327
+ total: shared.totalDownloadBytes,
328
+ }),
329
+ );
330
+ lastReportedProgress = percent;
331
+ }
332
+ }
333
+ chunks.push(value);
334
+ const bytesRemaining = bytesDownloaded - bytesProcessed;
335
+ if (
336
+ shared.totalDownloadBytes !== undefined &&
337
+ bytesRemaining > shared.rowLength * shared.chunkSize
338
+ ) {
339
+ const vertexCount = Math.floor(bytesRemaining / shared.rowLength);
340
+ const concatenated = new Uint8Array(bytesRemaining);
341
+ let offset = 0;
342
+ for (const chunk of chunks) {
343
+ concatenated.set(chunk, offset);
344
+ offset += chunk.length;
345
+ }
346
+ chunks.length = 0;
347
+ if (bytesRemaining > vertexCount * shared.rowLength) {
348
+ const extra = new Uint8Array(bytesRemaining - vertexCount * shared.rowLength);
349
+ extra.set(concatenated.subarray(bytesRemaining - extra.length), 0);
350
+ chunks.push(extra);
351
+ }
352
+ const buffer = new Uint8Array(vertexCount * shared.rowLength);
353
+ buffer.set(concatenated.subarray(0, buffer.byteLength), 0);
354
+ const matrices = pushDataBuffer(shared, buffer.buffer, vertexCount);
355
+ shared.worker.postMessage(
356
+ {
357
+ method: 'push',
358
+ src: shared.url,
359
+ length: shared.numVertices * 16,
360
+ matrices: matrices.buffer,
361
+ },
362
+ [matrices.buffer],
363
+ );
364
+ bytesProcessed += vertexCount * shared.rowLength;
365
+ shared.onProgress?.(
366
+ new ProgressEvent('progress', {
367
+ lengthComputable,
368
+ loaded: shared.totalDownloadBytes,
369
+ total: shared.totalDownloadBytes,
370
+ }),
371
+ );
372
+ }
373
+ } catch (error) {
374
+ console.error(error);
375
+ break;
376
+ }
377
+ }
378
+ if (bytesDownloaded - bytesProcessed > 0) {
379
+ const concatenated = new Uint8Array(chunks.reduce((sum, chunk) => sum + chunk.length, 0));
380
+ let offset = 0;
381
+ for (const chunk of chunks) {
382
+ concatenated.set(chunk, offset);
383
+ offset += chunk.length;
384
+ }
385
+ const numVertices = Math.floor(concatenated.byteLength / shared.rowLength);
386
+ const matrices = pushDataBuffer(shared, concatenated.buffer, numVertices);
387
+ shared.worker.postMessage(
388
+ { method: 'push', src: shared.url, length: numVertices * 16, matrices: matrices.buffer },
389
+ [matrices.buffer],
390
+ );
391
+ }
392
+ shared.loaded = true;
393
+ shared.manager.itemEnd(shared.url);
394
+ }
395
+
396
+ function update(
397
+ camera: THREE.Camera,
398
+ shared: SharedState,
399
+ target: TargetMesh,
400
+ hashed: boolean,
401
+ ): void {
402
+ camera.updateMatrixWorld();
403
+ shared.gl.getCurrentViewport(target.viewport);
404
+ target.material.viewport!.x = target.viewport.z;
405
+ target.material.viewport!.y = target.viewport.w;
406
+ target.material.focal = (target.viewport.w / 2) * Math.abs(camera.projectionMatrix.elements[5]);
407
+ if (target.ready) {
408
+ if (hashed && target.sorted) return;
409
+ target.ready = false;
410
+ const view = new Float32Array([
411
+ target.modelViewMatrix.elements[2],
412
+ -target.modelViewMatrix.elements[6],
413
+ target.modelViewMatrix.elements[10],
414
+ target.modelViewMatrix.elements[14],
415
+ ]);
416
+ shared.worker.postMessage(
417
+ { method: 'sort', src: shared.url, key: target.uuid, view: view.buffer, hashed },
418
+ [view.buffer],
419
+ );
420
+ if (hashed && shared.loaded) target.sorted = true;
421
+ }
422
+ }
423
+
424
+ function connect(shared: SharedState, target: TargetMesh): () => void {
425
+ if (!shared.loading) void lazyLoad(shared);
426
+ target.ready = false;
427
+ target.pm = new THREE.Matrix4();
428
+ target.vm1 = new THREE.Matrix4();
429
+ target.vm2 = new THREE.Matrix4();
430
+ target.viewport = new THREE.Vector4();
431
+ const splatIndexArray = new Uint32Array(shared.bufferTextureWidth * shared.bufferTextureHeight);
432
+ const splatIndexes = new THREE.InstancedBufferAttribute(splatIndexArray, 1, false);
433
+ splatIndexes.setUsage(THREE.DynamicDrawUsage);
434
+ const geometry = (target.geometry = new THREE.InstancedBufferGeometry());
435
+ const positions = new THREE.BufferAttribute(new Float32Array(18), 3);
436
+ geometry.setAttribute('position', positions);
437
+ positions.setXYZ(2, -2, 2, 0);
438
+ positions.setXYZ(1, 2, 2, 0);
439
+ positions.setXYZ(0, -2, -2, 0);
440
+ positions.setXYZ(5, -2, -2, 0);
441
+ positions.setXYZ(4, 2, 2, 0);
442
+ positions.setXYZ(3, 2, -2, 0);
443
+ positions.needsUpdate = true;
444
+ geometry.setAttribute('splatIndex', splatIndexes);
445
+ geometry.instanceCount = 1;
446
+ function listener(event: MessageEvent<{ key: string; indices: Uint32Array }>) {
447
+ if (target && event.data.key === target.uuid) {
448
+ const indexes = new Uint32Array(event.data.indices);
449
+ (geometry.attributes.splatIndex as THREE.InstancedBufferAttribute).set(indexes);
450
+ geometry.attributes.splatIndex.needsUpdate = true;
451
+ geometry.instanceCount = indexes.length;
452
+ target.ready = true;
453
+ }
454
+ }
455
+ shared.worker.addEventListener('message', listener);
456
+ async function wait() {
457
+ while (true) {
458
+ const centerProperties = shared.gl.properties.get(shared.centerAndScaleTexture) as
459
+ { __webglTexture?: unknown } | undefined;
460
+ const colorProperties = shared.gl.properties.get(shared.covAndColorTexture) as
461
+ { __webglTexture?: unknown } | undefined;
462
+ if (
463
+ centerProperties?.__webglTexture &&
464
+ colorProperties?.__webglTexture &&
465
+ shared.loadedVertexCount > 0
466
+ )
467
+ break;
468
+ await new Promise((resolve) => setTimeout(resolve, 10));
469
+ }
470
+ target.ready = true;
471
+ }
472
+ void wait();
473
+ return () => shared.worker.removeEventListener('message', listener);
474
+ }
475
+
476
+ function pushDataBuffer(
477
+ shared: SharedState,
478
+ buffer: ArrayBufferLike,
479
+ requestedVertexCount: number,
480
+ ): Float32Array {
481
+ const context = shared.gl.getContext();
482
+ let vertexCount = requestedVertexCount;
483
+ if (shared.loadedVertexCount + vertexCount > shared.maxVertexes)
484
+ vertexCount = shared.maxVertexes - shared.loadedVertexCount;
485
+ if (vertexCount <= 0) throw 'Failed to parse file';
486
+ const bytes = new Uint8Array(buffer);
487
+ const floats = new Float32Array(buffer);
488
+ const matrices = new Float32Array(vertexCount * 16);
489
+ const colorBytes = new Uint8Array(shared.covAndColorData.buffer);
490
+ const covariance = new Int16Array(shared.covAndColorData.buffer);
491
+ for (let index = 0; index < vertexCount; index++) {
492
+ const quaternion = new THREE.Quaternion(
493
+ -(bytes[32 * index + 29] - 128) / 128,
494
+ (bytes[32 * index + 30] - 128) / 128,
495
+ (bytes[32 * index + 31] - 128) / 128,
496
+ -(bytes[32 * index + 28] - 128) / 128,
497
+ );
498
+ quaternion.invert();
499
+ const center = new THREE.Vector3(
500
+ floats[8 * index],
501
+ floats[8 * index + 1],
502
+ -floats[8 * index + 2],
503
+ );
504
+ const scale = new THREE.Vector3(
505
+ floats[8 * index + 3],
506
+ floats[8 * index + 4],
507
+ floats[8 * index + 5],
508
+ );
509
+ const matrix = new THREE.Matrix4();
510
+ matrix.makeRotationFromQuaternion(quaternion);
511
+ matrix.transpose();
512
+ matrix.scale(scale);
513
+ const transpose = matrix.clone();
514
+ matrix.transpose();
515
+ matrix.premultiply(transpose);
516
+ matrix.setPosition(center);
517
+ const covarianceIndexes = [0, 1, 2, 5, 6, 10];
518
+ let maximum = 0;
519
+ for (const covarianceIndex of covarianceIndexes)
520
+ if (Math.abs(matrix.elements[covarianceIndex]) > maximum)
521
+ maximum = Math.abs(matrix.elements[covarianceIndex]);
522
+ let destination = shared.loadedVertexCount * 4 + index * 4;
523
+ shared.centerAndScaleData[destination] = center.x;
524
+ shared.centerAndScaleData[destination + 1] = -center.y;
525
+ shared.centerAndScaleData[destination + 2] = center.z;
526
+ shared.centerAndScaleData[destination + 3] = maximum / 32767;
527
+ destination = shared.loadedVertexCount * 8 + index * 8;
528
+ for (let component = 0; component < covarianceIndexes.length; component++)
529
+ covariance[destination + component] =
530
+ (matrix.elements[covarianceIndexes[component]] * 32767) / maximum;
531
+ destination = shared.loadedVertexCount * 16 + (index * 4 + 3) * 4;
532
+ const color = new THREE.Color(
533
+ bytes[32 * index + 24] / 255,
534
+ bytes[32 * index + 25] / 255,
535
+ bytes[32 * index + 26] / 255,
536
+ );
537
+ color.convertSRGBToLinear();
538
+ colorBytes[destination] = color.r * 255;
539
+ colorBytes[destination + 1] = color.g * 255;
540
+ colorBytes[destination + 2] = color.b * 255;
541
+ colorBytes[destination + 3] = bytes[32 * index + 27];
542
+ matrix.elements[15] = (Math.max(scale.x, scale.y, scale.z) * bytes[32 * index + 27]) / 255;
543
+ for (let component = 0; component < 16; component++)
544
+ matrices[index * 16 + component] = matrix.elements[component];
545
+ }
546
+ while (vertexCount > 0) {
547
+ let width = 0;
548
+ let height = 0;
549
+ const xOffset = shared.loadedVertexCount % shared.bufferTextureWidth;
550
+ const yOffset = Math.floor(shared.loadedVertexCount / shared.bufferTextureWidth);
551
+ if (shared.loadedVertexCount % shared.bufferTextureWidth !== 0) {
552
+ width = Math.min(shared.bufferTextureWidth, xOffset + vertexCount) - xOffset;
553
+ height = 1;
554
+ } else if (Math.floor(vertexCount / shared.bufferTextureWidth) > 0) {
555
+ width = shared.bufferTextureWidth;
556
+ height = Math.floor(vertexCount / shared.bufferTextureWidth);
557
+ } else {
558
+ width = vertexCount % shared.bufferTextureWidth;
559
+ height = 1;
560
+ }
561
+ const centerProperties = shared.gl.properties.get(shared.centerAndScaleTexture) as {
562
+ __webglTexture: WebGLTexture;
563
+ };
564
+ context.bindTexture(context.TEXTURE_2D, centerProperties.__webglTexture);
565
+ context.texSubImage2D(
566
+ context.TEXTURE_2D,
567
+ 0,
568
+ xOffset,
569
+ yOffset,
570
+ width,
571
+ height,
572
+ context.RGBA,
573
+ context.FLOAT,
574
+ shared.centerAndScaleData,
575
+ shared.loadedVertexCount * 4,
576
+ );
577
+ const colorProperties = shared.gl.properties.get(shared.covAndColorTexture) as {
578
+ __webglTexture: WebGLTexture;
579
+ };
580
+ context.bindTexture(context.TEXTURE_2D, colorProperties.__webglTexture);
581
+ context.texSubImage2D(
582
+ context.TEXTURE_2D,
583
+ 0,
584
+ xOffset,
585
+ yOffset,
586
+ width,
587
+ height,
588
+ (context as WebGL2RenderingContext).RGBA_INTEGER,
589
+ context.UNSIGNED_INT,
590
+ shared.covAndColorData,
591
+ shared.loadedVertexCount * 4,
592
+ );
593
+ shared.gl.resetState();
594
+ shared.loadedVertexCount += width * height;
595
+ vertexCount -= width * height;
596
+ }
597
+ return matrices;
598
+ }
@@ -0,0 +1,57 @@
1
+ import { useFrame, type RefObject } from '@octanejs/three';
2
+ import { useEffect, useLayoutEffect, useMemo, useRef, useState } from '@octanejs/three/renderer';
3
+ import { AnimationAction, AnimationClip, AnimationMixer, Object3D } from 'three';
4
+ import { slotDefault } from './compiler-slot.js';
5
+
6
+ type Api<T extends AnimationClip> = {
7
+ ref: RefObject<Object3D | undefined | null>;
8
+ clips: AnimationClip[];
9
+ mixer: AnimationMixer;
10
+ names: T['name'][];
11
+ actions: { [key in T['name']]: AnimationAction | null };
12
+ };
13
+
14
+ export function useAnimations<T extends AnimationClip>(
15
+ clips: T[],
16
+ root?: RefObject<Object3D | undefined | null> | Object3D,
17
+ ): Api<T> {
18
+ root = slotDefault(root, undefined);
19
+ const ref = useRef<Object3D | null>(null);
20
+ const [actualRef] = useState(
21
+ () => root ? (root instanceof Object3D ? { current: root } : root) : ref,
22
+ );
23
+ const [mixer] = useState(() => new AnimationMixer(undefined as unknown as Object3D));
24
+ useLayoutEffect(() => {
25
+ if (root) actualRef.current = root instanceof Object3D ? root : root.current;
26
+ (mixer as AnimationMixer & { _root: Object3D | null | undefined })._root = actualRef.current;
27
+ });
28
+ const lazyActions = useRef<Record<string, AnimationAction>>({});
29
+ const api = useMemo<Api<T>>(() => {
30
+ const actions = {} as { [key in T['name']]: AnimationAction | null };
31
+ clips.forEach(
32
+ (clip) => Object.defineProperty(actions, clip.name, {
33
+ enumerable: true,
34
+ get() {
35
+ if (actualRef.current) {
36
+ return lazyActions.current[clip.name] ||
37
+ (lazyActions.current[clip.name] = mixer.clipAction(clip, actualRef.current));
38
+ }
39
+ },
40
+ configurable: true,
41
+ }),
42
+ );
43
+ return { ref: actualRef, clips, actions, names: clips.map((clip) => clip.name), mixer };
44
+ }, [clips]);
45
+ useFrame((_, delta) => mixer.update(delta));
46
+ useEffect(() => {
47
+ const currentRoot = actualRef.current;
48
+ return () => {
49
+ lazyActions.current = {};
50
+ mixer.stopAllAction();
51
+ Object.values(api.actions).forEach((action) => {
52
+ if (currentRoot) mixer.uncacheAction(action as unknown as AnimationClip, currentRoot);
53
+ });
54
+ };
55
+ }, [clips]);
56
+ return api;
57
+ }
@@ -0,0 +1,10 @@
1
+ import { useThree } from '@octanejs/three';
2
+ import { slotDefault } from './compiler-slot.js';
3
+
4
+ export function useAspect(width: number, height: number, factor = 1): [number, number, number] {
5
+ factor = slotDefault(factor, 1);
6
+ const viewport = useThree((state) => state.viewport);
7
+ const scale =
8
+ viewport.aspect > width / height ? viewport.width / width : viewport.height / height;
9
+ return [width * scale * factor, height * scale * factor, 1];
10
+ }