spoint 0.1.61 → 0.1.63
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/client/{ARControls.js → XRControls.js} +11 -11
- package/client/app.js +13 -13
- package/client/camera.js +11 -0
- package/package.json +1 -1
- package/src/client/InputHandler.js +10 -8
- package/src/client/MessageHandler.js +7 -8
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as THREE from 'three'
|
|
2
2
|
|
|
3
|
-
export class
|
|
3
|
+
export class XRControls {
|
|
4
4
|
constructor(options = {}) {
|
|
5
5
|
this.enabled = false
|
|
6
6
|
this.options = {
|
|
@@ -57,13 +57,13 @@ export class ARControls {
|
|
|
57
57
|
|
|
58
58
|
async init(renderer) {
|
|
59
59
|
if (!navigator.xr) {
|
|
60
|
-
console.warn('[
|
|
60
|
+
console.warn('[XR] WebXR not supported')
|
|
61
61
|
return false
|
|
62
62
|
}
|
|
63
63
|
|
|
64
64
|
const isSupported = await navigator.xr.isSessionSupported('immersive-ar')
|
|
65
65
|
if (!isSupported) {
|
|
66
|
-
console.warn('[
|
|
66
|
+
console.warn('[XR] immersive-ar not supported')
|
|
67
67
|
return false
|
|
68
68
|
}
|
|
69
69
|
|
|
@@ -96,10 +96,10 @@ export class ARControls {
|
|
|
96
96
|
this.session.addEventListener('planesdetected', (e) => this.onPlanesDetected(e))
|
|
97
97
|
|
|
98
98
|
this.enabled = true
|
|
99
|
-
console.log('[
|
|
99
|
+
console.log('[XR] Session started')
|
|
100
100
|
return true
|
|
101
101
|
} catch (err) {
|
|
102
|
-
console.error('[
|
|
102
|
+
console.error('[XR] Failed to start session:', err)
|
|
103
103
|
return false
|
|
104
104
|
}
|
|
105
105
|
}
|
|
@@ -116,7 +116,7 @@ export class ARControls {
|
|
|
116
116
|
this.hitTestSource = null
|
|
117
117
|
this.planeDetected = false
|
|
118
118
|
this.anchorPlaced = false
|
|
119
|
-
console.log('[
|
|
119
|
+
console.log('[XR] Session ended')
|
|
120
120
|
}
|
|
121
121
|
|
|
122
122
|
onPlanesDetected(event) {
|
|
@@ -180,7 +180,7 @@ export class ARControls {
|
|
|
180
180
|
this.anchorRotation.copy(this.reticle.quaternion)
|
|
181
181
|
this.anchorPlaced = true
|
|
182
182
|
this.hideReticle()
|
|
183
|
-
console.log('[
|
|
183
|
+
console.log('[XR] Anchor placed at:', this.anchorPosition)
|
|
184
184
|
return true
|
|
185
185
|
}
|
|
186
186
|
|
|
@@ -200,7 +200,7 @@ export class ARControls {
|
|
|
200
200
|
this.anchorRotation.copy(this.cameraQuaternion)
|
|
201
201
|
this.anchorPlaced = true
|
|
202
202
|
this.hideReticle()
|
|
203
|
-
console.log('[
|
|
203
|
+
console.log('[XR] Anchor placed at camera:', this.anchorPosition)
|
|
204
204
|
return true
|
|
205
205
|
}
|
|
206
206
|
|
|
@@ -233,7 +233,7 @@ export class ARControls {
|
|
|
233
233
|
this.anchorRotation.copy(yawQuat)
|
|
234
234
|
this.anchorPlaced = true
|
|
235
235
|
this.hideReticle()
|
|
236
|
-
console.log('[
|
|
236
|
+
console.log('[XR] Set initial FPS position:', this.anchorPosition)
|
|
237
237
|
return true
|
|
238
238
|
}
|
|
239
239
|
|
|
@@ -258,7 +258,7 @@ export class ARControls {
|
|
|
258
258
|
}
|
|
259
259
|
}
|
|
260
260
|
|
|
261
|
-
export async function
|
|
261
|
+
export async function createXRButton(renderer, onStart, onEnd) {
|
|
262
262
|
if (!navigator.xr) return null
|
|
263
263
|
|
|
264
264
|
const isSupported = await navigator.xr.isSessionSupported('immersive-ar')
|
|
@@ -288,7 +288,7 @@ export async function createARButton(renderer, onStart, onEnd) {
|
|
|
288
288
|
if (onStart) {
|
|
289
289
|
const started = await onStart()
|
|
290
290
|
if (started) {
|
|
291
|
-
button.textContent = 'Exit
|
|
291
|
+
button.textContent = 'Exit XR'
|
|
292
292
|
button.style.background = 'rgba(150, 0, 0, 0.8)'
|
|
293
293
|
if (onEnd) {
|
|
294
294
|
button.onclick = onEnd
|
package/client/app.js
CHANGED
|
@@ -18,7 +18,7 @@ import { LoadingManager } from './LoadingManager.js'
|
|
|
18
18
|
import { fetchCached } from './ModelCache.js'
|
|
19
19
|
import { createLoadingScreen } from './createLoadingScreen.js'
|
|
20
20
|
import { MobileControls, detectDevice } from './MobileControls.js'
|
|
21
|
-
import {
|
|
21
|
+
import { XRControls, createXRButton } from './XRControls.js'
|
|
22
22
|
|
|
23
23
|
const loadingMgr = new LoadingManager()
|
|
24
24
|
const loadingScreen = createLoadingScreen(loadingMgr)
|
|
@@ -89,7 +89,7 @@ let vignetteOpacity = 0
|
|
|
89
89
|
let vignetteTargetOpacity = 0
|
|
90
90
|
|
|
91
91
|
let mobileControls = null
|
|
92
|
-
let
|
|
92
|
+
let xrControls = null
|
|
93
93
|
let arButton = null
|
|
94
94
|
let arEnabled = false
|
|
95
95
|
const deviceInfo = detectDevice()
|
|
@@ -104,8 +104,8 @@ if (deviceInfo.isMobile) {
|
|
|
104
104
|
console.log('[Mobile] Touch controls initialized:', deviceInfo)
|
|
105
105
|
}
|
|
106
106
|
|
|
107
|
-
|
|
108
|
-
const arReticle =
|
|
107
|
+
xrControls = new XRControls({ placementMode: true, planeDetection: true })
|
|
108
|
+
const arReticle = xrControls.createReticle()
|
|
109
109
|
scene.add(arReticle)
|
|
110
110
|
|
|
111
111
|
function createLaserPointer() {
|
|
@@ -1199,10 +1199,10 @@ function initInputHandler() {
|
|
|
1199
1199
|
}
|
|
1200
1200
|
|
|
1201
1201
|
async function initAR() {
|
|
1202
|
-
const supported = await
|
|
1202
|
+
const supported = await xrControls.init(renderer)
|
|
1203
1203
|
if (supported) {
|
|
1204
|
-
arButton = await
|
|
1205
|
-
const started = await
|
|
1204
|
+
arButton = await createXRButton(renderer, async () => {
|
|
1205
|
+
const started = await xrControls.start()
|
|
1206
1206
|
if (started) {
|
|
1207
1207
|
arEnabled = true
|
|
1208
1208
|
scene.background = null
|
|
@@ -1213,7 +1213,7 @@ async function initAR() {
|
|
|
1213
1213
|
}
|
|
1214
1214
|
return false
|
|
1215
1215
|
}, async () => {
|
|
1216
|
-
await
|
|
1216
|
+
await xrControls.end()
|
|
1217
1217
|
arEnabled = false
|
|
1218
1218
|
scene.background = new THREE.Color(0x87ceeb)
|
|
1219
1219
|
ground.visible = true
|
|
@@ -1566,10 +1566,10 @@ function animate(timestamp) {
|
|
|
1566
1566
|
if (arEnabled) {
|
|
1567
1567
|
const xrFrame = renderer.xr.getFrame()
|
|
1568
1568
|
if (xrFrame) {
|
|
1569
|
-
|
|
1569
|
+
xrControls.update(xrFrame, camera, scene)
|
|
1570
1570
|
const arLocal = playerStates.get(client.playerId)
|
|
1571
|
-
if (arLocal?.position && !
|
|
1572
|
-
|
|
1571
|
+
if (arLocal?.position && !xrControls.anchorPlaced) {
|
|
1572
|
+
xrControls.setInitialFPSPosition(arLocal.position, cam.yaw)
|
|
1573
1573
|
}
|
|
1574
1574
|
}
|
|
1575
1575
|
}
|
|
@@ -1583,11 +1583,11 @@ setupControllers()
|
|
|
1583
1583
|
setupHands()
|
|
1584
1584
|
window.__VR_DEBUG__ = false
|
|
1585
1585
|
window.debug = {
|
|
1586
|
-
scene, camera, renderer, client, playerMeshes, entityMeshes, appModules, inputHandler, playerVrms, playerAnimators, loadingMgr, loadingScreen, controllerModels, controllerGrips, handModels, mobileControls,
|
|
1586
|
+
scene, camera, renderer, client, playerMeshes, entityMeshes, appModules, inputHandler, playerVrms, playerAnimators, loadingMgr, loadingScreen, controllerModels, controllerGrips, handModels, mobileControls, xrControls,
|
|
1587
1587
|
enableVRDebug: () => { window.__VR_DEBUG__ = true; console.log('[VR] Debug enabled - button/axis logging active') },
|
|
1588
1588
|
disableVRDebug: () => { window.__VR_DEBUG__ = false; console.log('[VR] Debug disabled') },
|
|
1589
1589
|
vrInput: () => inputHandler?.getInput() || null,
|
|
1590
1590
|
vrSettings: () => vrSettings,
|
|
1591
1591
|
deviceInfo: () => deviceInfo,
|
|
1592
|
-
placeARAnchor: () =>
|
|
1592
|
+
placeARAnchor: () => xrControls?.placeAnchor() || xrControls?.placeAtCamera()
|
|
1593
1593
|
}
|
package/client/camera.js
CHANGED
|
@@ -37,6 +37,7 @@ export function createCameraController(camera, scene) {
|
|
|
37
37
|
let editCamSpeed = 8
|
|
38
38
|
const envMeshes = []
|
|
39
39
|
let fpsRayTimer = 0, tpsRayTimer = 0, cachedClipDist = 10, cachedAimPoint = null
|
|
40
|
+
let fpsPushX = 0, fpsPushY = 0, fpsPushZ = 0
|
|
40
41
|
let cameraBone = null
|
|
41
42
|
let headBone = null
|
|
42
43
|
let headBoneHidden = false
|
|
@@ -150,12 +151,16 @@ export function createCameraController(camera, scene) {
|
|
|
150
151
|
} else {
|
|
151
152
|
camera.position.copy(camTarget)
|
|
152
153
|
}
|
|
154
|
+
camera.position.x += fpsPushX
|
|
155
|
+
camera.position.y += fpsPushY
|
|
156
|
+
camera.position.z += fpsPushZ
|
|
153
157
|
if (headBone && !headBoneHidden) { headBone.scale.set(0, 0, 0); headBoneHidden = true }
|
|
154
158
|
const wallDist = 0.35
|
|
155
159
|
const fwdWallDist = 0.25
|
|
156
160
|
fpsRayTimer += frameDt
|
|
157
161
|
if (fpsRayTimer >= 0.05 && envMeshes.length) {
|
|
158
162
|
fpsRayTimer = 0
|
|
163
|
+
fpsPushX = 0; fpsPushY = 0; fpsPushZ = 0
|
|
159
164
|
_fpsRayOrigin.copy(camera.position)
|
|
160
165
|
_fpsRayDir.set(-fwdX, -fwdY, -fwdZ)
|
|
161
166
|
camRaycaster.set(_fpsRayOrigin, _fpsRayDir)
|
|
@@ -166,6 +171,9 @@ export function createCameraController(camera, scene) {
|
|
|
166
171
|
if (localMesh && isDescendant(hit.object, localMesh)) continue
|
|
167
172
|
const push = wallDist - hit.distance
|
|
168
173
|
if (push > 0) {
|
|
174
|
+
fpsPushX += fwdX * push
|
|
175
|
+
fpsPushY += fwdY * push
|
|
176
|
+
fpsPushZ += fwdZ * push
|
|
169
177
|
camera.position.x += fwdX * push
|
|
170
178
|
camera.position.y += fwdY * push
|
|
171
179
|
camera.position.z += fwdZ * push
|
|
@@ -181,6 +189,9 @@ export function createCameraController(camera, scene) {
|
|
|
181
189
|
if (localMesh && isDescendant(hit.object, localMesh)) continue
|
|
182
190
|
const push = fwdWallDist - hit.distance
|
|
183
191
|
if (push > 0) {
|
|
192
|
+
fpsPushX -= fwdX * push
|
|
193
|
+
fpsPushY -= fwdY * push
|
|
194
|
+
fpsPushZ -= fwdZ * push
|
|
184
195
|
camera.position.x -= fwdX * push
|
|
185
196
|
camera.position.y -= fwdY * push
|
|
186
197
|
camera.position.z -= fwdZ * push
|
package/package.json
CHANGED
|
@@ -248,17 +248,19 @@ export class InputHandler {
|
|
|
248
248
|
const primaryY = axes[1] ?? 0
|
|
249
249
|
const secondaryX = axes.length > 2 ? (axes[2] ?? 0) : 0
|
|
250
250
|
const secondaryY = axes.length > 3 ? (axes[3] ?? 0) : 0
|
|
251
|
+
const moveX = axes.length > 2 ? secondaryX : primaryX
|
|
252
|
+
const moveY = axes.length > 3 ? secondaryY : primaryY
|
|
251
253
|
|
|
252
254
|
if (source.handedness === 'left') {
|
|
253
|
-
if (Math.abs(
|
|
254
|
-
analogRight =
|
|
255
|
-
if (
|
|
256
|
-
if (
|
|
255
|
+
if (Math.abs(moveX) > DEAD) {
|
|
256
|
+
analogRight = moveX
|
|
257
|
+
if (moveX > THRESH) right = true
|
|
258
|
+
if (moveX < -THRESH) left = true
|
|
257
259
|
}
|
|
258
|
-
if (Math.abs(
|
|
259
|
-
analogForward = -
|
|
260
|
-
if (
|
|
261
|
-
if (
|
|
260
|
+
if (Math.abs(moveY) > DEAD) {
|
|
261
|
+
analogForward = -moveY
|
|
262
|
+
if (moveY < -THRESH) forward = true
|
|
263
|
+
if (moveY > THRESH) backward = true
|
|
262
264
|
}
|
|
263
265
|
|
|
264
266
|
if (btns[0]?.pressed) jump = true
|
|
@@ -56,15 +56,14 @@ export class MessageHandler {
|
|
|
56
56
|
const oldPlayerId = this._playerId
|
|
57
57
|
this._playerId = payload.playerId
|
|
58
58
|
this._currentTick = payload.tick
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
this.
|
|
63
|
-
}
|
|
64
|
-
if (!this._predEngine) {
|
|
65
|
-
this._predEngine = new PredictionEngine(this._config.tickRate || 128)
|
|
66
|
-
this._predEngine.init(this._playerId)
|
|
59
|
+
snapProc?.clear()
|
|
60
|
+
if (this._smoothInterp) {
|
|
61
|
+
this._smoothInterp.reset()
|
|
62
|
+
this._smoothInterp.setLocalPlayer(this._playerId)
|
|
67
63
|
}
|
|
64
|
+
if (oldPlayerId) this._callbacks.onPlayerLeft?.(oldPlayerId)
|
|
65
|
+
this._predEngine = new PredictionEngine(this._config.tickRate || 128)
|
|
66
|
+
this._predEngine.init(this._playerId)
|
|
68
67
|
if (this._config.smoothInterpolation !== false && !this._smoothInterp) {
|
|
69
68
|
this._smoothInterp = new SmoothInterpolation({ predictionEnabled: this._config.predictionEnabled !== false })
|
|
70
69
|
this._smoothInterp.setLocalPlayer(this._playerId)
|