This adds the ability to load and animate a mesh for hand tracking on
the Oculus Quest. It is more or less identical to the current
functionality on the vrapi driver.
One key part of this change is that getPose in OpenXR will see if action
spaces are active before locating their spaces. This is due to some
behavior observed on the Oculus Quest with hand tracking where pose
actions for controllers would return invalid data with all of the
location flags erroneously set. The only way to detect and work around
this is to check the pose action state. When this happens, we fall back
to returning the pose of the wrist joint, which is where the Oculus hand
mesh wants to be drawn. In the event that both controllers and hand
tracking are active, the controller pose will be returned by getPose but
the wrist joint can still be accessed using getSkeleton.
Note that this does not yet include support for properly scaling the
hand mesh.
There are numerous opportunities for optimization here that may be
investigated in the future, though performance is well within an
acceptable range right now.
Currently there is a single allocator function used in arr_t. Its
behavior depends on the values for the pointer and size arguments:
- If pointer is NULL, it should allocate new memory.
- If pointer is non-NULL and size is positive, it should resize memory.
- If size is zero, it should free memory.
All instances of arr_t use realloc for this right now. The problem
is that realloc's behavior is undefined when the size argument is zero.
On Windows and Linux, realloc will free the pointer, but on macOS this
isn't the case. This means that arr_t leaks memory on macOS.
It's best to not rely on undefined behavior like this, so let's instead
use a helper function that behaves the way we want.
The VrApi implementation now checks that X, Y, A, B buttons exist on that
specific controller. X,Y are on left; A,B on the right controller. That
mapping covers Quest Touch and Quest 2 controllers.
The current flag did not work because float shader flags are not
supported. It was also not very useful because it was per-shader
and did not use the alpha cutoff property of glTF materials.
Instead, let's turn the shader flag into an enable/disable boolean,
and add a scalar material property named "alphacutoff" that gets
read by the glTF importer.
When the alphaCutoff flag is enabled, the material property will be
compared against the pixel's alpha value to decide whether it should
get discarded.
- When calling lovr.graphics.stencil, the color mask is initially
disabled, and gets restored to its initial state afterwards.
- However, when it's restored, it uses lovrGraphicsSetColorMask, which
just sets shadow state that doesn't make it all the way to GL until
another draw is done.
- The consequence of this is that if you call .stencil and then don't do
a draw, any clears that happen will use the old (disabled) color mask,
preventing the color buffer from being cleared.
- The solution here is to lower the color mask change down into opengl.c
where it can directly hit OpenGL.