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

Does originOffset behave differently for identity reference space? #477

Closed
thetuvix opened this issue Jan 15, 2019 · 10 comments
Closed

Does originOffset behave differently for identity reference space? #477

thetuvix opened this issue Jan 15, 2019 · 10 comments
Assignees
Labels
fixed by pending PR A PR that is in review will resolve this issue. ready for editing Design is decided, issue ready for Editor to fix spec bug Inconsistencies in spec
Milestone

Comments

@thetuvix
Copy link
Contributor

XRViewerPose objects retrieved with this reference space will have a transform that is equal to the reference space's originOffset.

Isn't this the reverse of what we defined originOffset to do for all other reference spaces?

For the "bounded" space:
Imagine that the viewer is located at position (2, 2, 2) within the default "bounded" reference space. If I then "offset" the bounded reference space's "origin" by (2, 2, 2), I would expect the viewer to now be at position (0, 0, 0), since I shifted that space's origin over by (2, 2, 2) to now coincide with the viewer.

This means that applying an originOffset of (2, 2, 2) to the "bounded" reference space causes the viewer's pose to translate by (-2, -2, -2).

For the "identity" space:
However, per this PR, if I apply an originOffset of (2, 2, 2) to the "identity" reference space, the viewer's position changes from (0, 0, 0) to (2, 2, 2).

This means that applying an originOffset of (2, 2, 2) to the "identity" reference space causes the viewer's pose to translate by (2, 2, 2), the opposite behavior as with the "bounded" reference space.

Originally posted by @thetuvix in #459

@NellWaliczek NellWaliczek added this to the Next Working Draft milestone Jan 15, 2019
@NellWaliczek NellWaliczek added agenda Request discussion in the next telecon/FTF spec bug Inconsistencies in spec labels Jan 17, 2019
@toji
Copy link
Member

toji commented Jan 22, 2019

Sorry for the delay in commenting on this. This issue highlights the fact that there's actually two valid ways to think about this, and we need to:

  1. Make sure everyone agrees on which interpretation is desired.
  2. State which way the spec intends much more clearly.

And in order sort that out I'm going to subject you all to my scribbles. 😁

project - drawing 160404524081023811

This is, I hope we can all agree, the default state of things. No originOffset has been applied, so the virtual scene's origin is aligned with the tracking system's origin, which in this case considers the center of the room at the floor to be (0,0,0). Very typical bounded scenario.

My assumption when writing the originOffset text, and subsequently the identity reference space text, was that if you set it's position to, say, (1, 0, -3) you'd get an effect like this, which we'll call Interpretation A:

project - drawing 13051976579337510665

Specifically, in this case the originOffset is interpreted to be a value that is added to the native tracking space values, such that setting the originOffset effectively makes that point in the virtual scene align with the tracking origin of your physical space. This seemed intuitive to me, since developers using it as a teleport mechanism would say, for instance "I want the player to be warped to (x, y, z) in the virtual scene" and so (x, y, z) is what you would set the originOffset.position to.

Alex's interpretation (or at least my interpretation of his interpretation) is the inverse, which is also a valid reading of it. In this world view the same position of (1, 0, -3) would yield something like this, which we'll call Interpretation B:

project - drawing 17257068832970786181

In this scenario the origin of the virtual space is offset from the origin of the tracking space by the originOffset, which has the practical effect of "moving" the user through the scene by the inverse amount. This felt backwards to me because of it, but when looking at it from a perspective that views the user's environment as immutable (because, as far as our software is concerned, it is) this approach makes a lot of sense.

The TL;DR of the above is:

Interpretation A thinks of the originOffset as applying to the tracking origin.
Interpretation B thinks of the originOffset as applying to the virtual scene's origin.

Either can be wholly mathematically consistent, and either can be made clear by the spec text, I'm just curious which approach the WG as a whole finds to be the most intuitive.

@NellWaliczek
Copy link
Member

This is a FANTASTIC explanation of the confusion. And whichever one we decide, that image is going into the explainer.

Talking to the Sumerian folks, it sounds like either would really be fine. If anything, we have a slight preference for Interpretation A.

@thetuvix
Copy link
Contributor Author

Thanks for the awesome drawings!

Agreed that Option A (where the offset is applied to all returned poses) is more likely what the developer was looking to accomplish in the first place. However, I notice that only in Option B does @toji's description of the originOffset attribute actually naturally use the words "origin" and "offset" in the description.

Regarding the tl;dr, I've thought of originOffset as applying an offset of the origin of the XRSpace being created in that call. Even with the awesome drawings, I'm still having trouble wrapping my mind around the notion of it offsetting the physical tracking origin of the world away from me - that feels backwards.

If we do decide to go with Option A, we should probably rename the attribute to something like poseOffset or such, to make clear that it is the poses you request relative to this space that will be offset, rather than the origin of the space itself. Even then, it may be confusing that the effect would be backwards if you swap the order of the spaces in your getPose call, though if reference spaces are generally the relativeTo space anyway, that might typically be moot.

@toji
Copy link
Member

toji commented Feb 1, 2019

This issue was discussed and the Jan 2019 F2F and it seemed that Interpretation B was the more popular understanding. (If I recall correctly the straw poll was 12 to 7.) So that's the interpretation that we'll be working with moving forward. I'll post some PRs soon to update the spec language to make this clearer.

@cwilso cwilso added ready for editing Design is decided, issue ready for Editor to fix and removed agenda Request discussion in the next telecon/FTF labels Feb 11, 2019
@klausw
Copy link
Contributor

klausw commented Mar 5, 2019

As awesome as Brandon's images are, I think they are still potentially ambiguous due to not including a rotation. Does this picture look like a correct interpretation?

originoffset-combined

It shows the player teleported to position (-1, 0, +0.5) in virtual world space, meaning that's where the feet will be located, and then rotated in place 45 degrees left (+45 degree rotation around +y axis) so that they are looking towards the (-1, 0, -1) direction. (The Y axis points up as per the usual convention.)

If I'm interpreting it right, this would mean I'd need to set originOffset to position (+1.06, 0, +0.35), with an orientation of (-45 degree rotation around +y axis). Here's what this calculation would look like in three.js:

var playerInWorldSpace = new THREE.Vector3(-1, 0, 0.5);
var yaxis = new THREE.Vector3(0, 1, 0);
var playerRotation = new THREE.Quaternion();
playerRotation.setFromAxisAngle(yaxis, Math.PI * 45 / 180);
// Quaternion {_x: 0, _y: 0.3826834323650898, _z: 0, _w: 0.9238795325112867}

var originOffsetRot = playerRotation.clone();
originOffsetRot.inverse();
// Quaternion {_x: -0, _y: -0.3826834323650898, _z: -0, _w: 0.9238795325112867}

var originOffsetPos = playerInWorldSpace.clone();
originOffsetPos.negate();
originOffsetPos.applyQuaternion(originOffsetRot);
// Vector3 {x: 1.0606601717798212, y: 0, z: 0.35355339059327373}

And to apply this in WebXR, I'd do something like this:

xrRefSpace.originOffset = new XRRigidTransform(
  {x: originOffsetPos.x, y: originOffsetPos.y, z: originOffsetPos.z},
  originOffsetRot);
var pose = xrFrame.getViewerPose(xrRefSpace);

Does that look right?

@toji
Copy link
Member

toji commented Mar 6, 2019

That looks right to me, though I'd be happy to hear from anyone that feels it's incorrect. Oh, and for what it's worth if your orignOffsetPos has an x, y, and z attribute then the constructor signature should just accept it as-is, even if it's not a DOMPoint variant:

xrRefSpace.originOffset = new XRRigidTransform(originOffsetPos, originOffsetRot);

@klausw
Copy link
Contributor

klausw commented Mar 7, 2019

Brandon, thanks for confirming. I think the explainer and spec definitely need more explicit descriptions to be unambiguous. For example, currently the explainer says: Teleport the user a certain number of meters along the X, Y, and Z axes, but we have two sets of axes (the virtual world space and the tracking space) which may be oriented differently, and it's also unclear what sign is supposed to be used for the movement.

In case it helps, the following appear to be true for the proposed interpretation according to testing:

  • As far as the headset view is concerned (ignoring controllers), setting originOffset to an XRRigidTransform is equivalent to leaving originOffset unmodified and post-multiplying each of the viewer pose's view matrices with the same XRRigidTransform's matrix element.

  • originOffset's position component equals the tracking space coordinates of the virtual world space origin. In the picture above, the virtual world space origin is the intersection of the two purple axes. Its coordinates in tracking space (green axes) is approximately (+1.06, 0, +0.35), and those are also the numeric values of originOffset.position's x/y/z elements.

Here's a code snippet using glmatrix as used in webxr-samples:

        // Inputs:
        //   playerPosition is the coordinates of the viewer's feet in virtual world space
        //   playerHeadingDegrees is an in-place rotation around +Y, positive values turn left

        // Calculate the XRRigidTransform
        quat.identity(invOrientation);
        quat.rotateY(invOrientation, invOrientation, -playerHeadingDegrees * Math.PI / 180);
        vec3.negate(invPosition, playerPosition);
        vec3.transformQuat(invPosition, invPosition, invOrientation);
        let xform = new XRRigidTransform(
          {x: invPosition[0], y: invPosition[1], z: invPosition[2]},
          {x: invOrientation[0], y: invOrientation[1], z: invOrientation[2], w: invOrientation[3]});

        // Normal teleport/rotation by setting originOffset to this transform:
        if (useOriginOffsetForTeleport) {
          refSpace.originOffset = xform;
        }

        let pose = frame.getViewerPose(refSpace);

        // Alternatively, update head view by manually applying the transform for sanity checking.
        // (Don't do both.)
        if (!useOriginOffsetForTeleport) {
          for (let view of pose.views) {
            mat4.multiply(view.viewMatrix, view.viewMatrix, xform.matrix);
          }
        }

@klausw
Copy link
Contributor

klausw commented Mar 12, 2019

After some more experiments with the implementation, it turns out that my descriptions earlier in this thread were incomplete and/or misleading due to assuming that the user is standing at the origin. On a 6DoF headset where the player is at an arbitrary position in the tracked volume, things get a bit more complicated. Here's an updated sketch:

originoffset-combined

The originOffset calculations themselves still remain the same, but are based on moving the tracking space origin location in world space. That's a valid implementation (IIRC the game "Budget Cuts" does something like this), but it's only intuitive if the user knows their location relative to the tracking space origin.

If you want to teleport the user's feet to a specific world position, or rotate the user in place, there's some additional application logic necessary to adjust the offset correctly when the user isn't centered in tracking space.

Here's an example of a more complex teleportation with rotation around the Y axis added:

originoffset-teleport

In this example, the user teleports from point U to point U' in world space, combined with rotating the tracking space a bit to the left. For that to work correctly, the tracking space origin needs to move from point T to T' in world space.

We can get the needed adjustment by transforming the tracking space player position to world space. The offsetOrigin matrix transforms coordinates from world space to tracking space, and its inverse transforms from tracking space to world space. However, there's a chicken-and-egg problem here - we're trying to calculate the new originOffset from an origin position and rotation, so we can't use its inverse for calculating the origin position.

However, we do know the old originOffset for the pre-teleport state, and we know the change of rotation angle for the tracking space, so we can just rotate the old TU vector around the Y axis by the difference of tracking space orientation angles:

Notation: XY = vector from point X to Y in world space coordinates, for example:

OT' = vector from origin to new tracking space origin
    = coordinates of tracking space origin in world space
TU = vector from old tracking space origin to old player position in world space
T'U' = vector from new tracking space origin to new player position in world space

R = rotation part of originOffset transform (ignoring translation)
a = tracking space heading angle, 0=facing -z, positive rotates left

  OT' = OU' - T'U'
      = OU' - inv(R') * R * TU
      = OU' - inv(rotateY(-a')) * rotateY(-a) * TU
      = OU' - rotateY(a') * rotateY(-a) * TU
      = OU' - rotateY(a' - a) * TU

@cwilso
Copy link
Member

cwilso commented Apr 29, 2019

Should be fixed by #612

@toji
Copy link
Member

toji commented May 1, 2019

The combination of #612 and #587 should make this mechanic much clearer now.

@toji toji closed this as completed May 1, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
fixed by pending PR A PR that is in review will resolve this issue. ready for editing Design is decided, issue ready for Editor to fix spec bug Inconsistencies in spec
Projects
None yet
Development

No branches or pull requests

6 participants
@cwilso @toji @thetuvix @klausw @NellWaliczek and others