dylanebert HF staff commited on
Commit
95af024
1 Parent(s): 49c931b

working detached library

Browse files
viewer/src/lib/splat.js/controls/OrbitControls.ts CHANGED
@@ -36,8 +36,7 @@ class OrbitControls {
36
  };
37
 
38
  const onPointerDown = (e: PointerEvent) => {
39
- e.preventDefault();
40
- e.stopPropagation();
41
 
42
  dragging = true;
43
  if (e.pointerType === "touch") {
@@ -52,8 +51,7 @@ class OrbitControls {
52
  };
53
 
54
  const onPointerUp = (e: PointerEvent) => {
55
- e.preventDefault();
56
- e.stopPropagation();
57
 
58
  dragging = false;
59
  panning = false;
@@ -62,8 +60,7 @@ class OrbitControls {
62
  };
63
 
64
  const onPointerMove = (e: PointerEvent) => {
65
- e.preventDefault();
66
- e.stopPropagation();
67
 
68
  if (!dragging) return;
69
 
@@ -90,8 +87,7 @@ class OrbitControls {
90
  };
91
 
92
  const onWheel = (e: WheelEvent) => {
93
- e.preventDefault();
94
- e.stopPropagation();
95
 
96
  const zoomNorm = computeZoomNorm();
97
  desiredRadius += e.deltaY * this.zoomSpeed * 0.02 * zoomNorm;
@@ -119,12 +115,27 @@ class OrbitControls {
119
  camera.rotation = Matrix3.RotationFromEuler(new Vector3(rx, ry, 0));
120
  };
121
 
 
 
 
 
 
122
  this.dispose = () => {
 
 
 
 
 
123
  domElement.removeEventListener("pointerdown", onPointerDown);
124
  domElement.removeEventListener("pointermove", onPointerMove);
125
  domElement.removeEventListener("wheel", onWheel);
126
  };
127
 
 
 
 
 
 
128
  domElement.addEventListener("pointerdown", onPointerDown);
129
  domElement.addEventListener("pointermove", onPointerMove);
130
  domElement.addEventListener("wheel", onWheel);
 
36
  };
37
 
38
  const onPointerDown = (e: PointerEvent) => {
39
+ preventDefault(e);
 
40
 
41
  dragging = true;
42
  if (e.pointerType === "touch") {
 
51
  };
52
 
53
  const onPointerUp = (e: PointerEvent) => {
54
+ preventDefault(e);
 
55
 
56
  dragging = false;
57
  panning = false;
 
60
  };
61
 
62
  const onPointerMove = (e: PointerEvent) => {
63
+ preventDefault(e);
 
64
 
65
  if (!dragging) return;
66
 
 
87
  };
88
 
89
  const onWheel = (e: WheelEvent) => {
90
+ preventDefault(e);
 
91
 
92
  const zoomNorm = computeZoomNorm();
93
  desiredRadius += e.deltaY * this.zoomSpeed * 0.02 * zoomNorm;
 
115
  camera.rotation = Matrix3.RotationFromEuler(new Vector3(rx, ry, 0));
116
  };
117
 
118
+ const preventDefault = (e: Event) => {
119
+ e.preventDefault();
120
+ e.stopPropagation();
121
+ };
122
+
123
  this.dispose = () => {
124
+ domElement.removeEventListener("dragenter", preventDefault);
125
+ domElement.removeEventListener("dragover", preventDefault);
126
+ domElement.removeEventListener("dragleave", preventDefault);
127
+ domElement.removeEventListener("contextmenu", preventDefault);
128
+
129
  domElement.removeEventListener("pointerdown", onPointerDown);
130
  domElement.removeEventListener("pointermove", onPointerMove);
131
  domElement.removeEventListener("wheel", onWheel);
132
  };
133
 
134
+ domElement.addEventListener("dragenter", preventDefault);
135
+ domElement.addEventListener("dragover", preventDefault);
136
+ domElement.addEventListener("dragleave", preventDefault);
137
+ domElement.addEventListener("contextmenu", preventDefault);
138
+
139
  domElement.addEventListener("pointerdown", onPointerDown);
140
  domElement.addEventListener("pointermove", onPointerMove);
141
  domElement.addEventListener("wheel", onWheel);
viewer/src/lib/splat.js/core/Scene.ts CHANGED
@@ -16,8 +16,6 @@ class Scene extends Object3D {
16
 
17
  const rowLength = 3 * 4 + 3 * 4 + 4 + 4;
18
  this.vertexCount = data.length / rowLength;
19
-
20
- console.log("setData", this.vertexCount);
21
  }
22
  }
23
 
 
16
 
17
  const rowLength = 3 * 4 + 3 * 4 + 4 + 4;
18
  this.vertexCount = data.length / rowLength;
 
 
19
  }
20
  }
21
 
viewer/src/lib/splat.js/index.ts CHANGED
@@ -8,8 +8,3 @@ export { Quaternion } from "./math/Quaternion";
8
  export { Vector3 } from "./math/Vector3";
9
  export { Matrix4 } from "./math/Matrix4";
10
  export { Matrix3 } from "./math/Matrix3";
11
-
12
- export { createWorker } from "./renderers/webgl/utils/worker";
13
- export { getViewMatrix } from "./renderers/webgl/utils/transformations";
14
- export { vertex } from "./renderers/webgl/shaders/vertex.glsl";
15
- export { frag } from "./renderers/webgl/shaders/frag.glsl";
 
8
  export { Vector3 } from "./math/Vector3";
9
  export { Matrix4 } from "./math/Matrix4";
10
  export { Matrix3 } from "./math/Matrix3";
 
 
 
 
 
viewer/src/lib/splat.js/renderers/WebGLRenderer.ts CHANGED
@@ -2,217 +2,224 @@ import type { Camera } from "../cameras/Camera";
2
  import type { Scene } from "../core/Scene";
3
  import type { Renderer } from "./Renderer";
4
 
5
- import { getViewMatrix } from "./webgl/utils/transformations";
6
  import { createWorker } from "./webgl/utils/worker";
7
 
8
  import { vertex } from "./webgl/shaders/vertex.glsl";
9
  import { frag } from "./webgl/shaders/frag.glsl";
 
10
 
11
  export class WebGLRenderer implements Renderer {
12
- canvas: HTMLCanvasElement;
13
-
14
- activeCamera: Camera | null = null;
15
- activeScene: Scene | null = null;
16
- vertexCount: number = 0;
17
-
18
- gl: WebGLRenderingContext | null = null;
19
- ext: ANGLE_instanced_arrays | null = null;
20
- worker: Worker | null = null;
21
-
22
- projectionMatrix: number[] = [];
23
- vertexShader: WebGLShader | null = null;
24
- fragmentShader: WebGLShader | null = null;
25
- program: WebGLProgram | null = null;
26
- a_position: number = 0;
27
- a_center: number = 0;
28
- a_color: number = 0;
29
- a_covA: number = 0;
30
- a_covB: number = 0;
31
- vertexBuffer: WebGLBuffer | null = null;
32
- centerBuffer: WebGLBuffer | null = null;
33
- colorBuffer: WebGLBuffer | null = null;
34
- covABuffer: WebGLBuffer | null = null;
35
- covBBuffer: WebGLBuffer | null = null;
36
-
37
- constructor(canvas?: HTMLCanvasElement) {
38
- this.canvas =
39
- canvas ?? (document.createElementNS("http://www.w3.org/1999/xhtml", "canvas") as HTMLCanvasElement);
40
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
41
 
42
- initWebGL() {
43
- this.gl = (this.canvas.getContext("webgl") ||
44
- this.canvas.getContext("experimental-webgl")) as WebGLRenderingContext;
45
- this.ext = this.gl.getExtension("ANGLE_instanced_arrays") as ANGLE_instanced_arrays;
46
-
47
- this.worker = new Worker(
48
- URL.createObjectURL(
49
- new Blob(["(", createWorker.toString(), ")(self)"], {
50
- type: "application/javascript",
51
- })
52
- )
53
- );
54
-
55
- this.canvas.width = innerWidth;
56
- this.canvas.height = innerHeight;
57
- this.gl.viewport(0, 0, this.canvas.width, this.canvas.height);
58
-
59
- this.activeCamera!.updateProjectionMatrix(this.canvas.width, this.canvas.height);
60
-
61
- let viewMatrix = getViewMatrix(this.activeCamera!);
62
-
63
- this.vertexShader = this.gl.createShader(this.gl.VERTEX_SHADER) as WebGLShader;
64
- this.gl.shaderSource(this.vertexShader, vertex);
65
- this.gl.compileShader(this.vertexShader);
66
- if (!this.gl.getShaderParameter(this.vertexShader, this.gl.COMPILE_STATUS)) {
67
- console.error(this.gl.getShaderInfoLog(this.vertexShader));
68
- }
69
-
70
- this.fragmentShader = this.gl.createShader(this.gl.FRAGMENT_SHADER) as WebGLShader;
71
- this.gl.shaderSource(this.fragmentShader, frag);
72
- this.gl.compileShader(this.fragmentShader);
73
- if (!this.gl.getShaderParameter(this.fragmentShader, this.gl.COMPILE_STATUS)) {
74
- console.error(this.gl.getShaderInfoLog(this.fragmentShader));
75
- }
76
-
77
- this.program = this.gl.createProgram() as WebGLProgram;
78
- this.gl.attachShader(this.program, this.vertexShader);
79
- this.gl.attachShader(this.program, this.fragmentShader);
80
- this.gl.linkProgram(this.program);
81
- this.gl.useProgram(this.program);
82
-
83
- if (!this.gl.getProgramParameter(this.program, this.gl.LINK_STATUS)) {
84
- console.error(this.gl.getProgramInfoLog(this.program));
85
- }
86
-
87
- this.gl.disable(this.gl.DEPTH_TEST); // Disable depth testing
88
-
89
- // Enable blending
90
- this.gl.enable(this.gl.BLEND);
91
-
92
- // Set blending function
93
- this.gl.blendFuncSeparate(this.gl.ONE_MINUS_DST_ALPHA, this.gl.ONE, this.gl.ONE_MINUS_DST_ALPHA, this.gl.ONE);
94
-
95
- // Set blending equation
96
- this.gl.blendEquationSeparate(this.gl.FUNC_ADD, this.gl.FUNC_ADD);
97
-
98
- // projection
99
- const u_projection = this.gl.getUniformLocation(this.program, "projection");
100
- this.gl.uniformMatrix4fv(u_projection, false, this.projectionMatrix);
101
-
102
- // viewport
103
- const u_viewport = this.gl.getUniformLocation(this.program, "viewport");
104
- this.gl.uniform2fv(u_viewport, new Float32Array([this.canvas.width, this.canvas.height]));
105
-
106
- // focal
107
- const u_focal = this.gl.getUniformLocation(this.program, "focal");
108
- this.gl.uniform2fv(u_focal, new Float32Array([this.activeCamera!.fx, this.activeCamera!.fy]));
109
-
110
- // view
111
- const u_view = this.gl.getUniformLocation(this.program, "view");
112
- this.gl.uniformMatrix4fv(u_view, false, viewMatrix.buffer);
113
-
114
- // positions
115
- const triangleVertices = new Float32Array([-2, -2, 2, -2, 2, 2, -2, 2]);
116
- this.vertexBuffer = this.gl.createBuffer();
117
- this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.vertexBuffer);
118
- this.gl.bufferData(this.gl.ARRAY_BUFFER, triangleVertices, this.gl.STATIC_DRAW);
119
-
120
- this.a_position = this.gl.getAttribLocation(this.program, "position");
121
- this.gl.enableVertexAttribArray(this.a_position);
122
- this.gl.vertexAttribPointer(this.a_position, 2, this.gl.FLOAT, false, 0, 0);
123
-
124
- // center
125
- this.centerBuffer = this.gl.createBuffer();
126
- this.a_center = this.gl.getAttribLocation(this.program, "center");
127
- this.gl.enableVertexAttribArray(this.a_center);
128
- this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.centerBuffer);
129
- this.gl.vertexAttribPointer(this.a_center, 3, this.gl.FLOAT, false, 0, 0);
130
- this.ext.vertexAttribDivisorANGLE(this.a_center, 1); // Use the extension here
131
-
132
- // color
133
- this.colorBuffer = this.gl.createBuffer();
134
- this.a_color = this.gl.getAttribLocation(this.program, "color");
135
- this.gl.enableVertexAttribArray(this.a_color);
136
- this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.colorBuffer);
137
- this.gl.vertexAttribPointer(this.a_color, 4, this.gl.FLOAT, false, 0, 0);
138
- this.ext.vertexAttribDivisorANGLE(this.a_color, 1); // Use the extension here
139
-
140
- // cov
141
- this.covABuffer = this.gl.createBuffer();
142
- this.a_covA = this.gl.getAttribLocation(this.program, "covA");
143
- this.gl.enableVertexAttribArray(this.a_covA);
144
- this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.covABuffer);
145
- this.gl.vertexAttribPointer(this.a_covA, 3, this.gl.FLOAT, false, 0, 0);
146
- this.ext.vertexAttribDivisorANGLE(this.a_covA, 1); // Use the extension here
147
-
148
- this.covBBuffer = this.gl.createBuffer();
149
- this.a_covB = this.gl.getAttribLocation(this.program, "covB");
150
- this.gl.enableVertexAttribArray(this.a_covB);
151
- this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.covBBuffer);
152
- this.gl.vertexAttribPointer(this.a_covB, 3, this.gl.FLOAT, false, 0, 0);
153
- this.ext.vertexAttribDivisorANGLE(this.a_covB, 1); // Use the extension here
154
-
155
- this.worker.onmessage = (e) => {
156
- if (e.data.buffer) {
157
- this.activeScene!.setData(new Uint8Array(e.data.buffer));
158
- const blob = new Blob([this.activeScene!.data.buffer], {
159
- type: "application/octet-stream",
160
- });
161
- const link = document.createElement("a");
162
- link.download = "model.splat";
163
- link.href = URL.createObjectURL(blob);
164
- document.body.appendChild(link);
165
- link.click();
166
- } else {
167
- let { covA, covB, center, color } = e.data;
168
- this.vertexCount = center.length / 3;
169
 
170
- const gl = this.gl!;
 
171
 
172
- gl.bindBuffer(gl.ARRAY_BUFFER, this.centerBuffer);
173
- gl.bufferData(gl.ARRAY_BUFFER, center, gl.DYNAMIC_DRAW);
174
 
175
- gl.bindBuffer(gl.ARRAY_BUFFER, this.colorBuffer);
176
- gl.bufferData(gl.ARRAY_BUFFER, color, gl.DYNAMIC_DRAW);
 
 
 
 
177
 
178
- gl.bindBuffer(gl.ARRAY_BUFFER, this.covABuffer);
179
- gl.bufferData(gl.ARRAY_BUFFER, covA, gl.DYNAMIC_DRAW);
 
 
 
 
180
 
181
- gl.bindBuffer(gl.ARRAY_BUFFER, this.covBBuffer);
182
- gl.bufferData(gl.ARRAY_BUFFER, covB, gl.DYNAMIC_DRAW);
 
 
 
 
 
183
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
184
  };
185
 
186
- this.frame = () => {
187
- this.activeCamera!.updateProjectionMatrix(this.canvas.width, this.canvas.height);
 
 
 
188
 
189
- const viewProj = this.activeCamera!.projectionMatrix.multiply(viewMatrix);
190
- this.worker!.postMessage({ view: viewProj.buffer });
191
 
192
- if (this.vertexCount > 0) {
193
- this.gl!.uniformMatrix4fv(u_view, false, viewMatrix.buffer);
194
- this.ext!.drawArraysInstancedANGLE(this.gl!.TRIANGLE_FAN, 0, 4, this.vertexCount);
 
 
 
 
 
 
 
 
195
  } else {
196
- this.gl!.clear(this.gl!.COLOR_BUFFER_BIT);
197
  }
198
  };
199
- }
200
 
201
- frame: () => void = () => {};
 
 
 
202
 
203
- render(scene: Scene, camera: Camera) {
204
- if (this.activeScene !== scene || this.activeCamera !== camera) {
205
- if (this.activeScene !== null && this.activeCamera !== null) {
206
- this.dispose();
207
- }
208
 
209
- this.activeScene = scene;
210
- this.activeCamera = camera;
211
- this.initWebGL();
212
- }
213
 
214
- this.frame();
 
215
  }
216
-
217
- dispose() {}
218
  }
 
2
  import type { Scene } from "../core/Scene";
3
  import type { Renderer } from "./Renderer";
4
 
 
5
  import { createWorker } from "./webgl/utils/worker";
6
 
7
  import { vertex } from "./webgl/shaders/vertex.glsl";
8
  import { frag } from "./webgl/shaders/frag.glsl";
9
+ import { Matrix4 } from "../math/Matrix4";
10
 
11
  export class WebGLRenderer implements Renderer {
12
+ render: (scene: Scene, camera: Camera) => void;
13
+ dispose: () => void;
14
+
15
+ constructor(canvas: HTMLCanvasElement) {
16
+ const gl = (canvas.getContext("webgl") || canvas.getContext("experimental-webgl")) as WebGLRenderingContext;
17
+ const ext = gl.getExtension("ANGLE_instanced_arrays") as ANGLE_instanced_arrays;
18
+
19
+ let activeScene: Scene;
20
+ let activeCamera: Camera;
21
+
22
+ let worker: Worker;
23
+ let vertexCount = 0;
24
+
25
+ let vertexShader: WebGLShader;
26
+ let fragmentShader: WebGLShader;
27
+ let program: WebGLProgram;
28
+
29
+ let u_projection: WebGLUniformLocation;
30
+ let u_viewport: WebGLUniformLocation;
31
+ let u_focal: WebGLUniformLocation;
32
+ let u_view: WebGLUniformLocation;
33
+
34
+ let positionAttribute: number;
35
+ let centerAttribute: number;
36
+ let colorAttribute: number;
37
+ let covAAttribute: number;
38
+ let covBAttribute: number;
39
+
40
+ let vertexBuffer: WebGLBuffer;
41
+ let centerBuffer: WebGLBuffer;
42
+ let colorBuffer: WebGLBuffer;
43
+ let covABuffer: WebGLBuffer;
44
+ let covBBuffer: WebGLBuffer;
45
+
46
+ let initialized = false;
47
+
48
+ const getViewMatrix = (camera: Camera) => {
49
+ const R = camera.rotation.buffer;
50
+ const t = camera.position.flat();
51
+ const camToWorld = [
52
+ [R[0], R[1], R[2], 0],
53
+ [R[3], R[4], R[5], 0],
54
+ [R[6], R[7], R[8], 0],
55
+ [
56
+ -t[0] * R[0] - t[1] * R[3] - t[2] * R[6],
57
+ -t[0] * R[1] - t[1] * R[4] - t[2] * R[7],
58
+ -t[0] * R[2] - t[1] * R[5] - t[2] * R[8],
59
+ 1,
60
+ ],
61
+ ].flat();
62
+ return new Matrix4(...camToWorld);
63
+ };
64
 
65
+ const initWebGL = () => {
66
+ worker = new Worker(
67
+ URL.createObjectURL(
68
+ new Blob(["(", createWorker.toString(), ")(self)"], { type: "application/javascript" })
69
+ )
70
+ );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
71
 
72
+ canvas.width = innerWidth;
73
+ canvas.height = innerHeight;
74
 
75
+ gl.viewport(0, 0, canvas.width, canvas.height);
76
+ activeCamera.updateProjectionMatrix(canvas.width, canvas.height);
77
 
78
+ vertexShader = gl.createShader(gl.VERTEX_SHADER) as WebGLShader;
79
+ gl.shaderSource(vertexShader, vertex);
80
+ gl.compileShader(vertexShader);
81
+ if (!gl.getShaderParameter(vertexShader, gl.COMPILE_STATUS)) {
82
+ console.error(gl.getShaderInfoLog(vertexShader));
83
+ }
84
 
85
+ fragmentShader = gl.createShader(gl.FRAGMENT_SHADER) as WebGLShader;
86
+ gl.shaderSource(fragmentShader, frag);
87
+ gl.compileShader(fragmentShader);
88
+ if (!gl.getShaderParameter(fragmentShader, gl.COMPILE_STATUS)) {
89
+ console.error(gl.getShaderInfoLog(fragmentShader));
90
+ }
91
 
92
+ program = gl.createProgram() as WebGLProgram;
93
+ gl.attachShader(program, vertexShader);
94
+ gl.attachShader(program, fragmentShader);
95
+ gl.linkProgram(program);
96
+ gl.useProgram(program);
97
+ if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
98
+ console.error(gl.getProgramInfoLog(program));
99
  }
100
+
101
+ gl.disable(gl.DEPTH_TEST);
102
+ gl.enable(gl.BLEND);
103
+ gl.blendFuncSeparate(gl.ONE_MINUS_DST_ALPHA, gl.ONE, gl.ONE_MINUS_DST_ALPHA, gl.ONE);
104
+ gl.blendEquationSeparate(gl.FUNC_ADD, gl.FUNC_ADD);
105
+
106
+ const viewMatrix = getViewMatrix(activeCamera);
107
+
108
+ u_projection = gl.getUniformLocation(program, "projection") as WebGLUniformLocation;
109
+ gl.uniformMatrix4fv(u_projection, false, activeCamera.projectionMatrix.buffer);
110
+
111
+ u_viewport = gl.getUniformLocation(program, "viewport") as WebGLUniformLocation;
112
+ gl.uniform2fv(u_viewport, new Float32Array([canvas.width, canvas.height]));
113
+
114
+ u_focal = gl.getUniformLocation(program, "focal") as WebGLUniformLocation;
115
+ gl.uniform2fv(u_focal, new Float32Array([activeCamera.fx, activeCamera.fy]));
116
+
117
+ u_view = gl.getUniformLocation(program, "view") as WebGLUniformLocation;
118
+ gl.uniformMatrix4fv(u_view, false, viewMatrix.buffer);
119
+
120
+ const triangleVertices = new Float32Array([-2, -2, 2, -2, 2, 2, -2, 2]);
121
+ vertexBuffer = gl.createBuffer() as WebGLBuffer;
122
+ gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
123
+ gl.bufferData(gl.ARRAY_BUFFER, triangleVertices, gl.STATIC_DRAW);
124
+
125
+ positionAttribute = gl.getAttribLocation(program, "position");
126
+ gl.enableVertexAttribArray(positionAttribute);
127
+ gl.vertexAttribPointer(positionAttribute, 2, gl.FLOAT, false, 0, 0);
128
+
129
+ centerBuffer = gl.createBuffer() as WebGLBuffer;
130
+ centerAttribute = gl.getAttribLocation(program, "center");
131
+ gl.enableVertexAttribArray(centerAttribute);
132
+ gl.bindBuffer(gl.ARRAY_BUFFER, centerBuffer);
133
+ gl.vertexAttribPointer(centerAttribute, 3, gl.FLOAT, false, 0, 0);
134
+ ext.vertexAttribDivisorANGLE(centerAttribute, 1);
135
+
136
+ colorBuffer = gl.createBuffer() as WebGLBuffer;
137
+ colorAttribute = gl.getAttribLocation(program, "color");
138
+ gl.enableVertexAttribArray(colorAttribute);
139
+ gl.bindBuffer(gl.ARRAY_BUFFER, colorBuffer);
140
+ gl.vertexAttribPointer(colorAttribute, 4, gl.FLOAT, false, 0, 0);
141
+ ext.vertexAttribDivisorANGLE(colorAttribute, 1);
142
+
143
+ covABuffer = gl.createBuffer() as WebGLBuffer;
144
+ covAAttribute = gl.getAttribLocation(program, "covA");
145
+ gl.enableVertexAttribArray(covAAttribute);
146
+ gl.bindBuffer(gl.ARRAY_BUFFER, covABuffer);
147
+ gl.vertexAttribPointer(covAAttribute, 3, gl.FLOAT, false, 0, 0);
148
+ ext.vertexAttribDivisorANGLE(covAAttribute, 1);
149
+
150
+ covBBuffer = gl.createBuffer() as WebGLBuffer;
151
+ covBAttribute = gl.getAttribLocation(program, "covB");
152
+ gl.enableVertexAttribArray(covBAttribute);
153
+ gl.bindBuffer(gl.ARRAY_BUFFER, covBBuffer);
154
+ gl.vertexAttribPointer(covBAttribute, 3, gl.FLOAT, false, 0, 0);
155
+ ext.vertexAttribDivisorANGLE(covBAttribute, 1);
156
+
157
+ worker.onmessage = (e) => {
158
+ if (e.data.center) {
159
+ let { covA, covB, center, color } = e.data;
160
+ vertexCount = center.length / 3;
161
+
162
+ gl.bindBuffer(gl.ARRAY_BUFFER, centerBuffer);
163
+ gl.bufferData(gl.ARRAY_BUFFER, center, gl.DYNAMIC_DRAW);
164
+
165
+ gl.bindBuffer(gl.ARRAY_BUFFER, colorBuffer);
166
+ gl.bufferData(gl.ARRAY_BUFFER, color, gl.DYNAMIC_DRAW);
167
+
168
+ gl.bindBuffer(gl.ARRAY_BUFFER, covABuffer);
169
+ gl.bufferData(gl.ARRAY_BUFFER, covA, gl.DYNAMIC_DRAW);
170
+
171
+ gl.bindBuffer(gl.ARRAY_BUFFER, covBBuffer);
172
+ gl.bufferData(gl.ARRAY_BUFFER, covB, gl.DYNAMIC_DRAW);
173
+ }
174
+ };
175
+
176
+ worker.postMessage({
177
+ buffer: activeScene.data.buffer,
178
+ vertexCount: activeScene.vertexCount,
179
+ });
180
+
181
+ initialized = true;
182
  };
183
 
184
+ this.render = (scene: Scene, camera: Camera) => {
185
+ if (scene !== activeScene || camera !== activeCamera) {
186
+ if (initialized) {
187
+ this.dispose();
188
+ }
189
 
190
+ activeScene = scene;
191
+ activeCamera = camera;
192
 
193
+ initWebGL();
194
+ }
195
+
196
+ activeCamera.updateProjectionMatrix(canvas.width, canvas.height);
197
+ const viewMatrix = getViewMatrix(activeCamera);
198
+ const viewProj = activeCamera.projectionMatrix.multiply(viewMatrix);
199
+ worker.postMessage({ view: viewProj.buffer });
200
+
201
+ if (vertexCount > 0) {
202
+ gl.uniformMatrix4fv(u_view, false, viewMatrix.buffer);
203
+ ext.drawArraysInstancedANGLE(gl.TRIANGLE_FAN, 0, 4, vertexCount);
204
  } else {
205
+ gl.clear(gl.COLOR_BUFFER_BIT);
206
  }
207
  };
 
208
 
209
+ this.dispose = () => {
210
+ gl.deleteShader(vertexShader);
211
+ gl.deleteShader(fragmentShader);
212
+ gl.deleteProgram(program);
213
 
214
+ gl.deleteBuffer(vertexBuffer);
215
+ gl.deleteBuffer(centerBuffer);
216
+ gl.deleteBuffer(colorBuffer);
217
+ gl.deleteBuffer(covABuffer);
218
+ gl.deleteBuffer(covBBuffer);
219
 
220
+ worker.terminate();
 
 
 
221
 
222
+ initialized = false;
223
+ };
224
  }
 
 
225
  }
viewer/src/lib/splat.js/renderers/webgl/utils/transformations.ts DELETED
@@ -1,19 +0,0 @@
1
- import type { Camera } from "$lib/splat.js/cameras/Camera";
2
- import { Matrix4 } from "$lib/splat.js/math/Matrix4";
3
-
4
- export function getViewMatrix(camera: Camera) {
5
- const R = camera.rotation.buffer;
6
- const t = camera.position.flat();
7
- const camToWorld = [
8
- [R[0], R[1], R[2], 0],
9
- [R[3], R[4], R[5], 0],
10
- [R[6], R[7], R[8], 0],
11
- [
12
- -t[0] * R[0] - t[1] * R[3] - t[2] * R[6],
13
- -t[0] * R[1] - t[1] * R[4] - t[2] * R[7],
14
- -t[0] * R[2] - t[1] * R[5] - t[2] * R[8],
15
- 1,
16
- ],
17
- ].flat();
18
- return new Matrix4(...camToWorld);
19
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
viewer/src/routes/viewer/[slug]/BabylonViewer.ts CHANGED
@@ -4,10 +4,12 @@ import "@babylonjs/loaders/glTF";
4
  import "@babylonjs/loaders/OBJ";
5
 
6
  export class BabylonViewer implements IViewer {
 
 
7
  engine: BABYLON.Engine;
8
  scene: BABYLON.Scene;
9
  camera: BABYLON.ArcRotateCamera;
10
- canvas: HTMLCanvasElement;
11
  triangleCount: number = 0;
12
 
13
  constructor(canvas: HTMLCanvasElement) {
 
4
  import "@babylonjs/loaders/OBJ";
5
 
6
  export class BabylonViewer implements IViewer {
7
+ canvas: HTMLCanvasElement;
8
+
9
  engine: BABYLON.Engine;
10
  scene: BABYLON.Scene;
11
  camera: BABYLON.ArcRotateCamera;
12
+
13
  triangleCount: number = 0;
14
 
15
  constructor(canvas: HTMLCanvasElement) {
viewer/src/routes/viewer/[slug]/SplatViewer.ts CHANGED
@@ -3,396 +3,51 @@ import * as SPLAT from "$lib/splat.js";
3
 
4
  export class SplatViewer implements IViewer {
5
  canvas: HTMLCanvasElement;
6
- gl: WebGLRenderingContext;
7
- ext: ANGLE_instanced_arrays;
8
- camera: SPLAT.Camera;
9
- orbitControls: SPLAT.OrbitControls;
10
-
11
- splatData: Uint8Array;
12
- vertexCount: number;
13
- worker: Worker;
14
-
15
- vertexShader: WebGLShader;
16
- fragmentShader: WebGLShader;
17
- program: WebGLProgram;
18
- a_position: number;
19
- a_center: number;
20
- a_color: number;
21
- a_covA: number;
22
- a_covB: number;
23
- vertexBuffer: WebGLBuffer | null;
24
- centerBuffer: WebGLBuffer | null;
25
- colorBuffer: WebGLBuffer | null;
26
- covABuffer: WebGLBuffer | null;
27
- covBBuffer: WebGLBuffer | null;
28
 
29
- dragging = false;
30
- panning = false;
31
- lastX: number = 0;
32
- lastY: number = 0;
33
 
34
- animationFrameId: number | null = null;
35
  disposed: boolean = false;
36
 
37
  constructor(canvas: HTMLCanvasElement) {
38
- this.disposed = false;
39
  this.canvas = canvas;
40
- this.gl = this.initWebGL();
41
- this.ext = this.gl.getExtension("ANGLE_instanced_arrays") as ANGLE_instanced_arrays;
42
- this.camera = new SPLAT.Camera();
43
- this.orbitControls = new SPLAT.OrbitControls(this.camera, this.canvas);
44
-
45
- this.splatData = new Uint8Array();
46
- this.vertexCount = 0;
47
-
48
- this.worker = new Worker(
49
- URL.createObjectURL(
50
- new Blob(["(", SPLAT.createWorker.toString(), ")(self)"], {
51
- type: "application/javascript",
52
- })
53
- )
54
- );
55
-
56
- const gl = this.gl;
57
-
58
- this.canvas.width = innerWidth;
59
- this.canvas.height = innerHeight;
60
- gl.viewport(0, 0, this.canvas.width, this.canvas.height);
61
- this.camera.updateProjectionMatrix(this.canvas.width, this.canvas.height);
62
-
63
- let viewMatrix = SPLAT.getViewMatrix(this.camera);
64
-
65
- this.vertexShader = gl.createShader(gl.VERTEX_SHADER) as WebGLShader;
66
- gl.shaderSource(this.vertexShader, SPLAT.vertex);
67
- gl.compileShader(this.vertexShader);
68
- if (!gl.getShaderParameter(this.vertexShader, gl.COMPILE_STATUS)) {
69
- console.error(gl.getShaderInfoLog(this.vertexShader));
70
- }
71
-
72
- this.fragmentShader = gl.createShader(gl.FRAGMENT_SHADER) as WebGLShader;
73
- gl.shaderSource(this.fragmentShader, SPLAT.frag);
74
- gl.compileShader(this.fragmentShader);
75
- if (!gl.getShaderParameter(this.fragmentShader, gl.COMPILE_STATUS)) {
76
- console.error(gl.getShaderInfoLog(this.fragmentShader));
77
- }
78
-
79
- this.program = gl.createProgram() as WebGLProgram;
80
- gl.attachShader(this.program, this.vertexShader);
81
- gl.attachShader(this.program, this.fragmentShader);
82
- gl.linkProgram(this.program);
83
- gl.useProgram(this.program);
84
-
85
- if (!gl.getProgramParameter(this.program, gl.LINK_STATUS)) {
86
- console.error(gl.getProgramInfoLog(this.program));
87
- }
88
-
89
- gl.disable(gl.DEPTH_TEST); // Disable depth testing
90
-
91
- // Enable blending
92
- gl.enable(gl.BLEND);
93
-
94
- // Set blending function
95
- gl.blendFuncSeparate(gl.ONE_MINUS_DST_ALPHA, gl.ONE, gl.ONE_MINUS_DST_ALPHA, gl.ONE);
96
-
97
- // Set blending equation
98
- gl.blendEquationSeparate(gl.FUNC_ADD, gl.FUNC_ADD);
99
-
100
- // projection
101
- const u_projection = gl.getUniformLocation(this.program, "projection");
102
- gl.uniformMatrix4fv(u_projection, false, this.camera.projectionMatrix.buffer);
103
-
104
- // viewport
105
- const u_viewport = gl.getUniformLocation(this.program, "viewport");
106
- gl.uniform2fv(u_viewport, new Float32Array([this.canvas.width, this.canvas.height]));
107
-
108
- // focal
109
- const u_focal = gl.getUniformLocation(this.program, "focal");
110
- gl.uniform2fv(u_focal, new Float32Array([this.camera.fx, this.camera.fy]));
111
-
112
- // view
113
- const u_view = gl.getUniformLocation(this.program, "view");
114
- gl.uniformMatrix4fv(u_view, false, viewMatrix.buffer);
115
-
116
- // positions
117
- const triangleVertices = new Float32Array([-2, -2, 2, -2, 2, 2, -2, 2]);
118
- this.vertexBuffer = gl.createBuffer();
119
- gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer);
120
- gl.bufferData(gl.ARRAY_BUFFER, triangleVertices, gl.STATIC_DRAW);
121
-
122
- this.a_position = gl.getAttribLocation(this.program, "position");
123
- gl.enableVertexAttribArray(this.a_position);
124
- gl.vertexAttribPointer(this.a_position, 2, gl.FLOAT, false, 0, 0);
125
-
126
- // center
127
- this.centerBuffer = gl.createBuffer();
128
- this.a_center = gl.getAttribLocation(this.program, "center");
129
- gl.enableVertexAttribArray(this.a_center);
130
- gl.bindBuffer(gl.ARRAY_BUFFER, this.centerBuffer);
131
- gl.vertexAttribPointer(this.a_center, 3, gl.FLOAT, false, 0, 0);
132
- this.ext.vertexAttribDivisorANGLE(this.a_center, 1); // Use the extension here
133
-
134
- // color
135
- this.colorBuffer = gl.createBuffer();
136
- this.a_color = gl.getAttribLocation(this.program, "color");
137
- gl.enableVertexAttribArray(this.a_color);
138
- gl.bindBuffer(gl.ARRAY_BUFFER, this.colorBuffer);
139
- gl.vertexAttribPointer(this.a_color, 4, gl.FLOAT, false, 0, 0);
140
- this.ext.vertexAttribDivisorANGLE(this.a_color, 1); // Use the extension here
141
-
142
- // cov
143
- this.covABuffer = gl.createBuffer();
144
- this.a_covA = gl.getAttribLocation(this.program, "covA");
145
- gl.enableVertexAttribArray(this.a_covA);
146
- gl.bindBuffer(gl.ARRAY_BUFFER, this.covABuffer);
147
- gl.vertexAttribPointer(this.a_covA, 3, gl.FLOAT, false, 0, 0);
148
- this.ext.vertexAttribDivisorANGLE(this.a_covA, 1); // Use the extension here
149
-
150
- this.covBBuffer = gl.createBuffer();
151
- this.a_covB = gl.getAttribLocation(this.program, "covB");
152
- gl.enableVertexAttribArray(this.a_covB);
153
- gl.bindBuffer(gl.ARRAY_BUFFER, this.covBBuffer);
154
- gl.vertexAttribPointer(this.a_covB, 3, gl.FLOAT, false, 0, 0);
155
- this.ext.vertexAttribDivisorANGLE(this.a_covB, 1); // Use the extension here
156
-
157
- this.worker.onmessage = (e) => {
158
- if (e.data.buffer) {
159
- this.splatData = new Uint8Array(e.data.buffer);
160
- const blob = new Blob([this.splatData.buffer], {
161
- type: "application/octet-stream",
162
- });
163
- const link = document.createElement("a");
164
- link.download = "model.splat";
165
- link.href = URL.createObjectURL(blob);
166
- document.body.appendChild(link);
167
- link.click();
168
- } else {
169
- let { covA, covB, center, color } = e.data;
170
- vertexCount = center.length / 3;
171
-
172
- gl.bindBuffer(gl.ARRAY_BUFFER, this.centerBuffer);
173
- gl.bufferData(gl.ARRAY_BUFFER, center, gl.DYNAMIC_DRAW);
174
-
175
- gl.bindBuffer(gl.ARRAY_BUFFER, this.colorBuffer);
176
- gl.bufferData(gl.ARRAY_BUFFER, color, gl.DYNAMIC_DRAW);
177
-
178
- gl.bindBuffer(gl.ARRAY_BUFFER, this.covABuffer);
179
- gl.bufferData(gl.ARRAY_BUFFER, covA, gl.DYNAMIC_DRAW);
180
-
181
- gl.bindBuffer(gl.ARRAY_BUFFER, this.covBBuffer);
182
- gl.bufferData(gl.ARRAY_BUFFER, covB, gl.DYNAMIC_DRAW);
183
- }
184
- };
185
-
186
- let vertexCount = 0;
187
-
188
- const frame = () => {
189
- this.orbitControls.update();
190
-
191
- this.camera.updateProjectionMatrix(this.canvas.width, this.canvas.height);
192
- viewMatrix = SPLAT.getViewMatrix(this.camera);
193
-
194
- const viewProj = this.camera.projectionMatrix.multiply(viewMatrix);
195
- this.worker.postMessage({ view: viewProj.buffer });
196
-
197
- if (vertexCount > 0) {
198
- gl.uniformMatrix4fv(u_view, false, viewMatrix.buffer);
199
- this.ext.drawArraysInstancedANGLE(gl.TRIANGLE_FAN, 0, 4, vertexCount);
200
- } else {
201
- gl.clear(gl.COLOR_BUFFER_BIT);
202
- }
203
 
204
- if (!this.disposed) {
205
- this.animationFrameId = requestAnimationFrame(frame);
206
- }
207
- };
208
-
209
- this.animationFrameId = requestAnimationFrame(frame);
210
-
211
- document.addEventListener("dragenter", this.preventDefault);
212
- document.addEventListener("dragover", this.preventDefault);
213
- document.addEventListener("dragleave", this.preventDefault);
214
- document.addEventListener("contextmenu", this.preventDefault);
215
-
216
- this.handleDrop = this.handleDrop.bind(this);
217
- this.handleResize = this.handleResize.bind(this);
218
-
219
- this.canvas.addEventListener("drop", this.handleDrop);
220
-
221
- window.addEventListener("resize", this.handleResize);
222
- }
223
-
224
- initWebGL(): WebGLRenderingContext {
225
- const gl = this.canvas.getContext("webgl") || this.canvas.getContext("experimental-webgl");
226
- if (!gl) {
227
- alert("WebGL is not supported on your browser!");
228
- }
229
- return gl as WebGLRenderingContext;
230
- }
231
-
232
- preventDefault(e: Event) {
233
- e.preventDefault();
234
- e.stopPropagation();
235
  }
236
 
237
- async loadScene(url: string, onProgress?: (progress: number) => void) {
238
- const req = await fetch(url, {
239
- mode: "cors",
240
- credentials: "omit",
241
  });
242
- console.log(req);
243
- if (req.status != 200) {
244
- throw new Error(req.status + " Unable to load " + req.url);
245
- }
246
-
247
- const rowLength = 3 * 4 + 3 * 4 + 4 + 4;
248
- const reader = req.body!.getReader();
249
- this.splatData = new Uint8Array(req.headers.get("content-length") as any);
250
-
251
- console.log("Vertex Count", this.splatData.length / rowLength);
252
-
253
- let bytesRead = 0;
254
- let lastVertexCount = -1;
255
- let stopLoading = false;
256
-
257
- while (true) {
258
- const { done, value } = await reader.read();
259
- if (done || stopLoading) break;
260
 
261
- this.splatData.set(value, bytesRead);
262
- bytesRead += value.length;
263
-
264
- if (onProgress) {
265
- onProgress(bytesRead / this.splatData.length);
266
- }
267
-
268
- if (this.vertexCount > lastVertexCount) {
269
- this.worker.postMessage({
270
- buffer: this.splatData.buffer,
271
- vertexCount: Math.floor(bytesRead / rowLength),
272
- });
273
- lastVertexCount = this.vertexCount;
274
- }
275
- }
276
- if (!stopLoading) {
277
- this.worker.postMessage({
278
- buffer: this.splatData.buffer,
279
- vertexCount: Math.floor(bytesRead / rowLength),
280
- });
281
- }
282
- }
283
-
284
- handleDrop(e: DragEvent) {
285
- e.preventDefault();
286
- e.stopPropagation();
287
- this.selectFile(e.dataTransfer!.files[0]);
288
- }
289
 
290
- selectFile(file: File) {
291
- const reader = new FileReader();
292
- reader.onload = () => {
293
- this.splatData = new Uint8Array(reader.result as ArrayBuffer);
294
- if (
295
- this.splatData[0] !== 112 ||
296
- this.splatData[1] !== 108 ||
297
- this.splatData[2] !== 121 ||
298
- this.splatData[3] !== 10
299
- ) {
300
- alert("Invalid file format");
301
- return;
302
  }
303
- this.worker.postMessage({ ply: this.splatData.buffer });
304
  };
305
- reader.readAsArrayBuffer(file);
306
- }
307
-
308
- handleResize() {
309
- this.canvas.width = innerWidth;
310
- this.canvas.height = innerHeight;
311
- this.gl.viewport(0, 0, this.canvas.width, this.canvas.height);
312
- this.camera.updateProjectionMatrix(this.canvas.width, this.canvas.height);
313
 
314
- const u_projection = this.gl.getUniformLocation(this.program, "projection");
315
- this.gl.uniformMatrix4fv(u_projection, false, this.camera.projectionMatrix.buffer);
316
 
317
- const u_viewport = this.gl.getUniformLocation(this.program, "viewport");
318
- this.gl.uniform2fv(u_viewport, new Float32Array([this.canvas.width, this.canvas.height]));
319
  }
320
 
321
  dispose() {
322
- this.worker.terminate();
323
- this.orbitControls.dispose();
324
-
325
- this.gl.disableVertexAttribArray(this.a_position);
326
- this.gl.disableVertexAttribArray(this.a_center);
327
- this.gl.disableVertexAttribArray(this.a_color);
328
- this.gl.disableVertexAttribArray(this.a_covA);
329
- this.gl.disableVertexAttribArray(this.a_covB);
330
-
331
- this.gl.deleteBuffer(this.vertexBuffer);
332
- this.gl.deleteBuffer(this.centerBuffer);
333
- this.gl.deleteBuffer(this.colorBuffer);
334
- this.gl.deleteBuffer(this.covABuffer);
335
- this.gl.deleteBuffer(this.covBBuffer);
336
-
337
- this.gl.detachShader(this.program, this.vertexShader);
338
- this.gl.detachShader(this.program, this.fragmentShader);
339
- this.gl.deleteShader(this.vertexShader);
340
- this.gl.deleteShader(this.fragmentShader);
341
-
342
- this.gl.deleteProgram(this.program);
343
-
344
- this.vertexBuffer = null;
345
- this.centerBuffer = null;
346
- this.colorBuffer = null;
347
- this.covABuffer = null;
348
- this.covBBuffer = null;
349
-
350
- this.splatData = new Uint8Array();
351
-
352
- if (this.animationFrameId) {
353
- cancelAnimationFrame(this.animationFrameId);
354
- }
355
 
356
  this.disposed = true;
357
-
358
- document.removeEventListener("dragenter", this.preventDefault);
359
- document.removeEventListener("dragover", this.preventDefault);
360
- document.removeEventListener("dragleave", this.preventDefault);
361
- document.removeEventListener("contextmenu", this.preventDefault);
362
-
363
- this.canvas.removeEventListener("drop", this.handleDrop);
364
-
365
- window.removeEventListener("resize", this.handleResize);
366
  }
367
 
368
  async capture(): Promise<string | null> {
369
- return new Promise((resolve) => {
370
- requestAnimationFrame(() => {
371
- const offscreenCanvas = document.createElement("canvas");
372
- offscreenCanvas.width = 512;
373
- offscreenCanvas.height = 512;
374
- const offscreenContext = offscreenCanvas.getContext("2d")!;
375
-
376
- const x = (this.canvas.width - offscreenCanvas.width) / 2;
377
- const y = (this.canvas.height - offscreenCanvas.height) / 2;
378
-
379
- offscreenContext.drawImage(
380
- this.canvas,
381
- x,
382
- y,
383
- offscreenCanvas.width,
384
- offscreenCanvas.height,
385
- 0,
386
- 0,
387
- offscreenCanvas.width,
388
- offscreenCanvas.height
389
- );
390
- const dataURL = offscreenCanvas.toDataURL("image/png");
391
- offscreenCanvas.remove();
392
-
393
- resolve(dataURL);
394
- });
395
- });
396
  }
397
 
398
  getStats(): { name: string; value: any }[] {
 
3
 
4
  export class SplatViewer implements IViewer {
5
  canvas: HTMLCanvasElement;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6
 
7
+ renderer: SPLAT.Renderer;
8
+ scene: SPLAT.Scene;
9
+ camera: SPLAT.Camera;
10
+ controls: SPLAT.OrbitControls;
11
 
 
12
  disposed: boolean = false;
13
 
14
  constructor(canvas: HTMLCanvasElement) {
 
15
  this.canvas = canvas;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
16
 
17
+ this.renderer = new SPLAT.WebGLRenderer(canvas);
18
+ this.scene = new SPLAT.Scene();
19
+ this.camera = new SPLAT.Camera();
20
+ this.controls = new SPLAT.OrbitControls(this.camera, canvas);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
21
  }
22
 
23
+ async loadScene(url: string, loadingBarCallback?: (progress: number) => void) {
24
+ await SPLAT.Loader.LoadAsync(url, this.scene, (progress) => {
25
+ loadingBarCallback?.(progress);
 
26
  });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
27
 
28
+ const frame = () => {
29
+ this.controls.update();
30
+ this.renderer.render(this.scene, this.camera);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
31
 
32
+ if (!this.disposed) {
33
+ requestAnimationFrame(frame);
 
 
 
 
 
 
 
 
 
 
34
  }
 
35
  };
 
 
 
 
 
 
 
 
36
 
37
+ this.disposed = false;
 
38
 
39
+ requestAnimationFrame(frame);
 
40
  }
41
 
42
  dispose() {
43
+ this.controls.dispose();
44
+ this.renderer.dispose();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
45
 
46
  this.disposed = true;
 
 
 
 
 
 
 
 
 
47
  }
48
 
49
  async capture(): Promise<string | null> {
50
+ return null;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
51
  }
52
 
53
  getStats(): { name: string; value: any }[] {