Skip to content

Commit 0c68e63

Browse files
committed
fix: restore VR support. Fix rotation / position camera bugs
1 parent 04a85e9 commit 0c68e63

File tree

10 files changed

+76
-45
lines changed

10 files changed

+76
-45
lines changed

renderer/viewer/lib/worldrendererCommon.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ export const defaultWorldRendererConfig = {
4343
starfield: true,
4444
addChunksBatchWaitTime: 200,
4545
vrSupport: true,
46+
vrPageGameRendering: true,
4647
renderEntities: true,
4748
fov: 75,
4849
fetchPlayerSkins: true,

renderer/viewer/three/cameraShake.ts

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import * as THREE from 'three'
2+
import { WorldRendererThree } from './worldrendererThree'
23

34
export class CameraShake {
45
private rollAngle = 0
@@ -8,7 +9,7 @@ export class CameraShake {
89
private basePitch = 0
910
private baseYaw = 0
1011

11-
constructor (public camera: THREE.Camera, public onRenderCallbacks: Array<() => void>) {
12+
constructor (public worldRenderer: WorldRendererThree, public onRenderCallbacks: Array<() => void>) {
1213
onRenderCallbacks.push(() => {
1314
this.update()
1415
})
@@ -62,14 +63,21 @@ export class CameraShake {
6263
}
6364
}
6465

65-
// Create rotation quaternions
66-
const pitchQuat = new THREE.Quaternion().setFromAxisAngle(new THREE.Vector3(1, 0, 0), this.basePitch)
67-
const yawQuat = new THREE.Quaternion().setFromAxisAngle(new THREE.Vector3(0, 1, 0), this.baseYaw)
68-
const rollQuat = new THREE.Quaternion().setFromAxisAngle(new THREE.Vector3(0, 0, 1), THREE.MathUtils.degToRad(this.rollAngle))
66+
const camera = this.worldRenderer.cameraGroupVr || this.worldRenderer.camera
6967

70-
// Combine rotations in the correct order: pitch -> yaw -> roll
71-
const finalQuat = yawQuat.multiply(pitchQuat).multiply(rollQuat)
72-
this.camera.setRotationFromQuaternion(finalQuat)
68+
if (this.worldRenderer.cameraGroupVr) {
69+
// For VR camera, only apply yaw rotation
70+
const yawQuat = new THREE.Quaternion().setFromAxisAngle(new THREE.Vector3(0, 1, 0), this.baseYaw)
71+
camera.setRotationFromQuaternion(yawQuat)
72+
} else {
73+
// For regular camera, apply all rotations
74+
const pitchQuat = new THREE.Quaternion().setFromAxisAngle(new THREE.Vector3(1, 0, 0), this.basePitch)
75+
const yawQuat = new THREE.Quaternion().setFromAxisAngle(new THREE.Vector3(0, 1, 0), this.baseYaw)
76+
const rollQuat = new THREE.Quaternion().setFromAxisAngle(new THREE.Vector3(0, 0, 1), THREE.MathUtils.degToRad(this.rollAngle))
77+
// Combine rotations in the correct order: pitch -> yaw -> roll
78+
const finalQuat = yawQuat.multiply(pitchQuat).multiply(rollQuat)
79+
camera.setRotationFromQuaternion(finalQuat)
80+
}
7381
}
7482

7583
private easeOut (t: number): number {

renderer/viewer/three/documentRenderer.ts

Lines changed: 19 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import Stats from 'stats.js'
33
import StatsGl from 'stats-gl'
44
import * as tween from '@tweenjs/tween.js'
55
import { GraphicsBackendConfig, GraphicsInitOptions } from '../../../src/appViewer'
6+
import { WorldRendererConfig } from '../lib/worldrendererCommon'
67

78
export class DocumentRenderer {
89
readonly canvas = document.createElement('canvas')
@@ -23,6 +24,7 @@ export class DocumentRenderer {
2324
droppedFpsPercentage: number
2425
config: GraphicsBackendConfig
2526
onRender = [] as Array<(sizeChanged: boolean) => void>
27+
inWorldRenderingConfig: WorldRendererConfig | undefined
2628

2729
constructor (initOptions: GraphicsInitOptions) {
2830
this.config = initOptions.config
@@ -94,7 +96,7 @@ export class DocumentRenderer {
9496
if (this.disconnected) return
9597
this.animationFrameId = requestAnimationFrame(animate)
9698

97-
if (this.paused) return
99+
if (this.paused || (this.renderer.xr.isPresenting && !this.inWorldRenderingConfig?.vrPageGameRendering)) return
98100

99101
// Handle FPS limiting
100102
if (this.config.fpsLimit) {
@@ -117,18 +119,7 @@ export class DocumentRenderer {
117119
sizeChanged = true
118120
}
119121

120-
this.preRender()
121-
this.stats.markStart()
122-
tween.update()
123-
if (!window.freezeRender) {
124-
this.render(sizeChanged)
125-
}
126-
for (const fn of this.onRender) {
127-
fn(sizeChanged)
128-
}
129-
this.renderedFps++
130-
this.stats.markEnd()
131-
this.postRender()
122+
this.frameRender(sizeChanged)
132123

133124
// Update stats visibility each frame
134125
if (this.config.statsVisible !== undefined) {
@@ -139,6 +130,21 @@ export class DocumentRenderer {
139130
animate()
140131
}
141132

133+
frameRender (sizeChanged: boolean) {
134+
this.preRender()
135+
this.stats.markStart()
136+
tween.update()
137+
if (!window.freezeRender) {
138+
this.render(sizeChanged)
139+
}
140+
for (const fn of this.onRender) {
141+
fn(sizeChanged)
142+
}
143+
this.renderedFps++
144+
this.stats.markEnd()
145+
this.postRender()
146+
}
147+
142148
setPaused (paused: boolean) {
143149
this.paused = paused
144150
}

renderer/viewer/three/graphicsBackend.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import supportedVersions from '../../../src/supportedVersions.mjs'
88
import { WorldRendererThree } from './worldrendererThree'
99
import { DocumentRenderer } from './documentRenderer'
1010
import { PanoramaRenderer } from './panorama'
11+
import { initVR } from './world/vr'
1112

1213
// https://discourse.threejs.org/t/updates-to-color-management-in-three-js-r152/50791
1314
THREE.ColorManagement.enabled = false
@@ -87,10 +88,12 @@ const createGraphicsBackend: GraphicsBackendLoader = (initOptions: GraphicsInitO
8788
panoramaRenderer = null
8889
}
8990
worldRenderer = new WorldRendererThree(documentRenderer.renderer, initOptions, displayOptions)
91+
void initVR(worldRenderer, documentRenderer)
9092
await worldRenderer.worldReadyPromise
9193
documentRenderer.render = (sizeChanged: boolean) => {
9294
worldRenderer?.render(sizeChanged)
9395
}
96+
documentRenderer.inWorldRenderingConfig = displayOptions.inWorldRenderingConfig
9497
window.world = worldRenderer
9598
callModsMethod('worldReady', worldRenderer)
9699
}

renderer/viewer/three/world/vr.ts

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,9 @@ import { XRControllerModelFactory } from 'three/examples/jsm/webxr/XRControllerM
44
import { buttonMap as standardButtonsMap } from 'contro-max/build/gamepad'
55
import * as THREE from 'three'
66
import { WorldRendererThree } from '../worldrendererThree'
7+
import { DocumentRenderer } from '../documentRenderer'
78

8-
export async function initVR (worldRenderer: WorldRendererThree) {
9+
export async function initVR (worldRenderer: WorldRendererThree, documentRenderer: DocumentRenderer) {
910
if (!('xr' in navigator) || !worldRenderer.worldRendererConfig.vrSupport) return
1011
const { renderer } = worldRenderer
1112

@@ -26,12 +27,13 @@ export async function initVR (worldRenderer: WorldRendererThree) {
2627

2728
function enableVr () {
2829
renderer.xr.enabled = true
30+
// renderer.xr.setReferenceSpaceType('local-floor')
2931
worldRenderer.reactiveState.preventEscapeMenu = true
3032
}
3133

3234
function disableVr () {
3335
renderer.xr.enabled = false
34-
worldRenderer.cameraObjectOverride = undefined
36+
worldRenderer.cameraGroupVr = undefined
3537
worldRenderer.reactiveState.preventEscapeMenu = false
3638
worldRenderer.scene.remove(user)
3739
vrButtonContainer.hidden = true
@@ -189,24 +191,21 @@ export async function initVR (worldRenderer: WorldRendererThree) {
189191
}
190192

191193
// appViewer.backend?.updateCamera(null, yawOffset, 0)
192-
worldRenderer.updateCamera(null, bot.entity.yaw, bot.entity.pitch)
194+
// worldRenderer.updateCamera(null, bot.entity.yaw, bot.entity.pitch)
193195

194196
// todo restore this logic (need to preserve ability to move camera)
195197
// const xrCamera = renderer.xr.getCamera()
196198
// const d = xrCamera.getWorldDirection(new THREE.Vector3())
197199
// bot.entity.yaw = Math.atan2(-d.x, -d.z)
198200
// bot.entity.pitch = Math.asin(d.y)
199201

200-
// todo ?
201-
// bot.physics.stepHeight = 1
202-
203-
worldRenderer.render()
202+
documentRenderer.frameRender(false)
204203
})
205204
renderer.xr.addEventListener('sessionstart', () => {
206-
worldRenderer.cameraObjectOverride = user
205+
worldRenderer.cameraGroupVr = user
207206
})
208207
renderer.xr.addEventListener('sessionend', () => {
209-
worldRenderer.cameraObjectOverride = undefined
208+
worldRenderer.cameraGroupVr = undefined
210209
})
211210

212211
worldRenderer.abortController.signal.addEventListener('abort', disableVr)

renderer/viewer/three/worldrendererThree.ts

Lines changed: 17 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@ import { armorModel } from './entity/armorModels'
2020
import { disposeObject } from './threeJsUtils'
2121
import { CursorBlock } from './world/cursorBlock'
2222
import { getItemUv } from './appShared'
23-
import { initVR } from './world/vr'
2423
import { Entities } from './entities'
2524
import { ThreeJsSound } from './threeJsSound'
2625
import { CameraShake } from './cameraShake'
@@ -42,7 +41,7 @@ export class WorldRendererThree extends WorldRendererCommon {
4241
ambientLight = new THREE.AmbientLight(0xcc_cc_cc)
4342
directionalLight = new THREE.DirectionalLight(0xff_ff_ff, 0.5)
4443
entities = new Entities(this)
45-
cameraObjectOverride?: THREE.Object3D // for xr
44+
cameraGroupVr?: THREE.Object3D
4645
material = new THREE.MeshLambertMaterial({ vertexColors: true, transparent: true, alphaTest: 0.1 })
4746
itemsTexture: THREE.Texture
4847
cursorBlock = new CursorBlock(this)
@@ -91,10 +90,9 @@ export class WorldRendererThree extends WorldRendererCommon {
9190
this.addDebugOverlay()
9291
this.resetScene()
9392
void this.init()
94-
void initVR(this)
9593

9694
this.soundSystem = new ThreeJsSound(this)
97-
this.cameraShake = new CameraShake(this.camera, this.onRender)
95+
this.cameraShake = new CameraShake(this, this.onRender)
9896
this.media = new ThreeJsMedia(this)
9997
// this.fountain = new Fountain(this.scene, this.scene, {
10098
// position: new THREE.Vector3(0, 10, 0),
@@ -106,6 +104,10 @@ export class WorldRendererThree extends WorldRendererCommon {
106104
this.worldSwitchActions()
107105
}
108106

107+
get cameraObject () {
108+
return this.cameraGroupVr || this.camera
109+
}
110+
109111
worldSwitchActions () {
110112
this.onWorldSwitched.push(() => {
111113
// clear custom blocks
@@ -301,7 +303,7 @@ export class WorldRendererThree extends WorldRendererCommon {
301303

302304
updateViewerPosition (pos: Vec3): void {
303305
this.viewerPosition = pos
304-
const cameraPos = this.camera.position.toArray().map(x => Math.floor(x / 16)) as [number, number, number]
306+
const cameraPos = this.cameraObject.position.toArray().map(x => Math.floor(x / 16)) as [number, number, number]
305307
this.cameraSectionPos = new Vec3(...cameraPos)
306308
// eslint-disable-next-line guard-for-in
307309
for (const key in this.sectionObjects) {
@@ -429,10 +431,8 @@ export class WorldRendererThree extends WorldRendererCommon {
429431
}
430432

431433
setFirstPersonCamera (pos: Vec3 | null, yaw: number, pitch: number) {
432-
const cam = this.cameraObjectOverride || this.camera
433434
const yOffset = this.displayOptions.playerState.getEyeHeight()
434435

435-
this.camera = cam as THREE.PerspectiveCamera
436436
this.updateCamera(pos?.offset(0, yOffset, 0) ?? null, yaw, pitch)
437437
this.media.tryIntersectMedia()
438438
}
@@ -445,7 +445,11 @@ export class WorldRendererThree extends WorldRendererCommon {
445445
// }
446446

447447
if (pos) {
448-
new tweenJs.Tween(this.camera.position).to({ x: pos.x, y: pos.y, z: pos.z }, 50).start()
448+
if (this.renderer.xr.isPresenting) {
449+
pos.y -= this.camera.position.y // Fix Y position of camera in world
450+
}
451+
452+
new tweenJs.Tween(this.cameraObject.position).to({ x: pos.x, y: pos.y, z: pos.z }, 50).start()
449453
// this.freeFlyState.position = pos
450454
}
451455
this.cameraShake.setBaseRotation(pitch, yaw)
@@ -467,13 +471,13 @@ export class WorldRendererThree extends WorldRendererCommon {
467471
this.entities.render()
468472

469473
// eslint-disable-next-line @typescript-eslint/non-nullable-type-assertion-style
470-
const cam = this.camera instanceof THREE.Group ? this.camera.children.find(child => child instanceof THREE.PerspectiveCamera) as THREE.PerspectiveCamera : this.camera
474+
const cam = this.cameraGroupVr instanceof THREE.Group ? this.cameraGroupVr.children.find(child => child instanceof THREE.PerspectiveCamera) as THREE.PerspectiveCamera : this.camera
471475
this.renderer.render(this.scene, cam)
472476

473-
if (this.displayOptions.inWorldRenderingConfig.showHand && !this.playerState.shouldHideHand /* && !this.freeFlyMode */) {
474-
this.holdingBlock.render(this.camera, this.renderer, this.ambientLight, this.directionalLight)
475-
this.holdingBlockLeft.render(this.camera, this.renderer, this.ambientLight, this.directionalLight)
476-
}
477+
// if (this.displayOptions.inWorldRenderingConfig.showHand && !this.playerState.shouldHideHand /* && !this.freeFlyMode */) {
478+
// this.holdingBlock.render(this.camera, this.renderer, this.ambientLight, this.directionalLight)
479+
// this.holdingBlockLeft.render(this.camera, this.renderer, this.ambientLight, this.directionalLight)
480+
// }
477481

478482
for (const fountain of this.fountains) {
479483
if (this.sectionObjects[fountain.sectionId] && !this.sectionObjects[fountain.sectionId].foutain) {

src/appViewer.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -199,7 +199,7 @@ export class AppViewer {
199199
resetBackend (cleanState = false) {
200200
this.disconnectBackend(cleanState)
201201
if (this.backendLoader) {
202-
this.loadBackend(this.backendLoader)
202+
void this.loadBackend(this.backendLoader)
203203
}
204204
}
205205

src/optionsGuiScheme.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -488,7 +488,11 @@ export const guiOptionsScheme: {
488488
</>
489489
)
490490
},
491-
vrSupport: {}
491+
vrSupport: {},
492+
vrPageGameRendering: {
493+
text: 'Page Game Rendering',
494+
tooltip: 'Wether to continue rendering page even when vr is active.',
495+
}
492496
},
493497
],
494498
advanced: [

src/optionsStorage.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,7 @@ const defaultOptions = {
104104
autoJump: 'auto' as 'auto' | 'always' | 'never',
105105
autoParkour: false,
106106
vrSupport: true, // doesn't directly affect the VR mode, should only disable the button which is annoying to android users
107+
vrPageGameRendering: false,
107108
renderDebug: 'basic' as 'none' | 'advanced' | 'basic',
108109

109110
// advanced bot options

src/watchOptions.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,11 @@ export const watchOptionsAfterViewerInit = () => {
8080
updateFpsLimit(o)
8181
})
8282

83+
watchValue(options, o => {
84+
appViewer.inWorldRenderingConfig.vrSupport = o.vrSupport
85+
appViewer.inWorldRenderingConfig.vrPageGameRendering = o.vrPageGameRendering
86+
})
87+
8388
watchValue(options, (o, isChanged) => {
8489
appViewer.inWorldRenderingConfig.clipWorldBelowY = o.clipWorldBelowY
8590
appViewer.inWorldRenderingConfig.extraBlockRenderers = !o.disableSignsMapsSupport

0 commit comments

Comments
 (0)