mirror of
https://github.com/mitch030504/Wiicompiled_VR_Frame.git
synced 2026-10-06 01:00:14 +02:00
Curl the runtime hand mesh's fingers towards the palm
The Quest offers XR_FB_hand_tracking_mesh without the app declaring hand tracking, so a Quest 3 draws the runtime's own hand mesh rather than the procedural gloves, and its fingers bent backwards on grip: the mesh's joints point -Z towards the fingertip and +Y out of the back of the hand, so flexion is negative about the joint's own X, on both hands. Fix and test by the repository owner; OPENXR.md said the Quest always drew the gloves, which was wrong. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
90a09aec0b
commit
248bd6787d
3 files changed
+76
-3
No files matched your search
@@ -137,7 +137,10 @@ inline void runtime_hand(std::vector<Vertex>& out, const AuroraCockpitHand& hand
|
||||
M local=parent>=0&&parent<26?compose(mesh.inverseBind[parent],mesh.bind[j]):mesh.bind[j];
|
||||
const bool fingerJoint=j>=2 && j!=6 && j!=11 && j!=16 && j!=21;
|
||||
if(fingerJoint) {
|
||||
const float a=curl*(j<6?0.3f:0.75f),c=std::cos(a),s=std::sin(a);
|
||||
// OpenXR joints point -Z toward the fingertip and +Y out of the back
|
||||
// of the hand. Flexion is therefore negative about local X, for both
|
||||
// hands; positive angles bend the fingers backward on runtime meshes.
|
||||
const float a=-curl*(j<6?0.3f:0.75f),c=std::cos(a),s=std::sin(a);
|
||||
local=compose(local,M{1,0,0,0,0,c,-s,0,0,s,c,0});
|
||||
}
|
||||
posed[j]=parent>=0&&parent<26?compose(posed[parent],local):local;
|
||||
|
||||
@@ -153,6 +153,73 @@ TEST_F(CockpitGeometry, GloveFingersRunAlongTheHandAndCloseIntoThePalm) {
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(CockpitGeometry, RuntimeFingersCurlTowardPalmForSqueezeAndWheelGrab) {
|
||||
using namespace aurora::gfx::cockpit;
|
||||
// OpenXR joint space: -Z runs toward the fingertip, +Y out of the back
|
||||
// of the hand, for BOTH hands. Mirror positions, not the curl direction.
|
||||
for (int side = 0; side < 2; ++side) {
|
||||
SCOPED_TRACE(side);
|
||||
HandMesh mesh;
|
||||
mesh.parents.fill(1);
|
||||
mesh.parents[1] = -1;
|
||||
const float rootPose[7]{0, 0, 0.70710678f, 0.70710678f, 0.12f, -0.08f, 0.03f};
|
||||
const M root = from_pose(rootPose);
|
||||
mesh.bind.fill(root);
|
||||
const int bases[]{2, 6, 11, 16, 21};
|
||||
for (int finger = 0; finger < 5; ++finger) {
|
||||
const int base = bases[finger];
|
||||
const int count = finger == 0 ? 4 : 5;
|
||||
for (int bone = 0; bone < count; ++bone) {
|
||||
M bind = identity();
|
||||
bind[3] = (side == 0 ? -1.0f : 1.0f) * (finger - 2) * 0.018f;
|
||||
bind[11] = -0.025f * (bone + 1);
|
||||
mesh.bind[base + bone] = compose(root, bind);
|
||||
mesh.parents[base + bone] = bone == 0 ? 1 : base + bone - 1;
|
||||
}
|
||||
}
|
||||
for (int j = 0; j < 26; ++j) mesh.inverseBind[j] = inverse(mesh.bind[j]);
|
||||
// A tiny triangle rigidly weighted to each joint, including each fingertip.
|
||||
for (int j = 0; j < 26; ++j) {
|
||||
for (V offset : {V{0, 0, 0}, V{0.001f, 0, 0}, V{0, 0, 0.001f}}) {
|
||||
AuroraVRHandVertex vertex{};
|
||||
const V p = point(mesh.bind[j].data(), offset);
|
||||
std::memcpy(vertex.position, p.data(), sizeof(vertex.position));
|
||||
vertex.joints[0] = j;
|
||||
vertex.weights[0] = 1;
|
||||
mesh.indices.push_back(static_cast<uint16_t>(mesh.vertices.size()));
|
||||
mesh.vertices.push_back(vertex);
|
||||
}
|
||||
}
|
||||
AuroraCockpitHand hand{};
|
||||
set_identity(hand.seatFromGrip, {0, 0, 0});
|
||||
const auto build = [&](float squeeze, bool held) {
|
||||
hand.squeeze = squeeze;
|
||||
hand.held = held;
|
||||
std::vector<Vertex> vertices;
|
||||
runtime_hand(vertices, hand, mesh);
|
||||
return vertices;
|
||||
};
|
||||
const auto open = build(0, false);
|
||||
for (int j = 0; j < 26; ++j) {
|
||||
const V bind = point(mesh.inverseBind[1].data(), point(mesh.bind[j].data(), {0, 0, 0}));
|
||||
for (int axis = 0; axis < 3; ++axis)
|
||||
EXPECT_NEAR(open[j * 3].position[axis], bind[axis] + (axis == 2 ? 0.04f : 0), 1e-6f);
|
||||
}
|
||||
for (const auto& closed : {build(0.5f, false), build(1, false), build(0, true)}) {
|
||||
ASSERT_TRUE(all_finite(closed));
|
||||
for (int tip : {5, 10, 15, 20, 25}) {
|
||||
EXPECT_LT(closed[tip * 3].position[1], open[tip * 3].position[1] - 0.005f)
|
||||
<< "fingertip must move toward palm (-Y), joint " << tip;
|
||||
EXPECT_GT(closed[tip * 3].position[2], open[tip * 3].position[2])
|
||||
<< "curl must shorten finger reach, joint " << tip;
|
||||
}
|
||||
for (int rigid : {0, 1, 6, 11, 16, 21})
|
||||
for (int axis = 0; axis < 3; ++axis)
|
||||
EXPECT_NEAR(closed[rigid * 3].position[axis], open[rigid * 3].position[axis], 1e-6f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(CockpitGeometry, RuntimeHandMeshIsSkinnedWithoutNans) {
|
||||
using namespace aurora::gfx::cockpit;
|
||||
auto mesh = std::make_shared<HandMesh>();
|
||||
|
||||
Reference in new issue
Block a user