Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix a few issues with world scale in XR #14756

Merged
merged 1 commit into from
Jan 31, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
9 changes: 4 additions & 5 deletions packages/dev/core/src/XR/features/WebXRHandTracking.ts
Original file line number Diff line number Diff line change
Expand Up @@ -389,19 +389,16 @@ export class WebXRHand implements IDisposable {
* Sets the current hand mesh to render for the WebXRHand.
* @param handMesh The rigged hand mesh that will be tracked to the user's hand.
* @param rigMapping The mapping from XRHandJoint to bone names to use with the mesh.
* @param xrSessionManager The XRSessionManager used to initialize the hand mesh.
* @param _xrSessionManager The XRSessionManager used to initialize the hand mesh.
*/
public setHandMesh(handMesh: AbstractMesh, rigMapping: Nullable<XRHandMeshRigMapping>, xrSessionManager?: WebXRSessionManager) {
public setHandMesh(handMesh: AbstractMesh, rigMapping: Nullable<XRHandMeshRigMapping>, _xrSessionManager?: WebXRSessionManager) {
this._handMesh = handMesh;

// Avoid any strange frustum culling. We will manually control visibility via attach and detach.
handMesh.alwaysSelectAsActiveMesh = true;
handMesh.getChildMeshes().forEach((mesh) => {
mesh.alwaysSelectAsActiveMesh = true;
});
if (xrSessionManager) {
handMesh.scaling.setAll(xrSessionManager.worldScalingFactor);
}

// Link the bones in the hand mesh to the transform nodes that will be bound to the WebXR tracked joints.
if (this._handMesh.skeleton) {
Expand Down Expand Up @@ -809,6 +806,8 @@ export class WebXRHandTracking extends WebXRAbstractFeature {
// Apply meshes to existing hands if already tracking.
this._trackingHands.left?.setHandMesh(this._handResources.handMeshes.left, this._handResources.rigMappings.left, this._xrSessionManager);
this._trackingHands.right?.setHandMesh(this._handResources.handMeshes.right, this._handResources.rigMappings.right, this._xrSessionManager);
this._handResources.handMeshes.left.scaling.setAll(this._xrSessionManager.worldScalingFactor);
this._handResources.handMeshes.right.scaling.setAll(this._xrSessionManager.worldScalingFactor);
});
this._xrSessionManager.onWorldScaleFactorChangedObservable.add((scalingFactors) => {
if (this._handResources.handMeshes) {
Expand Down
52 changes: 36 additions & 16 deletions packages/dev/core/src/XR/features/WebXRNearInteraction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,19 @@ export class WebXRNearInteraction extends WebXRAbstractFeature {
pickedPointVisualCue: selectionMesh,
};

this._xrSessionManager.onWorldScaleFactorChangedObservable.add((values) => {
if (values.newScaleFactor !== values.previousScaleFactor) {
this._controllers[xrController.uniqueId].touchCollisionMesh.dispose();
this._controllers[xrController.uniqueId].pickedPointVisualCue.dispose();

const { touchCollisionMesh, touchCollisionMeshFunction, hydrateCollisionMeshFunction } = this._generateNewTouchPointMesh();
this._controllers[xrController.uniqueId].touchCollisionMesh = touchCollisionMesh;
this._controllers[xrController.uniqueId].touchCollisionMeshFunction = touchCollisionMeshFunction;
this._controllers[xrController.uniqueId].hydrateCollisionMeshFunction = hydrateCollisionMeshFunction;
this._controllers[xrController.uniqueId].pickedPointVisualCue = this._generateVisualCue();
}
});

if (this._attachedController) {
if (
!this._options.enableNearInteractionOnAllControllers &&
Expand Down Expand Up @@ -426,8 +439,8 @@ export class WebXRNearInteraction extends WebXRAbstractFeature {
controllerData.grabRay.origin.addInPlace(this._tmpRay.direction.scale(0.05));
}

controllerData.grabRay.length = this._nearGrabLengthScale * this._hoverRadius;
controllerData.touchCollisionMesh.position.copyFrom(controllerData.grabRay.origin);
controllerData.grabRay.length = this._nearGrabLengthScale * this._hoverRadius * this._xrSessionManager.worldScalingFactor;
controllerData.touchCollisionMesh.position.copyFrom(controllerData.grabRay.origin).scaleInPlace(this._xrSessionManager.worldScalingFactor);
}

protected _onXRFrame(_xrFrame: XRFrame) {
Expand Down Expand Up @@ -455,9 +468,7 @@ export class WebXRNearInteraction extends WebXRAbstractFeature {
const indexTipPose = _xrFrame.getJointPose!(xrIndexTip, this._xrSessionManager.referenceSpace);
if (indexTipPose && indexTipPose.transform) {
const axisRHSMultiplier = this._scene.useRightHandedSystem ? 1 : -1;
TmpVectors.Vector3[0]
.set(indexTipPose.transform.position.x, indexTipPose.transform.position.y, indexTipPose.transform.position.z * axisRHSMultiplier)
.scaleInPlace(this._xrSessionManager.worldScalingFactor);
TmpVectors.Vector3[0].set(indexTipPose.transform.position.x, indexTipPose.transform.position.y, indexTipPose.transform.position.z * axisRHSMultiplier);
TmpVectors.Quaternion[0].set(
indexTipPose.transform.orientation.x,
indexTipPose.transform.orientation.y,
Expand Down Expand Up @@ -520,11 +531,19 @@ export class WebXRNearInteraction extends WebXRAbstractFeature {
// near interaction hover
let utilitySceneHoverPick = null;
if (this._options.useUtilityLayer && this._utilityLayerScene) {
utilitySceneHoverPick = this._pickWithSphere(controllerData, this._hoverRadius, this._utilityLayerScene, (mesh: AbstractMesh) =>
this._nearInteractionPredicate(mesh)
utilitySceneHoverPick = this._pickWithSphere(
controllerData,
this._hoverRadius * this._xrSessionManager.worldScalingFactor,
this._utilityLayerScene,
(mesh: AbstractMesh) => this._nearInteractionPredicate(mesh)
);
}
const originalSceneHoverPick = this._pickWithSphere(controllerData, this._hoverRadius, this._scene, (mesh: AbstractMesh) => this._nearInteractionPredicate(mesh));
const originalSceneHoverPick = this._pickWithSphere(
controllerData,
this._hoverRadius * this._xrSessionManager.worldScalingFactor,
this._scene,
(mesh: AbstractMesh) => this._nearInteractionPredicate(mesh)
);

const hoverPickInfo = accuratePickInfo(originalSceneHoverPick, utilitySceneHoverPick);
if (hoverPickInfo && hoverPickInfo.hit) {
Expand All @@ -537,7 +556,7 @@ export class WebXRNearInteraction extends WebXRAbstractFeature {
// near interaction pick
if (controllerData.hoverInteraction) {
let utilitySceneNearPick = null;
const radius = handData ? this._pickRadius : this._controllerPickRadius;
const radius = (handData ? this._pickRadius : this._controllerPickRadius) * this._xrSessionManager.worldScalingFactor;
if (this._options.useUtilityLayer && this._utilityLayerScene) {
utilitySceneNearPick = this._pickWithSphere(controllerData, radius, this._utilityLayerScene, (mesh: AbstractMesh) => this._nearPickPredicate(mesh));
}
Expand Down Expand Up @@ -593,7 +612,7 @@ export class WebXRNearInteraction extends WebXRAbstractFeature {
const selectionMesh = CreateSphere(
"nearInteraction",
{
diameter: 0.0035 * 3,
diameter: 0.0035 * 3 * this._xrSessionManager.worldScalingFactor,
},
sceneToRenderTo
);
Expand Down Expand Up @@ -793,10 +812,11 @@ export class WebXRNearInteraction extends WebXRAbstractFeature {
}

private _generateNewTouchPointMesh() {
const worldScale = this._xrSessionManager.worldScalingFactor;
// populate information for near hover, pick and pinch
const meshCreationScene = this._options.useUtilityLayer ? this._options.customUtilityLayerScene || UtilityLayerRenderer.DefaultUtilityLayer.utilityLayerScene : this._scene;

const touchCollisionMesh = CreateSphere("PickSphere", { diameter: 1 }, meshCreationScene);
const touchCollisionMesh = CreateSphere("PickSphere", { diameter: 1 * worldScale }, meshCreationScene);
touchCollisionMesh.isVisible = false;

// Generate the material for the touch mesh visuals
Expand All @@ -814,15 +834,15 @@ export class WebXRNearInteraction extends WebXRAbstractFeature {
// Adjust the visual size based off of the size of the touch collision orb.
// Having the size perfectly match for hover gives a more accurate tell for when the user will start interacting with the target
// Sizes for other states are somewhat arbitrary, as they are based on what feels nice during an interaction
const hoverSizeVec = new Vector3(this._controllerPickRadius, this._controllerPickRadius, this._controllerPickRadius);
const hoverSizeVec = new Vector3(this._controllerPickRadius, this._controllerPickRadius, this._controllerPickRadius).scaleInPlace(worldScale);
const touchSize = this._controllerPickRadius * (4 / 3);
const touchSizeVec = new Vector3(touchSize, touchSize, touchSize);
const touchSizeVec = new Vector3(touchSize, touchSize, touchSize).scaleInPlace(worldScale);
const hydrateTransitionSize = this._controllerPickRadius * (7 / 6);
const hydrateTransitionSizeVec = new Vector3(hydrateTransitionSize, hydrateTransitionSize, hydrateTransitionSize);
const hydrateTransitionSizeVec = new Vector3(hydrateTransitionSize, hydrateTransitionSize, hydrateTransitionSize).scaleInPlace(worldScale);
const touchHoverTransitionSize = this._controllerPickRadius * (4 / 5);
const touchHoverTransitionSizeVec = new Vector3(touchHoverTransitionSize, touchHoverTransitionSize, touchHoverTransitionSize);
const touchHoverTransitionSizeVec = new Vector3(touchHoverTransitionSize, touchHoverTransitionSize, touchHoverTransitionSize).scaleInPlace(worldScale);
const hoverTouchTransitionSize = this._controllerPickRadius * (3 / 2);
const hoverTouchTransitionSizeVec = new Vector3(hoverTouchTransitionSize, hoverTouchTransitionSize, hoverTouchTransitionSize);
const hoverTouchTransitionSizeVec = new Vector3(hoverTouchTransitionSize, hoverTouchTransitionSize, hoverTouchTransitionSize).scaleInPlace(worldScale);

const touchKeys = [
{ frame: 0, value: hoverSizeVec },
Expand Down
30 changes: 21 additions & 9 deletions packages/dev/core/src/XR/webXRCamera.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,13 @@ export class WebXRCamera extends FreeCamera {
this._referenceQuaternion.copyFromFloats(0, 0, 0, 1);
// first frame - camera's y position should be 0 for the correct offset
this._firstFrame = this.compensateOnFirstFrame;
this._xrSessionManager.onWorldScaleFactorChangedObservable.add(() => {
// only run if in session
if (!this._xrSessionManager.currentFrame) {
return;
}
this._updateDepthNearFar();
});
});

// Check transformation changes on each frame. Callback is added to be first so that the transformation will be
Expand Down Expand Up @@ -201,6 +208,19 @@ export class WebXRCamera extends FreeCamera {
this._lastXRViewerPose = undefined;
}

private _updateDepthNearFar() {
const far = (this.maxZ || 10000) * this._xrSessionManager.worldScalingFactor;
const xrRenderState: XRRenderStateInit = {
// if maxZ is 0 it should be "Infinity", but it doesn't work with the WebXR API. Setting to a large number.
depthFar: far,
depthNear: this.minZ,
};

this._xrSessionManager.updateRenderState(xrRenderState);
this._cache.minZ = this.minZ;
this._cache.maxZ = far;
}

private _rotate180 = new Quaternion(0, 1, 0, 0);

private _updateFromXRSession() {
Expand All @@ -217,15 +237,7 @@ export class WebXRCamera extends FreeCamera {

// check min/max Z and update if not the same as in cache
if (this.minZ !== this._cache.minZ || this.maxZ !== this._cache.maxZ) {
const xrRenderState: XRRenderStateInit = {
// if maxZ is 0 it should be "Infinity", but it doesn't work with the WebXR API. Setting to a large number.
depthFar: this.maxZ || 10000,
depthNear: this.minZ,
};

this._xrSessionManager.updateRenderState(xrRenderState);
this._cache.minZ = this.minZ;
this._cache.maxZ = this.maxZ;
this._updateDepthNearFar();
}

if (pose.transform) {
Expand Down