VRTowerDef/Plugins/MetaXR/Source/OculusXRInput/Private/OculusXRHandTracking.cpp

688 lines
26 KiB
C++

// @lint-ignore-every LICENSELINT
// Copyright Epic Games, Inc. All Rights Reserved.
#include "OculusXRHandTracking.h"
#include "OculusXRHMD.h"
#include "Misc/CoreDelegates.h"
#include "IOculusXRInputModule.h"
#include "Animation/Skeleton.h"
#include "BoneWeights.h"
#include "Components/SkeletalMeshComponent.h"
#include "Rendering/SkeletalMeshLODRenderData.h"
#include "Rendering/SkeletalMeshRenderData.h"
#include "Rendering/SkeletalMeshLODModel.h"
#include "Rendering/SkeletalMeshModel.h"
#include "Materials/Material.h"
#include "Materials/MaterialInstanceDynamic.h"
#include "Engine/SkinnedAssetCommon.h"
#include "Model.h"
#include "MaterialDomain.h"
#define OCULUS_TO_UE4_SCALE 100.0f
namespace OculusXRInput
{
static FInputDeviceId GetDeviceID(int32 ControllerId)
{
IPlatformInputDeviceMapper& DeviceMapper = IPlatformInputDeviceMapper::Get();
FPlatformUserId InPlatformUser = FGenericPlatformMisc::GetPlatformUserForUserIndex(ControllerId);
FInputDeviceId InDeviceId = INPUTDEVICEID_NONE;
DeviceMapper.RemapControllerIdToPlatformUserAndDevice(ControllerId, InPlatformUser, InDeviceId);
return InDeviceId;
}
FQuat FOculusHandTracking::GetBoneRotation(const int32 ControllerIndex, const EOculusXRHandType DeviceHand, const EOculusXRBone BoneId)
{
FQuat Rotation = FQuat::Identity;
if (BoneId <= EOculusXRBone::Invalid && BoneId >= EOculusXRBone::Bone_Max)
{
return Rotation;
}
#if OCULUS_INPUT_SUPPORTED_PLATFORMS
TSharedPtr<FOculusXRInput> OculusXRInputModule = StaticCastSharedPtr<FOculusXRInput>(IOculusXRInputModule::Get().GetInputDevice());
if (OculusXRInputModule.IsValid())
{
const FInputDeviceId InDeviceId = GetDeviceID(ControllerIndex);
TArray<FOculusControllerPair> ControllerPairs = OculusXRInputModule.Get()->ControllerPairs;
for (const FOculusControllerPair& HandPair : ControllerPairs)
{
if (HandPair.DeviceId == InDeviceId)
{
if (DeviceHand != EOculusXRHandType::None)
{
ovrpHand Hand = DeviceHand == EOculusXRHandType::HandLeft ? ovrpHand_Left : ovrpHand_Right;
const FOculusHandControllerState& HandState = HandPair.HandControllerStates[Hand];
int32 OvrBoneId = ToOvrBone(BoneId);
Rotation = HandState.BoneRotations[OvrBoneId];
break;
}
}
}
}
#endif
return Rotation;
}
float FOculusHandTracking::GetHandScale(const int32 ControllerIndex, const EOculusXRHandType DeviceHand)
{
#if OCULUS_INPUT_SUPPORTED_PLATFORMS
TSharedPtr<FOculusXRInput> OculusXRInputModule = StaticCastSharedPtr<FOculusXRInput>(IOculusXRInputModule::Get().GetInputDevice());
if (OculusXRInputModule.IsValid())
{
const FInputDeviceId InDeviceId = GetDeviceID(ControllerIndex);
TArray<FOculusControllerPair> ControllerPairs = OculusXRInputModule.Get()->ControllerPairs;
for (const FOculusControllerPair& HandPair : ControllerPairs)
{
if (HandPair.DeviceId == InDeviceId)
{
if (DeviceHand != EOculusXRHandType::None)
{
ovrpHand Hand = DeviceHand == EOculusXRHandType::HandLeft ? ovrpHand_Left : ovrpHand_Right;
return HandPair.HandControllerStates[Hand].HandScale;
}
}
}
}
#endif
return 1.0f;
}
EOculusXRTrackingConfidence FOculusHandTracking::GetTrackingConfidence(const int32 ControllerIndex, const EOculusXRHandType DeviceHand)
{
#if OCULUS_INPUT_SUPPORTED_PLATFORMS
TSharedPtr<FOculusXRInput> OculusXRInputModule = StaticCastSharedPtr<FOculusXRInput>(IOculusXRInputModule::Get().GetInputDevice());
if (OculusXRInputModule.IsValid())
{
const FInputDeviceId InDeviceId = GetDeviceID(ControllerIndex);
TArray<FOculusControllerPair> ControllerPairs = OculusXRInputModule.Get()->ControllerPairs;
for (const FOculusControllerPair& HandPair : ControllerPairs)
{
if (HandPair.DeviceId == InDeviceId)
{
if (DeviceHand != EOculusXRHandType::None)
{
ovrpHand Hand = DeviceHand == EOculusXRHandType::HandLeft ? ovrpHand_Left : ovrpHand_Right;
return HandPair.HandControllerStates[Hand].TrackingConfidence;
}
}
}
}
#endif
return EOculusXRTrackingConfidence::Low;
}
EOculusXRTrackingConfidence FOculusHandTracking::GetFingerTrackingConfidence(const int32 ControllerIndex, const EOculusXRHandType DeviceHand, const EOculusHandAxes Finger)
{
TSharedPtr<FOculusXRInput> OculusXRInputModule = StaticCastSharedPtr<FOculusXRInput>(IOculusXRInputModule::Get().GetInputDevice());
if (OculusXRInputModule.IsValid())
{
const FInputDeviceId InDeviceId = GetDeviceID(ControllerIndex);
TArray<FOculusControllerPair> ControllerPairs = OculusXRInputModule.Get()->ControllerPairs;
for (const FOculusControllerPair& HandPair : ControllerPairs)
{
if (HandPair.DeviceId == InDeviceId)
{
if (DeviceHand != EOculusXRHandType::None)
{
ovrpHand Hand = DeviceHand == EOculusXRHandType::HandLeft ? ovrpHand_Left : ovrpHand_Right;
return HandPair.HandControllerStates[Hand].FingerConfidences[(int)Finger];
}
}
}
}
return EOculusXRTrackingConfidence::Low;
}
FTransform FOculusHandTracking::GetPointerPose(const int32 ControllerIndex, const EOculusXRHandType DeviceHand, const float WorldToMeters)
{
#if OCULUS_INPUT_SUPPORTED_PLATFORMS
TSharedPtr<FOculusXRInput> OculusXRInputModule = StaticCastSharedPtr<FOculusXRInput>(IOculusXRInputModule::Get().GetInputDevice());
if (OculusXRInputModule.IsValid())
{
const FInputDeviceId InDeviceId = GetDeviceID(ControllerIndex);
TArray<FOculusControllerPair> ControllerPairs = OculusXRInputModule.Get()->ControllerPairs;
for (const FOculusControllerPair& HandPair : ControllerPairs)
{
if (HandPair.DeviceId == InDeviceId)
{
if (DeviceHand != EOculusXRHandType::None)
{
ovrpHand Hand = DeviceHand == EOculusXRHandType::HandLeft ? ovrpHand_Left : ovrpHand_Right;
FTransform PoseTransform = HandPair.HandControllerStates[Hand].PointerPose;
PoseTransform.SetLocation(PoseTransform.GetLocation() * WorldToMeters);
return PoseTransform;
}
}
}
}
#endif
return FTransform();
}
bool FOculusHandTracking::IsPointerPoseValid(const int32 ControllerIndex, const EOculusXRHandType DeviceHand)
{
#if OCULUS_INPUT_SUPPORTED_PLATFORMS
TSharedPtr<FOculusXRInput> OculusXRInputModule = StaticCastSharedPtr<FOculusXRInput>(IOculusXRInputModule::Get().GetInputDevice());
if (OculusXRInputModule.IsValid())
{
const FInputDeviceId InDeviceId = GetDeviceID(ControllerIndex);
TArray<FOculusControllerPair> ControllerPairs = OculusXRInputModule.Get()->ControllerPairs;
for (const FOculusControllerPair& HandPair : ControllerPairs)
{
if (HandPair.DeviceId == InDeviceId)
{
if (DeviceHand != EOculusXRHandType::None)
{
ovrpHand Hand = DeviceHand == EOculusXRHandType::HandLeft ? ovrpHand_Left : ovrpHand_Right;
return HandPair.HandControllerStates[Hand].bIsPointerPoseValid;
}
}
}
}
#endif
return false;
}
bool FOculusHandTracking::IsHandTrackingEnabled()
{
#if OCULUS_INPUT_SUPPORTED_PLATFORMS
ovrpBool result;
FOculusXRHMDModule::GetPluginWrapper().GetHandTrackingEnabled(&result);
return result == ovrpBool_True;
#else
return false;
#endif
}
bool FOculusHandTracking::IsHandDominant(const int32 ControllerIndex, const EOculusXRHandType DeviceHand)
{
#if OCULUS_INPUT_SUPPORTED_PLATFORMS
TSharedPtr<FOculusXRInput> OculusXRInputModule = StaticCastSharedPtr<FOculusXRInput>(IOculusXRInputModule::Get().GetInputDevice());
if (OculusXRInputModule.IsValid())
{
const FInputDeviceId InDeviceId = GetDeviceID(ControllerIndex);
TArray<FOculusControllerPair> ControllerPairs = OculusXRInputModule.Get()->ControllerPairs;
for (const FOculusControllerPair& HandPair : ControllerPairs)
{
if (HandPair.DeviceId == InDeviceId)
{
if (DeviceHand != EOculusXRHandType::None)
{
ovrpHand Hand = DeviceHand == EOculusXRHandType::HandLeft ? ovrpHand_Left : ovrpHand_Right;
return HandPair.HandControllerStates[Hand].bIsDominantHand;
}
}
}
}
#endif
return false;
}
bool FOculusHandTracking::IsHandPositionValid(int32 ControllerIndex, EOculusXRHandType DeviceHand)
{
TSharedPtr<FOculusXRInput> OculusXRInputModule = StaticCastSharedPtr<FOculusXRInput>(IOculusXRInputModule::Get().GetInputDevice());
if (OculusXRInputModule.IsValid())
{
const FInputDeviceId InDeviceId = GetDeviceID(ControllerIndex);
TArray<FOculusControllerPair> ControllerPairs = OculusXRInputModule.Get()->ControllerPairs;
for (const FOculusControllerPair& HandPair : ControllerPairs)
{
if (HandPair.DeviceId == InDeviceId)
{
if (DeviceHand != EOculusXRHandType::None)
{
ovrpHand Hand = DeviceHand == EOculusXRHandType::HandLeft ? ovrpHand_Left : ovrpHand_Right;
return HandPair.HandControllerStates[Hand].bIsPositionValid;
}
}
}
}
return false;
}
bool FOculusHandTracking::GetHandSkeletalMesh(USkeletalMesh* HandSkeletalMesh, const EOculusXRHandType SkeletonType, const EOculusXRHandType MeshType, const float WorldToMeters)
{
#if OCULUS_INPUT_SUPPORTED_PLATFORMS
if (HandSkeletalMesh)
{
ovrpMesh* OvrMesh = new ovrpMesh();
ovrpSkeleton2* OvrSkeleton = new ovrpSkeleton2();
ovrpSkeletonType OvrSkeletonType = (ovrpSkeletonType)((int32)SkeletonType - 1);
ovrpMeshType OvrMeshType = (ovrpMeshType)((int32)MeshType - 1);
ovrpResult SkelResult = FOculusXRHMDModule::GetPluginWrapper().GetSkeleton2(OvrSkeletonType, OvrSkeleton);
ovrpResult MeshResult = FOculusXRHMDModule::GetPluginWrapper().GetMesh(OvrMeshType, OvrMesh);
if (SkelResult != ovrpSuccess || MeshResult != ovrpSuccess)
{
#if !WITH_EDITOR
UE_LOG(LogOcHandTracking, Error, TEXT("Failed to get mesh or skeleton data from Oculus runtime."));
#endif
delete OvrMesh;
delete OvrSkeleton;
return false;
}
// Create Skeletal Mesh LOD Render Data
#if WITH_EDITOR
FSkeletalMeshLODModel* LodRenderData = new FSkeletalMeshLODModel();
HandSkeletalMesh->GetImportedModel()->LODModels.Add(LodRenderData);
#else
FSkeletalMeshLODRenderData* LodRenderData = new FSkeletalMeshLODRenderData();
HandSkeletalMesh->AllocateResourceForRendering();
HandSkeletalMesh->GetResourceForRendering()->LODRenderData.Add(LodRenderData);
#endif
// Set default LOD Info
FSkeletalMeshLODInfo& LodInfo = HandSkeletalMesh->AddLODInfo();
LodInfo.ScreenSize = 0.3f;
LodInfo.LODHysteresis = 0.2f;
LodInfo.BuildSettings.bUseFullPrecisionUVs = true;
InitializeHandSkeleton(HandSkeletalMesh, OvrSkeleton, WorldToMeters);
// Add default material as backup
LodInfo.LODMaterialMap.Add(0);
UMaterialInterface* DefaultMaterial = UMaterial::GetDefaultMaterial(MD_Surface);
HandSkeletalMesh->GetMaterials().Add(DefaultMaterial);
HandSkeletalMesh->GetMaterials()[0].UVChannelData.bInitialized = true;
// Set skeletal mesh properties
HandSkeletalMesh->SetHasVertexColors(true);
HandSkeletalMesh->SetHasBeenSimplified(false);
HandSkeletalMesh->SetEnablePerPolyCollision(false);
InitializeHandMesh(HandSkeletalMesh, OvrMesh, WorldToMeters);
#if WITH_EDITOR
HandSkeletalMesh->InvalidateDeriveDataCacheGUID();
HandSkeletalMesh->PostEditChange();
#endif
// Create Skeleton object and merge all bones
HandSkeletalMesh->SetSkeleton(NewObject<USkeleton>());
HandSkeletalMesh->GetSkeleton()->MergeAllBonesToBoneTree(HandSkeletalMesh);
HandSkeletalMesh->PostLoad();
delete OvrMesh;
delete OvrSkeleton;
return true;
}
#endif
return false;
}
void FOculusHandTracking::InitializeHandMesh(USkeletalMesh* SkeletalMesh, const ovrpMesh* OvrMesh, const float WorldToMeters)
{
#if WITH_EDITOR
FSkeletalMeshLODModel* LodRenderData = &SkeletalMesh->GetImportedModel()->LODModels[0];
// Initialize mesh section
LodRenderData->Sections.SetNumUninitialized(1);
new (&LodRenderData->Sections[0]) FSkelMeshSection();
auto& MeshSection = LodRenderData->Sections[0];
// Set default mesh section properties
MeshSection.MaterialIndex = 0;
MeshSection.BaseIndex = 0;
MeshSection.NumTriangles = OvrMesh->NumIndices / 3;
MeshSection.BaseVertexIndex = 0;
MeshSection.MaxBoneInfluences = 4;
MeshSection.NumVertices = OvrMesh->NumVertices;
float MaxDistSq = MIN_flt;
for (uint32_t VertexIndex = 0; VertexIndex < OvrMesh->NumVertices; VertexIndex++)
{
FSoftSkinVertex SoftVertex;
FMemory::Memzero(SoftVertex.InfluenceWeights);
FMemory::Memzero(SoftVertex.InfluenceBones);
// Update vertex data
SoftVertex.Color = FColor::White;
ovrpVector3f VertexPosition = OvrMesh->VertexPositions[VertexIndex];
ovrpVector3f Normal = OvrMesh->VertexNormals[VertexIndex];
SoftVertex.Position = FVector3f(VertexPosition.x, VertexPosition.z, VertexPosition.y) * WorldToMeters;
SoftVertex.TangentZ = FVector3f(Normal.x, Normal.z, Normal.y);
SoftVertex.TangentX = FVector3f(1.0f, 0.0f, 0.0f);
SoftVertex.TangentY = FVector3f(0.0f, 1.0f, 0.0f); // SoftVertex.TangentZ^ SoftVertex.TangentX* SoftVertex.TangentZ.W;
SoftVertex.UVs[0] = FVector2f(OvrMesh->VertexUV0[VertexIndex].x, OvrMesh->VertexUV0[VertexIndex].y);
// Update the Bounds
float VertexDistSq = SoftVertex.Position.SizeSquared();
if (VertexDistSq > MaxDistSq)
MaxDistSq = VertexDistSq;
// Update blend weights and indices
ovrpVector4f BlendWeights = OvrMesh->BlendWeights[VertexIndex];
ovrpVector4s BlendIndices = OvrMesh->BlendIndices[VertexIndex];
SoftVertex.InfluenceWeights[0] = UE::AnimationCore::MaxRawBoneWeightFloat * BlendWeights.x;
SoftVertex.InfluenceBones[0] = BlendIndices.x;
SoftVertex.InfluenceWeights[1] = UE::AnimationCore::MaxRawBoneWeightFloat * BlendWeights.y;
SoftVertex.InfluenceBones[1] = BlendIndices.y;
SoftVertex.InfluenceWeights[2] = UE::AnimationCore::MaxRawBoneWeightFloat * BlendWeights.z;
SoftVertex.InfluenceBones[2] = BlendIndices.z;
SoftVertex.InfluenceWeights[3] = UE::AnimationCore::MaxRawBoneWeightFloat * BlendWeights.w;
SoftVertex.InfluenceBones[3] = BlendIndices.w;
MeshSection.SoftVertices.Add(SoftVertex);
}
// Update bone map
for (uint32 BoneIndex = 0; BoneIndex < (uint32)SkeletalMesh->GetRefSkeleton().GetNum(); BoneIndex++)
{
MeshSection.BoneMap.Add(BoneIndex);
}
// Update LOD render data
LodRenderData->NumVertices = OvrMesh->NumVertices;
LodRenderData->NumTexCoords = 1;
// Create index buffer
for (uint32_t Index = 0; Index < OvrMesh->NumIndices; Index++)
{
LodRenderData->IndexBuffer.Add(OvrMesh->Indices[Index]);
}
// Finalize Bounds
float MaxDist = FMath::Sqrt(MaxDistSq);
FBoxSphereBounds Bounds;
Bounds.Origin = FVector::ZeroVector;
Bounds.BoxExtent = FVector(MaxDist);
Bounds.SphereRadius = MaxDist;
SkeletalMesh->SetImportedBounds(Bounds);
#else
FSkeletalMeshLODRenderData* LodRenderData = &SkeletalMesh->GetResourceForRendering()->LODRenderData[0];
// Initialize Mesh Section
LodRenderData->RenderSections.SetNumUninitialized(1);
new (&LodRenderData->RenderSections[0]) FSkelMeshRenderSection();
auto& MeshSection = LodRenderData->RenderSections[0];
// Initialize render section properties
MeshSection.MaterialIndex = 0;
MeshSection.BaseIndex = 0;
MeshSection.NumTriangles = OvrMesh->NumIndices / 3;
MeshSection.BaseVertexIndex = 0;
MeshSection.MaxBoneInfluences = 4;
MeshSection.NumVertices = OvrMesh->NumVertices;
MeshSection.bCastShadow = true;
MeshSection.bDisabled = false;
MeshSection.bRecomputeTangent = false;
// Initialize Vertex Buffers
LodRenderData->StaticVertexBuffers.PositionVertexBuffer.Init(OvrMesh->NumVertices);
LodRenderData->StaticVertexBuffers.StaticMeshVertexBuffer.Init(OvrMesh->NumVertices, 1);
LodRenderData->StaticVertexBuffers.ColorVertexBuffer.Init(OvrMesh->NumVertices);
// Initialize Skin Weights
TArray<FSkinWeightInfo> InWeights;
InWeights.AddUninitialized(OvrMesh->NumVertices);
float MaxDistSq = MIN_flt;
TMap<int32, TArray<int32>> OverlappingVertices;
for (uint32_t VertexIndex = 0; VertexIndex < OvrMesh->NumVertices; VertexIndex++)
{
FMemory::Memzero(InWeights[VertexIndex].InfluenceWeights);
FMemory::Memzero(InWeights[VertexIndex].InfluenceBones);
// Initialize vertex data
FModelVertex ModelVertex;
// Update Model Vertex
ovrpVector3f VertexPosition = OvrMesh->VertexPositions[VertexIndex];
ovrpVector3f Normal = OvrMesh->VertexNormals[VertexIndex];
ModelVertex.Position = FVector3f(VertexPosition.x, VertexPosition.z, VertexPosition.y) * WorldToMeters;
ModelVertex.TangentZ = FVector3f(Normal.x, Normal.z, Normal.y);
ModelVertex.TangentX = FVector3f(1.0f, 0.0f, 0.0f);
ModelVertex.TexCoord = FVector2f(OvrMesh->VertexUV0[VertexIndex].x, OvrMesh->VertexUV0[VertexIndex].y);
// Add Model Vertex data to vertex buffer
LodRenderData->StaticVertexBuffers.PositionVertexBuffer.VertexPosition(VertexIndex) = ModelVertex.Position;
LodRenderData->StaticVertexBuffers.StaticMeshVertexBuffer.SetVertexTangents(VertexIndex, ModelVertex.TangentX, ModelVertex.GetTangentY(), ModelVertex.TangentZ);
LodRenderData->StaticVertexBuffers.StaticMeshVertexBuffer.SetVertexUV(VertexIndex, 0, ModelVertex.TexCoord);
// Update the Bounds
float VertexDistSq = ModelVertex.Position.SizeSquared();
if (VertexDistSq > MaxDistSq)
MaxDistSq = VertexDistSq;
// Set vertex blend weights and indices
TArray<int32> Vertices;
ovrpVector4f BlendWeights = OvrMesh->BlendWeights[VertexIndex];
ovrpVector4s BlendIndices = OvrMesh->BlendIndices[VertexIndex];
InWeights[VertexIndex].InfluenceWeights[0] = UE::AnimationCore::MaxRawBoneWeightFloat * BlendWeights.x;
InWeights[VertexIndex].InfluenceBones[0] = BlendIndices.x;
Vertices.Add(BlendIndices.x);
InWeights[VertexIndex].InfluenceWeights[1] = UE::AnimationCore::MaxRawBoneWeightFloat * BlendWeights.y;
InWeights[VertexIndex].InfluenceBones[1] = BlendIndices.y;
Vertices.Add(BlendIndices.y);
InWeights[VertexIndex].InfluenceWeights[2] = UE::AnimationCore::MaxRawBoneWeightFloat * BlendWeights.z;
InWeights[VertexIndex].InfluenceBones[2] = BlendIndices.z;
Vertices.Add(BlendIndices.z);
InWeights[VertexIndex].InfluenceWeights[3] = UE::AnimationCore::MaxRawBoneWeightFloat * BlendWeights.w;
InWeights[VertexIndex].InfluenceBones[3] = BlendIndices.w;
Vertices.Add(BlendIndices.w);
OverlappingVertices.Add(VertexIndex, Vertices);
}
// Update bone map for mesh section
for (uint32 BoneIndex = 0; BoneIndex < (uint32)SkeletalMesh->GetRefSkeleton().GetNum(); BoneIndex++)
{
MeshSection.BoneMap.Add(BoneIndex);
}
// Finalize Bounds
float MaxDist = FMath::Sqrt(MaxDistSq);
FBoxSphereBounds Bounds;
Bounds.Origin = FVector::ZeroVector;
Bounds.BoxExtent = FVector(MaxDist);
Bounds.SphereRadius = MaxDist;
SkeletalMesh->SetImportedBounds(Bounds);
// Assign skin weights to vertex buffer
LodRenderData->SkinWeightVertexBuffer = InWeights;
MeshSection.DuplicatedVerticesBuffer.Init(OvrMesh->NumVertices, OverlappingVertices);
// Set index buffer
LodRenderData->MultiSizeIndexContainer.CreateIndexBuffer(sizeof(uint16_t));
for (uint32_t Index = 0; Index < OvrMesh->NumIndices; Index++)
{
LodRenderData->MultiSizeIndexContainer.GetIndexBuffer()->AddItem(OvrMesh->Indices[Index]);
}
#endif
}
void FOculusHandTracking::InitializeHandSkeleton(USkeletalMesh* SkeletalMesh, const ovrpSkeleton2* OvrSkeleton, const float WorldToMeters)
{
SkeletalMesh->GetRefSkeleton().Empty(OvrSkeleton->NumBones);
#if WITH_EDITOR
FSkeletalMeshLODModel* LodRenderData = &SkeletalMesh->GetImportedModel()->LODModels[0];
#else
FSkeletalMeshLODRenderData* LodRenderData = &SkeletalMesh->GetResourceForRendering()->LODRenderData[0];
#endif
SkeletalMesh->SetHasBeenSimplified(false);
SkeletalMesh->SetHasVertexColors(true);
checkf(OvrSkeleton->NumBones <= static_cast<unsigned int>(TNumericLimits<uint8>::Max()), TEXT("Bone indices are stored as uint8 type."));
for (uint8 BoneIndex = 0; BoneIndex < static_cast<uint8>(OvrSkeleton->NumBones); BoneIndex++)
{
LodRenderData->ActiveBoneIndices.Add(BoneIndex);
LodRenderData->RequiredBones.Add(BoneIndex);
FText BoneDisplayName;
if (!FindBoneDisplayName(BoneDisplayName, BoneIndex))
{
UE_LOG(LogOcHandTracking, Error, TEXT("Cannot find bone display name for bone index: %d."), BoneIndex)
continue;
}
FString BoneString = BoneDisplayName.ToString();
FName BoneName = FName(*BoneString);
FTransform Transform = FTransform::Identity;
FVector BonePosition = OvrBoneVectorToFVector(OvrSkeleton->Bones[BoneIndex].Pose.Position, WorldToMeters);
FQuat BoneRotation = BoneIndex == 0 ? FQuat(-1.0f, 0.0f, 0.0f, 1.0f) : OvrBoneQuatToFQuat(OvrSkeleton->Bones[BoneIndex].Pose.Orientation);
Transform.SetLocation(BonePosition);
Transform.SetRotation(BoneRotation);
FReferenceSkeletonModifier Modifier = FReferenceSkeletonModifier(SkeletalMesh->GetRefSkeleton(), nullptr);
int32 ParentIndex = -1;
if (BoneIndex > 0)
{
if (OvrSkeleton->Bones[BoneIndex].ParentBoneIndex == ovrpBoneId::ovrpBoneId_Invalid)
{
ParentIndex = 0;
}
else
{
ParentIndex = OvrSkeleton->Bones[BoneIndex].ParentBoneIndex;
}
}
Modifier.Add(FMeshBoneInfo(BoneName, BoneString, ParentIndex), Transform);
}
SkeletalMesh->CalculateInvRefMatrices();
}
TArray<FOculusXRCapsuleCollider> FOculusHandTracking::InitializeHandPhysics(const EOculusXRHandType SkeletonType, USkinnedMeshComponent* HandComponent, const float WorldToMeters)
{
TArray<FOculusXRCapsuleCollider> CollisionCapsules;
ovrpSkeleton2* OvrSkeleton = new ovrpSkeleton2();
#if OCULUS_INPUT_SUPPORTED_PLATFORMS
ovrpSkeletonType OvrSkeletonType = (ovrpSkeletonType)((int32)SkeletonType - 1);
if (FOculusXRHMDModule::GetPluginWrapper().GetSkeleton2(OvrSkeletonType, OvrSkeleton) != ovrpSuccess)
{
#if !WITH_EDITOR
UE_LOG(LogOcHandTracking, Error, TEXT("Failed to get skeleton data from Oculus runtime."));
#endif
delete OvrSkeleton;
return CollisionCapsules;
}
#endif
TArray<UPrimitiveComponent*> IgnoreCapsules;
CollisionCapsules.AddDefaulted(OvrSkeleton->NumBoneCapsules);
for (uint32 CapsuleIndex = 0; CapsuleIndex < OvrSkeleton->NumBoneCapsules; CapsuleIndex++)
{
ovrpBoneCapsule OvrBoneCapsule = OvrSkeleton->BoneCapsules[CapsuleIndex];
UCapsuleComponent* Capsule = NewObject<UCapsuleComponent>(HandComponent);
FVector CapsulePointZero = OvrBoneVectorToFVector(OvrBoneCapsule.Points[0], WorldToMeters);
FVector CapsulePointOne = OvrBoneVectorToFVector(OvrBoneCapsule.Points[1], WorldToMeters);
FVector Delta = (CapsulePointOne - CapsulePointZero);
FName BoneName = HandComponent->GetSkinnedAsset()->GetRefSkeleton().GetBoneName(OvrBoneCapsule.BoneIndex);
float CapsuleHeight = Delta.Size();
float CapsuleRadius = OvrBoneCapsule.Radius * WorldToMeters;
Capsule->SetCapsuleRadius(CapsuleRadius);
Capsule->SetCapsuleHalfHeight(Delta.Size() / 2 + CapsuleRadius);
Capsule->SetupAttachment(HandComponent, BoneName);
Capsule->SetCollisionProfileName(HandComponent->GetCollisionProfileName());
Capsule->RegisterComponentWithWorld(HandComponent->GetWorld());
Capsule->SetCollisionEnabled(ECollisionEnabled::QueryAndPhysics);
FRotator CapsuleRotation = FQuat::FindBetweenVectors(FVector::RightVector, Delta).Rotator() + FRotator(0, 0, 90);
;
Capsule->SetRelativeRotation(CapsuleRotation);
Capsule->SetRelativeLocation(CapsulePointZero + (Delta / 2));
CollisionCapsules[CapsuleIndex].Capsule = Capsule;
CollisionCapsules[CapsuleIndex].BoneId = (EOculusXRBone)OvrBoneCapsule.BoneIndex;
IgnoreCapsules.Add(Capsule);
}
for (int32 CapsuleIndex = 0; CapsuleIndex < CollisionCapsules.Num(); CapsuleIndex++)
{
CollisionCapsules[CapsuleIndex].Capsule->MoveIgnoreComponents = IgnoreCapsules;
}
return CollisionCapsules;
}
ovrpBoneId FOculusHandTracking::ToOvrBone(EOculusXRBone Bone)
{
if (Bone > EOculusXRBone::Bone_Max)
return ovrpBoneId_Invalid;
return static_cast<ovrpBoneId>(Bone);
}
FString FOculusHandTracking::GetBoneName(const uint8 Bone)
{
FText DisplayName;
if (FindBoneDisplayName(DisplayName, Bone))
{
return DisplayName.ToString();
}
if (FindBoneDisplayName(DisplayName, static_cast<uint8>(EOculusXRBone::Invalid)))
{
return DisplayName.ToString();
}
return { "Invalid" };
}
bool FOculusHandTracking::FindBoneDisplayName(FText& DisplayName, uint8 Bone)
{
return StaticEnum<EOculusXRBone>()->FindDisplayNameTextByValue(DisplayName, Bone);
}
EOculusXRTrackingConfidence FOculusHandTracking::ToEOculusXRTrackingConfidence(ovrpTrackingConfidence Confidence)
{
EOculusXRTrackingConfidence TrackingConfidence = EOculusXRTrackingConfidence::Low;
switch (Confidence)
{
case ovrpTrackingConfidence_Low:
TrackingConfidence = EOculusXRTrackingConfidence::Low;
break;
case ovrpTrackingConfidence_High:
TrackingConfidence = EOculusXRTrackingConfidence::High;
break;
}
return TrackingConfidence;
}
FVector FOculusHandTracking::OvrBoneVectorToFVector(ovrpVector3f ovrpVector, float WorldToMeters)
{
return FVector(ovrpVector.x, -ovrpVector.y, ovrpVector.z) * WorldToMeters;
}
FQuat FOculusHandTracking::OvrBoneQuatToFQuat(ovrpQuatf ovrpQuat)
{
return FQuat(ovrpQuat.x, -ovrpQuat.y, ovrpQuat.z, -ovrpQuat.w);
}
EOculusXRControllerDrivenHandPoseTypes FOculusHandTracking::ControllerDrivenHandType = EOculusXRControllerDrivenHandPoseTypes::None;
void FOculusHandTracking::SetControllerDrivenHandPoses(const EOculusXRControllerDrivenHandPoseTypes Type)
{
FOculusHandTracking::ControllerDrivenHandType = Type;
switch (Type)
{
case EOculusXRControllerDrivenHandPoseTypes::None:
FOculusXRHMDModule::GetPluginWrapper().SetControllerDrivenHandPoses(false);
FOculusXRHMDModule::GetPluginWrapper().SetControllerDrivenHandPosesAreNatural(false);
break;
case EOculusXRControllerDrivenHandPoseTypes::Natural:
FOculusXRHMDModule::GetPluginWrapper().SetControllerDrivenHandPoses(true);
FOculusXRHMDModule::GetPluginWrapper().SetControllerDrivenHandPosesAreNatural(true);
break;
case EOculusXRControllerDrivenHandPoseTypes::Controller:
FOculusXRHMDModule::GetPluginWrapper().SetControllerDrivenHandPoses(true);
FOculusXRHMDModule::GetPluginWrapper().SetControllerDrivenHandPosesAreNatural(false);
break;
}
}
} // namespace OculusXRInput