@popaya/pgsg-viewer 0.1.0 → 0.1.2
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.
- package/dist/{chunk-TBA7YDMF.js → chunk-UDJ24YL2.js} +2 -2
- package/dist/core/index.d.ts +1 -0
- package/dist/core/index.js +1 -1
- package/dist/index.js +1 -1
- package/dist/{pgsg-three-viewer-ZU5OI33Q.js → pgsg-three-viewer-2ONIARDQ.js} +123 -171
- package/dist/pgsg-three-viewer-2ONIARDQ.js.map +1 -0
- package/dist/react/index.d.ts +2 -1
- package/dist/react/index.js +9 -2
- package/dist/react/index.js.map +1 -1
- package/package.json +1 -1
- package/dist/pgsg-three-viewer-ZU5OI33Q.js.map +0 -1
- /package/dist/{chunk-TBA7YDMF.js.map → chunk-UDJ24YL2.js.map} +0 -0
|
@@ -10,7 +10,7 @@ var PGSGViewer = class {
|
|
|
10
10
|
const { PGSGPlayCanvasViewer } = await import("./pgsg-playcanvas-viewer-4WORM4WU.js");
|
|
11
11
|
this.engine = new PGSGPlayCanvasViewer(this.options);
|
|
12
12
|
} else {
|
|
13
|
-
const { PGSGThreeViewer } = await import("./pgsg-three-viewer-
|
|
13
|
+
const { PGSGThreeViewer } = await import("./pgsg-three-viewer-2ONIARDQ.js");
|
|
14
14
|
this.engine = new PGSGThreeViewer(this.options);
|
|
15
15
|
}
|
|
16
16
|
await this.engine.load();
|
|
@@ -30,4 +30,4 @@ var PGSGViewer = class {
|
|
|
30
30
|
export {
|
|
31
31
|
PGSGViewer
|
|
32
32
|
};
|
|
33
|
-
//# sourceMappingURL=chunk-
|
|
33
|
+
//# sourceMappingURL=chunk-UDJ24YL2.js.map
|
package/dist/core/index.d.ts
CHANGED
package/dist/core/index.js
CHANGED
package/dist/index.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
// src/viewers/three/pgsg-three-viewer.ts
|
|
2
|
-
import * as
|
|
2
|
+
import * as THREE3 from "three";
|
|
3
|
+
import { GLTFLoader } from "three/examples/jsm/Addons.js";
|
|
3
4
|
|
|
4
5
|
// src/viewers/spark/SparkSplatRenderer.ts
|
|
5
6
|
import { SplatMesh } from "@sparkjsdev/spark";
|
|
6
|
-
import * as THREE from "three";
|
|
7
7
|
var SparkSplatRenderer = class {
|
|
8
8
|
mesh;
|
|
9
9
|
async load(source) {
|
|
@@ -13,13 +13,18 @@ var SparkSplatRenderer = class {
|
|
|
13
13
|
this.mesh = new SplatMesh({
|
|
14
14
|
url: source.url
|
|
15
15
|
});
|
|
16
|
-
this.mesh.
|
|
16
|
+
await this.mesh.initialized;
|
|
17
|
+
this.mesh.updateMatrixWorld(true);
|
|
18
|
+
const box = this.mesh.getBoundingBox(true);
|
|
19
|
+
const min = box.min.clone();
|
|
20
|
+
const max = box.max.clone();
|
|
17
21
|
this.mesh.position.set(0, 0, 0);
|
|
22
|
+
this.mesh.quaternion.identity();
|
|
18
23
|
return {
|
|
19
24
|
mesh: this.mesh,
|
|
20
25
|
bounds: {
|
|
21
|
-
min
|
|
22
|
-
max
|
|
26
|
+
min,
|
|
27
|
+
max
|
|
23
28
|
}
|
|
24
29
|
};
|
|
25
30
|
}
|
|
@@ -35,7 +40,7 @@ var SparkSplatRenderer = class {
|
|
|
35
40
|
};
|
|
36
41
|
|
|
37
42
|
// src/viewers/three/controls/WalkCapsuleController.ts
|
|
38
|
-
import * as
|
|
43
|
+
import * as THREE from "three";
|
|
39
44
|
var WalkCapsuleController = class {
|
|
40
45
|
dom;
|
|
41
46
|
camera;
|
|
@@ -48,21 +53,22 @@ var WalkCapsuleController = class {
|
|
|
48
53
|
lookSensitivity;
|
|
49
54
|
yaw = 0;
|
|
50
55
|
pitch = 0;
|
|
51
|
-
velocity = new
|
|
56
|
+
velocity = new THREE.Vector3();
|
|
52
57
|
onGround = false;
|
|
53
58
|
keys = /* @__PURE__ */ new Set();
|
|
54
|
-
raycaster = new
|
|
59
|
+
raycaster = new THREE.Raycaster();
|
|
55
60
|
constructor(opts) {
|
|
56
61
|
this.dom = opts.dom;
|
|
57
62
|
this.camera = opts.camera;
|
|
58
63
|
this.colliders = opts.colliders;
|
|
64
|
+
console.log("colliders", this.colliders.length);
|
|
59
65
|
this.eyeHeight = opts.eyeHeight ?? 1.6;
|
|
60
66
|
this.radius = opts.radius ?? 0.35;
|
|
61
67
|
this.gravity = opts.gravity ?? 9.8;
|
|
62
68
|
this.walkSpeed = opts.walkSpeed ?? 2.5;
|
|
63
69
|
this.runMultiplier = opts.runMultiplier ?? 2;
|
|
64
70
|
this.lookSensitivity = opts.lookSensitivity ?? 22e-4;
|
|
65
|
-
const euler = new
|
|
71
|
+
const euler = new THREE.Euler().setFromQuaternion(
|
|
66
72
|
this.camera.quaternion,
|
|
67
73
|
"YXZ"
|
|
68
74
|
);
|
|
@@ -105,13 +111,13 @@ var WalkCapsuleController = class {
|
|
|
105
111
|
if (document.pointerLockElement !== this.dom) return;
|
|
106
112
|
this.yaw -= e.movementX * this.lookSensitivity;
|
|
107
113
|
this.pitch -= e.movementY * this.lookSensitivity;
|
|
108
|
-
this.pitch =
|
|
114
|
+
this.pitch = THREE.MathUtils.clamp(
|
|
109
115
|
this.pitch,
|
|
110
116
|
-Math.PI / 2 + 0.01,
|
|
111
117
|
Math.PI / 2 - 0.01
|
|
112
118
|
);
|
|
113
119
|
this.camera.quaternion.setFromEuler(
|
|
114
|
-
new
|
|
120
|
+
new THREE.Euler(this.pitch, this.yaw, 0, "YXZ")
|
|
115
121
|
);
|
|
116
122
|
};
|
|
117
123
|
onKeyDown = (e) => {
|
|
@@ -126,30 +132,25 @@ var WalkCapsuleController = class {
|
|
|
126
132
|
update(dt) {
|
|
127
133
|
if (!Number.isFinite(this.camera.position.y)) return;
|
|
128
134
|
dt = Math.min(dt, 0.05);
|
|
129
|
-
const forward = new
|
|
135
|
+
const forward = new THREE.Vector3();
|
|
130
136
|
this.camera.getWorldDirection(forward);
|
|
131
137
|
forward.y = 0;
|
|
132
138
|
forward.normalize();
|
|
133
|
-
const right = new
|
|
134
|
-
const wish = new
|
|
139
|
+
const right = new THREE.Vector3().crossVectors(forward, new THREE.Vector3(0, 1, 0)).normalize();
|
|
140
|
+
const wish = new THREE.Vector3();
|
|
135
141
|
if (this.keys.has("w")) wish.add(forward);
|
|
136
142
|
if (this.keys.has("s")) wish.sub(forward);
|
|
137
143
|
if (this.keys.has("d")) wish.add(right);
|
|
138
144
|
if (this.keys.has("a")) wish.sub(right);
|
|
139
145
|
if (wish.lengthSq() > 0) wish.normalize();
|
|
140
|
-
if (this.keys.size > 0) {
|
|
141
|
-
console.log("walking", [...this.keys]);
|
|
142
|
-
}
|
|
143
146
|
const speed = this.walkSpeed * (this.keys.has("shift") ? this.runMultiplier : 1);
|
|
144
147
|
this.velocity.x = wish.x * speed;
|
|
145
148
|
this.velocity.z = wish.z * speed;
|
|
146
|
-
|
|
147
|
-
this.velocity.y -= this.gravity * dt;
|
|
148
|
-
} else {
|
|
149
|
-
this.velocity.y = 0;
|
|
150
|
-
}
|
|
149
|
+
this.velocity.y -= this.gravity * dt;
|
|
151
150
|
const nextPos = this.camera.position.clone();
|
|
152
|
-
nextPos.
|
|
151
|
+
nextPos.x += this.velocity.x * dt;
|
|
152
|
+
nextPos.z += this.velocity.z * dt;
|
|
153
|
+
nextPos.y += this.velocity.y * dt;
|
|
153
154
|
const resolved = this.resolveGroundAndCollisions(nextPos);
|
|
154
155
|
this.camera.position.copy(resolved);
|
|
155
156
|
}
|
|
@@ -158,16 +159,31 @@ var WalkCapsuleController = class {
|
|
|
158
159
|
// --------------------------------------------------
|
|
159
160
|
resolveGroundAndCollisions(desiredCamPos) {
|
|
160
161
|
const camPos = desiredCamPos.clone();
|
|
161
|
-
const
|
|
162
|
-
const origin =
|
|
163
|
-
|
|
164
|
-
this.raycaster.
|
|
162
|
+
const maxRayDown = 10;
|
|
163
|
+
const origin = camPos.clone();
|
|
164
|
+
origin.y += 2;
|
|
165
|
+
this.raycaster.set(origin, new THREE.Vector3(0, -1, 0));
|
|
166
|
+
this.raycaster.near = 0;
|
|
167
|
+
this.raycaster.far = maxRayDown;
|
|
165
168
|
const hits = this.raycaster.intersectObjects(this.colliders, true);
|
|
166
|
-
|
|
167
|
-
|
|
169
|
+
let groundHit = null;
|
|
170
|
+
for (const hit of hits) {
|
|
171
|
+
if (!hit.face) continue;
|
|
172
|
+
const normalMatrix = new THREE.Matrix3().getNormalMatrix(
|
|
173
|
+
hit.object.matrixWorld
|
|
174
|
+
);
|
|
175
|
+
const worldNormal = hit.face.normal.clone().applyMatrix3(normalMatrix).normalize();
|
|
176
|
+
if (worldNormal.y > 0.5) {
|
|
177
|
+
groundHit = hit;
|
|
178
|
+
break;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
if (groundHit) {
|
|
182
|
+
const groundY = groundHit.point.y;
|
|
168
183
|
const targetY = groundY + this.eyeHeight;
|
|
169
|
-
const
|
|
170
|
-
|
|
184
|
+
const falling = this.velocity.y <= 0;
|
|
185
|
+
const maxSnap = 1;
|
|
186
|
+
if (falling && camPos.y <= targetY + maxSnap) {
|
|
171
187
|
camPos.y = targetY;
|
|
172
188
|
this.velocity.y = 0;
|
|
173
189
|
this.onGround = true;
|
|
@@ -180,10 +196,10 @@ var WalkCapsuleController = class {
|
|
|
180
196
|
const center = camPos.clone();
|
|
181
197
|
center.y -= this.eyeHeight * 0.9;
|
|
182
198
|
const dirs = [
|
|
183
|
-
new
|
|
184
|
-
new
|
|
185
|
-
new
|
|
186
|
-
new
|
|
199
|
+
new THREE.Vector3(1, 0, 0),
|
|
200
|
+
new THREE.Vector3(-1, 0, 0),
|
|
201
|
+
new THREE.Vector3(0, 0, 1),
|
|
202
|
+
new THREE.Vector3(0, 0, -1)
|
|
187
203
|
];
|
|
188
204
|
for (const d of dirs) {
|
|
189
205
|
this.raycaster.set(center, d);
|
|
@@ -193,18 +209,12 @@ var WalkCapsuleController = class {
|
|
|
193
209
|
const overlap = this.radius + 0.25 - wallHits[0].distance;
|
|
194
210
|
if (overlap > 0) camPos.addScaledVector(d, -overlap);
|
|
195
211
|
}
|
|
196
|
-
console.log({
|
|
197
|
-
camY: camPos.y.toFixed(2),
|
|
198
|
-
footY: footY.toFixed(2),
|
|
199
|
-
groundHit: hits[0]?.point.y.toFixed(2),
|
|
200
|
-
onGround: this.onGround
|
|
201
|
-
});
|
|
202
212
|
return camPos;
|
|
203
213
|
}
|
|
204
214
|
};
|
|
205
215
|
|
|
206
216
|
// src/viewers/three/controls/OrbitCameraController.ts
|
|
207
|
-
import * as
|
|
217
|
+
import * as THREE2 from "three";
|
|
208
218
|
var OrbitCameraController = class {
|
|
209
219
|
camera;
|
|
210
220
|
dom;
|
|
@@ -256,7 +266,7 @@ var OrbitCameraController = class {
|
|
|
256
266
|
syncFromCamera() {
|
|
257
267
|
const offset = this.camera.position.clone().sub(this.target);
|
|
258
268
|
this.distance = offset.length();
|
|
259
|
-
const spherical = new
|
|
269
|
+
const spherical = new THREE2.Spherical().setFromVector3(offset);
|
|
260
270
|
this.yaw = spherical.theta;
|
|
261
271
|
this.pitch = spherical.phi;
|
|
262
272
|
}
|
|
@@ -285,21 +295,21 @@ var OrbitCameraController = class {
|
|
|
285
295
|
if (this.dragging) {
|
|
286
296
|
this.yaw -= dx * this.rotateSpeed;
|
|
287
297
|
this.pitch -= dy * this.rotateSpeed;
|
|
288
|
-
this.pitch =
|
|
298
|
+
this.pitch = THREE2.MathUtils.clamp(this.pitch, 0.01, Math.PI - 0.01);
|
|
289
299
|
this.updateCamera();
|
|
290
300
|
return;
|
|
291
301
|
}
|
|
292
302
|
if (this.panning) {
|
|
293
303
|
const panScale = this.distance * this.panSpeed;
|
|
294
|
-
const right = new
|
|
295
|
-
const up = new
|
|
304
|
+
const right = new THREE2.Vector3();
|
|
305
|
+
const up = new THREE2.Vector3();
|
|
296
306
|
this.camera.getWorldDirection(up);
|
|
297
307
|
right.crossVectors(this.camera.up, up).normalize();
|
|
298
308
|
up.crossVectors(
|
|
299
309
|
right,
|
|
300
|
-
this.camera.getWorldDirection(new
|
|
310
|
+
this.camera.getWorldDirection(new THREE2.Vector3())
|
|
301
311
|
).normalize();
|
|
302
|
-
const panOffset = new
|
|
312
|
+
const panOffset = new THREE2.Vector3().addScaledVector(right, dx * panScale).addScaledVector(up, -dy * panScale);
|
|
303
313
|
this.target.add(panOffset);
|
|
304
314
|
this.camera.position.add(panOffset);
|
|
305
315
|
}
|
|
@@ -308,7 +318,7 @@ var OrbitCameraController = class {
|
|
|
308
318
|
e.preventDefault();
|
|
309
319
|
const zoomFactor = Math.pow(this.zoomSpeed, e.deltaY / 100);
|
|
310
320
|
this.distance *= zoomFactor;
|
|
311
|
-
this.distance =
|
|
321
|
+
this.distance = THREE2.MathUtils.clamp(
|
|
312
322
|
this.distance,
|
|
313
323
|
this.minDistance,
|
|
314
324
|
this.maxDistance
|
|
@@ -319,8 +329,8 @@ var OrbitCameraController = class {
|
|
|
319
329
|
// Camera math
|
|
320
330
|
// -----------------------------
|
|
321
331
|
updateCamera() {
|
|
322
|
-
const spherical = new
|
|
323
|
-
const pos = new
|
|
332
|
+
const spherical = new THREE2.Spherical(this.distance, this.pitch, this.yaw);
|
|
333
|
+
const pos = new THREE2.Vector3().setFromSpherical(spherical).add(this.target);
|
|
324
334
|
this.camera.position.copy(pos);
|
|
325
335
|
this.camera.lookAt(this.target);
|
|
326
336
|
}
|
|
@@ -340,72 +350,35 @@ var PGSGThreeViewer = class {
|
|
|
340
350
|
throw new Error("[PGSGThreeViewer] Container is required");
|
|
341
351
|
}
|
|
342
352
|
}
|
|
343
|
-
scene = new
|
|
353
|
+
scene = new THREE3.Scene();
|
|
344
354
|
camera;
|
|
345
355
|
renderer;
|
|
346
356
|
splats;
|
|
347
357
|
container;
|
|
348
358
|
controls;
|
|
349
359
|
running = false;
|
|
350
|
-
orbitTarget = new
|
|
351
|
-
// 🔑 CRITICAL
|
|
360
|
+
orbitTarget = new THREE3.Vector3();
|
|
352
361
|
lastTime = performance.now();
|
|
353
362
|
colliders = [];
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
splatMesh;
|
|
358
|
-
splatPresets = [
|
|
359
|
-
// P0: identity
|
|
360
|
-
{ r: [0, 0, 0], s: [1, 1, 1] },
|
|
361
|
-
// P1: Z-up -> Y-up (common GS)
|
|
362
|
-
{ r: [-Math.PI / 2, 0, 0], s: [1, 1, 1] },
|
|
363
|
-
// P2: Z-up -> Y-up + 90° roll (your screenshot strongly suggests this)
|
|
364
|
-
{ r: [-Math.PI / 2, 0, Math.PI / 2], s: [1, 1, 1] },
|
|
365
|
-
// P3: Z-up -> Y-up + -90° roll
|
|
366
|
-
{ r: [-Math.PI / 2, 0, -Math.PI / 2], s: [1, 1, 1] },
|
|
367
|
-
// P4: Z-up -> Y-up + flip forward
|
|
368
|
-
{ r: [-Math.PI / 2, Math.PI, 0], s: [1, 1, 1] },
|
|
369
|
-
// P5: Z-up -> Y-up + 90° roll + flip forward
|
|
370
|
-
{ r: [-Math.PI / 2, Math.PI, Math.PI / 2], s: [1, 1, 1] },
|
|
371
|
-
// P6: Z-up -> Y-up + handedness fix
|
|
372
|
-
{ r: [-Math.PI / 2, 0, 0], s: [1, 1, -1] },
|
|
373
|
-
// P7: Z-up -> Y-up + 90° roll + handedness fix
|
|
374
|
-
{ r: [-Math.PI / 2, 0, Math.PI / 2], s: [1, 1, -1] }
|
|
375
|
-
];
|
|
376
|
-
splatPresetIndex = 0;
|
|
363
|
+
worldRoot = new THREE3.Object3D();
|
|
364
|
+
splatPivot = new THREE3.Object3D();
|
|
365
|
+
colliderMesh;
|
|
377
366
|
async load() {
|
|
378
367
|
this.initRenderer();
|
|
379
368
|
this.initCamera();
|
|
380
369
|
this.initScene();
|
|
370
|
+
this.scene.add(this.worldRoot);
|
|
381
371
|
this.splats = new SparkSplatRenderer();
|
|
382
372
|
const { mesh, bounds } = await this.splats.load(this.options.source);
|
|
383
373
|
this.splatPivot.clear();
|
|
384
374
|
this.splatPivot.add(mesh);
|
|
385
|
-
this.
|
|
386
|
-
this.
|
|
387
|
-
this.
|
|
388
|
-
this.
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
);
|
|
393
|
-
ground.rotation.x = -Math.PI / 2;
|
|
394
|
-
ground.position.y = this.FLOOR_Y;
|
|
395
|
-
ground.updateMatrixWorld(true);
|
|
396
|
-
this.scene.add(ground);
|
|
397
|
-
this.colliders = [ground];
|
|
398
|
-
console.log("[PLY bounds]", bounds.min, bounds.max);
|
|
399
|
-
this.addBoxCollider(
|
|
400
|
-
new THREE4.Vector3(0.2, 3, 10),
|
|
401
|
-
// thickness, height, length
|
|
402
|
-
new THREE4.Vector3(2.5, 1.5, 0)
|
|
403
|
-
// center
|
|
404
|
-
);
|
|
405
|
-
this.addBoxCollider(
|
|
406
|
-
new THREE4.Vector3(1.5, 0.8, 0.8),
|
|
407
|
-
new THREE4.Vector3(0, 0.4, -1)
|
|
408
|
-
);
|
|
375
|
+
this.worldRoot.add(this.splatPivot);
|
|
376
|
+
this.worldRoot.rotation.x = Math.PI;
|
|
377
|
+
this.worldRoot.position.y -= bounds.min.y;
|
|
378
|
+
this.worldRoot.updateMatrixWorld(true);
|
|
379
|
+
if (this.options.colliderSource) {
|
|
380
|
+
await this.loadCollider(this.options.colliderSource);
|
|
381
|
+
}
|
|
409
382
|
this.switchToOrbit();
|
|
410
383
|
window.addEventListener("keydown", this.onKeyToggle);
|
|
411
384
|
}
|
|
@@ -452,13 +425,13 @@ var PGSGThreeViewer = class {
|
|
|
452
425
|
}
|
|
453
426
|
initRenderer() {
|
|
454
427
|
if (this.renderer) return;
|
|
455
|
-
this.renderer = new
|
|
456
|
-
antialias: this.options.renderer?.antialias ??
|
|
428
|
+
this.renderer = new THREE3.WebGLRenderer({
|
|
429
|
+
antialias: this.options.renderer?.antialias ?? false,
|
|
457
430
|
alpha: false,
|
|
458
431
|
powerPreference: "high-performance"
|
|
459
432
|
});
|
|
460
433
|
this.renderer.setClearColor(1118481, 1);
|
|
461
|
-
this.renderer.outputColorSpace =
|
|
434
|
+
this.renderer.outputColorSpace = THREE3.SRGBColorSpace;
|
|
462
435
|
this.renderer.setSize(
|
|
463
436
|
this.container.clientWidth,
|
|
464
437
|
this.container.clientHeight
|
|
@@ -469,49 +442,65 @@ var PGSGThreeViewer = class {
|
|
|
469
442
|
this.container.appendChild(this.renderer.domElement);
|
|
470
443
|
this.renderer.domElement.addEventListener("webglcontextlost", (e) => {
|
|
471
444
|
e.preventDefault();
|
|
472
|
-
console.
|
|
445
|
+
console.log("[PGSG Viewer] WebGL context lost");
|
|
473
446
|
});
|
|
474
447
|
}
|
|
475
448
|
initScene() {
|
|
476
|
-
this.scene = new
|
|
477
|
-
this.scene.background = new
|
|
478
|
-
const
|
|
479
|
-
|
|
480
|
-
new THREE4.MeshStandardMaterial({ color: 2236962 })
|
|
481
|
-
);
|
|
482
|
-
ground.rotation.x = -Math.PI / 2;
|
|
483
|
-
ground.position.y = this.FLOOR_Y;
|
|
484
|
-
ground.receiveShadow = true;
|
|
485
|
-
ground.name = "ground";
|
|
486
|
-
this.colliders.push(ground);
|
|
487
|
-
const light = new THREE4.HemisphereLight(16777215, 2236962, 1);
|
|
449
|
+
this.scene = new THREE3.Scene();
|
|
450
|
+
this.scene.background = new THREE3.Color(1118481);
|
|
451
|
+
const gridHelper = new THREE3.GridHelper(10, 10);
|
|
452
|
+
const light = new THREE3.HemisphereLight(16777215, 2236962, 1);
|
|
488
453
|
this.scene.add(light);
|
|
489
|
-
const axes = new THREE4.AxesHelper(0.5);
|
|
490
454
|
}
|
|
491
455
|
initCamera() {
|
|
492
|
-
this.camera = new
|
|
493
|
-
|
|
456
|
+
this.camera = new THREE3.PerspectiveCamera(
|
|
457
|
+
60,
|
|
494
458
|
this.container.clientWidth / this.container.clientHeight,
|
|
495
|
-
|
|
496
|
-
|
|
459
|
+
0.01,
|
|
460
|
+
1e4
|
|
497
461
|
);
|
|
498
462
|
this.camera.position.set(0, 1.6, 3);
|
|
499
|
-
this.camera.lookAt(0,
|
|
463
|
+
this.camera.lookAt(0, 0, 0);
|
|
464
|
+
}
|
|
465
|
+
async loadCollider(url) {
|
|
466
|
+
const loader = new GLTFLoader();
|
|
467
|
+
const gltf = await loader.loadAsync(url);
|
|
468
|
+
this.colliderMesh = gltf.scene;
|
|
469
|
+
this.worldRoot.add(this.colliderMesh);
|
|
470
|
+
this.colliders.length = 0;
|
|
471
|
+
this.colliderMesh.traverse((child) => {
|
|
472
|
+
if (child.isMesh) {
|
|
473
|
+
child.material = new THREE3.MeshNormalMaterial();
|
|
474
|
+
child.updateMatrixWorld(true);
|
|
475
|
+
this.colliders.push(child);
|
|
476
|
+
}
|
|
477
|
+
});
|
|
478
|
+
this.worldRoot.updateMatrixWorld(true);
|
|
479
|
+
}
|
|
480
|
+
fitCameraToWorldBox(box) {
|
|
481
|
+
const center = box.getCenter(new THREE3.Vector3());
|
|
482
|
+
const size = box.getSize(new THREE3.Vector3());
|
|
483
|
+
const maxDim = Math.max(size.x, size.y, size.z);
|
|
484
|
+
const fov = THREE3.MathUtils.degToRad(this.camera.fov);
|
|
485
|
+
const distance = maxDim / (2 * Math.tan(fov / 2));
|
|
486
|
+
const direction = new THREE3.Vector3(0, 0, 1);
|
|
487
|
+
const position = center.clone().add(direction.multiplyScalar(distance * 1.2));
|
|
488
|
+
this.camera.position.copy(position);
|
|
489
|
+
this.camera.lookAt(center);
|
|
490
|
+
this.orbitTarget.copy(center);
|
|
500
491
|
}
|
|
501
492
|
fitCameraToBounds(bounds) {
|
|
502
493
|
const center = bounds.min.clone().add(bounds.max).multiplyScalar(0.5);
|
|
503
494
|
const size = bounds.max.clone().sub(bounds.min);
|
|
504
|
-
const
|
|
505
|
-
const
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
this.orbitTarget.copy(center);
|
|
512
|
-
this.camera.position.copy(center).addScaledVector(dir, distance);
|
|
495
|
+
const fov = THREE3.MathUtils.degToRad(this.camera.fov);
|
|
496
|
+
const distance = size.z / (2 * Math.tan(fov / 2));
|
|
497
|
+
this.camera.position.set(
|
|
498
|
+
center.x,
|
|
499
|
+
center.y + size.y * 0.4,
|
|
500
|
+
center.z + distance * 1.2
|
|
501
|
+
);
|
|
513
502
|
this.camera.lookAt(center);
|
|
514
|
-
this.
|
|
503
|
+
this.orbitTarget.copy(center);
|
|
515
504
|
}
|
|
516
505
|
onKeyToggle = (e) => {
|
|
517
506
|
if (e.key !== "v") return;
|
|
@@ -523,8 +512,6 @@ var PGSGThreeViewer = class {
|
|
|
523
512
|
};
|
|
524
513
|
switchToWalk() {
|
|
525
514
|
this.controls?.dispose();
|
|
526
|
-
this.camera.position.y = this.FLOOR_Y + 1.6;
|
|
527
|
-
this.snapPlayerToGround();
|
|
528
515
|
this.controls = new WalkCapsuleController({
|
|
529
516
|
camera: this.camera,
|
|
530
517
|
dom: this.renderer.domElement,
|
|
@@ -548,43 +535,8 @@ var PGSGThreeViewer = class {
|
|
|
548
535
|
}
|
|
549
536
|
);
|
|
550
537
|
}
|
|
551
|
-
snapPlayerToGround() {
|
|
552
|
-
const origin = this.camera.position.clone();
|
|
553
|
-
origin.y = 50;
|
|
554
|
-
const ray = new THREE4.Raycaster(
|
|
555
|
-
origin,
|
|
556
|
-
new THREE4.Vector3(0, -1, 0),
|
|
557
|
-
0,
|
|
558
|
-
100
|
|
559
|
-
);
|
|
560
|
-
const hits = ray.intersectObjects(this.colliders, true);
|
|
561
|
-
if (!hits.length) return;
|
|
562
|
-
const groundY = hits[0].point.y;
|
|
563
|
-
this.camera.position.y = groundY + 1.6;
|
|
564
|
-
}
|
|
565
|
-
addBoxCollider(size, position, rotation) {
|
|
566
|
-
const geo = new THREE4.BoxGeometry(size.x, size.y, size.z);
|
|
567
|
-
const mat = new THREE4.MeshBasicMaterial({ visible: false });
|
|
568
|
-
const box = new THREE4.Mesh(geo, mat);
|
|
569
|
-
box.position.copy(position);
|
|
570
|
-
if (rotation) box.rotation.copy(rotation);
|
|
571
|
-
box.updateMatrixWorld(true);
|
|
572
|
-
this.scene.add(box);
|
|
573
|
-
this.colliders.push(box);
|
|
574
|
-
return box;
|
|
575
|
-
}
|
|
576
|
-
applySplatPreset(i) {
|
|
577
|
-
const p = this.splatPresets[i % this.splatPresets.length];
|
|
578
|
-
this.splatPresetIndex = i % this.splatPresets.length;
|
|
579
|
-
this.splatPivot.rotation.set(p.r[0], p.r[1], p.r[2]);
|
|
580
|
-
this.splatPivot.scale.set(p.s[0], p.s[1], p.s[2]);
|
|
581
|
-
this.splatPivot.updateMatrixWorld(true);
|
|
582
|
-
console.log(
|
|
583
|
-
`[SPLAT PRESET ${this.splatPresetIndex}] rot=(${p.r.map((x) => x.toFixed(3)).join(",")}) scale=(${p.s.join(",")})`
|
|
584
|
-
);
|
|
585
|
-
}
|
|
586
538
|
};
|
|
587
539
|
export {
|
|
588
540
|
PGSGThreeViewer
|
|
589
541
|
};
|
|
590
|
-
//# sourceMappingURL=pgsg-three-viewer-
|
|
542
|
+
//# sourceMappingURL=pgsg-three-viewer-2ONIARDQ.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/viewers/three/pgsg-three-viewer.ts","../src/viewers/spark/SparkSplatRenderer.ts","../src/viewers/three/controls/WalkCapsuleController.ts","../src/viewers/three/controls/OrbitCameraController.ts"],"sourcesContent":["import type { PGSGViewerOptions } from \"../../core/types\";\nimport * as THREE from \"three\";\nimport { GLTFLoader } from \"three/examples/jsm/Addons.js\";\n\nimport { SparkSplatRenderer } from \"../spark/SparkSplatRenderer\";\nimport { WalkCapsuleController } from \"./controls/WalkCapsuleController\";\nimport { OrbitCameraController } from \"./controls/OrbitCameraController\";\n\nexport class PGSGThreeViewer {\n private scene = new THREE.Scene();\n private camera!: THREE.PerspectiveCamera;\n private renderer!: THREE.WebGLRenderer;\n private splats!: SparkSplatRenderer;\n\n private container: HTMLElement;\n\n private controls!: WalkCapsuleController | OrbitCameraController;\n private running = false;\n\n private orbitTarget = new THREE.Vector3();\n private lastTime = performance.now();\n\n private colliders: THREE.Object3D[] = [];\n\n private worldRoot = new THREE.Object3D();\n private splatPivot = new THREE.Object3D();\n private colliderMesh?: THREE.Object3D;\n\n constructor(private options: PGSGViewerOptions) {\n this.container = options.container;\n this.options = options;\n if (!this.container) {\n throw new Error(\"[PGSGThreeViewer] Container is required\");\n }\n }\n\n async load() {\n this.initRenderer();\n this.initCamera();\n this.initScene();\n\n this.scene.add(this.worldRoot);\n\n this.splats = new SparkSplatRenderer();\n const { mesh, bounds } = await this.splats.load(this.options.source);\n\n this.splatPivot.clear();\n this.splatPivot.add(mesh);\n this.worldRoot.add(this.splatPivot);\n\n this.worldRoot.rotation.x = Math.PI;\n this.worldRoot.position.y -= bounds.min.y;\n this.worldRoot.updateMatrixWorld(true);\n\n // const box = mesh.getBoundingBox();\n // box.applyMatrix4(this.worldRoot.matrixWorld);\n // this.fitCameraToWorldBox(box);\n\n if (this.options.colliderSource) {\n await this.loadCollider(this.options.colliderSource);\n }\n\n // const center = bounds.min.clone().add(bounds.max).multiplyScalar(0.5);\n // const size = bounds.max.clone().sub(bounds.min);\n\n // console.log(\"Center:\", center);\n // console.log(\"Size:\", size);\n\n // this.fitCameraToBounds(bounds);\n\n this.switchToOrbit();\n window.addEventListener(\"keydown\", this.onKeyToggle);\n }\n\n start() {\n if (this.running) return;\n this.running = true;\n this.renderer.setAnimationLoop((time) => {\n const now = performance.now();\n const delta = (now - this.lastTime) / 1000;\n this.lastTime = now;\n\n (this.controls as any)?.update?.(delta);\n\n this.renderer.render(this.scene, this.camera);\n });\n }\n\n pause() {\n this.running = false;\n }\n\n resize() {\n if (!this.renderer || !this.camera) return;\n\n const w = Math.max(1, this.container.clientWidth);\n const h = Math.max(1, this.container.clientHeight);\n\n this.renderer.setSize(w, h);\n this.camera.aspect = w / h;\n this.camera.updateProjectionMatrix();\n }\n\n destroy() {\n this.running = false;\n this.renderer.setAnimationLoop(null);\n this.controls?.dispose();\n this.renderer.dispose();\n }\n\n setCameraMode(mode: \"orbit\" | \"walk\") {\n if (mode === \"orbit\") {\n document.exitPointerLock();\n }\n\n if (mode === \"walk\") {\n // FORCE pointer lock NOW (gesture already happened)\n setTimeout(() => {\n if (document.pointerLockElement !== this.renderer.domElement) {\n this.renderer.domElement.focus();\n this.renderer.domElement.requestPointerLock();\n }\n }, 0);\n }\n }\n\n private initRenderer() {\n if (this.renderer) return;\n\n this.renderer = new THREE.WebGLRenderer({\n antialias: this.options.renderer?.antialias ?? false,\n alpha: false,\n powerPreference: \"high-performance\",\n });\n\n this.renderer.setClearColor(0x111111, 1);\n this.renderer.outputColorSpace = THREE.SRGBColorSpace;\n\n this.renderer.setSize(\n this.container.clientWidth,\n this.container.clientHeight,\n );\n\n this.renderer.setPixelRatio(\n this.options.renderer?.pixelRatio ?? window.devicePixelRatio,\n );\n\n this.container.appendChild(this.renderer.domElement);\n\n this.renderer.domElement.addEventListener(\"webglcontextlost\", (e) => {\n e.preventDefault();\n console.log(\"[PGSG Viewer] WebGL context lost\");\n });\n }\n\n private initScene() {\n this.scene = new THREE.Scene();\n this.scene.background = new THREE.Color(0x111111);\n\n const gridHelper = new THREE.GridHelper(10, 10);\n // this.scene.add(gridHelper);\n\n const light = new THREE.HemisphereLight(0xffffff, 0x222222, 1.0);\n this.scene.add(light);\n }\n\n private initCamera() {\n this.camera = new THREE.PerspectiveCamera(\n 60,\n this.container.clientWidth / this.container.clientHeight,\n 0.01,\n 10000,\n );\n\n this.camera.position.set(0, 1.6, 3);\n this.camera.lookAt(0, 0, 0);\n }\n\n private async loadCollider(url: string) {\n const loader = new GLTFLoader();\n const gltf = await loader.loadAsync(url);\n\n this.colliderMesh = gltf.scene;\n this.worldRoot.add(this.colliderMesh);\n\n this.colliders.length = 0;\n\n this.colliderMesh.traverse((child: any) => {\n if ((child as THREE.Mesh).isMesh) {\n child.material = new THREE.MeshNormalMaterial();\n child.updateMatrixWorld(true);\n this.colliders.push(child);\n }\n });\n\n this.worldRoot.updateMatrixWorld(true);\n }\n\n private fitCameraToWorldBox(box: THREE.Box3) {\n const center = box.getCenter(new THREE.Vector3());\n const size = box.getSize(new THREE.Vector3());\n\n const maxDim = Math.max(size.x, size.y, size.z);\n\n const fov = THREE.MathUtils.degToRad(this.camera.fov);\n const distance = maxDim / (2 * Math.tan(fov / 2));\n\n const direction = new THREE.Vector3(0, 0, 1); // view from front\n const position = center\n .clone()\n .add(direction.multiplyScalar(distance * 1.2));\n\n this.camera.position.copy(position);\n this.camera.lookAt(center);\n\n this.orbitTarget.copy(center);\n }\n\n private fitCameraToBounds(bounds: {\n min: THREE.Vector3;\n max: THREE.Vector3;\n }) {\n const center = bounds.min.clone().add(bounds.max).multiplyScalar(0.5);\n const size = bounds.max.clone().sub(bounds.min);\n\n const fov = THREE.MathUtils.degToRad(this.camera.fov);\n const distance = size.z / (2 * Math.tan(fov / 2));\n\n this.camera.position.set(\n center.x,\n center.y + size.y * 0.4,\n center.z + distance * 1.2,\n );\n\n this.camera.lookAt(center);\n this.orbitTarget.copy(center);\n }\n\n private onKeyToggle = (e: KeyboardEvent) => {\n if (e.key !== \"v\") return;\n\n if (this.controls instanceof WalkCapsuleController) {\n this.switchToOrbit();\n } else {\n this.switchToWalk();\n }\n };\n\n private switchToWalk() {\n this.controls?.dispose();\n this.controls = new WalkCapsuleController({\n camera: this.camera,\n dom: this.renderer.domElement,\n colliders: this.colliders,\n eyeHeight: 1.6,\n radius: 0.35,\n walkSpeed: 2.5,\n runMultiplier: 2.0,\n gravity: 9.8,\n }) as any;\n }\n\n private switchToOrbit() {\n this.controls?.dispose();\n\n const distance = this.camera.position.distanceTo(this.orbitTarget);\n\n this.controls = new OrbitCameraController(\n this.camera,\n this.renderer.domElement,\n {\n target: this.orbitTarget.clone(),\n distance: Math.max(distance, 0.5),\n },\n );\n }\n}\n","import { SplatMesh } from \"@sparkjsdev/spark\";\nimport * as THREE from \"three\";\nimport type { ViewerSource } from \"../../core/types\";\nimport { computePLYBounds, parsePLYHeader } from \"../three/utils\";\n\nexport class SparkSplatRenderer {\n private mesh!: SplatMesh;\n\n async load(source: ViewerSource) {\n if (source.type !== \"ply\" && source.type !== \"pgsg\") {\n throw new Error(\"SparkJS supports splat-based sources only\");\n }\n\n // const res = await fetch(source.url);\n // const buffer = await res.arrayBuffer();\n\n // const { vertexCount, headerSize } = parsePLYHeader(buffer);\n // const bounds = computePLYBounds(buffer, headerSize, vertexCount);\n\n this.mesh = new SplatMesh({\n url: source.url,\n });\n\n await this.mesh.initialized;\n this.mesh.updateMatrixWorld(true);\n\n const box = this.mesh.getBoundingBox(true);\n\n const min = box.min.clone();\n const max = box.max.clone();\n\n // Match SparkJS doc defaults\n this.mesh.position.set(0, 0, 0);\n this.mesh.quaternion.identity();\n\n return {\n mesh: this.mesh,\n bounds: {\n min,\n max,\n },\n };\n }\n\n addToScene(scene: THREE.Scene) {\n scene.add(this.mesh);\n }\n\n update(dt: number) {\n // optional: rotation / animation\n }\n\n destroy(scene: THREE.Scene) {\n scene.remove(this.mesh);\n this.mesh.dispose?.();\n }\n}\n","import * as THREE from \"three\";\n\nexport type WalkCapsuleOptions = {\n dom: HTMLElement;\n camera: THREE.PerspectiveCamera;\n colliders: THREE.Object3D[];\n\n eyeHeight?: number;\n radius?: number;\n gravity?: number;\n walkSpeed?: number;\n runMultiplier?: number;\n lookSensitivity?: number;\n};\n\nexport class WalkCapsuleController {\n private dom: HTMLElement;\n private camera: THREE.PerspectiveCamera;\n private colliders: THREE.Object3D[];\n\n private eyeHeight: number;\n private radius: number;\n\n private gravity: number;\n private walkSpeed: number;\n private runMultiplier: number;\n private lookSensitivity: number;\n\n private yaw = 0;\n private pitch = 0;\n\n private velocity = new THREE.Vector3();\n private onGround = false;\n\n private keys = new Set<string>();\n private raycaster = new THREE.Raycaster();\n\n constructor(opts: WalkCapsuleOptions) {\n this.dom = opts.dom;\n this.camera = opts.camera;\n this.colliders = opts.colliders;\n console.log(\"colliders\", this.colliders.length);\n\n this.eyeHeight = opts.eyeHeight ?? 1.6;\n this.radius = opts.radius ?? 0.35;\n this.gravity = opts.gravity ?? 9.8;\n this.walkSpeed = opts.walkSpeed ?? 2.5;\n this.runMultiplier = opts.runMultiplier ?? 2.0;\n this.lookSensitivity = opts.lookSensitivity ?? 0.0022;\n\n // Init yaw / pitch from camera\n const euler = new THREE.Euler().setFromQuaternion(\n this.camera.quaternion,\n \"YXZ\",\n );\n this.pitch = euler.x;\n this.yaw = euler.y;\n\n this.bind();\n }\n\n dispose() {\n this.unbind();\n document.exitPointerLock();\n }\n\n // --------------------------------------------------\n // Input binding (SAFE for pointer lock)\n // --------------------------------------------------\n\n private bind() {\n // 🔴 IMPORTANT: pointer lock ONLY on click\n this.dom.addEventListener(\"click\", this.onClick);\n\n window.addEventListener(\"mousemove\", this.onMouseMove);\n window.addEventListener(\"keydown\", this.onKeyDown);\n window.addEventListener(\"keyup\", this.onKeyUp);\n window.addEventListener(\"blur\", this.onBlur);\n\n this.dom.addEventListener(\"contextmenu\", (e) => e.preventDefault());\n }\n\n private unbind() {\n this.dom.removeEventListener(\"click\", this.onClick);\n window.removeEventListener(\"mousemove\", this.onMouseMove);\n window.removeEventListener(\"keydown\", this.onKeyDown);\n window.removeEventListener(\"keyup\", this.onKeyUp);\n window.removeEventListener(\"blur\", this.onBlur);\n }\n\n private onClick = () => {\n if (document.pointerLockElement !== this.dom) {\n this.dom.requestPointerLock();\n }\n };\n\n private onBlur = () => {\n document.exitPointerLock();\n this.keys.clear();\n };\n\n private onMouseMove = (e: MouseEvent) => {\n if (document.pointerLockElement !== this.dom) return;\n\n this.yaw -= e.movementX * this.lookSensitivity;\n this.pitch -= e.movementY * this.lookSensitivity;\n\n this.pitch = THREE.MathUtils.clamp(\n this.pitch,\n -Math.PI / 2 + 0.01,\n Math.PI / 2 - 0.01,\n );\n\n this.camera.quaternion.setFromEuler(\n new THREE.Euler(this.pitch, this.yaw, 0, \"YXZ\"),\n );\n };\n\n private onKeyDown = (e: KeyboardEvent) => {\n this.keys.add(e.key.toLowerCase());\n };\n\n private onKeyUp = (e: KeyboardEvent) => {\n this.keys.delete(e.key.toLowerCase());\n };\n\n // --------------------------------------------------\n // Update loop\n // --------------------------------------------------\n\n update(dt: number) {\n if (!Number.isFinite(this.camera.position.y)) return;\n\n dt = Math.min(dt, 0.05);\n\n // Direction vectors (yaw-only)\n const forward = new THREE.Vector3();\n this.camera.getWorldDirection(forward);\n forward.y = 0;\n forward.normalize();\n\n const right = new THREE.Vector3()\n .crossVectors(forward, new THREE.Vector3(0, 1, 0))\n .normalize();\n\n const wish = new THREE.Vector3();\n if (this.keys.has(\"w\")) wish.add(forward);\n if (this.keys.has(\"s\")) wish.sub(forward);\n if (this.keys.has(\"d\")) wish.add(right);\n if (this.keys.has(\"a\")) wish.sub(right);\n\n if (wish.lengthSq() > 0) wish.normalize();\n\n // if (this.keys.size > 0) {\n // console.log(\"walking\", [...this.keys]);\n // }\n\n const speed =\n this.walkSpeed * (this.keys.has(\"shift\") ? this.runMultiplier : 1);\n\n this.velocity.x = wish.x * speed;\n this.velocity.z = wish.z * speed;\n\n // gravity (once)\n this.velocity.y -= this.gravity * dt;\n\n // integrate position once\n const nextPos = this.camera.position.clone();\n nextPos.x += this.velocity.x * dt;\n nextPos.z += this.velocity.z * dt;\n nextPos.y += this.velocity.y * dt;\n\n const resolved = this.resolveGroundAndCollisions(nextPos);\n this.camera.position.copy(resolved);\n }\n\n // --------------------------------------------------\n // Collision & Ground\n // --------------------------------------------------\n\n private resolveGroundAndCollisions(desiredCamPos: THREE.Vector3) {\n const camPos = desiredCamPos.clone();\n\n // ----- Ground check (robust) -----\n const maxRayDown = 10.0;\n\n const origin = camPos.clone();\n origin.y += 2.0;\n\n this.raycaster.set(origin, new THREE.Vector3(0, -1, 0));\n this.raycaster.near = 0;\n this.raycaster.far = maxRayDown;\n\n const hits = this.raycaster.intersectObjects(this.colliders, true);\n\n let groundHit: THREE.Intersection | null = null;\n\n for (const hit of hits) {\n if (!hit.face) continue;\n\n // Convert face normal to world space\n const normalMatrix = new THREE.Matrix3().getNormalMatrix(\n hit.object.matrixWorld,\n );\n\n const worldNormal = hit.face.normal\n .clone()\n .applyMatrix3(normalMatrix)\n .normalize();\n\n // Accept only surfaces that face upward\n if (worldNormal.y > 0.5) {\n groundHit = hit;\n break;\n }\n }\n\n if (groundHit) {\n const groundY = groundHit.point.y;\n const targetY = groundY + this.eyeHeight;\n\n const falling = this.velocity.y <= 0;\n const maxSnap = 1.0;\n\n if (falling && camPos.y <= targetY + maxSnap) {\n camPos.y = targetY;\n this.velocity.y = 0;\n this.onGround = true;\n } else {\n this.onGround = false;\n }\n } else {\n this.onGround = false;\n }\n\n // ----- Wall push -----\n const center = camPos.clone();\n center.y -= this.eyeHeight * 0.9;\n\n const dirs = [\n new THREE.Vector3(1, 0, 0),\n new THREE.Vector3(-1, 0, 0),\n new THREE.Vector3(0, 0, 1),\n new THREE.Vector3(0, 0, -1),\n ];\n\n for (const d of dirs) {\n this.raycaster.set(center, d);\n this.raycaster.far = this.radius + 0.25;\n\n const wallHits = this.raycaster.intersectObjects(this.colliders, true);\n if (!wallHits.length) continue;\n\n const overlap = this.radius + 0.25 - wallHits[0].distance;\n if (overlap > 0) camPos.addScaledVector(d, -overlap);\n }\n\n // console.log({\n // camY: camPos.y.toFixed(2),\n // footY: footY.toFixed(2),\n // groundHit: hits[0]?.point.y.toFixed(2),\n // onGround: this.onGround,\n // });\n\n return camPos;\n }\n}\n","import * as THREE from \"three\";\n\nexport interface OrbitOptions {\n target: THREE.Vector3;\n distance: number;\n minDistance?: number;\n maxDistance?: number;\n rotateSpeed?: number;\n zoomSpeed?: number;\n}\n\nexport class OrbitCameraController {\n private camera: THREE.PerspectiveCamera;\n private dom: HTMLElement;\n\n private target: THREE.Vector3;\n private distance: number;\n private minDistance: number;\n private maxDistance: number;\n\n private yaw = 0;\n private pitch = 0;\n\n private rotateSpeed: number;\n private zoomSpeed: number;\n\n private dragging = false;\n private lastX = 0;\n private lastY = 0;\n\n private panning = false;\n private panSpeed = 0.002;\n\n constructor(\n camera: THREE.PerspectiveCamera,\n dom: HTMLElement,\n opts: OrbitOptions\n ) {\n this.camera = camera;\n this.dom = dom;\n\n this.target = opts.target.clone();\n this.distance = opts.distance;\n this.minDistance = opts.minDistance ?? 0.2;\n this.maxDistance = opts.maxDistance ?? 500;\n\n this.rotateSpeed = opts.rotateSpeed ?? 0.005;\n this.zoomSpeed = opts.zoomSpeed ?? 1.1;\n\n this.syncFromCamera();\n this.bind();\n this.updateCamera();\n }\n\n dispose() {\n this.unbind();\n }\n\n // -----------------------------\n // Setup\n // -----------------------------\n\n private bind() {\n this.dom.addEventListener(\"contextmenu\", (e) => e.preventDefault());\n this.dom.addEventListener(\"mousedown\", this.onMouseDown);\n window.addEventListener(\"mouseup\", this.onMouseUp);\n window.addEventListener(\"mousemove\", this.onMouseMove);\n this.dom.addEventListener(\"wheel\", this.onWheel, { passive: false });\n }\n\n private unbind() {\n this.dom.removeEventListener(\"mousedown\", this.onMouseDown);\n window.removeEventListener(\"mouseup\", this.onMouseUp);\n window.removeEventListener(\"mousemove\", this.onMouseMove);\n this.dom.removeEventListener(\"wheel\", this.onWheel);\n }\n\n private syncFromCamera() {\n const offset = this.camera.position.clone().sub(this.target);\n this.distance = offset.length();\n\n const spherical = new THREE.Spherical().setFromVector3(offset);\n this.yaw = spherical.theta;\n this.pitch = spherical.phi;\n }\n\n // -----------------------------\n // Input\n // -----------------------------\n\n private onMouseDown = (e: MouseEvent) => {\n if (e.button === 0) {\n // LEFT = orbit\n this.dragging = true;\n } else if (e.button === 2 || e.button === 1) {\n // RIGHT or MIDDLE = pan\n this.panning = true;\n }\n\n this.lastX = e.clientX;\n this.lastY = e.clientY;\n };\n\n private onMouseUp = () => {\n this.dragging = false;\n this.panning = false;\n };\n\n private onMouseMove = (e: MouseEvent) => {\n if (!this.dragging && !this.panning) return;\n\n const dx = e.clientX - this.lastX;\n const dy = e.clientY - this.lastY;\n\n this.lastX = e.clientX;\n this.lastY = e.clientY;\n\n // -----------------\n // ORBIT ROTATION\n // -----------------\n if (this.dragging) {\n this.yaw -= dx * this.rotateSpeed;\n this.pitch -= dy * this.rotateSpeed;\n\n this.pitch = THREE.MathUtils.clamp(this.pitch, 0.01, Math.PI - 0.01);\n\n this.updateCamera();\n return;\n }\n\n // -----------------\n // PANNING\n // -----------------\n if (this.panning) {\n const panScale = this.distance * this.panSpeed;\n\n const right = new THREE.Vector3();\n const up = new THREE.Vector3();\n\n this.camera.getWorldDirection(up);\n right.crossVectors(this.camera.up, up).normalize();\n up.crossVectors(\n right,\n this.camera.getWorldDirection(new THREE.Vector3())\n ).normalize();\n\n const panOffset = new THREE.Vector3()\n .addScaledVector(right, dx * panScale)\n .addScaledVector(up, -dy * panScale);\n\n this.target.add(panOffset);\n this.camera.position.add(panOffset);\n\n // no updateCamera() — we already moved both\n }\n };\n\n private onWheel = (e: WheelEvent) => {\n e.preventDefault();\n\n const zoomFactor = Math.pow(this.zoomSpeed, e.deltaY / 100);\n this.distance *= zoomFactor;\n\n this.distance = THREE.MathUtils.clamp(\n this.distance,\n this.minDistance,\n this.maxDistance\n );\n\n this.updateCamera();\n };\n\n // -----------------------------\n // Camera math\n // -----------------------------\n\n private updateCamera() {\n const spherical = new THREE.Spherical(this.distance, this.pitch, this.yaw);\n\n const pos = new THREE.Vector3()\n .setFromSpherical(spherical)\n .add(this.target);\n\n this.camera.position.copy(pos);\n this.camera.lookAt(this.target);\n }\n\n setTarget(v: THREE.Vector3) {\n this.target.copy(v);\n this.updateCamera();\n }\n}\n"],"mappings":"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","names":["THREE","THREE"]}
|
package/dist/react/index.d.ts
CHANGED
|
@@ -6,7 +6,8 @@ type PGSGCanvasProps = {
|
|
|
6
6
|
className?: string;
|
|
7
7
|
onReady?: (viewer: PGSGViewer) => void;
|
|
8
8
|
type?: "pgsg" | "ply";
|
|
9
|
+
colliderSrc?: string;
|
|
9
10
|
};
|
|
10
|
-
declare function PGSGCanvas({ src, type, className, onReady }: PGSGCanvasProps): react_jsx_runtime.JSX.Element;
|
|
11
|
+
declare function PGSGCanvas({ src, type, className, onReady, colliderSrc, }: PGSGCanvasProps): react_jsx_runtime.JSX.Element;
|
|
11
12
|
|
|
12
13
|
export { PGSGCanvas, type PGSGCanvasProps };
|
package/dist/react/index.js
CHANGED
|
@@ -1,11 +1,17 @@
|
|
|
1
1
|
import {
|
|
2
2
|
PGSGViewer
|
|
3
|
-
} from "../chunk-
|
|
3
|
+
} from "../chunk-UDJ24YL2.js";
|
|
4
4
|
|
|
5
5
|
// src/react/PGSGCanvas.tsx
|
|
6
6
|
import * as React from "react";
|
|
7
7
|
import { jsx } from "react/jsx-runtime";
|
|
8
|
-
function PGSGCanvas({
|
|
8
|
+
function PGSGCanvas({
|
|
9
|
+
src,
|
|
10
|
+
type = "pgsg",
|
|
11
|
+
className,
|
|
12
|
+
onReady,
|
|
13
|
+
colliderSrc
|
|
14
|
+
}) {
|
|
9
15
|
const ref = React.useRef(null);
|
|
10
16
|
const viewRef = React.useRef(null);
|
|
11
17
|
React.useEffect(() => {
|
|
@@ -14,6 +20,7 @@ function PGSGCanvas({ src, type = "pgsg", className, onReady }) {
|
|
|
14
20
|
const viewer = new PGSGViewer({
|
|
15
21
|
container: ref.current,
|
|
16
22
|
source: { type, url: src },
|
|
23
|
+
colliderSource: colliderSrc,
|
|
17
24
|
debug: true,
|
|
18
25
|
viewerType: "three"
|
|
19
26
|
});
|
package/dist/react/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/react/PGSGCanvas.tsx"],"sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\n\nimport { PGSGViewer } from \"../viewer/pgsg-viewer\";\n\nexport type PGSGCanvasProps = {\n src: string;\n className?: string;\n onReady?: (viewer: PGSGViewer) => void;\n type?: \"pgsg\" | \"ply\";\n};\n\nexport function PGSGCanvas({
|
|
1
|
+
{"version":3,"sources":["../../src/react/PGSGCanvas.tsx"],"sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\n\nimport { PGSGViewer } from \"../viewer/pgsg-viewer\";\n\nexport type PGSGCanvasProps = {\n src: string;\n className?: string;\n onReady?: (viewer: PGSGViewer) => void;\n type?: \"pgsg\" | \"ply\";\n colliderSrc?: string;\n};\n\nexport function PGSGCanvas({\n src,\n type = \"pgsg\",\n className,\n onReady,\n colliderSrc,\n}: PGSGCanvasProps) {\n const ref = React.useRef<HTMLDivElement | null>(null);\n const viewRef = React.useRef<PGSGViewer | null>(null);\n\n React.useEffect(() => {\n if (!ref.current) return;\n\n if (viewRef.current) return;\n\n const viewer = new PGSGViewer({\n container: ref.current,\n source: { type: type, url: src },\n colliderSource: colliderSrc,\n debug: true,\n viewerType: \"three\",\n });\n\n viewRef.current;\n\n viewer.load();\n\n return () => {\n viewer.destroy();\n viewRef.current = null;\n };\n }, [src]);\n\n return (\n <div\n ref={ref}\n className={className}\n style={{ width: \"100%\", height: \"100%\" }}\n />\n );\n}\n"],"mappings":";;;;;AAEA,YAAY,WAAW;AA8CnB;AAlCG,SAAS,WAAW;AAAA,EACzB;AAAA,EACA,OAAO;AAAA,EACP;AAAA,EACA;AAAA,EACA;AACF,GAAoB;AAClB,QAAM,MAAY,aAA8B,IAAI;AACpD,QAAM,UAAgB,aAA0B,IAAI;AAEpD,EAAM,gBAAU,MAAM;AACpB,QAAI,CAAC,IAAI,QAAS;AAElB,QAAI,QAAQ,QAAS;AAErB,UAAM,SAAS,IAAI,WAAW;AAAA,MAC5B,WAAW,IAAI;AAAA,MACf,QAAQ,EAAE,MAAY,KAAK,IAAI;AAAA,MAC/B,gBAAgB;AAAA,MAChB,OAAO;AAAA,MACP,YAAY;AAAA,IACd,CAAC;AAED,YAAQ;AAER,WAAO,KAAK;AAEZ,WAAO,MAAM;AACX,aAAO,QAAQ;AACf,cAAQ,UAAU;AAAA,IACpB;AAAA,EACF,GAAG,CAAC,GAAG,CAAC;AAER,SACE;AAAA,IAAC;AAAA;AAAA,MACC;AAAA,MACA;AAAA,MACA,OAAO,EAAE,OAAO,QAAQ,QAAQ,OAAO;AAAA;AAAA,EACzC;AAEJ;","names":[]}
|
package/package.json
CHANGED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/viewers/three/pgsg-three-viewer.ts","../src/viewers/spark/SparkSplatRenderer.ts","../src/viewers/three/controls/WalkCapsuleController.ts","../src/viewers/three/controls/OrbitCameraController.ts"],"sourcesContent":["import type { PGSGViewerOptions } from \"../../core/types\";\nimport * as THREE from \"three\";\nimport { SparkSplatRenderer } from \"../spark/SparkSplatRenderer\";\nimport { WalkCapsuleController } from \"./controls/WalkCapsuleController\";\nimport { OrbitCameraController } from \"./controls/OrbitCameraController\";\n\nexport class PGSGThreeViewer {\n private scene = new THREE.Scene();\n private camera!: THREE.PerspectiveCamera;\n private renderer!: THREE.WebGLRenderer;\n private splats!: SparkSplatRenderer;\n\n private container: HTMLElement;\n\n private controls!: WalkCapsuleController | OrbitCameraController;\n private running = false;\n\n private orbitTarget = new THREE.Vector3(); // 🔑 CRITICAL\n private lastTime = performance.now();\n\n private colliders: THREE.Object3D[] = [];\n private groundY = 0;\n private readonly FLOOR_Y = -1.6;\n\n private splatPivot = new THREE.Object3D();\n private splatMesh?: THREE.Object3D;\n\n private splatPresets = [\n // P0: identity\n { r: [0, 0, 0], s: [1, 1, 1] },\n\n // P1: Z-up -> Y-up (common GS)\n { r: [-Math.PI / 2, 0, 0], s: [1, 1, 1] },\n\n // P2: Z-up -> Y-up + 90° roll (your screenshot strongly suggests this)\n { r: [-Math.PI / 2, 0, Math.PI / 2], s: [1, 1, 1] },\n\n // P3: Z-up -> Y-up + -90° roll\n { r: [-Math.PI / 2, 0, -Math.PI / 2], s: [1, 1, 1] },\n\n // P4: Z-up -> Y-up + flip forward\n { r: [-Math.PI / 2, Math.PI, 0], s: [1, 1, 1] },\n\n // P5: Z-up -> Y-up + 90° roll + flip forward\n { r: [-Math.PI / 2, Math.PI, Math.PI / 2], s: [1, 1, 1] },\n\n // P6: Z-up -> Y-up + handedness fix\n { r: [-Math.PI / 2, 0, 0], s: [1, 1, -1] },\n\n // P7: Z-up -> Y-up + 90° roll + handedness fix\n { r: [-Math.PI / 2, 0, Math.PI / 2], s: [1, 1, -1] },\n ] as const;\n\n private splatPresetIndex = 0;\n\n constructor(private options: PGSGViewerOptions) {\n this.container = options.container;\n this.options = options;\n if (!this.container) {\n throw new Error(\"[PGSGThreeViewer] Container is required\");\n }\n }\n\n async load() {\n this.initRenderer();\n this.initCamera();\n this.initScene();\n\n /* ---------------- Spark Splats ---------------- */\n this.splats = new SparkSplatRenderer();\n const { mesh, bounds } = await this.splats.load(this.options.source);\n\n // mesh.rotation.set(0, Math.PI / 2, 0);\n this.splatPivot.clear();\n this.splatPivot.add(mesh);\n this.scene.add(this.splatPivot);\n\n // Snap splats to ground\n this.splatPivot.position.y -= bounds.min.y;\n // this.splatPivot.position.y += 0.01;\n this.splatPivot.updateMatrixWorld(true);\n this.groundY = 0;\n\n const ground = new THREE.Mesh(\n new THREE.PlaneGeometry(500, 500),\n new THREE.MeshBasicMaterial({ visible: false }),\n );\n ground.rotation.x = -Math.PI / 2;\n ground.position.y = this.FLOOR_Y;\n ground.updateMatrixWorld(true);\n\n this.scene.add(ground);\n this.colliders = [ground]; // important: reset, don’t stack\n\n // const movedBounds = {\n // min: bounds.min.clone().applyMatrix4(this.splatPivot.matrixWorld),\n // max: bounds.max.clone().applyMatrix4(this.splatPivot.matrixWorld),\n // };\n\n // // 🔑 THIS WAS MISSING\n // this.fitCameraToBounds(movedBounds);\n\n console.log(\"[PLY bounds]\", bounds.min, bounds.max);\n\n // this.addBoxCollider(\n // new THREE.Vector3(50, 0.1, 50),\n // new THREE.Vector3(0, 0, 0),\n // );\n this.addBoxCollider(\n new THREE.Vector3(0.2, 3, 10), // thickness, height, length\n new THREE.Vector3(2.5, 1.5, 0), // center\n );\n this.addBoxCollider(\n new THREE.Vector3(1.5, 0.8, 0.8),\n new THREE.Vector3(0, 0.4, -1),\n );\n\n this.switchToOrbit();\n\n window.addEventListener(\"keydown\", this.onKeyToggle);\n }\n\n start() {\n if (this.running) return;\n this.running = true;\n this.renderer.setAnimationLoop((time) => {\n const now = performance.now();\n const delta = (now - this.lastTime) / 1000;\n this.lastTime = now;\n\n (this.controls as any)?.update?.(delta);\n\n this.renderer.render(this.scene, this.camera);\n });\n }\n\n pause() {\n this.running = false;\n }\n\n resize() {\n if (!this.renderer || !this.camera) return;\n\n const w = Math.max(1, this.container.clientWidth);\n const h = Math.max(1, this.container.clientHeight);\n\n this.renderer.setSize(w, h);\n this.camera.aspect = w / h;\n this.camera.updateProjectionMatrix();\n }\n\n destroy() {\n this.running = false;\n this.renderer.setAnimationLoop(null);\n this.controls?.dispose();\n this.renderer.dispose();\n }\n\n setCameraMode(mode: \"orbit\" | \"walk\") {\n if (mode === \"orbit\") {\n document.exitPointerLock();\n }\n\n if (mode === \"walk\") {\n // 🔴 FORCE pointer lock NOW (gesture already happened)\n setTimeout(() => {\n if (document.pointerLockElement !== this.renderer.domElement) {\n this.renderer.domElement.focus();\n this.renderer.domElement.requestPointerLock();\n }\n }, 0);\n }\n }\n\n private initRenderer() {\n if (this.renderer) return; // 🔒 prevent double init\n\n this.renderer = new THREE.WebGLRenderer({\n antialias: this.options.renderer?.antialias ?? true,\n alpha: false,\n powerPreference: \"high-performance\",\n });\n\n this.renderer.setClearColor(0x111111, 1);\n this.renderer.outputColorSpace = THREE.SRGBColorSpace;\n\n this.renderer.setSize(\n this.container.clientWidth,\n this.container.clientHeight,\n );\n this.renderer.setPixelRatio(\n this.options.renderer?.pixelRatio ?? window.devicePixelRatio,\n );\n\n this.container.appendChild(this.renderer.domElement);\n\n this.renderer.domElement.addEventListener(\"webglcontextlost\", (e) => {\n e.preventDefault();\n console.error(\"[PGSG] WebGL context lost\");\n });\n }\n\n private initScene() {\n this.scene = new THREE.Scene();\n this.scene.background = new THREE.Color(0x111111);\n\n const ground = new THREE.Mesh(\n new THREE.PlaneGeometry(500, 500),\n new THREE.MeshStandardMaterial({ color: 0x222222 }),\n );\n ground.rotation.x = -Math.PI / 2;\n ground.position.y = this.FLOOR_Y;\n ground.receiveShadow = true;\n ground.name = \"ground\";\n // this.scene.add(ground);\n\n this.colliders.push(ground);\n\n const light = new THREE.HemisphereLight(0xffffff, 0x222222, 1.0);\n this.scene.add(light);\n\n const axes = new THREE.AxesHelper(0.5);\n // this.scene.add(axes);\n }\n\n private initCamera() {\n this.camera = new THREE.PerspectiveCamera(\n this.options.camera?.fov ?? 60,\n this.container.clientWidth / this.container.clientHeight,\n this.options.camera?.near ?? 0.01,\n this.options.camera?.far ?? 10000,\n );\n\n this.camera.position.set(0, 1.6, 3);\n this.camera.lookAt(0, 1.6, 0);\n }\n\n private fitCameraToBounds(bounds: {\n min: THREE.Vector3;\n max: THREE.Vector3;\n }) {\n const center = bounds.min.clone().add(bounds.max).multiplyScalar(0.5);\n const size = bounds.max.clone().sub(bounds.min);\n\n const maxDim = Math.max(size.x, size.y, size.z);\n const fov = THREE.MathUtils.degToRad(this.camera.fov);\n const aspect = this.camera.aspect;\n\n // Fit by whichever constraint is tighter: vertical or horizontal FOV\n const fitHeightDistance = (maxDim * 0.5) / Math.tan(fov * 0.5);\n const fitWidthDistance = fitHeightDistance / aspect;\n const distance = Math.max(fitHeightDistance, fitWidthDistance) * 1.2;\n\n // Put camera in a stable diagonal direction (or use previous direction)\n const dir = new THREE.Vector3(1, 0.6, 1).normalize();\n\n this.orbitTarget.copy(center);\n this.camera.position.copy(center).addScaledVector(dir, distance);\n this.camera.lookAt(center);\n this.camera.updateProjectionMatrix();\n }\n\n private onKeyToggle = (e: KeyboardEvent) => {\n if (e.key !== \"v\") return;\n\n if (this.controls instanceof WalkCapsuleController) {\n this.switchToOrbit();\n } else {\n this.switchToWalk();\n }\n };\n\n private switchToWalk() {\n this.controls?.dispose();\n\n this.camera.position.y = this.FLOOR_Y + 1.6;\n // Start above scene so ray always hits\n // this.camera.position.set(\n // this.orbitTarget.x,\n // this.orbitTarget.y + 5,\n // this.orbitTarget.z,\n // );\n\n this.snapPlayerToGround();\n\n this.controls = new WalkCapsuleController({\n camera: this.camera,\n dom: this.renderer.domElement,\n colliders: this.colliders,\n eyeHeight: 1.6,\n radius: 0.35,\n walkSpeed: 2.5,\n runMultiplier: 2.0,\n gravity: 9.8,\n }) as any;\n }\n\n private switchToOrbit() {\n this.controls?.dispose();\n\n const distance = this.camera.position.distanceTo(this.orbitTarget);\n\n this.controls = new OrbitCameraController(\n this.camera,\n this.renderer.domElement,\n {\n target: this.orbitTarget.clone(),\n distance: Math.max(distance, 0.5),\n },\n );\n }\n\n private snapPlayerToGround() {\n const origin = this.camera.position.clone();\n origin.y = 50; // guaranteed above everything\n\n const ray = new THREE.Raycaster(\n origin,\n new THREE.Vector3(0, -1, 0),\n 0,\n 100,\n );\n\n const hits = ray.intersectObjects(this.colliders, true);\n if (!hits.length) return;\n\n const groundY = hits[0].point.y;\n this.camera.position.y = groundY + 1.6;\n }\n\n private addBoxCollider(\n size: THREE.Vector3,\n position: THREE.Vector3,\n rotation?: THREE.Euler,\n ) {\n const geo = new THREE.BoxGeometry(size.x, size.y, size.z);\n const mat = new THREE.MeshBasicMaterial({ visible: false });\n const box = new THREE.Mesh(geo, mat);\n\n box.position.copy(position);\n if (rotation) box.rotation.copy(rotation);\n\n box.updateMatrixWorld(true);\n this.scene.add(box);\n this.colliders.push(box);\n\n return box;\n }\n\n private applySplatPreset(i: number) {\n const p = this.splatPresets[i % this.splatPresets.length];\n this.splatPresetIndex = i % this.splatPresets.length;\n\n this.splatPivot.rotation.set(p.r[0], p.r[1], p.r[2]);\n this.splatPivot.scale.set(p.s[0], p.s[1], p.s[2]);\n this.splatPivot.updateMatrixWorld(true);\n\n console.log(\n `[SPLAT PRESET ${this.splatPresetIndex}] rot=(${p.r\n .map((x) => x.toFixed(3))\n .join(\",\")}) scale=(${p.s.join(\",\")})`,\n );\n }\n}\n","import { SplatMesh } from \"@sparkjsdev/spark\";\nimport * as THREE from \"three\";\nimport type { ViewerSource } from \"../../core/types\";\nimport { computePLYBounds, parsePLYHeader } from \"../three/utils\";\n\nexport class SparkSplatRenderer {\n private mesh!: SplatMesh;\n\n async load(source: ViewerSource) {\n if (source.type !== \"ply\" && source.type !== \"pgsg\") {\n throw new Error(\"SparkJS supports splat-based sources only\");\n }\n\n // const res = await fetch(source.url);\n // const buffer = await res.arrayBuffer();\n\n // const { vertexCount, headerSize } = parsePLYHeader(buffer);\n // const bounds = computePLYBounds(buffer, headerSize, vertexCount);\n\n this.mesh = new SplatMesh({\n url: source.url,\n });\n\n // Match SparkJS doc defaults\n this.mesh.quaternion.set(1, 0, 0, 0);\n this.mesh.position.set(0, 0, 0);\n\n return {\n mesh: this.mesh,\n bounds: {\n min: new THREE.Vector3(0, 0, 0),\n max: new THREE.Vector3(0, 0, 0),\n },\n };\n }\n\n addToScene(scene: THREE.Scene) {\n scene.add(this.mesh);\n }\n\n update(dt: number) {\n // optional: rotation / animation\n }\n\n destroy(scene: THREE.Scene) {\n scene.remove(this.mesh);\n this.mesh.dispose?.();\n }\n}\n","import * as THREE from \"three\";\n\nexport type WalkCapsuleOptions = {\n dom: HTMLElement;\n camera: THREE.PerspectiveCamera;\n colliders: THREE.Object3D[];\n\n eyeHeight?: number;\n radius?: number;\n gravity?: number;\n walkSpeed?: number;\n runMultiplier?: number;\n lookSensitivity?: number;\n};\n\nexport class WalkCapsuleController {\n private dom: HTMLElement;\n private camera: THREE.PerspectiveCamera;\n private colliders: THREE.Object3D[];\n\n private eyeHeight: number;\n private radius: number;\n\n private gravity: number;\n private walkSpeed: number;\n private runMultiplier: number;\n private lookSensitivity: number;\n\n private yaw = 0;\n private pitch = 0;\n\n private velocity = new THREE.Vector3();\n private onGround = false;\n\n private keys = new Set<string>();\n private raycaster = new THREE.Raycaster();\n\n constructor(opts: WalkCapsuleOptions) {\n this.dom = opts.dom;\n this.camera = opts.camera;\n this.colliders = opts.colliders;\n\n this.eyeHeight = opts.eyeHeight ?? 1.6;\n this.radius = opts.radius ?? 0.35;\n this.gravity = opts.gravity ?? 9.8;\n this.walkSpeed = opts.walkSpeed ?? 2.5;\n this.runMultiplier = opts.runMultiplier ?? 2.0;\n this.lookSensitivity = opts.lookSensitivity ?? 0.0022;\n\n // Init yaw / pitch from camera\n const euler = new THREE.Euler().setFromQuaternion(\n this.camera.quaternion,\n \"YXZ\",\n );\n this.pitch = euler.x;\n this.yaw = euler.y;\n\n this.bind();\n }\n\n dispose() {\n this.unbind();\n document.exitPointerLock();\n }\n\n // --------------------------------------------------\n // Input binding (SAFE for pointer lock)\n // --------------------------------------------------\n\n private bind() {\n // 🔴 IMPORTANT: pointer lock ONLY on click\n this.dom.addEventListener(\"click\", this.onClick);\n\n window.addEventListener(\"mousemove\", this.onMouseMove);\n window.addEventListener(\"keydown\", this.onKeyDown);\n window.addEventListener(\"keyup\", this.onKeyUp);\n window.addEventListener(\"blur\", this.onBlur);\n\n this.dom.addEventListener(\"contextmenu\", (e) => e.preventDefault());\n }\n\n private unbind() {\n this.dom.removeEventListener(\"click\", this.onClick);\n window.removeEventListener(\"mousemove\", this.onMouseMove);\n window.removeEventListener(\"keydown\", this.onKeyDown);\n window.removeEventListener(\"keyup\", this.onKeyUp);\n window.removeEventListener(\"blur\", this.onBlur);\n }\n\n private onClick = () => {\n if (document.pointerLockElement !== this.dom) {\n this.dom.requestPointerLock();\n }\n };\n\n private onBlur = () => {\n document.exitPointerLock();\n this.keys.clear();\n };\n\n private onMouseMove = (e: MouseEvent) => {\n if (document.pointerLockElement !== this.dom) return;\n\n this.yaw -= e.movementX * this.lookSensitivity;\n this.pitch -= e.movementY * this.lookSensitivity;\n\n this.pitch = THREE.MathUtils.clamp(\n this.pitch,\n -Math.PI / 2 + 0.01,\n Math.PI / 2 - 0.01,\n );\n\n this.camera.quaternion.setFromEuler(\n new THREE.Euler(this.pitch, this.yaw, 0, \"YXZ\"),\n );\n };\n\n private onKeyDown = (e: KeyboardEvent) => {\n this.keys.add(e.key.toLowerCase());\n };\n\n private onKeyUp = (e: KeyboardEvent) => {\n this.keys.delete(e.key.toLowerCase());\n };\n\n // --------------------------------------------------\n // Update loop\n // --------------------------------------------------\n\n update(dt: number) {\n if (!Number.isFinite(this.camera.position.y)) return;\n\n dt = Math.min(dt, 0.05);\n\n // Direction vectors (yaw-only)\n const forward = new THREE.Vector3();\n this.camera.getWorldDirection(forward);\n forward.y = 0;\n forward.normalize();\n\n const right = new THREE.Vector3()\n .crossVectors(forward, new THREE.Vector3(0, 1, 0))\n .normalize();\n\n const wish = new THREE.Vector3();\n if (this.keys.has(\"w\")) wish.add(forward);\n if (this.keys.has(\"s\")) wish.sub(forward);\n if (this.keys.has(\"d\")) wish.add(right);\n if (this.keys.has(\"a\")) wish.sub(right);\n\n if (wish.lengthSq() > 0) wish.normalize();\n\n if (this.keys.size > 0) {\n console.log(\"walking\", [...this.keys]);\n }\n\n const speed =\n this.walkSpeed * (this.keys.has(\"shift\") ? this.runMultiplier : 1);\n\n this.velocity.x = wish.x * speed;\n this.velocity.z = wish.z * speed;\n\n if (!this.onGround) {\n this.velocity.y -= this.gravity * dt;\n } else {\n this.velocity.y = 0; //Math.min(this.velocity.y, 0);\n }\n\n const nextPos = this.camera.position.clone();\n nextPos.addScaledVector(this.velocity, dt);\n\n const resolved = this.resolveGroundAndCollisions(nextPos);\n this.camera.position.copy(resolved);\n }\n\n // --------------------------------------------------\n // Collision & Ground\n // --------------------------------------------------\n\n private resolveGroundAndCollisions(desiredCamPos: THREE.Vector3) {\n const camPos = desiredCamPos.clone();\n\n // ----- Ground check -----\n const footY = camPos.y - this.eyeHeight;\n const origin = new THREE.Vector3(camPos.x, footY + 0.5, camPos.z);\n\n this.raycaster.set(origin, new THREE.Vector3(0, -1, 0));\n this.raycaster.far = 2.0;\n\n const hits = this.raycaster.intersectObjects(this.colliders, true);\n\n if (hits.length) {\n const groundY = hits[0].point.y; //+ this.radius;\n const targetY = groundY + this.eyeHeight;\n const epsilon = 0.02;\n\n if (camPos.y <= targetY + epsilon) {\n camPos.y = targetY;\n this.velocity.y = 0;\n this.onGround = true;\n } else {\n this.onGround = false;\n }\n } else {\n this.onGround = false;\n }\n\n // ----- Wall push -----\n const center = camPos.clone();\n center.y -= this.eyeHeight * 0.9;\n\n const dirs = [\n new THREE.Vector3(1, 0, 0),\n new THREE.Vector3(-1, 0, 0),\n new THREE.Vector3(0, 0, 1),\n new THREE.Vector3(0, 0, -1),\n ];\n\n for (const d of dirs) {\n this.raycaster.set(center, d);\n this.raycaster.far = this.radius + 0.25;\n\n const wallHits = this.raycaster.intersectObjects(this.colliders, true);\n if (!wallHits.length) continue;\n\n const overlap = this.radius + 0.25 - wallHits[0].distance;\n if (overlap > 0) camPos.addScaledVector(d, -overlap);\n }\n\n console.log({\n camY: camPos.y.toFixed(2),\n footY: footY.toFixed(2),\n groundHit: hits[0]?.point.y.toFixed(2),\n onGround: this.onGround,\n });\n\n return camPos;\n }\n}\n","import * as THREE from \"three\";\n\nexport interface OrbitOptions {\n target: THREE.Vector3;\n distance: number;\n minDistance?: number;\n maxDistance?: number;\n rotateSpeed?: number;\n zoomSpeed?: number;\n}\n\nexport class OrbitCameraController {\n private camera: THREE.PerspectiveCamera;\n private dom: HTMLElement;\n\n private target: THREE.Vector3;\n private distance: number;\n private minDistance: number;\n private maxDistance: number;\n\n private yaw = 0;\n private pitch = 0;\n\n private rotateSpeed: number;\n private zoomSpeed: number;\n\n private dragging = false;\n private lastX = 0;\n private lastY = 0;\n\n private panning = false;\n private panSpeed = 0.002;\n\n constructor(\n camera: THREE.PerspectiveCamera,\n dom: HTMLElement,\n opts: OrbitOptions\n ) {\n this.camera = camera;\n this.dom = dom;\n\n this.target = opts.target.clone();\n this.distance = opts.distance;\n this.minDistance = opts.minDistance ?? 0.2;\n this.maxDistance = opts.maxDistance ?? 500;\n\n this.rotateSpeed = opts.rotateSpeed ?? 0.005;\n this.zoomSpeed = opts.zoomSpeed ?? 1.1;\n\n this.syncFromCamera();\n this.bind();\n this.updateCamera();\n }\n\n dispose() {\n this.unbind();\n }\n\n // -----------------------------\n // Setup\n // -----------------------------\n\n private bind() {\n this.dom.addEventListener(\"contextmenu\", (e) => e.preventDefault());\n this.dom.addEventListener(\"mousedown\", this.onMouseDown);\n window.addEventListener(\"mouseup\", this.onMouseUp);\n window.addEventListener(\"mousemove\", this.onMouseMove);\n this.dom.addEventListener(\"wheel\", this.onWheel, { passive: false });\n }\n\n private unbind() {\n this.dom.removeEventListener(\"mousedown\", this.onMouseDown);\n window.removeEventListener(\"mouseup\", this.onMouseUp);\n window.removeEventListener(\"mousemove\", this.onMouseMove);\n this.dom.removeEventListener(\"wheel\", this.onWheel);\n }\n\n private syncFromCamera() {\n const offset = this.camera.position.clone().sub(this.target);\n this.distance = offset.length();\n\n const spherical = new THREE.Spherical().setFromVector3(offset);\n this.yaw = spherical.theta;\n this.pitch = spherical.phi;\n }\n\n // -----------------------------\n // Input\n // -----------------------------\n\n private onMouseDown = (e: MouseEvent) => {\n if (e.button === 0) {\n // LEFT = orbit\n this.dragging = true;\n } else if (e.button === 2 || e.button === 1) {\n // RIGHT or MIDDLE = pan\n this.panning = true;\n }\n\n this.lastX = e.clientX;\n this.lastY = e.clientY;\n };\n\n private onMouseUp = () => {\n this.dragging = false;\n this.panning = false;\n };\n\n private onMouseMove = (e: MouseEvent) => {\n if (!this.dragging && !this.panning) return;\n\n const dx = e.clientX - this.lastX;\n const dy = e.clientY - this.lastY;\n\n this.lastX = e.clientX;\n this.lastY = e.clientY;\n\n // -----------------\n // ORBIT ROTATION\n // -----------------\n if (this.dragging) {\n this.yaw -= dx * this.rotateSpeed;\n this.pitch -= dy * this.rotateSpeed;\n\n this.pitch = THREE.MathUtils.clamp(this.pitch, 0.01, Math.PI - 0.01);\n\n this.updateCamera();\n return;\n }\n\n // -----------------\n // PANNING\n // -----------------\n if (this.panning) {\n const panScale = this.distance * this.panSpeed;\n\n const right = new THREE.Vector3();\n const up = new THREE.Vector3();\n\n this.camera.getWorldDirection(up);\n right.crossVectors(this.camera.up, up).normalize();\n up.crossVectors(\n right,\n this.camera.getWorldDirection(new THREE.Vector3())\n ).normalize();\n\n const panOffset = new THREE.Vector3()\n .addScaledVector(right, dx * panScale)\n .addScaledVector(up, -dy * panScale);\n\n this.target.add(panOffset);\n this.camera.position.add(panOffset);\n\n // no updateCamera() — we already moved both\n }\n };\n\n private onWheel = (e: WheelEvent) => {\n e.preventDefault();\n\n const zoomFactor = Math.pow(this.zoomSpeed, e.deltaY / 100);\n this.distance *= zoomFactor;\n\n this.distance = THREE.MathUtils.clamp(\n this.distance,\n this.minDistance,\n this.maxDistance\n );\n\n this.updateCamera();\n };\n\n // -----------------------------\n // Camera math\n // -----------------------------\n\n private updateCamera() {\n const spherical = new THREE.Spherical(this.distance, this.pitch, this.yaw);\n\n const pos = new THREE.Vector3()\n .setFromSpherical(spherical)\n .add(this.target);\n\n this.camera.position.copy(pos);\n this.camera.lookAt(this.target);\n }\n\n setTarget(v: THREE.Vector3) {\n this.target.copy(v);\n this.updateCamera();\n }\n}\n"],"mappings":"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","names":["THREE","THREE","THREE"]}
|
|
File without changes
|