Skip to content
Snippets Groups Projects
Commit 5ed8ed8b authored by sdegrande's avatar sdegrande
Browse files

Start to implement OpenXRHand

That class will be used to connect the joint matrices of OpenXR hand
tracking to a rigged hand model.

Currently, it can load a hand model and apply a V-pose as a test.
parent b58b85f7
Branches
No related tags found
No related merge requests found
......@@ -513,6 +513,8 @@ if(VRD_WITH_OPENXR)
target_sources(vairdraw PRIVATE
HardwareSet/OpenXRSet/OpenXR_helpers.cpp
HardwareSet/OpenXRSet/OpenXR_helpers.hpp
HardwareSet/OpenXRSet/OpenXRHand.cpp
HardwareSet/OpenXRSet/OpenXRHand.hpp
HardwareSet/OpenXRSet/OpenXRInput.cpp
HardwareSet/OpenXRSet/OpenXRInput.hpp
HardwareSet/OpenXRSet/OpenXRPassthrough.cpp
......
// SPDX-FileCopyrightText: Copyright (c) 2025 CRIStAL/PIRVI. All rights reserved.
// SPDX-FileCopyrightText: Copyright (c) 2025 Samuel Degrande
// SPDX-License-Identifier: BSD-3-Clause
#include "SceneGraph/Misc/Model3D.hpp"
#include "OpenXRHand.hpp"
#include "Core/Loaders/ContentLoader.hpp"
#include "AssetManager/AssetManager.hpp"
#include "Misc/Model3DImport.hpp"
#include "Utils/json_utils.hpp"
#include "Utils/logstream.hpp"
std::map<std::string, uint32_t> OpenXRHand::xrJoints {
{ "XR_HAND_PALM", XR_HAND_JOINT_PALM_EXT },
{ "XR_HAND_WRIST", XR_HAND_JOINT_WRIST_EXT },
{ "XR_HAND_THUMB_METACARPAL", XR_HAND_JOINT_THUMB_METACARPAL_EXT },
{ "XR_HAND_THUMB_PROXIMAL", XR_HAND_JOINT_THUMB_PROXIMAL_EXT },
{ "XR_HAND_THUMB_DISTAL", XR_HAND_JOINT_THUMB_DISTAL_EXT },
{ "XR_HAND_THUMB_TIP", XR_HAND_JOINT_THUMB_TIP_EXT },
{ "XR_HAND_INDEX_METACARPAL", XR_HAND_JOINT_INDEX_METACARPAL_EXT },
{ "XR_HAND_INDEX_PROXIMAL", XR_HAND_JOINT_INDEX_PROXIMAL_EXT },
{ "XR_HAND_INDEX_INTERMEDIATE", XR_HAND_JOINT_INDEX_INTERMEDIATE_EXT },
{ "XR_HAND_INDEX_DISTAL", XR_HAND_JOINT_INDEX_DISTAL_EXT },
{ "XR_HAND_INDEX_TIP", XR_HAND_JOINT_INDEX_TIP_EXT },
{ "XR_HAND_MIDDLE_METACARPAL", XR_HAND_JOINT_MIDDLE_METACARPAL_EXT },
{ "XR_HAND_MIDDLE_PROXIMAL", XR_HAND_JOINT_MIDDLE_PROXIMAL_EXT },
{ "XR_HAND_MIDDLE_INTERMEDIATE", XR_HAND_JOINT_MIDDLE_INTERMEDIATE_EXT },
{ "XR_HAND_MIDDLE_DISTAL", XR_HAND_JOINT_MIDDLE_DISTAL_EXT },
{ "XR_HAND_MIDDLE_TIP", XR_HAND_JOINT_MIDDLE_TIP_EXT },
{ "XR_HAND_RING_METACARPAL", XR_HAND_JOINT_RING_METACARPAL_EXT },
{ "XR_HAND_RING_PROXIMAL", XR_HAND_JOINT_RING_PROXIMAL_EXT },
{ "XR_HAND_RING_INTERMEDIATE", XR_HAND_JOINT_RING_INTERMEDIATE_EXT },
{ "XR_HAND_RING_DISTAL", XR_HAND_JOINT_RING_DISTAL_EXT },
{ "XR_HAND_RING_TIP", XR_HAND_JOINT_RING_TIP_EXT },
{ "XR_HAND_LITTLE_METACARPAL", XR_HAND_JOINT_LITTLE_METACARPAL_EXT },
{ "XR_HAND_LITTLE_PROXIMAL", XR_HAND_JOINT_LITTLE_PROXIMAL_EXT },
{ "XR_HAND_LITTLE_INTERMEDIATE", XR_HAND_JOINT_LITTLE_INTERMEDIATE_EXT },
{ "XR_HAND_LITTLE_DISTAL", XR_HAND_JOINT_LITTLE_DISTAL_EXT },
{ "XR_HAND_LITTLE_TIP", XR_HAND_JOINT_LITTLE_TIP_EXT }
};
OpenXRHand::OpenXRHand(const std::string& modelName, OxrHand handedness)
{
Json::Value jsConf { Json::objectValue };
jsConf["type"] = "model";
jsConf["file"] = modelName;
// We want to load the hand immediately
bool asyncLoading = ContentLoader::getAsyncLoading();
ContentLoader::setAsyncLoading(false);
handModel = ContentLoader::loadContent(jsConf);
ContentLoader::setAsyncLoading(asyncLoading);
if (!handModel) return;
importJointBindings(modelName);
}
OpenXRHand::~OpenXRHand()
{
if (handModel) delete handModel;
handModel = nullptr;
}
void OpenXRHand::render(Renderer* renderer, const glm::mat4& local)
{
if (handModel) handModel->render(renderer, local);
}
void OpenXRHand::importJointBindings(const std::string& modelName)
{
if (!handModel) return;
Model3D* node = dynamic_cast<Model3D*>(handModel);
if (!node) {
logstreams::warn << "can not cast " << typeid(handModel).name() << std::endl;
return;
}
std::string modelFullPath = AssetManager::getInstance()->findAsset(modelName);
if (modelFullPath.empty()) {
// Should not happen, since this function is called once the model has been loaded
logstreams::error << "Did not find the path of a model (" << modelName
<< "), but this should not happen since that model has been loaded ! VAirDraw is corrupted !"
<< std::endl;
return;
}
Model3DImport modelImporter;
Json::Value bindings = modelImporter.getImportProperties(modelName, modelFullPath, "xrhand-bindings");
jointBindings.resize(xrJoints.size(), nullptr);
// Fill jointBindings : associate each index of an Xr joint to a skeletonbone of the model (or to nullptr if that
// joint does not exist in the model)
for (auto it = bindings.begin(); it != bindings.end(); ++it) {
std::string key = it.key().asString();
std::string value = (*it).asString();
auto skeletonBone = node->getSkeletonBoneFromIndex(key);
if (!skeletonBone) {
logstreams::warn << "Did not find a bone (" << key << ") in " << modelName
<< ". Check its import.json file." << std::endl;
continue;
}
jointBindings[xrJoints[value]] = skeletonBone;
}
}
// Apply a V-Pose to the hand. Left hand only !
// Used to test the hand's model and its xrhand-bindings.
void OpenXRHand::debugApplyVPose()
{
glm::mat4 vPose[100] { 1.0f };
uint32_t nbBones { 0 };
// clang-format off
// XR_HAND_JOINT_PALM_EXT
vPose[nbBones++] = glm::mat4( 1.000, 0.000, 0.000, 0.000,
0.000, 1.000, 0.000, 0.000,
0.000, 0.000, 1.000, 0.000,
0.000, 0.000, 0.000, 1.000);
// XR_HAND_JOINT_WRIST_EXT
vPose[nbBones++] = glm::mat4( 1.000, 0.000, 0.000, -0.001,
0.000, 1.000, 0.000, 0.007,
0.000, 0.000, 1.000, 0.073,
0.000, 0.000, 0.000, 1.000);
// XR_HAND_JOINT_THUMB_METACARPAL_EXT
vPose[nbBones++] = glm::mat4(-0.277, 0.940, -0.199, 0.024,
-0.704, -0.058, 0.708, -0.020,
0.654, 0.336, 0.678, 0.031,
0.000, 0.000, 0.000, 1.000);
// XR_HAND_JOINT_THUMB_PROXIMAL_EXT
vPose[nbBones++] = glm::mat4(-0.149, 0.718, 0.680, 0.031,
-0.780, -0.509, 0.366, -0.046,
0.608, -0.476, 0.635, 0.006,
0.000, 0.000, 0.000, 1.000);
// XR_HAND_JOINT_THUMB_DISTAL_EXT
vPose[nbBones++] = glm::mat4(-0.164, 0.358, 0.919, 0.006,
-0.660, -0.732, 0.167, -0.059,
0.733, -0.579, 0.356, -0.017,
0.000, 0.000, 0.000, 1.000);
// XR_HAND_JOINT_THUMB_TIP_EXT
vPose[nbBones++] = glm::mat4(-0.164, 0.358, 0.919, -0.019,
-0.660, -0.732, 0.167, -0.064,
0.733, -0.579, 0.356, -0.028,
0.000, 0.000, 0.000, 1.000);
// XR_HAND_JOINT_INDEX_METACARPAL_EXT
vPose[nbBones++] = glm::mat4( 1.000, 0.000, 0.000, 0.017,
0.000, 1.000, 0.000, -0.008,
0.000, 0.000, 1.000, 0.031,
0.000, 0.000, 0.000, 1.000);
// XR_HAND_JOINT_INDEX_PROXIMAL_EXT
vPose[nbBones++] = glm::mat4( 0.965, 0.093, -0.246, 0.025,
-0.047, 0.982, 0.185, -0.001,
0.258, -0.167, 0.952, -0.033,
0.000, 0.000, 0.000, 1.000);
// XR_HAND_JOINT_INDEX_INTERMEDIATE_EXT
vPose[nbBones++] = glm::mat4( 0.972, 0.063, -0.227, 0.036,
0.001, 0.963, 0.270, -0.009,
0.236, -0.262, 0.936, -0.074,
0.000, 0.000, 0.000, 1.000);
// XR_HAND_JOINT_INDEX_DISTAL_EXT
vPose[nbBones++] = glm::mat4( 0.984, 0.004, -0.178, 0.042,
0.021, 0.990, 0.139, -0.016,
0.177, -0.141, 0.974, -0.099,
0.000, 0.000, 0.000, 1.000);
// XR_HAND_JOINT_INDEX_TIP_EXT
vPose[nbBones++] = glm::mat4( 0.984, 0.004, -0.178, 0.046,
0.021, 0.990, 0.139, -0.019,
0.177, -0.141, 0.974, -0.123,
0.000, 0.000, 0.000, 1.000);
// XR_HAND_JOINT_MIDDLE_METACARPAL_EXT
vPose[nbBones++] = glm::mat4( 1.000, 0.000, 0.000, -0.001,
0.000, 1.000, 0.000, -0.004,
0.000, 0.000, 1.000, 0.033,
0.000, 0.000, 0.000, 1.000);
// XR_HAND_JOINT_MIDDLE_PROXIMAL_EXT
vPose[nbBones++] = glm::mat4( 0.997, -0.036, 0.064, 0.001,
0.013, 0.944, 0.330, 0.004,
-0.072, -0.329, 0.942, -0.033,
0.000, 0.000, 0.000, 1.000);
// XR_HAND_JOINT_MIDDLE_INTERMEDIATE_EXT
vPose[nbBones++] = glm::mat4( 0.996, -0.062, 0.070, -0.002,
0.031, 0.927, 0.374, -0.012,
-0.088, -0.370, 0.925, -0.078,
0.000, 0.000, 0.000, 1.000);
// XR_HAND_JOINT_MIDDLE_DISTAL_EXT
vPose[nbBones++] = glm::mat4( 0.987, -0.140, 0.078, -0.004,
0.107, 0.939, 0.328, -0.023,
-0.119, -0.316, 0.941, -0.106,
0.000, 0.000, 0.000, 1.000);
// XR_HAND_JOINT_MIDDLE_TIP_EXT
vPose[nbBones++] = glm::mat4( 0.987, -0.140, 0.078, -0.007,
0.107, 0.939, 0.328, -0.031,
-0.119, -0.316, 0.941, -0.133,
0.000, 0.000, 0.000, 1.000);
// XR_HAND_JOINT_RING_METACARPAL_EXT
vPose[nbBones++] = glm::mat4( 1.000, 0.000, 0.000, -0.017,
0.000, 1.000, 0.000, 0.000,
0.000, 0.000, 1.000, 0.035,
0.000, 0.000, 0.000, 1.000);
// XR_HAND_JOINT_RING_PROXIMAL_EXT
vPose[nbBones++] = glm::mat4( 0.976, -0.211, 0.061, -0.020,
0.090, 0.638, 0.764, -0.000,
-0.201, -0.740, 0.642, -0.025,
0.000, 0.000, 0.000, 1.000);
// XR_HAND_JOINT_RING_INTERMEDIATE_EXT
vPose[nbBones++] = glm::mat4( 0.950, -0.052, -0.306, -0.023,
0.123, -0.843, 0.524, -0.034,
-0.285, -0.536, -0.794, -0.053,
0.000, 0.000, 0.000, 1.000);
// XR_HAND_JOINT_RING_DISTAL_EXT
vPose[nbBones++] = glm::mat4( 0.938, 0.226, -0.264, -0.014,
0.068, -0.865, -0.498, -0.049,
-0.340, 0.449, -0.826, -0.030,
0.000, 0.000, 0.000, 1.000);
// XR_HAND_JOINT_RING_TIP_EXT
vPose[nbBones++] = glm::mat4( 0.938, 0.226, -0.264, -0.006,
0.068, -0.865, -0.498, -0.037,
-0.340, 0.449, -0.826, -0.006,
0.000, 0.000, 0.000, 1.000);
// XR_HAND_JOINT_LITTLE_METACARPAL_EXT
vPose[nbBones++] = glm::mat4( 0.875, -0.406, 0.264, -0.026,
0.396, 0.914, 0.094, -0.004,
-0.279, 0.023, 0.960, 0.035,
0.000, 0.000, 0.000, 1.000);
// XR_HAND_JOINT_LITTLE_PROXIMAL_EXT
vPose[nbBones++] = glm::mat4( 0.917, -0.398, -0.012, -0.040,
0.253, 0.558, 0.790, -0.008,
-0.308, -0.728, 0.612, -0.013,
0.000, 0.000, 0.000, 1.000);
// XR_HAND_JOINT_LITTLE_INTERMEDIATE_EXT
vPose[nbBones++] = glm::mat4( 0.856, -0.160, -0.491, -0.039,
0.219, -0.748, 0.626, -0.035,
-0.468, -0.644, -0.606, -0.034,
0.000, 0.000, 0.000, 1.000);
// XR_HAND_JOINT_LITTLE_DISTAL_EXT
vPose[nbBones++] = glm::mat4( 0.810, 0.368, -0.457, -0.028,
0.238, -0.918, -0.318, -0.050,
-0.536, 0.148, -0.831, -0.021,
0.000, 0.000, 0.000, 1.000);
// XR_HAND_JOINT_LITTLE_TIP_EXT
vPose[nbBones++] = glm::mat4( 0.810, 0.368, -0.457, -0.016,
0.238, -0.918, -0.318, -0.043,
-0.536, 0.148, -0.831, -0.000,
0.000, 0.000, 0.000, 1.000);
// clang-format on
for (uint32_t i = 0; i < nbBones; ++i) {
if (i < jointBindings.size() && jointBindings[i]) {
jointBindings[i]->setMatrix(glm::transpose(vPose[i]));
}
}
// Apply a transformation so that the hand is visible enough
handModel->setScale({ 3.0f, 3.0f, 3.0f });
handModel->setPosition({ 0.0f, 1.0f, 0.0f });
handModel->setOrientation(glm::rotate(3.14f, glm::vec3 { 0.0f, 1.0f, 0.0f })
* glm::rotate(1.57f, glm::vec3 { 1.0f, 0.0f, 0.0f }));
}
// SPDX-FileCopyrightText: Copyright (c) 2025 CRIStAL/PIRVI. All rights reserved.
// SPDX-FileCopyrightText: Copyright (c) 2025 Samuel Degrande
// SPDX-License-Identifier: BSD-3-Clause
#ifndef SRC_HARDWARESET_OPENXRSET_OPENXRHAND_HPP_
#define SRC_HARDWARESET_OPENXRSET_OPENXRHAND_HPP_
#include "SceneGraph/Node.hpp"
#include "OpenXR_helpers.hpp"
class SkeletonBone;
class OpenXRHand : public Node
{
public:
OpenXRHand(const std::string& modelName, OxrHand handedness);
~OpenXRHand();
void debugApplyVPose();
void render(Renderer* renderer, const glm::mat4& local) override;
private:
static std::map<std::string, uint32_t> xrJoints;
Node* handModel { nullptr };
std::vector<SkeletonBone*> jointBindings { nullptr };
private:
void importJointBindings(const std::string& modelName);
};
#endif // SRC_HARDWARESET_OPENXRSET_OPENXRHAND_HPP_
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment