11import { Entity } from './Entity'
22import { clamp } from '../utils'
33import * as THREE from '../extras/three'
4+ import { XRControllerModelFactory } from 'three/addons'
45import { Layers } from '../extras/Layers'
56import { DEG2RAD , RAD2DEG } from '../extras/general'
67import { createNode } from '../extras/createNode'
@@ -105,6 +106,12 @@ export class PlayerLocal extends Entity {
105106 prevTransform : new THREE . Matrix4 ( ) ,
106107 }
107108
109+ this . xrControllerModelFactory = null
110+ this . xrControllerModel1 = null
111+ this . xrControllerModel2 = null
112+ this . xrRig = new THREE . Object3D ( )
113+ this . xrRig . rotation . reorder ( 'YXZ' )
114+
108115 this . mode = Modes . IDLE
109116 this . axis = new THREE . Vector3 ( )
110117 this . gaze = new THREE . Vector3 ( )
@@ -117,6 +124,9 @@ export class PlayerLocal extends Entity {
117124 this . base . position . fromArray ( this . data . position )
118125 this . base . quaternion . fromArray ( this . data . quaternion )
119126
127+ this . hmdDelta = new THREE . Vector3 ( )
128+ this . hmdLast = new THREE . Vector3 ( )
129+
120130 this . aura = createNode ( 'group' )
121131
122132 this . nametag = createNode ( 'nametag' , { label : '' , health : this . data . health , active : false } )
@@ -175,6 +185,7 @@ export class PlayerLocal extends Entity {
175185 this . initControl ( )
176186
177187 this . world . setHot ( this , true )
188+ this . world . on ( 'xrSession' , this . onXRSession )
178189 this . world . emit ( 'ready' , true )
179190 }
180191
@@ -310,6 +321,62 @@ export class PlayerLocal extends Entity {
310321 // this.control.setActions([{ type: 'escape', label: 'Menu' }])
311322 }
312323
324+ onXRSession = session => {
325+ if ( session ) {
326+ if ( ! this . xrControllerModel1 ) {
327+ this . xrControllerModelFactory = new XRControllerModelFactory ( )
328+ this . xrControllerModel1 = this . world . graphics . renderer . xr . getControllerGrip ( 0 )
329+ this . xrControllerModel1 . add ( this . xrControllerModelFactory . createControllerModel ( this . xrControllerModel1 ) )
330+ this . xrRig . add ( this . xrControllerModel1 )
331+ this . xrControllerModel2 = this . world . graphics . renderer . xr . getControllerGrip ( 1 )
332+ this . xrControllerModel2 . add ( this . xrControllerModelFactory . createControllerModel ( this . xrControllerModel2 ) )
333+ this . xrRig . add ( this . xrControllerModel2 )
334+ }
335+ this . world . stage . scene . add ( this . xrRig )
336+ this . xrRig . add ( this . world . camera )
337+ this . cam . zoom = 0
338+ this . control . camera . write = false
339+ this . isXR = true
340+ } else {
341+ this . world . stage . scene . remove ( this . xrRig )
342+ this . world . rig . add ( this . world . camera )
343+ this . world . camera . position . set ( 0 , 0 , 0 )
344+ this . world . camera . rotation . set ( 0 , 0 , 0 )
345+ this . cam . zoom = 1
346+ this . control . camera . write = true
347+ this . isXR = false
348+ }
349+ }
350+
351+ setXRPlayerPosition ( position ) {
352+ const parent = this . xrRig
353+ const child = this . world . camera
354+ const feetWorldPos = child . getWorldPosition ( v2 )
355+ feetWorldPos . y -= child . position . y
356+ const offset = v1 . subVectors ( position , feetWorldPos )
357+ parent . position . add ( offset )
358+
359+ // const offset = v1.copy(position)
360+ // const offset = v1.copy(position)
361+ // offset.x -= parent.position.x + child.position.x
362+ // offset.y -= parent.position.y
363+ // offset.z -= parent.position.z + child.position.z
364+ // parent.position.add(offset)
365+ }
366+
367+ turnXRRigAtPlayer ( degrees ) {
368+ const parent = this . xrRig
369+ const child = this . world . camera
370+ // console.log(child.getWorldPosition(new THREE.Vector3()))
371+ const pivotWorld = new THREE . Vector3 ( )
372+ child . getWorldPosition ( pivotWorld )
373+ parent . rotateOnAxis ( UP , degrees * THREE . MathUtils . DEG2RAD )
374+ const offset = child . position . clone ( )
375+ offset . applyQuaternion ( parent . quaternion )
376+ parent . position . copy ( pivotWorld ) . sub ( offset )
377+ // console.log(child.getWorldPosition(new THREE.Vector3()))
378+ }
379+
313380 toggleFlying ( value ) {
314381 value = isBoolean ( value ) ? value : ! this . flying
315382 if ( this . flying === value ) return
@@ -353,6 +420,7 @@ export class PlayerLocal extends Entity {
353420 }
354421
355422 fixedUpdate ( delta ) {
423+ const xr = this . isXR
356424 const freeze = this . data . effect ?. freeze
357425 const anchor = this . getAnchorMatrix ( )
358426 const snare = this . data . effect ?. snare || 0
@@ -709,25 +777,55 @@ export class PlayerLocal extends Entity {
709777 }
710778
711779 update ( delta ) {
712- const isXR = this . world . xr ?. session
780+ const xr = this . isXR
713781 const freeze = this . data . effect ?. freeze
714782 const anchor = this . getAnchorMatrix ( )
715783
784+ // if (xr) return
785+ // console.log('update')
786+
787+ if ( xr ) {
788+ // move the rig so that the ground underneath the camera aligns with the base player
789+ this . setXRPlayerPosition ( this . base . position )
790+ // fetch any physical movement delta
791+ this . world . camera . getWorldPosition ( v1 )
792+ v1 . y = 0
793+ v2 . copy ( this . xrRig . position )
794+ v2 . y = 0
795+ v3 . copy ( v1 ) . sub ( v2 )
796+ this . hmdDelta . copy ( v3 ) . sub ( this . hmdLast )
797+ this . hmdLast . copy ( v3 )
798+ // apply physical movement delta to capsule so physics stays with us if we wander
799+ const pose = this . capsule . getGlobalPose ( )
800+ v2 . copy ( pose . p ) . add ( this . hmdDelta )
801+ v2 . toPxVec3 ( pose . p )
802+ this . capsule . setGlobalPose ( pose )
803+ }
804+
716805 // update cam look direction
717- if ( isXR ) {
806+ if ( xr ) {
718807 // in xr clear camera rotation (handled internally)
719808 // in xr we only track turn here, which is added to the xr camera later on
720- this . cam . rotation . x = 0
721- this . cam . rotation . z = 0
809+ // this.cam.rotation.x = 0
810+ // this.cam.rotation.z = 0
722811 if ( this . control . xrRightStick . value . x === 0 && this . didSnapTurn ) {
723812 this . didSnapTurn = false
724813 } else if ( this . control . xrRightStick . value . x > 0 && ! this . didSnapTurn ) {
725- this . cam . rotation . y -= 45 * DEG2RAD
814+ this . turnXRRigAtPlayer ( - 45 )
726815 this . didSnapTurn = true
727816 } else if ( this . control . xrRightStick . value . x < 0 && ! this . didSnapTurn ) {
728- this . cam . rotation . y += 45 * DEG2RAD
817+ this . turnXRRigAtPlayer ( 45 )
729818 this . didSnapTurn = true
730819 }
820+ // if we did snap turn, we need to refresh the hmd position to cancel it out
821+ if ( this . didSnapTurn ) {
822+ this . world . camera . getWorldPosition ( v1 )
823+ v1 . y = 0
824+ v2 . copy ( this . xrRig . position )
825+ v2 . y = 0
826+ v3 . copy ( v1 ) . sub ( v2 )
827+ this . hmdLast . copy ( v3 )
828+ }
731829 } else if ( this . control . pointer . locked ) {
732830 // or pointer lock, rotate camera with pointer movement
733831 this . cam . rotation . x += - this . control . pointer . delta . y * POINTER_LOOK_SPEED * delta
@@ -741,25 +839,16 @@ export class PlayerLocal extends Entity {
741839 }
742840
743841 // ensure we can't look too far up/down
744- if ( ! isXR ) {
842+ if ( ! xr ) {
745843 this . cam . rotation . x = clamp ( this . cam . rotation . x , - 89 * DEG2RAD , 89 * DEG2RAD )
746844 }
747845
748846 // zoom camera if scrolling wheel
749- if ( ! isXR ) {
847+ if ( ! xr ) {
750848 this . cam . zoom += - this . control . scrollDelta . value * ZOOM_SPEED * delta
751849 this . cam . zoom = clamp ( this . cam . zoom , MIN_ZOOM , MAX_ZOOM )
752850 }
753851
754- // force zoom in xr to trigger first person (below)
755- if ( isXR && ! this . xrActive ) {
756- this . cam . zoom = 0
757- this . xrActive = true
758- } else if ( ! isXR && this . xrActive ) {
759- this . cam . zoom = 1
760- this . xrActive = false
761- }
762-
763852 // transition in and out of first person
764853 if ( this . cam . zoom < 1 && ! this . firstPerson ) {
765854 this . cam . zoom = 0
@@ -777,14 +866,14 @@ export class PlayerLocal extends Entity {
777866 }
778867
779868 // watch jump presses to either fly or air-jump
780- this . jumpDown = isXR ? this . control . xrRightBtn1 . down : this . control . space . down || this . control . touchA . down
781- if ( isXR ? this . control . xrRightBtn1 . pressed : this . control . space . pressed || this . control . touchA . pressed ) {
869+ this . jumpDown = xr ? this . control . xrRightBtn1 . down : this . control . space . down || this . control . touchA . down
870+ if ( xr ? this . control . xrRightBtn1 . pressed : this . control . space . pressed || this . control . touchA . pressed ) {
782871 this . jumpPressed = true
783872 }
784873
785874 // get our movement direction
786875 this . moveDir . set ( 0 , 0 , 0 )
787- if ( isXR ) {
876+ if ( xr ) {
788877 // in xr use controller input
789878 this . moveDir . x = this . control . xrLeftStick . value . x
790879 this . moveDir . z = this . control . xrLeftStick . value . z
@@ -830,7 +919,7 @@ export class PlayerLocal extends Entity {
830919 }
831920
832921 // determine if we're "running"
833- if ( this . stick ?. active || isXR ) {
922+ if ( this . stick ?. active || xr ) {
834923 // touch/xr joysticks at full extent
835924 this . running = this . moving && this . moveDir . length ( ) > 0.9
836925 } else {
@@ -842,9 +931,10 @@ export class PlayerLocal extends Entity {
842931 this . moveDir . normalize ( )
843932
844933 // flying direction
845- if ( isXR ) {
934+ if ( xr ) {
846935 this . flyDir . copy ( this . moveDir )
847- this . flyDir . applyQuaternion ( this . world . xr . camera . quaternion )
936+ this . world . camera . getWorldQuaternion ( q1 )
937+ this . flyDir . applyQuaternion ( q1 )
848938 } else {
849939 this . flyDir . copy ( this . moveDir )
850940 this . flyDir . applyQuaternion ( this . cam . quaternion )
@@ -868,9 +958,11 @@ export class PlayerLocal extends Entity {
868958 if ( moveDeg < 0 ) moveDeg += 360
869959
870960 // rotate direction to face camera Y direction
871- if ( isXR ) {
872- e1 . copy ( this . world . xr . camera . rotation ) . reorder ( 'YXZ' )
873- e1 . y += this . cam . rotation . y
961+ if ( xr ) {
962+ this . world . camera . getWorldQuaternion ( q1 )
963+ e1 . setFromQuaternion ( q1 ) . reorder ( 'YXZ' )
964+ // e1.y += this.xrRig.rotation.y
965+ // e1.y += this.cam.rotation.y // why not world.camera now?
874966 const yQuaternion = q1 . setFromAxisAngle ( UP , e1 . y )
875967 this . moveDir . applyQuaternion ( yQuaternion )
876968 } else {
@@ -881,9 +973,11 @@ export class PlayerLocal extends Entity {
881973 // get initial facing angle matching camera
882974 let rotY = 0
883975 let applyRotY
884- if ( isXR ) {
885- e1 . copy ( this . world . xr . camera . rotation ) . reorder ( 'YXZ' )
886- rotY = e1 . y + this . cam . rotation . y
976+ if ( xr ) {
977+ this . world . camera . getWorldQuaternion ( q1 )
978+ e1 . setFromQuaternion ( q1 ) . reorder ( 'YXZ' )
979+ rotY = e1 . y
980+ // rotY = e1.y + this.cam.rotation.y
887981 } else {
888982 rotY = this . cam . rotation . y
889983 }
@@ -932,8 +1026,9 @@ export class PlayerLocal extends Entity {
9321026 this . mode = mode
9331027
9341028 // set gaze direction
935- if ( isXR ) {
936- this . gaze . copy ( FORWARD ) . applyQuaternion ( this . world . xr . camera . quaternion )
1029+ if ( xr ) {
1030+ this . world . camera . getWorldQuaternion ( q1 )
1031+ this . gaze . copy ( FORWARD ) . applyQuaternion ( q1 )
9371032 } else {
9381033 this . gaze . copy ( FORWARD ) . applyQuaternion ( this . cam . quaternion )
9391034 if ( ! this . firstPerson ) {
@@ -1010,8 +1105,12 @@ export class PlayerLocal extends Entity {
10101105 }
10111106
10121107 lateUpdate ( delta ) {
1013- const isXR = this . world . xr ?. session
1108+ const xr = this . isXR
10141109 const anchor = this . getAnchorMatrix ( )
1110+
1111+ // if (xr) return
1112+ // console.log('lateUpdate')
1113+
10151114 // if we're anchored, force into that pose
10161115 if ( anchor ) {
10171116 this . base . position . setFromMatrixPosition ( anchor )
@@ -1022,7 +1121,7 @@ export class PlayerLocal extends Entity {
10221121 }
10231122 // make camera follow our position horizontally
10241123 this . cam . position . copy ( this . base . position )
1025- if ( isXR ) {
1124+ if ( xr ) {
10261125 // ...
10271126 } else {
10281127 // and vertically at our vrm model height
@@ -1034,10 +1133,10 @@ export class PlayerLocal extends Entity {
10341133 this . cam . position . add ( right . multiplyScalar ( 0.3 ) )
10351134 }
10361135 }
1037- if ( this . world . xr ?. session ) {
1136+ if ( xr ) {
10381137 // in vr snap camera
1039- this . control . camera . position . copy ( this . cam . position )
1040- this . control . camera . quaternion . copy ( this . cam . quaternion )
1138+ // this.control.camera.position.copy(this.cam.position)
1139+ // this.control.camera.quaternion.copy(this.cam.quaternion)
10411140 } else {
10421141 // otherwise interpolate camera towards target
10431142 simpleCamLerp ( this . world , this . control . camera , this . cam , delta )
0 commit comments