From 0d8101125d1c3d1860d3a932e6c8f98fe1b08812 Mon Sep 17 00:00:00 2001 From: Cailean Finn Date: Mon, 20 Oct 2025 14:17:23 +0200 Subject: [PATCH] VR interaction with items --- js/Player.js | 92 ++++++++++++++++++++++++++++++---------------------- 1 file changed, 53 insertions(+), 39 deletions(-) diff --git a/js/Player.js b/js/Player.js index c4b535f..646a0a4 100644 --- a/js/Player.js +++ b/js/Player.js @@ -214,10 +214,13 @@ export class Player { controller.add(debugSphere); const lineGeometry = new THREE.BufferGeometry().setFromPoints([new THREE.Vector3(0, 0, 0), new THREE.Vector3(0, 0, -1)]); - const line = new THREE.Line(lineGeometry); - line.scale.z = 5; + const lineMaterial = new THREE.LineBasicMaterial({ color: 0xff0000 }); // White color for the ray + const line = new THREE.Line(lineGeometry, lineMaterial); + line.name = 'controller-ray'; // Give the line a name to find it later + line.scale.z = this.maxInteractionDistance; controller.add(line); + controller.addEventListener('selectstart', () => this._OnVRSelectStart(i)); controller.addEventListener('selectend', () => this._OnVRSelectEnd(i)); controller.addEventListener('squeezestart', () => this._OnVRSqueezeStart(i)); @@ -232,6 +235,7 @@ export class Player { if (this.currentIntItem && !this.attachedItem) { this.attachedItem = this.currentIntItem; this.attachedItem.isActive = true; + console.log("Attaced Item to (VR) ", this.attachedItem.name); } else { this.isDrawing = true; } @@ -246,9 +250,13 @@ export class Player { _OnVRSelectEnd(controllerIndex) { // Right controller console.log(`Select End: ${controllerIndex}`); - if (controllerIndex === 0) { - this.isDrawing = false; + if (controllerIndex === 0 && this.attachedItem) { + this.attachedItem.isActive = false; + this.attachedItem._removeContentDisplay(); + this.attachedItem = null; + console.log("Dettached item from player (VR)"); } + // Left controller if (controllerIndex === 1) { this.teleporting = false; @@ -276,36 +284,36 @@ export class Player { } _handleVRJoystick() { - // Get the gamepad for the left controller (index 1 in your setup) - const gamepad = this.vrGamepads[1]; - - // You no longer need to check this.vrControllers[1] since the gamepad is null if disconnected. - if (!gamepad) return; - - // The axes array for a thumbstick is often at index 2 (X) and 3 (Y) for the primary stick. - // If the left stick is the primary for movement/teleport, these are the typical indices. - // Always check for undefined or use a safe index, just in case. - const joystickVertical = gamepad.axes[3]; - - if (joystickVertical !== undefined) { - // joystickVertical is -1 (forward) to 1 (backward). We want forward to be max distance. - // We'll map the [-1, 1] range to our [min, max] distance factor range. + // --- Left Controller (Teleport Distance) --- + const leftGamepad = this.vrGamepads[1]; + if (leftGamepad) { + const joystickVertical = leftGamepad.axes[3]; - // 1. Convert [-1, 1] to [0, 1] (Mapping: -1 -> 1, 0 -> 0.5, 1 -> 0) - // Since Y is typically -1 forward, using (-Y + 1) / 2 makes full forward (Y=-1) equal to 1. - const normalizedValue = (-joystickVertical + 1) / 2; - - // 2. Linearly interpolate between min and max factors - this.teleportDistanceFactor = this.minTeleportDistanceFactor + normalizedValue * (this.maxTeleportDistanceFactor - this.minTeleportDistanceFactor); - - // Optional: Apply a small deadzone to prevent accidental changes when the stick is centered - const deadzone = 0.05; - if (Math.abs(joystickVertical) < deadzone) { - // If centered, reset to the default factor (e.g., the midpoint of your min/max range) - this.teleportDistanceFactor = (this.minTeleportDistanceFactor + this.maxTeleportDistanceFactor) / 2; + if (joystickVertical !== undefined) { + const normalizedValue = (-joystickVertical + 1) / 2; + this.teleportDistanceFactor = this.minTeleportDistanceFactor + normalizedValue * (this.maxTeleportDistanceFactor - this.minTeleportDistanceFactor); + const deadzone = 0.05; + if (Math.abs(joystickVertical) < deadzone) { + this.teleportDistanceFactor = (this.minTeleportDistanceFactor + this.maxTeleportDistanceFactor) / 2; + } } + } - // console.log(`Normalized: ${normalizedValue.toFixed(2)}, Factor: ${this.teleportDistanceFactor.toFixed(2)}`); + // --- Right Controller (Content Interaction) --- + const rightGamepad = this.vrGamepads[0]; + if (rightGamepad && this.attachedItem && this.attachedItem.content) { + const joystickVertical = rightGamepad.axes[3]; // Y-axis of the thumbstick + const deadzone = 0.5; // Use a larger deadzone to prevent accidental scrolling + + if (joystickVertical < -deadzone) { // Pushed Up + if (typeof this.attachedItem.content.scrollUp === 'function') { + this.attachedItem.content.scrollUp(); + } + } else if (joystickVertical > deadzone) { // Pushed Down + if (typeof this.attachedItem.content.scrollDown === 'function') { + this.attachedItem.content.scrollDown(); + } + } } } @@ -453,14 +461,13 @@ export class Player { _checkForInteractableItems() { const ray = new THREE.Raycaster(); - let isVR = this.vrControllers[0] && this.vrControllers[0].visible; + let isVR = this.renderer.xr.isPresenting && this.vrControllers[0] && this.vrControllers[0].visible; if (isVR) { // Use right controller for interaction ray const controller = this.vrControllers[0]; - const controllerMatrix = controller.matrixWorld; - ray.ray.origin.setFromMatrixPosition(controllerMatrix); - ray.ray.direction.set(0, 0, -1).applyMatrix4(new THREE.Matrix4().extractRotation(controllerMatrix)); + controller.getWorldPosition(ray.ray.origin); + ray.ray.direction.set(0, 0, -1).applyQuaternion(controller.getWorldQuaternion(new THREE.Quaternion())); } else { // Use camera for desktop interaction ray ray.setFromCamera({ x: 0, y: 0 }, this.camera); @@ -472,6 +479,12 @@ export class Player { const intersects = ray.intersectObjects(itemObj, true); + // Update ray visualization + const controllerRay = this.vrControllers[0]?.getObjectByName('controller-ray'); + if (controllerRay) { + controllerRay.visible = isVR; + } + if (intersects.length > 0) { let intersectedObject = intersects[0].object; @@ -483,7 +496,7 @@ export class Player { // Find the item that corresponds to this root object const foundItem = nearbyItems.find(item => item.object === rootObject); - + if (foundItem) { if (this.currentIntItem !== foundItem) { // Optional: Add some visual feedback for the newly highlighted item @@ -563,14 +576,15 @@ export class Player { } if (this.enableInput) { - if (this.attachedItem) { + if (this.attachedItem && !this.renderer.xr.isPresenting) { // Only lock camera for desktop this._lockCameraForAttachedItem(); } else if (!this.renderer.xr.isPresenting) { // Only update movement if not in VR this._updatePlayerMovement(delta); - } - this._checkForInteractableItems(); + } } + this._checkForInteractableItems(); + this.input.mouseDelta.x = 0; this.input.mouseDelta.y = 0; }