Compare commits
1 commit
next
...
cursor/cre
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b9a9dfcf3e |
2 changed files with 149 additions and 1 deletions
|
|
@ -5,6 +5,7 @@ import { buttonMap as standardButtonsMap } from 'contro-max/build/gamepad'
|
|||
import * as THREE from 'three'
|
||||
import { WorldRendererThree } from '../worldrendererThree'
|
||||
import { DocumentRenderer } from '../documentRenderer'
|
||||
import { VRHud } from './vrHud'
|
||||
|
||||
export async function initVR (worldRenderer: WorldRendererThree, documentRenderer: DocumentRenderer) {
|
||||
if (!('xr' in navigator) || !worldRenderer.worldRendererConfig.vrSupport) return
|
||||
|
|
@ -15,6 +16,9 @@ export async function initVR (worldRenderer: WorldRendererThree, documentRendere
|
|||
|
||||
enableVr()
|
||||
|
||||
// Create VR HUD
|
||||
const vrHud = new VRHud(worldRenderer)
|
||||
|
||||
const vrButtonContainer = createVrButtonContainer(renderer)
|
||||
const updateVrButtons = () => {
|
||||
const newHidden = !worldRenderer.worldRendererConfig.vrSupport || !worldRenderer.worldRendererConfig.foreground
|
||||
|
|
@ -37,6 +41,9 @@ export async function initVR (worldRenderer: WorldRendererThree, documentRendere
|
|||
worldRenderer.reactiveState.preventEscapeMenu = false
|
||||
worldRenderer.scene.remove(user)
|
||||
vrButtonContainer.hidden = true
|
||||
// Detach HUD when VR is disabled
|
||||
vrHud.detachFromVRCamera(user)
|
||||
vrHud.setVisible(false)
|
||||
}
|
||||
|
||||
function createVrButtonContainer (renderer) {
|
||||
|
|
@ -199,16 +206,28 @@ export async function initVR (worldRenderer: WorldRendererThree, documentRendere
|
|||
// bot.entity.yaw = Math.atan2(-d.x, -d.z)
|
||||
// bot.entity.pitch = Math.asin(d.y)
|
||||
|
||||
// Update VR HUD
|
||||
vrHud.update()
|
||||
|
||||
documentRenderer.frameRender(false)
|
||||
})
|
||||
renderer.xr.addEventListener('sessionstart', () => {
|
||||
worldRenderer.cameraGroupVr = user
|
||||
// Attach HUD to VR camera when session starts
|
||||
vrHud.attachToVRCamera(user)
|
||||
vrHud.setVisible(true)
|
||||
})
|
||||
renderer.xr.addEventListener('sessionend', () => {
|
||||
worldRenderer.cameraGroupVr = undefined
|
||||
// Detach HUD when session ends
|
||||
vrHud.detachFromVRCamera(user)
|
||||
vrHud.setVisible(false)
|
||||
})
|
||||
|
||||
worldRenderer.abortController.signal.addEventListener('abort', disableVr)
|
||||
worldRenderer.abortController.signal.addEventListener('abort', () => {
|
||||
disableVr()
|
||||
vrHud.dispose()
|
||||
})
|
||||
}
|
||||
|
||||
const xrStandardRightButtonsMap = [
|
||||
|
|
|
|||
129
renderer/viewer/three/world/vrHud.ts
Normal file
129
renderer/viewer/three/world/vrHud.ts
Normal file
|
|
@ -0,0 +1,129 @@
|
|||
import * as THREE from 'three'
|
||||
import { WorldRendererThree } from '../worldrendererThree'
|
||||
|
||||
export class VRHud {
|
||||
private hudMesh: THREE.Mesh
|
||||
private hudCanvas: HTMLCanvasElement
|
||||
private hudContext: CanvasRenderingContext2D
|
||||
private hudTexture: THREE.CanvasTexture
|
||||
private hudGroup: THREE.Group
|
||||
|
||||
constructor(private worldRenderer: WorldRendererThree) {
|
||||
// Create canvas for HUD
|
||||
this.hudCanvas = document.createElement('canvas')
|
||||
this.hudCanvas.width = 1024
|
||||
this.hudCanvas.height = 512
|
||||
|
||||
this.hudContext = this.hudCanvas.getContext('2d')!
|
||||
|
||||
// Create texture from canvas
|
||||
this.hudTexture = new THREE.CanvasTexture(this.hudCanvas)
|
||||
this.hudTexture.minFilter = THREE.LinearFilter
|
||||
this.hudTexture.magFilter = THREE.LinearFilter
|
||||
|
||||
// Create HUD geometry - a plane that will display our canvas
|
||||
// Adjusted size for better VR viewing
|
||||
const hudGeometry = new THREE.PlaneGeometry(3, 1.5)
|
||||
const hudMaterial = new THREE.MeshBasicMaterial({
|
||||
map: this.hudTexture,
|
||||
transparent: true,
|
||||
opacity: 0.8,
|
||||
side: THREE.DoubleSide,
|
||||
depthTest: false,
|
||||
depthWrite: false
|
||||
})
|
||||
|
||||
this.hudMesh = new THREE.Mesh(hudGeometry, hudMaterial)
|
||||
this.hudMesh.renderOrder = 1000 // Render on top
|
||||
|
||||
// Create a group to hold the HUD
|
||||
this.hudGroup = new THREE.Group()
|
||||
this.hudGroup.add(this.hudMesh)
|
||||
|
||||
// Position the HUD in front of the camera
|
||||
// Slightly lower and further for comfortable VR viewing
|
||||
this.hudMesh.position.set(0, -0.3, -2.5)
|
||||
|
||||
// Initial render to show something
|
||||
this.update()
|
||||
}
|
||||
|
||||
attachToVRCamera(vrCameraGroup: THREE.Object3D) {
|
||||
// Add HUD to the VR camera group so it follows the player's view
|
||||
vrCameraGroup.add(this.hudGroup)
|
||||
}
|
||||
|
||||
detachFromVRCamera(vrCameraGroup: THREE.Object3D) {
|
||||
vrCameraGroup.remove(this.hudGroup)
|
||||
}
|
||||
|
||||
update() {
|
||||
// Get player data
|
||||
const bot = (window as any).bot
|
||||
const playerState = this.worldRenderer.playerState
|
||||
|
||||
// Clear canvas
|
||||
this.hudContext.clearRect(0, 0, this.hudCanvas.width, this.hudCanvas.height)
|
||||
|
||||
// Set up text styling
|
||||
this.hudContext.fillStyle = 'white'
|
||||
this.hudContext.strokeStyle = 'black'
|
||||
this.hudContext.lineWidth = 3
|
||||
this.hudContext.font = 'bold 32px Arial'
|
||||
this.hudContext.textAlign = 'left'
|
||||
this.hudContext.textBaseline = 'top'
|
||||
|
||||
// Top left - FPS and Ping
|
||||
const fps = Math.round(1000 / this.worldRenderer.renderTimeAvg) || 0
|
||||
const ping = bot?._client?.latency || 0
|
||||
|
||||
this.drawText(`FPS: ${fps}`, 50, 50)
|
||||
this.drawText(`Ping: ${ping}ms`, 50, 90)
|
||||
|
||||
// Top right - Velocity and Coords
|
||||
this.hudContext.textAlign = 'right'
|
||||
const velocity = playerState.getVelocity()
|
||||
const position = playerState.getPosition()
|
||||
const vel = Math.sqrt(velocity.x ** 2 + velocity.z ** 2).toFixed(2)
|
||||
|
||||
this.drawText(`Vel: ${vel} m/s`, this.hudCanvas.width - 50, 50)
|
||||
this.drawText(`X: ${position.x.toFixed(1)}`, this.hudCanvas.width - 50, 90)
|
||||
this.drawText(`Y: ${position.y.toFixed(1)}`, this.hudCanvas.width - 50, 130)
|
||||
this.drawText(`Z: ${position.z.toFixed(1)}`, this.hudCanvas.width - 50, 170)
|
||||
|
||||
// Bottom left - Health
|
||||
this.hudContext.textAlign = 'left'
|
||||
this.hudContext.textBaseline = 'bottom'
|
||||
const health = bot?.health || 10
|
||||
const maxHealth = 20
|
||||
const hearts = health / 2
|
||||
const maxHearts = maxHealth / 2
|
||||
|
||||
this.drawText(`HP: ${hearts}/${maxHearts} ❤`, 50, this.hudCanvas.height - 50)
|
||||
|
||||
// Bottom right - Game mode
|
||||
this.hudContext.textAlign = 'right'
|
||||
const gameMode = playerState.reactive.gameMode || 'survival'
|
||||
this.drawText(`Mode: ${gameMode}`, this.hudCanvas.width - 50, this.hudCanvas.height - 50)
|
||||
|
||||
// Update texture
|
||||
this.hudTexture.needsUpdate = true
|
||||
}
|
||||
|
||||
private drawText(text: string, x: number, y: number) {
|
||||
// Draw text with outline for better visibility
|
||||
this.hudContext.strokeText(text, x, y)
|
||||
this.hudContext.fillText(text, x, y)
|
||||
}
|
||||
|
||||
setVisible(visible: boolean) {
|
||||
this.hudMesh.visible = visible
|
||||
}
|
||||
|
||||
dispose() {
|
||||
this.hudTexture.dispose()
|
||||
this.hudMesh.geometry.dispose()
|
||||
;(this.hudMesh.material as THREE.Material).dispose()
|
||||
this.hudCanvas.remove()
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue