Update: This is a solution, but it’s not the solution to the problem I thought. I explain below (thanks to @Rectus) that the problem is more like the direction of the roll axes needs to be consistent and so it’s best to let XrLocateViews calculate it 1 time instead of try to add on top of it. (It doesn’t directly “simulate inner ear” but it’s probably the easiest way to avoid having bad “inner ear” results, if not the only way.)
I posted this late last night, and then realized, maybe this is what XrSpace is for obviously, and so I hit the Delete button. It took me a night of sleep to work my head around OpenXr’s highly funky design, and I thought I should post a solution since this is a problem that likely comes up in games that move the orientation out from under the main character, etc.
Also, I figure I might get some feedback in case this isn’t enough.
To work with XrSpace is very counterintuitive. I also have a recentering system to work with. To get space data you have to pass an XrTime, but I can’t exactly see how any of the spaces move except VIEW.
So, to query the recentered space, I have a constant XrSpace representing “identity”. I could probably store this transform, but it’s not convenient. So first I use xrLocateSpace against identity to get the centered space’s coordinates.
Then I reorient its quaternion part with a given value, and use xrCreateReferenceSpace to create a third reoriented space, and use that instead.
Now for some reason I don’t understand, my code has to invert the given quaternion, even though as far as I can see it’s working in OpenXR’s coordinate system (my app has to do a lot of convention conversion around this.) And also, yaw (rotation around vertical axis) for some reason causes a funny effect that is uncomfortable. It seems like rotating the space horizontally just isn’t accounted for, or it’s interacting badly with recentering, I don’t know… but yaw isn’t needed to restore balance anyway. So this wrapper reorientation API removes the yaw and returns it to the caller for their own calculations.
It took me a lot of trial-and-error to wrap my head around what was needed. But after making this change it no longer strikes me as feeling incorrect when I look up with the game controller and to the side with the set (the world doesn’t seem to shift on its axis) so I think it’s working.