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

Origin stuff; #671

Closed
wants to merge 1 commit into from
Closed

Origin stuff; #671

wants to merge 1 commit into from

Conversation

bjornbytes
Copy link
Owner

This contains a few changes to headset coordinate spaces. There are some interesting ideas here, but I'm not totally happy with everything and not sure it will get merged. Leaving it as a PR for a while to get visibility/feedback.

Changes

  • Add lovr.headset.get/setOffset, lovr.headset.translate, and lovr.headset.rotate. This is a commonly-requested feature that lets you define a "base transform" for the origin that all the other headset poses (view poses, device poses, velocities, skeletal joint poses) will get composed with.
  • The default coordinate space respects recentering now, which is what most apps want.
  • Add lovr.recenter event that is fired when the play area is recentered.
  • Adds t.headset.seated and lovr.headset.isSeated. In seated mode, y=0 will be at eye level. Otherwise, y=0 will be on the floor (with a 1.7m virtual floor offset added for headsets that can't track the floor). This replaces lovr.headset.getOriginType and t.headset.offset. The main difference here is that the LÖVR project is in control of whether it wants to arrange objects in a "seated" way or not, and either mode will work consistently on all headset types. Previously you were basically forced into authoring content in a roomscale way, and had no way of consistently setting up a "seated" coordinate space with y=0 at eye level.
  • Add lovr.headset.getBoundsPose (and getBoundsPosition/Orientation, names need work). This returns an offset to the middle of the play area. This can be different from the origin of the coordinate system due to recentering. This gives you a reliable way to position static content that aligns with the room and have it NOT move around during a recentering:
pass:push()
pass:transform(lovr.headset.getBoundsPose())
drawStaticEnvironment()
pass:pop()

The idea is that some apps will want to render this kind static environment, but also have more dynamic menus or objects that move around when the play area is recentered (think the Quest background environment vs. the Quest menus). Note that the bounds pose does not incorporate the headset offset, so that the above idiom works even if an offset is applied. That's not set in stone or anything, but seems more useful.

Issues

  • The "seated vs. standing" paradigm is a pretty dated concept at this point. Generally OpenXR/WebXR like to generalize origins into 5 different types of coordinate spaces, some discussion here. The design in this PR makes it hard to support unbounded tracking on AR headsets later without doing a breaking change.
    • A more modern version would be to have a HeadsetOrigin enum that is something like local, local-floor, stage, and unbounded, which you would set in conf.lua. However, I felt that this was a little bit too low-level and unfriendly for LÖVR.
  • The global offset sounds convenient on paper, but I think it will be pretty confusing, and the implementation is fairly complex. It's a stateful thing you have to be very careful about:
    • Make sure you set the offset before lovr.draw, because the view poses of the headset pass are set up at that point.
    • If you update the origin in the middle of lovr.update (maybe due to a teleport), you have to be careful because any poses you fetched are now out of date.
    • Does lovr.headset.translate take into account the offset's rotation or not? Currently it does, but if you don't want that then you can't use .translate and need to do setOffset manually.
  • Should the bounds pose be a floor device instead? So getBoundsPose would be lovr.headset.getPose('floor'). In that case the global headset offset would be applied to the pose, so you'd have to undo the offset, or do pass:origin before applying the floor pose when drawing.

@bjornbytes
Copy link
Owner Author

bjornbytes commented Jun 14, 2023

getBoundsPosition/Orientation/Pose could just be lovr.headset.getBoundsOffset, returning x, y, z, yaw, because the axis is guaranteed by OpenXR to be 0,1,0. The name is a little easier to understand IMO too.

Though maybe sticking with the convention used elsewhere is a good idea.

@bjornbytes
Copy link
Owner Author

The lovr.recenter event and the switch to local-floor space by default has been implemented on the dev branch.

@bjornbytes
Copy link
Owner Author

A floor device and a t.headset.seated flag have been added on the dev branch. The "global offset" didn't make it in due to perceived design issues. That's everything from this PR!

@bjornbytes bjornbytes closed this Jun 29, 2023
@bjornbytes bjornbytes deleted the origin-offset branch June 29, 2023 22:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

1 participant