XR controllers on scene

This commit is contained in:
2026-03-08 20:30:41 +02:00
parent 47f1552000
commit 5e142a2757
7 changed files with 71 additions and 55 deletions
@@ -48,7 +48,7 @@ class PairMatchingGame extends EventManager {
uv[xi].array[8 * i + 7] = s[1]; uv[xi].array[8 * i + 7] = s[1];
} }
let mesh = new Mesh(b[xi], material.clone()); let mesh = new Mesh(b[xi], material.clone());
mesh.position.set(xi * dx, o[xi][i] * dy, 0); mesh.position.set(xi * dx + Math.random() * (xi+0.5) * dx, o[xi][i] * dy, Math.random() * (xi+0.5) * dx);
mesh.userData.gd = { mesh.userData.gd = {
pair: xi, pair: xi,
id: i id: i
@@ -75,7 +75,7 @@ class PairMatchingGame extends EventManager {
let actionDef = {material:{emissiveIntensity:k=>(1+Math.sin((k*2+1.5)*Math.PI))*4 }}; let actionDef = {material:{emissiveIntensity:k=>(1+Math.sin((k*2+1.5)*Math.PI))*4 }};
let clickFn = (i) => { var clickFn = (i) => {
this.dispatchEvent({type:'interaction'}); this.dispatchEvent({type:'interaction'});
let oc = clicked; let oc = clicked;
if (done.includes(i.object.userData.gd.id)) return; if (done.includes(i.object.userData.gd.id)) return;
+18 -13
View File
@@ -1,7 +1,7 @@
import { Raycaster, Vector3 } from "three" import { Raycaster, Vector3 } from "three"
class Clickable { class Clickable {
constructor(defaultDistance) { constructor(engine, defaultDistance) {
const objects = []; const objects = [];
const raycaster = new Raycaster(); const raycaster = new Raycaster();
let v = new Vector3; let v = new Vector3;
@@ -21,34 +21,39 @@ class Clickable {
objects.splice(0, objects.length); objects.splice(0, objects.length);
} }
this.update = function (mouse, camera, event) { this.handleController = function(controller){
raycaster.setFromCamera(mouse, camera); raycaster.setFromXRController(controller);
this.handle();
}
this.handleMouse = function (mouse, event) {
raycaster.setFromCamera(mouse, engine.camera);
this.handle();
};
this.handle = function(){
let forExecute = []; let forExecute = [];
objects.filter(o=>{ objects.filter(o=>{
do { do {
if (o.__active === false) return false; if (o.__active === false || o.visible === false) return false;
o = o.parent; o = o.parent;
} while (o); } while (o);
return true; return true;
}).forEach(o => { }).forEach(o => {
o.getWorldPosition(v); o.getWorldPosition(v);
if (camera.position.distanceTo(v) <= o._clickable.distance && o.visible) { if (engine.cameraWorld.position.distanceTo(v) <= o._clickable.distance) {
const intersects = raycaster.intersectObject(o); const intersects = raycaster.intersectObject(o);
// intersects.forEach(i=>{ if (intersects[0]) {
// forExecute.push({o, i}) forExecute.push({ o, i: intersects[0] });
// }) }
if (intersects[0]) forExecute.push({ o, i: intersects[0] });
} }
}); });
if (forExecute[0]) { if (forExecute[0]) {
let sorted = forExecute.sort((a, b) => a.i.distance - b.i.distance); let sorted = forExecute.sort((a, b) => a.i.distance - b.i.distance);
sorted[0].o._clickable.fn(sorted[0].i); sorted[0].o._clickable.fn(sorted[0].i);
if (this.onclick) {
this.onclick(sorted[0].o, sorted[0].i, event);
} }
} }
};
} }
} }
export {Clickable} export { Clickable }
+2 -2
View File
@@ -29,7 +29,7 @@ class DashBoard extends EventManager {
dash.visible = false; dash.visible = false;
const k = 3.11; const k = 3.11;
const dashWidth = engine.aspect * k, dashHeight = k; const dashWidth = engine.aspect * k, dashHeight = k;
const dashDistance = 2.5; const dashDistance = 1.77;
this.width = dashWidth; this.width = dashWidth;
this.height = dashHeight; this.height = dashHeight;
@@ -202,7 +202,7 @@ class DashBoard extends EventManager {
//dashPlacement, rotate = false, plane = false //dashPlacement, rotate = false, plane = false
this.attach = (object, opts = {})=>{ this.attach = (object, opts = {})=>{
this.cameraFix = engine.hero.cameraZ; this.cameraFix = engine.hero.cameraZ;
engine.hero.cameraZ = 12; engine.hero.cameraZ = 6;
hud.visible = true; hud.visible = true;
hudPlane.visible = !!opts.plane; hudPlane.visible = !!opts.plane;
if (opts.plane){ if (opts.plane){
+16 -7
View File
@@ -1,7 +1,7 @@
import { Raycaster, Vector3 } from "three" import { Raycaster, Vector3 } from "three"
class Draggable{ class Draggable{
constructor(defaultDistance){ constructor(engine, defaultDistance){
const objects = []; const objects = [];
const raycaster = new Raycaster(); const raycaster = new Raycaster();
let v = new Vector3; let v = new Vector3;
@@ -16,13 +16,22 @@ class Draggable{
objects.splice(objects.indexOf(object), 1); objects.splice(objects.indexOf(object), 1);
} }
this.update = function(pointer, camera, action){ this.handleController = function(controller, action){
raycaster.setFromCamera(pointer, camera); raycaster.setFromXRController(controller);
if (action == 'start'){ this.handle(action);
}
this.handleMouse = function (mouse, action) {
raycaster.setFromCamera(mouse, engine.camera);
this.handle(action);
};
this.handle = function(action){
if (['start', 'selectstart'].includes(action)){
let forExecute = []; let forExecute = [];
objects.forEach(o=>{ objects.forEach(o=>{
o.getWorldPosition(v); o.getWorldPosition(v);
if (camera.position.distanceTo(v) <= o._draggable.distance && o.visible){ if (engine.cameraWorld.position.distanceTo(v) <= o._draggable.distance && o.visible){
const intersects = raycaster.intersectObject(o); const intersects = raycaster.intersectObject(o);
if (intersects[0]) forExecute.push({o, i:intersects[0]}) if (intersects[0]) forExecute.push({o, i:intersects[0]})
} }
@@ -34,10 +43,10 @@ class Draggable{
dragging = s; dragging = s;
dragging.zone = raycaster.intersectObject(s.o._draggable.dragZone)[0]; dragging.zone = raycaster.intersectObject(s.o._draggable.dragZone)[0];
} }
}else if (action == 'end' && dragging){ }else if (['end', 'selectend'].includes(action) && dragging){
dragging.o._draggable.fn.end && dragging.o._draggable.fn.end(dragging); dragging.o._draggable.fn.end && dragging.o._draggable.fn.end(dragging);
dragging = null; dragging = null;
}else if(action == 'drag' && dragging){ }else if(['drag', 'move'].includes(action) && dragging){
const intersect = raycaster.intersectObject(dragging.o._draggable.dragZone)[0]; const intersect = raycaster.intersectObject(dragging.o._draggable.dragZone)[0];
if (intersect?.uv && dragging.zone?.uv){ if (intersect?.uv && dragging.zone?.uv){
dragging.o.position.x += -4*(dragging.zone.uv.x - intersect.uv.x); dragging.o.position.x += -4*(dragging.zone.uv.x - intersect.uv.x);
+15 -16
View File
@@ -245,8 +245,8 @@ class GameEngine extends EventManager{
this.initXr(); this.initXr();
} }
this.clickable = new Clickable(20); this.clickable = new Clickable(this, 20);
this.draggable = new Draggable(20); this.draggable = new Draggable(this, 20);
} }
initXr() { initXr() {
@@ -261,7 +261,7 @@ class GameEngine extends EventManager{
// this.session = this.renderer.xr.getSession(); // this.session = this.renderer.xr.getSession();
// this.session.addEventListener('selectstart', this.onControllerEvent.bind(this)); // this.session.addEventListener('selectstart', this.onControllerEvent.bind(this));
}) })
this.scene.add(c1); this.cameraRig.add(c1);
let c2 = this.renderer.xr.getController(1); let c2 = this.renderer.xr.getController(1);
c2.addEventListener('select', this.onSelect.bind(this)); c2.addEventListener('select', this.onSelect.bind(this));
@@ -272,17 +272,17 @@ class GameEngine extends EventManager{
c2.addEventListener('connected', e => { c2.addEventListener('connected', e => {
c2.gamepad = e.data.gamepad; c2.gamepad = e.data.gamepad;
}) })
this.scene.add(c2); this.cameraRig.add(c2);
const controllerModelFactory = new XRControllerModelFactory(); const controllerModelFactory = new XRControllerModelFactory();
let controllerGrip1 = this.renderer.xr.getControllerGrip(0); let controllerGrip1 = this.renderer.xr.getControllerGrip(0);
controllerGrip1.add(controllerModelFactory.createControllerModel(controllerGrip1)); controllerGrip1.add(controllerModelFactory.createControllerModel(controllerGrip1));
this.scene.add(controllerGrip1); this.cameraRig.add(controllerGrip1);
let controllerGrip2 = this.renderer.xr.getControllerGrip(1); let controllerGrip2 = this.renderer.xr.getControllerGrip(1);
controllerGrip2.add(controllerModelFactory.createControllerModel(controllerGrip2)); controllerGrip2.add(controllerModelFactory.createControllerModel(controllerGrip2));
this.scene.add(controllerGrip2); this.cameraRig.add(controllerGrip2);
const geometry = new THREE.BufferGeometry().setFromPoints([new THREE.Vector3(0, 0, 0), new THREE.Vector3(0, 0, - 1)]); const geometry = new THREE.BufferGeometry().setFromPoints([new THREE.Vector3(0, 0, 0), new THREE.Vector3(0, 0, - 1)]);
@@ -379,14 +379,10 @@ class GameEngine extends EventManager{
} }
onControllerEvent(event) { onControllerEvent(event) {
//this.handleXrAction(event, this);
//event.type !== 'move' && console.log(event)
const controller = event.target; const controller = event.target;
//console.log(event) if (!controller.userData?.active) return;
if (this.opts.designMode){ if (this.opts.designMode){
if (!controller.userData) return
if (controller.userData.active === false) return;
this.transformControls.getRaycaster().setFromXRController(controller); this.transformControls.getRaycaster().setFromXRController(controller);
switch (event.type) { switch (event.type) {
case 'selectstart': case 'selectstart':
@@ -400,13 +396,13 @@ class GameEngine extends EventManager{
this.transformControls.pointerMove(null); this.transformControls.pointerMove(null);
break; break;
} }
}else{
this.draggable?.handleController(controller, event.type)
} }
} }
onSelect(event) { onSelect(event) {
const controller = event.target; const controller = event.target;
if (this.opts.designMode){
this.xrController1.userData.active = false; this.xrController1.userData.active = false;
this.xrController2.userData.active = false; this.xrController2.userData.active = false;
@@ -420,6 +416,7 @@ class GameEngine extends EventManager{
this.xrController2.add(this.controllerLine); this.xrController2.add(this.controllerLine);
} }
if (this.opts.designMode){
this.raycaster.setFromXRController(controller); this.raycaster.setFromXRController(controller);
const intersects = this.raycaster.intersectObjects(this.activeObjects.children, true); const intersects = this.raycaster.intersectObjects(this.activeObjects.children, true);
@@ -434,6 +431,8 @@ class GameEngine extends EventManager{
this.transformControls.attach(intersects[0].object); this.transformControls.attach(intersects[0].object);
}, 100); }, 100);
} }
}else{
this.clickable.handleController(controller, event);
} }
} }
@@ -525,14 +524,14 @@ class GameEngine extends EventManager{
onClick(mouseEvent, domElement){ onClick(mouseEvent, domElement){
let mouse = this.getMouseVector(mouseEvent, domElement); let mouse = this.getMouseVector(mouseEvent, domElement);
this.raycaster.setFromCamera(mouse, this.camera); //this.raycaster.setFromCamera(mouse, this.camera);
this.clickable.update(mouse, this.camera, mouseEvent); this.clickable.handleMouse(mouse, mouseEvent);
this.hero?.idleReset(); this.hero?.idleReset();
} }
onPointer(mouseEvent, domElement, type){ onPointer(mouseEvent, domElement, type){
let mouse = this.getMouseVector(mouseEvent, domElement); let mouse = this.getMouseVector(mouseEvent, domElement);
this.draggable?.update(mouse, this.camera, type); this.draggable?.handleMouse(mouse, type);
} }
setCamera(camera) { setCamera(camera) {
+4 -2
View File
@@ -95,11 +95,13 @@ class Hero{
this.updateCharacterControls(delta) this.updateCharacterControls(delta)
if (this.engine.renderer.xr.isPresenting){ if (this.engine.renderer.xr.isPresenting){
this.cameraMode = 'fixed' this.cameraMode = 'fixed'
this.cameraY = this.size.y * 1;
//this.camera = this.engine.cameraWorld; //this.camera = this.engine.cameraWorld;
// this.engine.activeObjects.position.x = -this.camera.position.x; // this.engine.activeObjects.position.x = -this.camera.position.x;
// this.engine.activeObjects.position.z = -this.camera.position.z; // this.engine.activeObjects.position.z = -this.camera.position.z;
}else{ }else{
this.cameraMode = 'rotate' this.cameraMode = 'rotate'
this.cameraY = this.size.y * 1.5;
// this.camera = this.engine.cameraWorld; // this.camera = this.engine.cameraWorld;
// this.engine.camera.position.set(0,0,0); // this.engine.camera.position.set(0,0,0);
// this.engine.camera.rotation.set(0,0,0); // this.engine.camera.rotation.set(0,0,0);
@@ -163,7 +165,7 @@ class Hero{
this.fadeDuration = 1; this.fadeDuration = 1;
} }
if (this.currentAction.startsWith('idle') && play != 'idle' && !pc.isLocked){ if (this.currentAction.startsWith('idle') && play != 'idle' && !pc.isLocked && !this.engine.renderer.xr.isPresenting){
this.cameraIdleDelta += delta * 0.33; this.cameraIdleDelta += delta * 0.33;
}else { }else {
this.cameraIdleDelta = 0; this.cameraIdleDelta = 0;
@@ -261,7 +263,7 @@ class Hero{
) )
} }
cameraPosition.lerp(cameraDesiredPosition, this.cameraMode == 'fixed' ? 1 : delta*2) cameraPosition.lerp(cameraDesiredPosition, this.cameraMode == 'fixed' ? delta*2 : delta*2)
this.camera.position.copy(cameraPosition) this.camera.position.copy(cameraPosition)
if (!pc.isLocked){ if (!pc.isLocked){
this.camera.lookAt( this.camera.lookAt(
+2 -1
View File
@@ -127,7 +127,8 @@ video{
.canvas-wrapper{ .canvas-wrapper{
width: 100%; width: 100%;
max-width: 100vw; max-width: 100vw;
height: calc(100vh - 288px); //height: calc(100vh - 288px);
aspect-ratio: 16 / 9;
} }
.canvas-wrapper:has(canvas:fullscreen){ .canvas-wrapper:has(canvas:fullscreen){