688 lines
26 KiB
C++
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
|