Up to Unreal 5.7

This commit is contained in:
Simeon "Waldo" Wallrath 2025-11-20 16:26:22 +01:00
parent 8a63902c06
commit 1a64805d43
239 changed files with 3947 additions and 296 deletions

View file

@ -1,2 +0,0 @@
* text=auto
*.bat eol=crlf

View file

@ -1,10 +0,0 @@
.hg/
binaries/
deriveddatacache/
.vs/
build/
intermediate/
PACKPLUGIN/
saved/
*.orig

View file

@ -1,19 +0,0 @@
Copyright Joshua Statzer
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View file

@ -1,53 +0,0 @@
{
"FileVersion": 3,
"Version": 5.5,
"VersionName": "5.5",
"FriendlyName": "OpenXRExpansionPlugin",
"Description": "An set of utility functions for OpenXR",
"Category": "Virtual Reality",
"CreatedBy": "Joshua (MordenTral) Statzer",
"CreatedByURL": "http://www.vreue4.com",
"DocsURL": "http://www.vreue4.com",
"MarketplaceURL": "",
"SupportURL": "http://www.vreue4.com",
"EnabledByDefault": false,
"CanContainContent": false,
"IsBetaVersion": false,
"Installed": true,
"SupportedTargetPlatforms": [
"Win64",
"Linux",
"Android",
"Mac",
"IOS"
],
"Modules": [
{
"Name": "OpenXRExpansionPlugin",
"Type": "Runtime",
"LoadingPhase": "PreDefault"
}
,
{
"Name": "OpenXRExpansionEditor",
"Type": "UnCookedOnly",
"LoadingPhase": "PreDefault"
}
],
"Plugins": [
{
"Name": "OpenXR",
"Enabled": true,
"PlatformAllowList": [
"Win64",
"Linux",
"Android"
]
},
{
"Name": "XRBase",
"Enabled": true
}
]
}

View file

@ -1,61 +0,0 @@
// Copyright 1998-2018 Epic Games, Inc. All Rights Reserved.
using System.IO;
namespace UnrealBuildTool.Rules
{
public class OpenXRExpansionEditor : ModuleRules
{
public OpenXRExpansionEditor(ReadOnlyTargetRules Target) : base(Target)
{
PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs;
PublicIncludePaths.AddRange(
new string[] {
// ... add public include paths required here ...
}
);
PrivateIncludePaths.AddRange(
new string[] {
// ... add other private include paths required here ...
}
);
PublicDependencyModuleNames.AddRange(
new string[]
{
// ... add other public dependencies that you statically link with here ...
"Engine",
"Core",
"CoreUObject"
}
);
PrivateDependencyModuleNames.AddRange(
new string[]
{
"UnrealEd",
"BlueprintGraph",
"AnimGraph",
"AnimGraphRuntime",
"SlateCore",
"Slate",
"InputCore",
"Engine",
"EditorStyle",
"AssetRegistry",
"OpenXRExpansionPlugin"
}
);
DynamicallyLoadedModuleNames.AddRange(
new string[]
{
// ... add any modules that your module loads dynamically here ...
}
);
}
}
}

View file

@ -1,34 +0,0 @@
// Copyright 1998-2018 Epic Games, Inc. All Rights Reserved.
#include "AnimGraphNode_ApplyOpenXRHandPose.h"
#include UE_INLINE_GENERATED_CPP_BY_NAME(AnimGraphNode_ApplyOpenXRHandPose)
/////////////////////////////////////////////////////
// UAnimGraphNode_ModifyBSHand
UAnimGraphNode_ApplyOpenXRHandPose::UAnimGraphNode_ApplyOpenXRHandPose(const FObjectInitializer& Initializer)
: Super(Initializer)
{
}
//Title Color!
FLinearColor UAnimGraphNode_ApplyOpenXRHandPose::GetNodeTitleColor() const
{
return FLinearColor(12, 12, 0, 1);
}
//Node Category
FString UAnimGraphNode_ApplyOpenXRHandPose::GetNodeCategory() const
{
return FString("OpenXR");
}
FText UAnimGraphNode_ApplyOpenXRHandPose::GetControllerDescription() const
{
return FText::FromString("Apply OpenXR Hand Pose");
}
FText UAnimGraphNode_ApplyOpenXRHandPose::GetNodeTitle(ENodeTitleType::Type TitleType) const
{
FText Result = GetControllerDescription();
return Result;
}

View file

@ -1,16 +0,0 @@
// Copyright 1998-2018 Epic Games, Inc. All Rights Reserved.
#include "OpenXRExpansionEditor.h"
#include "Editor/UnrealEdEngine.h"
#include "UnrealEdGlobals.h"
IMPLEMENT_MODULE(FOpenXRExpansionEditorModule, OpenXRExpansionEditor);
void FOpenXRExpansionEditorModule::StartupModule()
{
}
void FOpenXRExpansionEditorModule::ShutdownModule()
{
}

View file

@ -1,34 +0,0 @@
#pragma once
#include "AnimGraphDefinitions.h"
#include "Kismet2/BlueprintEditorUtils.h"
#include "Editor/AnimGraph/Public/AnimGraphNode_SkeletalControlBase.h"
#include "AnimNode_ApplyOpenXRHandPose.h"
// Copyright 1998-2018 Epic Games, Inc. All Rights Reserved.
#include "AnimGraphNode_ApplyOpenXRHandPose.generated.h"
UCLASS(MinimalAPI)
class UAnimGraphNode_ApplyOpenXRHandPose : public UAnimGraphNode_SkeletalControlBase
{
GENERATED_UCLASS_BODY()
UPROPERTY(EditAnywhere, Category = Settings)
FAnimNode_ApplyOpenXRHandPose Node;
public:
// UEdGraphNode interface
virtual FText GetNodeTitle(ENodeTitleType::Type TitleType) const override;
virtual FLinearColor GetNodeTitleColor() const override;
virtual FString GetNodeCategory() const override;
// End of UEdGraphNode interface
protected:
// UAnimGraphNode_SkeletalControlBase protected interface
virtual FText GetControllerDescription() const;
virtual const FAnimNode_SkeletalControlBase* GetNode() const override { return &Node; }
// End of UAnimGraphNode_SkeletalControlBase protected interface
};

View file

@ -1,13 +0,0 @@
// Copyright 1998-2018 Epic Games, Inc. All Rights Reserved.
#pragma once
#include "Runtime/Core/Public/Modules/ModuleInterface.h"
class FOpenXRExpansionEditorModule : public IModuleInterface
{
public:
virtual void StartupModule() override;
virtual void ShutdownModule() override;
};

View file

@ -1,76 +0,0 @@
// Copyright Epic Games, Inc. All Rights Reserved.
using UnrealBuildTool;
using System.IO;
namespace UnrealBuildTool.Rules
{
public class OpenXRExpansionPlugin: ModuleRules
{
public OpenXRExpansionPlugin(ReadOnlyTargetRules Target)
: base(Target)
{
PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs;
DefaultBuildSettings = BuildSettingsVersion.Latest;
IncludeOrderVersion = EngineIncludeOrderVersion.Latest;
SetupIrisSupport(Target);
PublicDependencyModuleNames.AddRange(
new string[]
{
//"InputDevice",
//"LiveLink",
//"LiveLinkInterface"
}
);
var EngineDir = Path.GetFullPath(Target.RelativeEnginePath);
PrivateIncludePaths.AddRange(
new string[] {
EngineDir + "Plugins/Runtime/OpenXR/Source/OpenXRHMD/Private",
EngineDir + "/Source/ThirdParty/OpenXR/include",
// ... add other private include paths required here ...
}
);
PrivateDependencyModuleNames.AddRange(
new string[]
{
"Core",
"NetCore",
"CoreUObject",
//"ApplicationCore",
"Engine",
//"InputDevice",
"InputCore",
"Slate",
"HeadMountedDisplay",
//"AnimGraph",
"AnimGraphRuntime",
"SlateCore",
"XRBase"
//"LiveLink",
//"LiveLinkInterface",
}
);
if (Target.Platform != UnrealTargetPlatform.Mac && Target.Platform != UnrealTargetPlatform.IOS)
{
PrivateDependencyModuleNames.AddRange(
new string[]
{
"OpenXRHMD"
}
);
PrivateDefinitions.AddRange(new string[] { "OPENXR_SUPPORTED" });
AddEngineThirdPartyPrivateStaticDependencies(Target, "OpenXR");
}
// if (Target.bBuildEditor == true)
// {
// PrivateDependencyModuleNames.Add("UnrealEd");
// }
}
}
}

View file

@ -1,488 +0,0 @@
// Fill out your copyright notice in the Description page of Project Settings.
#include "AnimNode_ApplyOpenXRHandPose.h"
#include UE_INLINE_GENERATED_CPP_BY_NAME(AnimNode_ApplyOpenXRHandPose)
//#include "EngineMinimal.h"
//#include "Engine/Engine.h"
//#include "CoreMinimal.h"
#include "OpenXRExpansionFunctionLibrary.h"
#include "AnimNode_ApplyOpenXRHandPose.h"
#include "AnimationRuntime.h"
#include "DrawDebugHelpers.h"
#include "OpenXRHandPoseComponent.h"
#include "Runtime/Engine/Public/Animation/AnimInstanceProxy.h"
#include "BoneControllers/AnimNode_SkeletalControlBase.h"
FAnimNode_ApplyOpenXRHandPose::FAnimNode_ApplyOpenXRHandPose()
: FAnimNode_SkeletalControlBase()
{
WorldIsGame = false;
Alpha = 1.f;
SkeletonType = EVROpenXRSkeletonType::OXR_SkeletonType_UE4Default_Right;
bIsOpenInputAnimationInstance = false;
bSkipRootBone = false;
bOnlyApplyWristTransform = false;
//WristAdjustment = FQuat::Identity;
}
void FAnimNode_ApplyOpenXRHandPose::OnInitializeAnimInstance(const FAnimInstanceProxy* InProxy, const UAnimInstance* InAnimInstance)
{
Super::OnInitializeAnimInstance(InProxy, InAnimInstance);
if (const UOpenXRAnimInstance * OpenXRAnimInstance = Cast<UOpenXRAnimInstance>(InAnimInstance))
{
bIsOpenInputAnimationInstance = true;
if (OpenXRAnimInstance->AnimInstanceProxy.HandSkeletalActionData.Num())
{
for (int i = 0; i < OpenXRAnimInstance->AnimInstanceProxy.HandSkeletalActionData.Num(); ++i)
{
EVRSkeletalHandIndex TargetHand = OpenXRAnimInstance->AnimInstanceProxy.HandSkeletalActionData[i].TargetHand;
if (OpenXRAnimInstance->AnimInstanceProxy.HandSkeletalActionData[i].bMirrorLeftRight)
{
TargetHand = (TargetHand == EVRSkeletalHandIndex::EActionHandIndex_Left) ? EVRSkeletalHandIndex::EActionHandIndex_Right : EVRSkeletalHandIndex::EActionHandIndex_Left;
}
if (TargetHand == MappedBonePairs.TargetHand)
{
bIsMirroringHand = OpenXRAnimInstance->AnimInstanceProxy.HandSkeletalActionData[i].bMirrorLeftRight;
break;
}
}
}
}
}
void FAnimNode_ApplyOpenXRHandPose::Initialize_AnyThread(const FAnimationInitializeContext& Context)
{
Super::Initialize_AnyThread(Context);
}
void FAnimNode_ApplyOpenXRHandPose::CacheBones_AnyThread(const FAnimationCacheBonesContext& Context)
{
Super::CacheBones_AnyThread(Context);
}
void FAnimNode_ApplyOpenXRHandPose::InitializeBoneReferences(const FBoneContainer& RequiredBones)
{
UObject* OwningAsset = RequiredBones.GetAsset();
if (!OwningAsset)
return;
USkeleton* AssetSkeleton = RequiredBones.GetSkeletonAsset();
if (!AssetSkeleton)
return;
if (!MappedBonePairs.bInitialized || OwningAsset->GetFName() != MappedBonePairs.LastInitializedName || SkeletonType != MappedBonePairs.LastInitializedSkeleton)
{
// Trigger a full re-build if our asset changed
if (MappedBonePairs.bInitialized && (OwningAsset->GetFName() != MappedBonePairs.LastInitializedName || SkeletonType != MappedBonePairs.LastInitializedSkeleton))
{
MappedBonePairs.ClearMapping();
}
MappedBonePairs.LastInitializedName = OwningAsset->GetFName();
MappedBonePairs.LastInitializedSkeleton = SkeletonType;
MappedBonePairs.bInitialized = false;
if (AssetSkeleton)
{
// If our bone pairs are empty, then setup our sane defaults
if (!MappedBonePairs.BonePairs.Num())
{
MappedBonePairs.ConstructDefaultMappings(SkeletonType, bSkipRootBone);
}
// Construct a reverse map of our joints
MappedBonePairs.ConstructReverseMapping();
TArray<FTransform> RefBones = AssetSkeleton->GetReferenceSkeleton().GetRefBonePose();
TArray<FMeshBoneInfo> RefBonesInfo = AssetSkeleton->GetReferenceSkeleton().GetRefBoneInfo();
for (FBPOpenXRSkeletalPair& BonePair : MappedBonePairs.BonePairs)
{
// Fill in the bone name for the reference
BonePair.ReferenceToConstruct.BoneName = BonePair.BoneToTarget;
// Init the reference
BonePair.ReferenceToConstruct.Initialize(AssetSkeleton);
BonePair.ReferenceToConstruct.CachedCompactPoseIndex = BonePair.ReferenceToConstruct.GetCompactPoseIndex(RequiredBones);
if ((BonePair.ReferenceToConstruct.CachedCompactPoseIndex != INDEX_NONE))
{
// Get our parent bones index
BonePair.ParentReference = RequiredBones.GetParentBoneIndex(BonePair.ReferenceToConstruct.CachedCompactPoseIndex);
}
}
MappedBonePairs.bInitialized = true;
if (SkeletonType == EVROpenXRSkeletonType::OXR_SkeletonType_OpenVRDefault_Left || SkeletonType == EVROpenXRSkeletonType::OXR_SkeletonType_OpenVRDefault_Right)
{
// We hard code this for now because I don't like their wrist being a different transform
MappedBonePairs.AdjustmentQuat = FRotator(0.f, 90.f, 180.f).Quaternion(); // Current one is incorrect without wrist
// Maybe do it in relative space?
}
else
{
CalculateSkeletalAdjustment(AssetSkeleton);
}
}
}
}
void FAnimNode_ApplyOpenXRHandPose::CalculateSkeletalAdjustment(USkeleton* AssetSkeleton)
{
TArray<FTransform> RefBones = AssetSkeleton->GetReferenceSkeleton().GetRefBonePose();
TArray<FMeshBoneInfo> RefBonesInfo = AssetSkeleton->GetReferenceSkeleton().GetRefBoneInfo();
if (!MappedBonePairs.bInitialized || MappedBonePairs.BonePairs.Num() < 4 || !RefBones.Num())
{
UE_LOG(LogTemp, Error, TEXT("Empty or incorrect mapping or skeleton data when calculating skeletal adjustment!"));
return;
}
FBPOpenXRSkeletalPair KnuckleIndexPair = MappedBonePairs.BonePairs[MappedBonePairs.ReverseBonePairMap[(int8)EXRHandJointType::OXR_HAND_JOINT_INDEX_PROXIMAL_EXT]];
FBPOpenXRSkeletalPair KnuckleMiddlePair = MappedBonePairs.BonePairs[MappedBonePairs.ReverseBonePairMap[(int8)EXRHandJointType::OXR_HAND_JOINT_MIDDLE_PROXIMAL_EXT]];
FBPOpenXRSkeletalPair KnuckleRingPair = MappedBonePairs.BonePairs[MappedBonePairs.ReverseBonePairMap[(int8)EXRHandJointType::OXR_HAND_JOINT_RING_PROXIMAL_EXT]];
FBPOpenXRSkeletalPair KnucklePinkyPair = MappedBonePairs.BonePairs[MappedBonePairs.ReverseBonePairMap[(int8)EXRHandJointType::OXR_HAND_JOINT_LITTLE_PROXIMAL_EXT]];
FBPOpenXRSkeletalPair WristPair = MappedBonePairs.BonePairs[MappedBonePairs.ReverseBonePairMap[(int8)EXRHandJointType::OXR_HAND_JOINT_WRIST_EXT]];
FVector KnuckleAverage = GetRefBoneInCS(RefBones, RefBonesInfo, KnuckleIndexPair.ReferenceToConstruct.BoneIndex).GetTranslation();
KnuckleAverage += GetRefBoneInCS(RefBones, RefBonesInfo, KnuckleMiddlePair.ReferenceToConstruct.BoneIndex).GetTranslation();
KnuckleAverage += GetRefBoneInCS(RefBones, RefBonesInfo, KnuckleRingPair.ReferenceToConstruct.BoneIndex).GetTranslation();
KnuckleAverage += GetRefBoneInCS(RefBones, RefBonesInfo, KnucklePinkyPair.ReferenceToConstruct.BoneIndex).GetTranslation();
// Get our average across the knuckles
KnuckleAverage /= 4.f;
// Obtain the UE4 wrist Side & Forward directions from first animation frame and place in cache
FTransform WristTransform_UE = GetRefBoneInCS(RefBones, RefBonesInfo, WristPair.ReferenceToConstruct.BoneIndex);
FVector ToKnuckleAverage_UE = KnuckleAverage - WristTransform_UE.GetTranslation();
ToKnuckleAverage_UE.Normalize();
WristForwardLS_UE = WristTransform_UE.GetRotation().UnrotateVector(ToKnuckleAverage_UE);
SetVectorToMaxElement(WristForwardLS_UE);
WristSideDirectionLS = FVector::CrossProduct(WristForwardLS_UE, FVector::RightVector);
SetVectorToMaxElement(WristSideDirectionLS);
CalculateOpenXRAdjustment();
}
void FAnimNode_ApplyOpenXRHandPose::CalculateOpenXRAdjustment()
{
// Base implementation is like valves
// Forward direction
static FVector OpenXRForwardDirection = FVector(1.0f, 0.f, 0.f);
// Side direction
// Do I need to flip this for left hand?
bool bUseLeftHandOffsets = false;
if ((!bIsMirroringHand && MappedBonePairs.TargetHand == EVRSkeletalHandIndex::EActionHandIndex_Left) ||
(bIsMirroringHand && MappedBonePairs.TargetHand == EVRSkeletalHandIndex::EActionHandIndex_Right))
{
bUseLeftHandOffsets = true;
}
//static FVector OpenXRSideDirection = FVector(0.f, 1.f, 0.f);
FVector OpenXRSideDirection = bUseLeftHandOffsets ? FVector(0.f, -1.f, 0.f) : FVector(0.f, 1.f, 0.f);
// Align forward vectors, openXR once in engine is X+ forward
FQuat AlignmentRot = FQuat::FindBetweenNormals(WristForwardLS_UE, OpenXRForwardDirection);
// Rotate about the aligned forward direction to make the side directions align
FVector WristSideDirectionMS_UE = AlignmentRot * WristSideDirectionLS;
// Rotate around, should the Side direction flip for openXR if its the left hand?
FQuat TwistRotation = CalcRotationAboutAxis(WristSideDirectionMS_UE, OpenXRSideDirection, OpenXRForwardDirection);
FRotator Difference = (TwistRotation * AlignmentRot).Rotator();
MappedBonePairs.AdjustmentQuat = (TwistRotation * AlignmentRot).GetNormalized();
}
void FAnimNode_ApplyOpenXRHandPose::ConvertHandTransformsSpace(TArray<FTransform>& OutTransforms, const TArray<FTransform>& WorldTransforms, FTransform AddTrans, bool bMirrorLeftRight, bool bMergeMissingUE4Bones)
{
// Fail if the count is too low
if (WorldTransforms.Num() < EHandKeypointCount)
return;
if (OutTransforms.Num() < WorldTransforms.Num())
{
OutTransforms.Empty(WorldTransforms.Num());
OutTransforms.AddUninitialized(WorldTransforms.Num());
}
TArray<FTransform> TempWorldTransforms = WorldTransforms;
// Ensure add trans is normalized
AddTrans.NormalizeRotation();
// Bone/Parent map
int32 BoneParents[26] =
{
// Manually build the parent hierarchy starting at the wrist which has no parent (-1)
1, // Palm -> Wrist
-1, // Wrist -> None
1, // ThumbMetacarpal -> Wrist
2, // ThumbProximal -> ThumbMetacarpal
3, // ThumbDistal -> ThumbProximal
4, // ThumbTip -> ThumbDistal
1, // IndexMetacarpal -> Wrist
6, // IndexProximal -> IndexMetacarpal
7, // IndexIntermediate -> IndexProximal
8, // IndexDistal -> IndexIntermediate
9, // IndexTip -> IndexDistal
1, // MiddleMetacarpal -> Wrist
11, // MiddleProximal -> MiddleMetacarpal
12, // MiddleIntermediate -> MiddleProximal
13, // MiddleDistal -> MiddleIntermediate
14, // MiddleTip -> MiddleDistal
1, // RingMetacarpal -> Wrist
16, // RingProximal -> RingMetacarpal
17, // RingIntermediate -> RingProximal
18, // RingDistal -> RingIntermediate
19, // RingTip -> RingDistal
1, // LittleMetacarpal -> Wrist
21, // LittleProximal -> LittleMetacarpal
22, // LittleIntermediate -> LittleProximal
23, // LittleDistal -> LittleIntermediate
24, // LittleTip -> LittleDistal
};
bool bUseAutoCalculatedRetarget = AddTrans.Equals(FTransform::Identity);
// Convert transforms to parent space
// The hand tracking transforms are in world space.
for (int32 Index = 0; Index < EHandKeypointCount; ++Index)
{
if (TempWorldTransforms[Index].ContainsNaN() || TempWorldTransforms[Index].Equals(FTransform::Identity))
{
OutTransforms[Index] = FTransform::Identity;
//continue;
}
// Ensure normalization
TempWorldTransforms[Index].NormalizeRotation();
if (bMirrorLeftRight)
{
TempWorldTransforms[Index].Mirror(EAxis::Y, EAxis::Y);
}
if (bUseAutoCalculatedRetarget)
{
TempWorldTransforms[Index].ConcatenateRotation(MappedBonePairs.AdjustmentQuat);
//WorldTransforms[Index].ConcatenateRotation(MappedBonePairs.BonePairs[0].RetargetRot);
}
else
{
TempWorldTransforms[Index].ConcatenateRotation(AddTrans.GetRotation());
}
}
// Make this into a single loop, their structure always has children after parent
for (int32 Index = 0; Index < EHandKeypointCount; ++Index)
{
FTransform& BoneTransform = TempWorldTransforms[Index];
//BoneTransform.NormalizeRotation();
int32 ParentIndex = BoneParents[Index];
int32 ParentParent = -1;
// Thumb keeps the metacarpal intact, we don't skip it
if (bMergeMissingUE4Bones)
{
if (Index != (int32)EXRHandJointType::OXR_HAND_JOINT_THUMB_PROXIMAL_EXT && ParentIndex > 0)
{
ParentParent = BoneParents[ParentIndex];
}
}
if (ParentIndex < 0)
{
// We are at the root, so use it.
OutTransforms[Index] = BoneTransform;
}
else
{
FTransform ParentTransform = FTransform::Identity;
// Merging missing metacarpal bone into the transform
if (bMergeMissingUE4Bones && ParentParent == 1) // Wrist
{
ParentTransform = TempWorldTransforms[ParentParent];
}
else
{
ParentTransform = TempWorldTransforms[ParentIndex];
}
//ParentTransform.NormalizeRotation();
OutTransforms[Index] = BoneTransform.GetRelativeTransform(ParentTransform);
}
}
}
void FAnimNode_ApplyOpenXRHandPose::EvaluateSkeletalControl_AnyThread(FComponentSpacePoseContext& Output, TArray<FBoneTransform>& OutBoneTransforms)
{
if (!MappedBonePairs.bInitialized)
return;
const FBoneContainer& BoneContainer = Output.Pose.GetPose().GetBoneContainer();
UObject* OwningAsset = BoneContainer.GetAsset();
if (!OwningAsset)
return;
// Trigger a full re-build if our asset or target skeleton changed, do it up here before finding the correct hand
if ((OwningAsset->GetFName() != MappedBonePairs.LastInitializedName || SkeletonType != MappedBonePairs.LastInitializedSkeleton))
{
InitializeBoneReferences(BoneContainer);
}
/*const */FBPOpenXRActionSkeletalData *StoredActionInfoPtr = nullptr;
if (bIsOpenInputAnimationInstance)
{
/*const*/ FOpenXRAnimInstanceProxy* OpenXRAnimInstance = (FOpenXRAnimInstanceProxy*)Output.AnimInstanceProxy;
if (OpenXRAnimInstance->HandSkeletalActionData.Num())
{
for (int i = 0; i <OpenXRAnimInstance->HandSkeletalActionData.Num(); ++i)
{
EVRSkeletalHandIndex TargetHand = OpenXRAnimInstance->HandSkeletalActionData[i].TargetHand;
if (OpenXRAnimInstance->HandSkeletalActionData[i].bMirrorLeftRight)
{
TargetHand = (TargetHand == EVRSkeletalHandIndex::EActionHandIndex_Left) ? EVRSkeletalHandIndex::EActionHandIndex_Right : EVRSkeletalHandIndex::EActionHandIndex_Left;
}
if (TargetHand == MappedBonePairs.TargetHand)
{
StoredActionInfoPtr = &OpenXRAnimInstance->HandSkeletalActionData[i];
break;
}
}
}
}
// If we have an empty hand pose but have a passed in custom one then use that
if (StoredActionInfoPtr == nullptr || !StoredActionInfoPtr->SkeletalTransforms.Num())
{
StoredActionInfoPtr = &OptionalStoredActionInfo;
}
if (!StoredActionInfoPtr->bHasValidData)
{
return;
}
//MappedBonePairs.AdjustmentQuat = WristAdjustment;
// Currently not blending correctly
const float BlendWeight = FMath::Clamp<float>(ActualAlpha, 0.f, 1.f);
uint8 BoneTransIndex = 0;
uint8 NumBones = StoredActionInfoPtr ? StoredActionInfoPtr->SkeletalTransforms.Num() : 0;
if (NumBones < 1)
{
// Early out, we don't have a valid data to work with
return;
}
FTransform trans = FTransform::Identity;
OutBoneTransforms.Reserve(MappedBonePairs.BonePairs.Num());
TArray<FBoneTransform> TransBones;
FTransform AdditionTransform = StoredActionInfoPtr->AdditionTransform;
FTransform TempTrans = FTransform::Identity;
FTransform ParentTrans = FTransform::Identity;
FTransform * ParentTransPtr = nullptr;
//AdditionTransform.SetRotation(MappedBonePairs.AdjustmentQuat);
TArray<FTransform> HandTransforms;
ConvertHandTransformsSpace(HandTransforms, StoredActionInfoPtr->SkeletalTransforms, AdditionTransform, StoredActionInfoPtr->bMirrorLeftRight, MappedBonePairs.bMergeMissingBonesUE4);
for (const FBPOpenXRSkeletalPair& BonePair : MappedBonePairs.BonePairs)
{
BoneTransIndex = (int8)BonePair.OpenXRBone;
ParentTrans = FTransform::Identity;
if (bSkipRootBone && BonePair.OpenXRBone == EXRHandJointType::OXR_HAND_JOINT_WRIST_EXT)
continue;
if (BoneTransIndex >= NumBones || BonePair.ReferenceToConstruct.CachedCompactPoseIndex == INDEX_NONE)
continue;
if (!BonePair.ReferenceToConstruct.IsValidToEvaluate(BoneContainer))
{
continue;
}
trans = Output.Pose.GetComponentSpaceTransform(BonePair.ReferenceToConstruct.CachedCompactPoseIndex);
if (BonePair.ParentReference != INDEX_NONE)
{
ParentTrans = Output.Pose.GetComponentSpaceTransform(BonePair.ParentReference);
ParentTrans.SetScale3D(FVector(1.f));
}
EXRHandJointType CurrentBone = (EXRHandJointType)BoneTransIndex;
TempTrans = (HandTransforms[BoneTransIndex]);
//TempTrans.ConcatenateRotation(BonePair.RetargetRot);
/*if (StoredActionInfoPtr->bMirrorHand)
{
FMatrix M = TempTrans.ToMatrixWithScale();
M.Mirror(EAxis::Z, EAxis::X);
M.Mirror(EAxis::X, EAxis::Z);
TempTrans.SetFromMatrix(M);
}*/
TempTrans = TempTrans * ParentTrans;
if (StoredActionInfoPtr->bAllowDeformingMesh || bOnlyApplyWristTransform)
trans.SetTranslation(TempTrans.GetTranslation());
trans.SetRotation(TempTrans.GetRotation());
TransBones.Add(FBoneTransform(BonePair.ReferenceToConstruct.CachedCompactPoseIndex, trans));
// Need to do it per bone so future bones are correct
// Only if in parent space though, can do it all at the end in component space
if (TransBones.Num())
{
Output.Pose.LocalBlendCSBoneTransforms(TransBones, BlendWeight);
TransBones.Reset();
}
if (bOnlyApplyWristTransform && CurrentBone == EXRHandJointType::OXR_HAND_JOINT_WRIST_EXT)
{
break; // Early out of the loop, we only wanted to apply the wrist
}
}
}
bool FAnimNode_ApplyOpenXRHandPose::IsValidToEvaluate(const USkeleton* Skeleton, const FBoneContainer& RequiredBones)
{
return(/*MappedBonePairs.bInitialized && */MappedBonePairs.BonePairs.Num() > 0);
}

View file

@ -1,587 +0,0 @@
// Fill out your copyright notice in the Description page of Project Settings.
#include "OpenXRExpansionFunctionLibrary.h"
#include UE_INLINE_GENERATED_CPP_BY_NAME(OpenXRExpansionFunctionLibrary)
//#include "EngineMinimal.h"
#include "Engine/Engine.h"
#include <openxr/openxr.h>
#include "CoreMinimal.h"
#include "IXRTrackingSystem.h"
//General Log
DEFINE_LOG_CATEGORY(OpenXRExpansionFunctionLibraryLog);
UOpenXRExpansionFunctionLibrary::UOpenXRExpansionFunctionLibrary(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{
}
//=============================================================================
UOpenXRExpansionFunctionLibrary::~UOpenXRExpansionFunctionLibrary()
{
}
void UOpenXRExpansionFunctionLibrary::GetXRMotionControllerType(FString& TrackingSystemName, EBPOpenXRControllerDeviceType& DeviceType, EBPXRResultSwitch& Result)
{
#if defined(OPENXR_SUPPORTED)
DeviceType = EBPOpenXRControllerDeviceType::DT_UnknownController;
Result = EBPXRResultSwitch::OnFailed;
if (FOpenXRHMD* pOpenXRHMD = GetOpenXRHMD())
{
XrInstance XRInstance = pOpenXRHMD->GetInstance();
XrSystemId XRSysID = pOpenXRHMD->GetSystem();
if (XRSysID && XRInstance)
{
XrSystemProperties systemProperties{ XR_TYPE_SYSTEM_PROPERTIES };
systemProperties.next = nullptr;
if (xrGetSystemProperties(XRInstance, XRSysID, &systemProperties) == XR_SUCCESS)
{
XrSession XRSesh = pOpenXRHMD->GetSession();
if (XRSesh)
{
XrPath myPath;
XrResult PathResult = xrStringToPath(XRInstance, "/user/hand/left", &myPath);
XrInteractionProfileState interactionProfile{ XR_TYPE_INTERACTION_PROFILE_STATE };
interactionProfile.next = nullptr;
XrResult QueryResult = xrGetCurrentInteractionProfile(XRSesh, myPath, &interactionProfile);
if (QueryResult == XR_SUCCESS)
{
char myPathy[XR_MAX_SYSTEM_NAME_SIZE];
uint32_t outputsize;
xrPathToString(XRInstance, interactionProfile.interactionProfile, XR_MAX_SYSTEM_NAME_SIZE, &outputsize, myPathy);
if (interactionProfile.interactionProfile == XR_NULL_PATH || outputsize < 1)
return;
FString InteractionName(ANSI_TO_TCHAR(myPathy));
if (InteractionName.Len() < 1)
return;
/*
* Interaction profile paths [6.4]
An interaction profile identifies a collection of buttons and
other input sources, and is of the form:
/interaction_profiles/<vendor_name>/<type_name>
Paths supported in the core 1.0 release
/interaction_profiles/khr/simple_control
/interaction_profiles/khr/simple_controller
/interaction_profiles/google/daydream_controller
/interaction_profiles/htc/vive_controller
/interaction_profiles/htc/vive_pro
/interaction_profiles/microsoft/motion_controller
/interaction_profiles/microsoft/xbox_controller
/interaction_profiles/oculus/go_controller
/interaction_profiles/oculus/touch_controller
/interaction_profiles/valve/index_controller
*/
// Not working currently?
/*XrInputSourceLocalizedNameGetInfo InputSourceInfo{ XR_TYPE_INPUT_SOURCE_LOCALIZED_NAME_GET_INFO };
InputSourceInfo.next = nullptr;
InputSourceInfo.whichComponents = XR_INPUT_SOURCE_LOCALIZED_NAME_INTERACTION_PROFILE_BIT;
InputSourceInfo.sourcePath = interactionProfile.interactionProfile;
char buffer[XR_MAX_SYSTEM_NAME_SIZE];
uint32_t usedBufferCount = 0;
if (xrGetInputSourceLocalizedName(XRSesh, &InputSourceInfo, XR_MAX_SYSTEM_NAME_SIZE, &usedBufferCount, (char*)&buffer) == XR_SUCCESS)
{
int g = 0;
}*/
if (InteractionName.Find("touch_controller", ESearchCase::IgnoreCase) != INDEX_NONE)
{
DeviceType = EBPOpenXRControllerDeviceType::DT_OculusTouchController;
}
else if (InteractionName.Contains("index_controller", ESearchCase::IgnoreCase))
{
DeviceType = EBPOpenXRControllerDeviceType::DT_ValveIndexController;
}
else if (InteractionName.Find("vive_controller", ESearchCase::IgnoreCase) != INDEX_NONE)
{
DeviceType = EBPOpenXRControllerDeviceType::DT_ViveController;
}
else if (InteractionName.Find("vive_pro", ESearchCase::IgnoreCase) != INDEX_NONE)
{
DeviceType = EBPOpenXRControllerDeviceType::DT_ViveProController;
}
else if (InteractionName.Find("simple_controller", ESearchCase::IgnoreCase) != INDEX_NONE)
{
DeviceType = EBPOpenXRControllerDeviceType::DT_SimpleController;
}
else if (InteractionName.Find("daydream_controller", ESearchCase::IgnoreCase) != INDEX_NONE)
{
DeviceType = EBPOpenXRControllerDeviceType::DT_DaydreamController;
}
else if (InteractionName.Find("motion_controller", ESearchCase::IgnoreCase) != INDEX_NONE)
{
DeviceType = EBPOpenXRControllerDeviceType::DT_MicrosoftMotionController;
}
else if (InteractionName.Find("xbox_controller", ESearchCase::IgnoreCase) != INDEX_NONE)
{
DeviceType = EBPOpenXRControllerDeviceType::DT_MicrosoftXboxController;
}
else if (InteractionName.Find("go_controller", ESearchCase::IgnoreCase) != INDEX_NONE)
{
DeviceType = EBPOpenXRControllerDeviceType::DT_OculusGoController;
}
else if (InteractionName.Find("neo3_controller", ESearchCase::IgnoreCase) != INDEX_NONE)
{
DeviceType = EBPOpenXRControllerDeviceType::DT_PicoNeo3Controller;
}
else if (InteractionName.Find("mixed_reality_controller", ESearchCase::IgnoreCase) != INDEX_NONE)
{
DeviceType = EBPOpenXRControllerDeviceType::DT_WMRController;
}
else
{
UE_LOG(OpenXRExpansionFunctionLibraryLog, Warning, TEXT("UNKNOWN OpenXR Interaction profile detected!!!: %s"), *InteractionName);
DeviceType = EBPOpenXRControllerDeviceType::DT_UnknownController;
}
Result = EBPXRResultSwitch::OnSucceeded;
}
TrackingSystemName = FString(ANSI_TO_TCHAR(systemProperties.systemName));// , XR_MAX_SYSTEM_NAME_SIZE);
//VendorID = systemProperties.vendorId;
return;
}
}
}
}
#endif
TrackingSystemName.Empty();
return;
}
bool UOpenXRExpansionFunctionLibrary::GetOpenXRHandPose(FBPOpenXRActionSkeletalData& HandPoseContainer, UOpenXRHandPoseComponent* HandPoseComponent, bool bGetMockUpPose)
{
FXRMotionControllerData MotionControllerData;
if (bGetMockUpPose)
{
GetMockUpControllerData(MotionControllerData, HandPoseContainer);
return true;
}
UHeadMountedDisplayFunctionLibrary::GetMotionControllerData((UObject*)HandPoseComponent, HandPoseContainer.TargetHand == EVRSkeletalHandIndex::EActionHandIndex_Left ? EControllerHand::Left : EControllerHand::Right, MotionControllerData);
if (MotionControllerData.bValid)
{
HandPoseContainer.SkeletalTransforms.Empty(MotionControllerData.HandKeyPositions.Num());
FTransform ParentTrans = FTransform::Identity;
if (MotionControllerData.DeviceVisualType == EXRVisualType::Controller)
{
ParentTrans = FTransform(MotionControllerData.GripRotation, MotionControllerData.GripPosition, FVector(1.f));
}
else // EXRVisualType::Hand visual type
{
ParentTrans = FTransform(MotionControllerData.HandKeyRotations[(uint8)EHandKeypoint::Palm], MotionControllerData.HandKeyPositions[(uint8)EHandKeypoint::Palm], FVector(1.f));
}
for (int i = 0; i < MotionControllerData.HandKeyPositions.Num(); ++i)
{
// Convert to component space, we convert then to parent space later when applying it
HandPoseContainer.SkeletalTransforms.Add(FTransform(MotionControllerData.HandKeyRotations[i].GetNormalized(), MotionControllerData.HandKeyPositions[i], FVector(1.f)).GetRelativeTransform(ParentTrans));
}
//if (bGetCurlValues)
{
GetFingerCurlValues(HandPoseContainer.SkeletalTransforms, HandPoseContainer.FingerCurls);
}
HandPoseContainer.bHasValidData = (HandPoseContainer.SkeletalTransforms.Num() == EHandKeypointCount);
return true;
}
HandPoseContainer.bHasValidData = false;
return false;
}
void UOpenXRExpansionFunctionLibrary::GetFingerCurlValues(TArray<FTransform>& TransformArray, TArray<float>& CurlArray)
{
// Fail if the count is too low
if (TransformArray.Num() < EHandKeypointCount)
return;
if (CurlArray.Num() < 5)
{
CurlArray.AddZeroed(5);
}
CurlArray[0] = GetCurlValueForBoneRoot(TransformArray, EHandKeypoint::ThumbMetacarpal);
CurlArray[1] = GetCurlValueForBoneRoot(TransformArray, EHandKeypoint::IndexProximal);
CurlArray[2] = GetCurlValueForBoneRoot(TransformArray, EHandKeypoint::MiddleProximal);
CurlArray[3] = GetCurlValueForBoneRoot(TransformArray, EHandKeypoint::RingProximal);
CurlArray[4] = GetCurlValueForBoneRoot(TransformArray, EHandKeypoint::LittleProximal);
}
bool UOpenXRExpansionFunctionLibrary::GetOpenXRFingerCurlValuesForHand(
UObject* WorldContextObject,
EControllerHand TargetHand,
float& ThumbCurl,
float& IndexCurl,
float& MiddleCurl,
float& RingCurl,
float& PinkyCurl)
{
FXRMotionControllerData MotionControllerData;
UHeadMountedDisplayFunctionLibrary::GetMotionControllerData(WorldContextObject, TargetHand, MotionControllerData);
// Fail if the count is too low
if (MotionControllerData.HandKeyPositions.Num() < EHandKeypointCount)
return false;
FTransform ParentTrans = FTransform::Identity;
if (MotionControllerData.DeviceVisualType == EXRVisualType::Controller)
{
ParentTrans = FTransform(MotionControllerData.GripRotation, MotionControllerData.GripPosition, FVector(1.f));
}
else // EXRVisualType::Hand visual type
{
ParentTrans = FTransform(MotionControllerData.HandKeyRotations[(uint8)EHandKeypoint::Palm], MotionControllerData.HandKeyPositions[(uint8)EHandKeypoint::Palm], FVector(1.f));
}
TArray<FTransform> TransformArray;
TransformArray.AddUninitialized(MotionControllerData.HandKeyPositions.Num());
for (int i = 0; i < MotionControllerData.HandKeyPositions.Num(); ++i)
{
// Convert to component space, we convert then to parent space later when applying it
TransformArray[i] = FTransform(MotionControllerData.HandKeyRotations[i].GetNormalized(), MotionControllerData.HandKeyPositions[i], FVector(1.f)).GetRelativeTransform(ParentTrans);
}
ThumbCurl = GetCurlValueForBoneRoot(TransformArray, EHandKeypoint::ThumbMetacarpal);
IndexCurl = GetCurlValueForBoneRoot(TransformArray, EHandKeypoint::IndexProximal);
MiddleCurl = GetCurlValueForBoneRoot(TransformArray, EHandKeypoint::MiddleProximal);
RingCurl = GetCurlValueForBoneRoot(TransformArray, EHandKeypoint::RingProximal);
PinkyCurl = GetCurlValueForBoneRoot(TransformArray, EHandKeypoint::LittleProximal);
return true;
}
float UOpenXRExpansionFunctionLibrary::GetCurlValueForBoneRoot(TArray<FTransform>& TransformArray, EHandKeypoint RootBone)
{
float Angle1 = 0.0f;
float Angle2 = 0.0f;
float Angle1Curl = 0.0f;
float Angle2Curl = 0.0f;
if (RootBone == EHandKeypoint::ThumbMetacarpal)
{
FVector Prox = TransformArray[(uint8)RootBone].GetRotation().GetForwardVector();
FVector Inter = TransformArray[(uint8)RootBone + 1].GetRotation().GetForwardVector();
FVector Distal = TransformArray[(uint8)RootBone + 2].GetRotation().GetForwardVector();
Prox = FVector::VectorPlaneProject(Prox, FVector::UpVector);
Inter = FVector::VectorPlaneProject(Inter, FVector::UpVector);
Distal = FVector::VectorPlaneProject(Distal, FVector::UpVector);
Angle1 = FMath::RadiansToDegrees(FMath::Acos(FVector::DotProduct(Inter, Distal)));
Angle2 = FMath::RadiansToDegrees(FMath::Acos(FVector::DotProduct(Prox, Inter)));
Angle1Curl = (Angle1 - 10.0f) / 64.0f;
Angle2Curl = (Angle2 - 20.0f) / 42.0f;
}
else
{
FVector Prox = TransformArray[(uint8)RootBone].GetRotation().GetForwardVector();
FVector Inter = TransformArray[(uint8)RootBone + 1].GetRotation().GetForwardVector();
FVector Distal = TransformArray[(uint8)RootBone + 2].GetRotation().GetForwardVector();
// We don't use the Y (splay) value, only X and Z plane
Prox = FVector::VectorPlaneProject(Prox, FVector::RightVector);
Inter = FVector::VectorPlaneProject(Inter, FVector::RightVector);
Distal = FVector::VectorPlaneProject(Distal, FVector::RightVector);
Angle1 = FMath::RadiansToDegrees(FMath::Acos(FVector::DotProduct(Inter, Distal)));
Angle2 = FMath::RadiansToDegrees(FMath::Acos(FVector::DotProduct(Prox, Inter)));
Angle1Curl = (Angle1 - 10.0f) / 60.0f;
Angle2Curl = (Angle2 - 10.0f) / 100.0f;
}
// Can lower number of variables by doing these
float FinalAngleAvg = FMath::Clamp((Angle1Curl + Angle2Curl) / 2.0f, 0.0f, 1.0f);
//GEngine->AddOnScreenDebugMessage(-1, 15.0f, FColor::Yellow, FString::Printf(TEXT("Finger Curl %f"), IndexCurl));
return FinalAngleAvg;
}
void UOpenXRExpansionFunctionLibrary::ConvertHandTransformsSpaceAndBack(TArray<FTransform>& OutTransforms, const TArray<FTransform>& WorldTransforms)
{
// Fail if the count is too low
if (WorldTransforms.Num() < EHandKeypointCount)
return;
if (OutTransforms.Num() < WorldTransforms.Num())
{
OutTransforms.Empty(WorldTransforms.Num());
OutTransforms.AddUninitialized(WorldTransforms.Num());
}
// Bone/Parent map
int32 BoneParents[26] =
{
// Manually build the parent hierarchy starting at the wrist which has no parent (-1)
1, // Palm -> Wrist
-1, // Wrist -> None
1, // ThumbMetacarpal -> Wrist
2, // ThumbProximal -> ThumbMetacarpal
3, // ThumbDistal -> ThumbProximal
4, // ThumbTip -> ThumbDistal
1, // IndexMetacarpal -> Wrist
6, // IndexProximal -> IndexMetacarpal
7, // IndexIntermediate -> IndexProximal
8, // IndexDistal -> IndexIntermediate
9, // IndexTip -> IndexDistal
1, // MiddleMetacarpal -> Wrist
11, // MiddleProximal -> MiddleMetacarpal
12, // MiddleIntermediate -> MiddleProximal
13, // MiddleDistal -> MiddleIntermediate
14, // MiddleTip -> MiddleDistal
1, // RingMetacarpal -> Wrist
16, // RingProximal -> RingMetacarpal
17, // RingIntermediate -> RingProximal
18, // RingDistal -> RingIntermediate
19, // RingTip -> RingDistal
1, // LittleMetacarpal -> Wrist
21, // LittleProximal -> LittleMetacarpal
22, // LittleIntermediate -> LittleProximal
23, // LittleDistal -> LittleIntermediate
24, // LittleTip -> LittleDistal
};
// Convert transforms to parent space
// The hand tracking transforms are in world space.
for (int32 Index = 0; Index < EHandKeypointCount; ++Index)
{
FTransform BoneTransform = WorldTransforms[Index];
BoneTransform.NormalizeRotation();
int32 ParentIndex = BoneParents[Index];
int32 ParentParent = -1;
if (ParentIndex > 0)
{
ParentParent = BoneParents[ParentIndex];
}
if (ParentIndex < 0)
{
// We are at the root, so use it.
OutTransforms[Index] = BoneTransform;
}
else
{
FTransform ParentTransform = FTransform::Identity;
// Merging missing metacarpal bone into the transform
if (ParentParent == 1) // Wrist
{
ParentTransform = WorldTransforms[ParentParent];
}
else
{
ParentTransform = WorldTransforms[ParentIndex];
}
ParentTransform.NormalizeRotation();
OutTransforms[Index] = BoneTransform.GetRelativeTransform(ParentTransform);
}
}
// Check on the easy component space conversion first
{
for (int32 Index = 0; Index < EHandKeypointCount; ++Index)
{
const FTransform& BoneTransform = WorldTransforms[Index];
int32 ParentIndex = BoneParents[Index];
int32 ParentParent = -1;
if (ParentIndex > 0)
{
ParentParent = BoneParents[ParentIndex];
}
if (ParentIndex > 0)
{
if (ParentParent == 1)
{
OutTransforms[Index] = OutTransforms[Index] * OutTransforms[ParentParent];
}
else
{
OutTransforms[Index] = OutTransforms[Index] * OutTransforms[ParentIndex];
}
}
}
}
}
void UOpenXRExpansionFunctionLibrary::GetMockUpControllerData(FXRMotionControllerData& MotionControllerData, FBPOpenXRActionSkeletalData& SkeletalMappingData, bool bOpenHand)
{
TArray<FQuat> HandRotationsClosed = {
// Closed palm
FQuat(-6.9388939039072284e-18f,-2.7755575615628914e-17f,-5.5511151231257827e-17f,1.0000000181623150),
FQuat(0.0010158333104005046f,-0.031842494413126823f,0.0082646248419453450f,-0.99945823120983279),
FQuat(0.49284713488980170f,-0.22607130273287540f,-0.44054329019960731f,0.71548243409346490),
FQuat(-0.60572821120045273f,-0.017497510794223320f,0.10943807503774633f,-0.78791529705201735),
FQuat(-0.61281359708005512f,-0.23245447006924613f,-0.058766632856478873f,-0.75297472319193537),
FQuat(-0.61281359708005512f,-0.23245447006924613f,-0.058766632856478873f,-0.75297472319193537),
FQuat(-0.11514346379358367f,-0.029229397612833233f,-0.076351329575539195f,0.98997885626789228),
FQuat(0.079333339113826437f,-0.72590009974051883f,0.050346047334316274f,-0.68135202233808378),
FQuat(0.0032539550806410245f,-0.97123336736010268f,0.098921247251489333f,0.21658665949113542),
FQuat(-0.064585069112672477f,-0.57963374972897053f,0.075445998290538954f,0.80880246209406348),
FQuat(0.064585069112672477f,0.57963374972897053f,-0.075445998290538954f,-0.80880246209406348),
FQuat(-7.7702472130181111e-08f,9.8656527815210726e-08f,1.3838491007001075e-06f,1.0000000181613493),
FQuat(0.085300231549708214f,-0.74048187833139134f,0.058532016219761618f,-0.66406663653752407),
FQuat(0.011595964719175678f,-0.98786834549923641f,0.099110835214894707f,0.11899052159928070),
FQuat(-0.063530326287074640f,-0.56012988021281451f,0.076145543493668810f,0.82244775363868539),
FQuat(0.030477923994508188f,0.55662337489051250f,-0.098559802475379960f,-0.82433459003921772),
FQuat(-0.025910339872061101f,0.052311401725670628f,0.042953782692297438f,0.99737013210455761),
FQuat(0.11635892750396379f,-0.74717191584145570f,-0.026909776929005647f,-0.65381238012001353),
FQuat(0.098078041656806447f,-0.98532297068866348f,0.071135591008198620f,0.12024601945419003),
FQuat(0.0028091467060491482f,-0.52817558741956572f,0.11864668261714340f,0.84080060571895254),
FQuat(-0.0028091467060491482f,0.52817558741956572f,-0.11864668261714340f,-0.84080060571895254),
FQuat(-0.11913260527111892f,0.10934177100849747f,0.11664955310670821f,0.97992077106196507),
FQuat(-0.18696185363314571f,0.78123174637415782f,0.043590203318890380f,0.59398834520762267),
FQuat(0.15903913486884827f,-0.97287570092949460f,0.091728860868847406f,0.14073122087670015),
FQuat(0.035141532005084519f,-0.48251853052338572f,0.18910886397987722f,0.85450501128943579),
FQuat(0.035141532005084519f,-0.48251853052338572f,0.18910886397987722f,0.85450501128943579)
};
TArray<FQuat> HandRotationsOpen = {
// Open Hand
FQuat(0.167000905f,-0.670308471f,0.304047525f,-0.656011939f),
FQuat(-0.129862994f,0.736467659f,-0.315623045f,0.584065497f),
FQuat(-0.030090153f,0.121532254f,-0.840178490f,0.527659237f),
FQuat(-0.126470163f,-0.262596279f,0.816956878f,-0.497623593f),
FQuat(-0.102322638f,-0.249194950f,0.821705163f,-0.502227187f),
FQuat(-0.102322638f,-0.249194950f,0.821705163f,-0.502227187f),
FQuat(-0.277370781f,0.686735749f,-0.258646101f,0.620130479f),
FQuat(0.193366051f,-0.808131576f,0.260262072f,-0.491728455f),
FQuat(0.145547777f,-0.854364336f,0.317562312f,-0.384749293f),
FQuat(0.107193023f,-0.879853010f,0.321882188f,-0.332806766f),
FQuat(0.107193023f,-0.879853010f,0.321882188f,-0.332806766f),
FQuat(0.166999936f,-0.670307159f,0.304047883f,-0.656013489f),
FQuat(0.206125781f,-0.815250278f,0.203173012f,-0.501596987f),
FQuat(0.164493740f,-0.890369833f,0.257293820f,-0.337613612f),
FQuat(0.114019498f,-0.937856555f,0.283619940f,-0.164267495f),
FQuat(0.107336335f,-0.925720870f,0.321023613f,-0.168710276f),
FQuat(0.156629071f,-0.719596088f,0.210793152f,-0.642817795f),
FQuat(0.194258988f,-0.858762920f,0.127883837f,-0.456546605f),
FQuat(0.166189745f,-0.930981100f,0.161785051f,-0.281922638f),
FQuat(0.119936436f,-0.970744252f,0.189072192f,-0.086731322f),
FQuat(0.119936436f,-0.970744252f,0.189072192f,-0.086731322f),
FQuat(0.160288095f,-0.812664807f,0.100923792f,-0.551087677f),
FQuat(0.207311243f,-0.870556056f,0.056644741f,-0.442656904f),
FQuat(0.191506147f,-0.944826961f,0.096772499f,-0.247511998f),
FQuat(0.116890728f,-0.981477261f,0.138804480f,-0.061412390f),
FQuat(0.116890728f,-0.981477261f,0.138804480f,-0.061412390f)
};
MotionControllerData.HandKeyRotations = /*SkeletalMappingData.TargetHand != EVRSkeletalHandIndex::EActionHandIndex_Left ? HandRotationsOpen :*/ HandRotationsClosed;
TArray<FVector> HandPositionsClosed = {
// Closed palm - Left
FVector(0.0000000000000000f,0.0000000000000000f,0.0000000000000000f),
FVector(-2.8690212431406792f,0.70708009295073815f,-0.47404338536985718f),
FVector(-1.1322360817697272f,-2.1125772981974671f,-1.2278703475596775f),
FVector(0.92697682070144727f,-5.5601677377459957f,-1.6753327187355360f),
FVector(4.0987554778339428f,-6.0520138168462640f,-2.1960898756852747f),
FVector(5.5854053809842918f,-5.4247506634349065f,-2.6631245417791525f),
FVector(-1.6026417502203387f,-1.4646945203797794f,-0.17057236434820122f),
FVector(5.7352432311721007f,-2.5389617545260998f,0.39061644722637634f),
FVector(5.4801464829170561f,-3.3344912297783416f,-3.8566611419550343f),
FVector(2.9179605371815693f,-3.2311985822561073f,-2.6652727318443148f),
FVector(3.2708935578922342f,-3.0117453368521279f,-1.6311186720587312f),
FVector(-1.1935619377191149f,-1.8034793735494103e-05f,1.4147048846974153e-06f),
FVector(5.9028526610092893f,1.3513666817788206e-05f,-4.3170989212359956e-06f),
FVector(5.4567759872551527f,-0.87968929643487392f,-4.1965100581882382f),
FVector(2.2252652348065158f,-0.87742006725177069f,-3.4067970851427791f),
FVector(2.6916877869696085f,-0.62360084690574125f,-2.2285708116727738f),
FVector(-1.1796822503165614f,1.3653411443775685f,-0.17694011615865479f),
FVector(5.3502831208188670f,1.9121382570769896f,-0.87930289382919313f),
FVector(4.8743654830862742f,1.3526757302541959f,-4.8457258101076217f),
FVector(2.1622015314362244f,0.85068796311660544f,-4.1307752690132205f),
FVector(2.6021369184528194f,1.0596020074600654f,-3.1860412064934174f),
FVector(-1.2905361603753163f,2.6108535683555365f,-0.46423293549223010f),
FVector(4.6820094577722964f,3.8858425146699327f,-1.9880098921962746f),
FVector(4.0115118130532306f,3.1678700881616777f,-4.8092930847360869f),
FVector(2.3757993445967389f,2.6579252395479291f,-4.2645319235961239f),
FVector(2.7329133227289351f,2.8811366857469527f,-3.6179750674182261f)
};
// Open Hand
TArray<FVector> HandPositionsOpen = {
FVector(-1014.001f,-478.278f,212.902f),
FVector(-1013.516f,-476.006f,214.688f),
FVector(-1016.362f,-479.642f,215.119f),
FVector(-1018.145f,-483.254f,214.805f),
FVector(-1019.682f,-485.682f,213.284f),
FVector(-1020.480f,-486.982f,212.581f),
FVector(-1014.360f,-478.927f,215.169f),
FVector(-1014.932f,-484.146f,209.902f),
FVector(-1016.872f,-486.643f,206.852f),
FVector(-1018.771f,-488.058f,205.231f),
FVector(-1019.613f,-488.507f,204.655f),
FVector(-1013.901f,-477.534f,213.831f),
FVector(-1014.494f,-481.954f,208.310f),
FVector(-1016.269f,-484.282f,205.146f),
FVector(-1018.657f,-485.834f,203.427f),
FVector(-1019.846f,-486.231f,203.113f),
FVector(-1013.816f,-476.436f,213.006f),
FVector(-1014.637f,-479.707f,207.344f),
FVector(-1016.703f,-481.540f,204.355f),
FVector(-1018.962f,-482.692f,203.000f),
FVector(-1019.978f,-482.975f,202.870f),
FVector(-1013.845f,-475.325f,212.363f),
FVector(-1015.993f,-477.665f,206.928f),
FVector(-1017.571f,-478.907f,204.670f),
FVector(-1019.033f,-479.652f,203.887f),
FVector(-1019.778f,-479.842f,203.819f)
};
MotionControllerData.HandKeyPositions = /*SkeletalMappingData.TargetHand != EVRSkeletalHandIndex::EActionHandIndex_Left ? HandPositionsOpen : */HandPositionsClosed;
if (SkeletalMappingData.TargetHand != EVRSkeletalHandIndex::EActionHandIndex_Left)
{
MotionControllerData.GripPosition = FVector(-1018.305f, -478.019f, 209.872f);
MotionControllerData.GripRotation = FQuat(-0.116352126f, 0.039430488f, -0.757644236f, 0.641001403f);
}
else
{
MotionControllerData.GripPosition = FVector(-1202.619f, -521.077f, 283.076f);
MotionControllerData.GripRotation = FQuat(0.040843058f, 0.116659224f, 0.980030060f, -0.155767411f);
}
MotionControllerData.DeviceName = TEXT("OpenXR");
SkeletalMappingData.SkeletalTransforms.Empty(SkeletalMappingData.SkeletalTransforms.Num());
FTransform ParentTrans = FTransform(MotionControllerData.GripRotation, MotionControllerData.GripPosition, FVector(1.f));
for (int i = 0; i < MotionControllerData.HandKeyPositions.Num(); i++)
{
SkeletalMappingData.SkeletalTransforms.Add(FTransform(MotionControllerData.HandKeyRotations[i], MotionControllerData.HandKeyPositions[i], FVector(1.f)).GetRelativeTransform(ParentTrans));
}
SkeletalMappingData.bHasValidData = (SkeletalMappingData.SkeletalTransforms.Num() == EHandKeypointCount);
}

View file

@ -1,24 +0,0 @@
// Copyright 1998-2016 Epic Games, Inc. All Rights Reserved.
#include "OpenXRExpansionPlugin.h"
#include "OpenXRExpansionFunctionLibrary.h"
#define LOCTEXT_NAMESPACE "FXRExpansionPluginModule"
void FOpenXRExpansionPluginModule::StartupModule()
{
// This code will execute after your module is loaded into memory; the exact timing is specified in the .uplugin file per-module
//LoadOpenVRModule();
}
void FOpenXRExpansionPluginModule::ShutdownModule()
{
// This function may be called during shutdown to clean up your module. For modules that support dynamic reloading,
// we call this function before unloading the module.
// UnloadOpenVRModule();
}
#undef LOCTEXT_NAMESPACE
IMPLEMENT_MODULE(FOpenXRExpansionPluginModule, OpenXRExpansionPlugin)

View file

@ -1,895 +0,0 @@
// Fill out your copyright notice in the Description page of Project Settings.
#include "OpenXRHandPoseComponent.h"
#include UE_INLINE_GENERATED_CPP_BY_NAME(OpenXRHandPoseComponent)
#include "Net/UnrealNetwork.h"
#include "MotionControllerComponent.h"
#include "OpenXRExpansionFunctionLibrary.h"
#include "Engine/NetSerialization.h"
#include "Net/Core/PushModel/PushModel.h"
#include "XRMotionControllerBase.h" // for GetHandEnumForSourceName()
//#include "EngineMinimal.h"
UOpenXRHandPoseComponent::UOpenXRHandPoseComponent(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{
PrimaryComponentTick.bCanEverTick = true;
PrimaryComponentTick.bStartWithTickEnabled = true;
ReplicationRateForSkeletalAnimations = 10.f;
bReplicateSkeletalData = false;
bSmoothReplicatedSkeletalData = true;
SkeletalNetUpdateCount = 0.f;
bDetectGestures = true;
SetIsReplicatedByDefault(true);
bGetMockUpPoseForDebugging = false;
}
void UOpenXRHandPoseComponent::GetLifetimeReplicatedProps(TArray< class FLifetimeProperty > & OutLifetimeProps) const
{
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
FDoRepLifetimeParams SkipOwnerParams;
SkipOwnerParams.Condition = COND_SkipOwner;
SkipOwnerParams.bIsPushBased = true;
// Skipping the owner with this as the owner will use the controllers location directly
DOREPLIFETIME_WITH_PARAMS_FAST(UOpenXRHandPoseComponent, LeftHandRep, SkipOwnerParams);
DOREPLIFETIME_WITH_PARAMS_FAST(UOpenXRHandPoseComponent, RightHandRep, SkipOwnerParams);
}
void UOpenXRHandPoseComponent::Server_SendSkeletalTransforms_Implementation(const FBPXRSkeletalRepContainer& SkeletalInfo)
{
for (int i = 0; i < HandSkeletalActions.Num(); i++)
{
if (HandSkeletalActions[i].TargetHand == SkeletalInfo.TargetHand)
{
if (SkeletalInfo.TargetHand == EVRSkeletalHandIndex::EActionHandIndex_Left)
{
if (bSmoothReplicatedSkeletalData)
{
LeftHandRepManager.PreCopyNewData(HandSkeletalActions[i], ReplicationRateForSkeletalAnimations, bUseExponentialSmoothing);
}
FBPXRSkeletalRepContainer::CopyReplicatedTo(SkeletalInfo, HandSkeletalActions[i]);
LeftHandRep = SkeletalInfo;
#if WITH_PUSH_MODEL
MARK_PROPERTY_DIRTY_FROM_NAME(UOpenXRHandPoseComponent, LeftHandRep, this);
#endif
if (bSmoothReplicatedSkeletalData)
{
LeftHandRepManager.NotifyNewData(HandSkeletalActions[i], ReplicationRateForSkeletalAnimations, bUseExponentialSmoothing);
}
}
else
{
if (bSmoothReplicatedSkeletalData)
{
RightHandRepManager.PreCopyNewData(HandSkeletalActions[i], ReplicationRateForSkeletalAnimations, bUseExponentialSmoothing);
}
FBPXRSkeletalRepContainer::CopyReplicatedTo(SkeletalInfo, HandSkeletalActions[i]);
RightHandRep = SkeletalInfo;
#if WITH_PUSH_MODEL
MARK_PROPERTY_DIRTY_FROM_NAME(UOpenXRHandPoseComponent, RightHandRep, this);
#endif
if (bSmoothReplicatedSkeletalData)
{
RightHandRepManager.NotifyNewData(HandSkeletalActions[i], ReplicationRateForSkeletalAnimations, bUseExponentialSmoothing);
}
}
break;
}
}
}
bool UOpenXRHandPoseComponent::Server_SendSkeletalTransforms_Validate(const FBPXRSkeletalRepContainer& SkeletalInfo)
{
return true;
}
void FOpenXRAnimInstanceProxy::PreUpdate(UAnimInstance* InAnimInstance, float DeltaSeconds)
{
Super::PreUpdate(InAnimInstance, DeltaSeconds);
if (UOpenXRAnimInstance* OwningInstance = Cast<UOpenXRAnimInstance>(InAnimInstance))
{
if (OwningInstance->OwningPoseComp)
{
if (HandSkeletalActionData.Num() != OwningInstance->OwningPoseComp->HandSkeletalActions.Num())
{
HandSkeletalActionData.Empty(OwningInstance->OwningPoseComp->HandSkeletalActions.Num());
for(FBPOpenXRActionSkeletalData& actionInfo : OwningInstance->OwningPoseComp->HandSkeletalActions)
{
HandSkeletalActionData.Add(actionInfo);
}
}
else
{
for (int i = 0; i < OwningInstance->OwningPoseComp->HandSkeletalActions.Num(); ++i)
{
HandSkeletalActionData[i] = OwningInstance->OwningPoseComp->HandSkeletalActions[i];
}
}
}
}
}
FOpenXRAnimInstanceProxy::FOpenXRAnimInstanceProxy(UAnimInstance* InAnimInstance)
: FAnimInstanceProxy(InAnimInstance)
{
}
void UOpenXRHandPoseComponent::BeginPlay()
{
/*if (UMotionControllerComponent * MotionParent = Cast<UMotionControllerComponent>(GetAttachParent()))
{
EControllerHand HandType;
if (!FXRMotionControllerBase::GetHandEnumForSourceName(MotionParent->MotionSource, HandType))
{
HandType = EControllerHand::Left;
}
for (int i = 0; i < HandSkeletalActions.Num(); i++)
{
if (HandType == EControllerHand::Left || HandType == EControllerHand::AnyHand)
HandSkeletalActions[i].SkeletalData.TargetHand = EVRSkeletalHandIndex::EActionHandIndex_Left;
else
HandSkeletalActions[i].SkeletalData.TargetHand = EVRSkeletalHandIndex::EActionHandIndex_Right;
}
}*/
Super::BeginPlay();
}
void UOpenXRHandPoseComponent::TickComponent(float DeltaTime, enum ELevelTick TickType, FActorComponentTickFunction *ThisTickFunction)
{
if (!IsLocallyControlled())
{
if (bReplicateSkeletalData)
{
// Handle bone lerping here if we are replicating
for (FBPOpenXRActionSkeletalData& actionInfo : HandSkeletalActions)
{
if (bSmoothReplicatedSkeletalData)
{
if (actionInfo.TargetHand == EVRSkeletalHandIndex::EActionHandIndex_Left)
{
LeftHandRepManager.UpdateManager(DeltaTime, actionInfo, this);
}
else
{
RightHandRepManager.UpdateManager(DeltaTime, actionInfo, this);
}
}
}
}
}
else // Get data and process
{
bool bGetCompressedTransforms = false;
if (bReplicateSkeletalData && HandSkeletalActions.Num() > 0)
{
SkeletalNetUpdateCount += DeltaTime;
if (SkeletalNetUpdateCount >= (1.0f / ReplicationRateForSkeletalAnimations))
{
SkeletalNetUpdateCount = 0.0f;
bGetCompressedTransforms = true;
}
}
for (FBPOpenXRActionSkeletalData& actionInfo : HandSkeletalActions)
{
if (UOpenXRExpansionFunctionLibrary::GetOpenXRHandPose(actionInfo, this, bGetMockUpPoseForDebugging))
{
if (bGetCompressedTransforms)
{
if (GetNetMode() == NM_Client)
{
if (actionInfo.bHasValidData)
{
FBPXRSkeletalRepContainer ContainerSend;
ContainerSend.CopyForReplication(actionInfo);
Server_SendSkeletalTransforms(ContainerSend);
}
}
else
{
if (actionInfo.bHasValidData)
{
if (actionInfo.TargetHand == EVRSkeletalHandIndex::EActionHandIndex_Left)
LeftHandRep.CopyForReplication(actionInfo);
else
RightHandRep.CopyForReplication(actionInfo);
}
}
}
}
if (bDetectGestures && actionInfo.bHasValidData && actionInfo.SkeletalTransforms.Num() > 0 && GesturesDB != nullptr && GesturesDB->Gestures.Num() > 0)
{
DetectCurrentPose(actionInfo);
}
}
}
Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
}
bool UOpenXRHandPoseComponent::SaveCurrentPose(FName RecordingName, EVRSkeletalHandIndex HandToSave)
{
if (!HandSkeletalActions.Num())
return false;
// Default to the first hand element so that single length arrays work as is.
FBPOpenXRActionSkeletalData* HandSkeletalAction = nullptr;
// Now check for the specific passed in hand if this is a multi hand
for (int i = 0; i < HandSkeletalActions.Num(); ++i)
{
if (HandSkeletalActions[i].TargetHand == HandToSave)
{
HandSkeletalAction = &HandSkeletalActions[i];
break;
}
}
if (!HandSkeletalAction || !HandSkeletalAction->bHasValidData || HandSkeletalAction->SkeletalTransforms.Num() < EHandKeypointCount)
return false;
if (GesturesDB)
{
FOpenXRGesture NewGesture;
int32 FingerMap[5] =
{
(int32)EXRHandJointType::OXR_HAND_JOINT_THUMB_TIP_EXT,
(int32)EXRHandJointType::OXR_HAND_JOINT_INDEX_TIP_EXT,
(int32)EXRHandJointType::OXR_HAND_JOINT_MIDDLE_TIP_EXT,
(int32)EXRHandJointType::OXR_HAND_JOINT_RING_TIP_EXT,
(int32)EXRHandJointType::OXR_HAND_JOINT_LITTLE_TIP_EXT
};
FVector WristLoc = FVector::ZeroVector;
if (HandToSave == EVRSkeletalHandIndex::EActionHandIndex_Left)
{
WristLoc = HandSkeletalAction->SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_WRIST_EXT].GetLocation().MirrorByVector(FVector::RightVector);
}
else
{
WristLoc = HandSkeletalAction->SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_WRIST_EXT].GetLocation();
}
for (int i = 0; i < 5; ++i)
{
if (HandToSave == EVRSkeletalHandIndex::EActionHandIndex_Left)
{
NewGesture.FingerValues[i] = FOpenXRGestureFingerPosition(HandSkeletalAction->SkeletalTransforms[FingerMap[i]].GetLocation().MirrorByVector(FVector::RightVector) - WristLoc, (EXRHandJointType)FingerMap[i]);
}
else
{
NewGesture.FingerValues[i] = FOpenXRGestureFingerPosition(HandSkeletalAction->SkeletalTransforms[FingerMap[i]].GetLocation() - WristLoc, (EXRHandJointType)FingerMap[i]);
}
}
NewGesture.Name = RecordingName;
GesturesDB->Gestures.Add(NewGesture);
return true;
}
return false;
}
bool UOpenXRHandPoseComponent::K2_DetectCurrentPose(UPARAM(ref) FBPOpenXRActionSkeletalData& SkeletalAction, FOpenXRGesture & GestureOut)
{
if (!GesturesDB || GesturesDB->Gestures.Num() < 1)
return false;
int32 FingerMap[5] =
{
(int32)EXRHandJointType::OXR_HAND_JOINT_THUMB_TIP_EXT,
(int32)EXRHandJointType::OXR_HAND_JOINT_INDEX_TIP_EXT,
(int32)EXRHandJointType::OXR_HAND_JOINT_MIDDLE_TIP_EXT,
(int32)EXRHandJointType::OXR_HAND_JOINT_RING_TIP_EXT,
(int32)EXRHandJointType::OXR_HAND_JOINT_LITTLE_TIP_EXT
};
FVector WristLoc = FVector::ZeroVector;
if (SkeletalAction.TargetHand == EVRSkeletalHandIndex::EActionHandIndex_Left)
{
WristLoc = SkeletalAction.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_WRIST_EXT].GetLocation().MirrorByVector(FVector::RightVector);
}
else
{
WristLoc = SkeletalAction.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_WRIST_EXT].GetLocation();
}
// Early fill in an array to keep from performing math for each gesture
TArray<FVector> CurrentTips;
CurrentTips.AddUninitialized(5);
for (int i = 0; i < 5; ++i)
{
if (SkeletalAction.TargetHand == EVRSkeletalHandIndex::EActionHandIndex_Left)
{
CurrentTips[i] = SkeletalAction.SkeletalTransforms[FingerMap[i]].GetLocation().MirrorByVector(FVector::RightVector) - WristLoc;
}
else
{
CurrentTips[i] = SkeletalAction.SkeletalTransforms[FingerMap[i]].GetLocation() - WristLoc;
}
}
for (const FOpenXRGesture& Gesture : GesturesDB->Gestures)
{
// If not enough indexs to match curl values, or if this gesture requires finger splay and the controller can't do it
if (Gesture.FingerValues.Num() < 5 || SkeletalAction.SkeletalTransforms.Num() < EHandKeypointCount)
continue;
bool bDetectedPose = true;
for (int i = 0; i < 5; ++i)
{
FVector GestureV = Gesture.FingerValues[i].Value;
FVector CurrentV = CurrentTips[i];
FVector Difference = GestureV - CurrentV;
if (!Gesture.FingerValues[i].Value.Equals(CurrentTips[i], Gesture.FingerValues[i].Threshold))
{
bDetectedPose = false;
break;
}
}
if (bDetectedPose)
{
GestureOut = Gesture;
return true;
}
}
return false;
}
bool UOpenXRHandPoseComponent::DetectCurrentPose(FBPOpenXRActionSkeletalData &SkeletalAction)
{
if (!GesturesDB || GesturesDB->Gestures.Num() < 1 || SkeletalAction.SkeletalTransforms.Num() < EHandKeypointCount)
return false;
FTransform BoneTransform = FTransform::Identity;
int32 FingerMap[5] =
{
(int32)EXRHandJointType::OXR_HAND_JOINT_THUMB_TIP_EXT,
(int32)EXRHandJointType::OXR_HAND_JOINT_INDEX_TIP_EXT,
(int32)EXRHandJointType::OXR_HAND_JOINT_MIDDLE_TIP_EXT,
(int32)EXRHandJointType::OXR_HAND_JOINT_RING_TIP_EXT,
(int32)EXRHandJointType::OXR_HAND_JOINT_LITTLE_TIP_EXT
};
FVector WristLoc = FVector::ZeroVector;
if (SkeletalAction.TargetHand == EVRSkeletalHandIndex::EActionHandIndex_Left)
{
WristLoc = SkeletalAction.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_WRIST_EXT].GetLocation().MirrorByVector(FVector::RightVector);
}
else
{
WristLoc = SkeletalAction.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_WRIST_EXT].GetLocation();
}
// Early fill in an array to keep from performing math for each gesture
TArray<FVector> CurrentTips;
CurrentTips.AddUninitialized(5);
for (int i = 0; i < 5; ++i)
{
if (SkeletalAction.TargetHand == EVRSkeletalHandIndex::EActionHandIndex_Left)
{
CurrentTips[i] = SkeletalAction.SkeletalTransforms[FingerMap[i]].GetLocation().MirrorByVector(FVector::RightVector) - WristLoc;
}
else
{
CurrentTips[i] = SkeletalAction.SkeletalTransforms[FingerMap[i]].GetLocation() - WristLoc;
}
}
for (auto GestureIterator = GesturesDB->Gestures.CreateConstIterator(); GestureIterator; ++GestureIterator)
{
const FOpenXRGesture &Gesture = *GestureIterator;
// If not enough indexs to match curl values, or if this gesture requires finger splay and the controller can't do it
if (Gesture.FingerValues.Num() < 5 || SkeletalAction.SkeletalTransforms.Num() < EHandKeypointCount)
continue;
bool bDetectedPose = true;
for (int i = 0; i < 5; ++i)
{
if (Gesture.FingerValues[i].Threshold <= 0.0f)
continue;
if (!Gesture.FingerValues[i].Value.Equals(CurrentTips[i], Gesture.FingerValues[i].Threshold))
{
bDetectedPose = false;
break;
}
}
if (bDetectedPose)
{
if (SkeletalAction.LastHandGesture != Gesture.Name)
{
if (SkeletalAction.LastHandGesture != NAME_None)
OnGestureEnded.Broadcast(SkeletalAction.LastHandGesture, SkeletalAction.LastHandGestureIndex, SkeletalAction.TargetHand);
SkeletalAction.LastHandGesture = Gesture.Name;
SkeletalAction.LastHandGestureIndex = GestureIterator.GetIndex();
OnNewGestureDetected.Broadcast(SkeletalAction.LastHandGesture, SkeletalAction.LastHandGestureIndex, SkeletalAction.TargetHand);
return true;
}
else
return false; // Same gesture
}
}
if (SkeletalAction.LastHandGesture != NAME_None)
{
OnGestureEnded.Broadcast(SkeletalAction.LastHandGesture, SkeletalAction.LastHandGestureIndex, SkeletalAction.TargetHand);
SkeletalAction.LastHandGesture = NAME_None;
SkeletalAction.LastHandGestureIndex = INDEX_NONE;
}
return false;
}
UOpenXRHandPoseComponent::FTransformLerpManager::FTransformLerpManager()
{
bReplicatedOnce = false;
bLerping = false;
UpdateCount = 0.0f;
UpdateRate = 0.0f;
}
void UOpenXRHandPoseComponent::FTransformLerpManager::PreCopyNewData(FBPOpenXRActionSkeletalData& ActionInfo, int NetUpdateRate, bool bExponentialSmoothing)
{
if (!bExponentialSmoothing)
{
if (ActionInfo.SkeletalTransforms.Num())
{
OldTransforms = ActionInfo.SkeletalTransforms;
}
}
}
void UOpenXRHandPoseComponent::FTransformLerpManager::NotifyNewData(FBPOpenXRActionSkeletalData& ActionInfo, int NetUpdateRate, bool bExponentialSmoothing)
{
UpdateRate = (1.0f / NetUpdateRate);
if (bReplicatedOnce)
{
bLerping = true;
UpdateCount = 0.0f;
NewTransforms = ActionInfo.SkeletalTransforms;
ActionInfo.SkeletalTransforms = OldTransforms;
}
else
{
if (bExponentialSmoothing)
{
OldTransforms = ActionInfo.SkeletalTransforms;
}
bReplicatedOnce = true;
}
}
void UOpenXRHandPoseComponent::FTransformLerpManager::UpdateManager(float DeltaTime, FBPOpenXRActionSkeletalData& ActionInfo, UOpenXRHandPoseComponent* ParentComp)
{
if (!ActionInfo.bHasValidData || !OldTransforms.Num())
return;
if (bLerping)
{
bool bExponentialSmoothing = ParentComp->bUseExponentialSmoothing;
float LerpVal = 0.0f;
if (!bExponentialSmoothing || ParentComp->InterpolationSpeed <= 0.f)
{
UpdateCount += DeltaTime;
// Keep LerpVal
LerpVal = FMath::Clamp(UpdateCount / UpdateRate, 0.0f, 1.0f);
}
else
{
LerpVal = FMath::Clamp(DeltaTime * ParentComp->InterpolationSpeed, 0.f, 1.f);
}
if (!bExponentialSmoothing && LerpVal >= 1.0f)
{
bLerping = false;
UpdateCount = 0.0f;
ActionInfo.SkeletalTransforms = NewTransforms;
}
else
{
int32 BoneCountAdjustment = 6 + (ActionInfo.bEnableUE4HandRepSavings ? 4 : 0);
if ((NewTransforms.Num() < (EHandKeypointCount - BoneCountAdjustment)) || (NewTransforms.Num() != ActionInfo.SkeletalTransforms.Num()))
{
return;
}
ActionInfo.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_PALM_EXT] = FTransform::Identity;
BlendBone((int32)EXRHandJointType::OXR_HAND_JOINT_WRIST_EXT, ActionInfo, LerpVal, bExponentialSmoothing);
BlendBone((int32)EXRHandJointType::OXR_HAND_JOINT_THUMB_METACARPAL_EXT, ActionInfo, LerpVal, bExponentialSmoothing);
BlendBone((int32)EXRHandJointType::OXR_HAND_JOINT_THUMB_PROXIMAL_EXT, ActionInfo, LerpVal, bExponentialSmoothing);
BlendBone((int32)EXRHandJointType::OXR_HAND_JOINT_THUMB_DISTAL_EXT, ActionInfo, LerpVal, bExponentialSmoothing);
//BlendBone((uint8)EVROpenXRBones::eBone_Thumb3, ActionInfo, LerpVal); // Technically can be projected instead of blended
if (!ActionInfo.bEnableUE4HandRepSavings)
{
BlendBone((int32)EXRHandJointType::OXR_HAND_JOINT_INDEX_METACARPAL_EXT, ActionInfo, LerpVal, bExponentialSmoothing);
}
BlendBone((int32)EXRHandJointType::OXR_HAND_JOINT_INDEX_PROXIMAL_EXT, ActionInfo, LerpVal, bExponentialSmoothing);
BlendBone((int32)EXRHandJointType::OXR_HAND_JOINT_INDEX_INTERMEDIATE_EXT, ActionInfo, LerpVal, bExponentialSmoothing);
BlendBone((int32)EXRHandJointType::OXR_HAND_JOINT_INDEX_DISTAL_EXT, ActionInfo, LerpVal, bExponentialSmoothing);
//BlendBone((uint8)EVROpenXRBones::eBone_IndexFinger4, ActionInfo, LerpVal); // Technically can be projected instead of blended
if (!ActionInfo.bEnableUE4HandRepSavings)
{
BlendBone((int32)EXRHandJointType::OXR_HAND_JOINT_MIDDLE_METACARPAL_EXT, ActionInfo, LerpVal, bExponentialSmoothing);
}
BlendBone((int32)EXRHandJointType::OXR_HAND_JOINT_MIDDLE_PROXIMAL_EXT, ActionInfo, LerpVal, bExponentialSmoothing);
BlendBone((int32)EXRHandJointType::OXR_HAND_JOINT_MIDDLE_INTERMEDIATE_EXT, ActionInfo, LerpVal, bExponentialSmoothing);
BlendBone((int32)EXRHandJointType::OXR_HAND_JOINT_MIDDLE_DISTAL_EXT, ActionInfo, LerpVal, bExponentialSmoothing);
//BlendBone((uint8)EVROpenXRBones::eBone_IndexFinger4, ActionInfo, LerpVal); // Technically can be projected instead of blended
if (!ActionInfo.bEnableUE4HandRepSavings)
{
BlendBone((int32)EXRHandJointType::OXR_HAND_JOINT_RING_METACARPAL_EXT, ActionInfo, LerpVal, bExponentialSmoothing);
}
BlendBone((int32)EXRHandJointType::OXR_HAND_JOINT_RING_PROXIMAL_EXT, ActionInfo, LerpVal, bExponentialSmoothing);
BlendBone((int32)EXRHandJointType::OXR_HAND_JOINT_RING_INTERMEDIATE_EXT, ActionInfo, LerpVal, bExponentialSmoothing);
BlendBone((int32)EXRHandJointType::OXR_HAND_JOINT_RING_DISTAL_EXT, ActionInfo, LerpVal, bExponentialSmoothing);
//BlendBone((uint8)EVROpenXRBones::eBone_IndexFinger4, ActionInfo, LerpVal); // Technically can be projected instead of blended
if (!ActionInfo.bEnableUE4HandRepSavings)
{
BlendBone((int32)EXRHandJointType::OXR_HAND_JOINT_LITTLE_METACARPAL_EXT, ActionInfo, LerpVal, bExponentialSmoothing);
}
BlendBone((int32)EXRHandJointType::OXR_HAND_JOINT_LITTLE_PROXIMAL_EXT, ActionInfo, LerpVal, bExponentialSmoothing);
BlendBone((int32)EXRHandJointType::OXR_HAND_JOINT_LITTLE_INTERMEDIATE_EXT, ActionInfo, LerpVal, bExponentialSmoothing);
BlendBone((int32)EXRHandJointType::OXR_HAND_JOINT_LITTLE_DISTAL_EXT, ActionInfo, LerpVal, bExponentialSmoothing);
//BlendBone((uint8)EVROpenXRBones::eBone_IndexFinger4, ActionInfo, LerpVal); // Technically can be projected instead of blended
// These are copied from the 3rd joints as they use the same transform but a different root
// Don't want to waste cpu time blending these
//ActionInfo.SkeletalTransforms[(uint8)EVROpenXRBones::eBone_Aux_Thumb] = ActionInfo.SkeletalData.SkeletalTransforms[(uint8)EVROpenXRBones::eBone_Thumb2];
//ActionInfo.SkeletalTransforms[(uint8)EVROpenXRBones::eBone_Aux_IndexFinger] = ActionInfo.SkeletalData.SkeletalTransforms[(uint8)EVROpenXRBones::eBone_IndexFinger3];
//ActionInfo.SkeletalTransforms[(uint8)EVROpenXRBones::eBone_Aux_MiddleFinger] = ActionInfo.SkeletalData.SkeletalTransforms[(uint8)EVROpenXRBones::eBone_MiddleFinger3];
//ActionInfo.SkeletalTransforms[(uint8)EVROpenXRBones::eBone_Aux_RingFinger] = ActionInfo.SkeletalData.SkeletalTransforms[(uint8)EVROpenXRBones::eBone_RingFinger3];
//ActionInfo.SkeletalTransforms[(uint8)EVROpenXRBones::eBone_Aux_PinkyFinger] = ActionInfo.SkeletalData.SkeletalTransforms[(uint8)EVROpenXRBones::eBone_PinkyFinger3];
}
}
}
void FBPXRSkeletalRepContainer::CopyForReplication(FBPOpenXRActionSkeletalData& Other)
{
TargetHand = Other.TargetHand;
if (!Other.bHasValidData)
return;
bAllowDeformingMesh = Other.bAllowDeformingMesh;
bEnableUE4HandRepSavings = Other.bEnableUE4HandRepSavings;
// Instead of doing this, we likely need to lerp but this is for testing
//SkeletalTransforms = Other.SkeletalData.SkeletalTransforms;
if (Other.SkeletalTransforms.Num() < EHandKeypointCount)
{
SkeletalTransforms.Empty();
return;
}
int32 BoneCountAdjustment = 6 + (bEnableUE4HandRepSavings ? 4 : 0);
if (SkeletalTransforms.Num() != EHandKeypointCount - BoneCountAdjustment)
{
SkeletalTransforms.Reset(EHandKeypointCount - BoneCountAdjustment); // Minus bones we don't need
SkeletalTransforms.AddUninitialized(EHandKeypointCount - BoneCountAdjustment);
}
int32 idx = 0;
// Root is always identity
//SkeletalTransforms[0] = Other.SkeletalData.SkeletalTransforms[(uint8)EVROpenInputBones::eBone_Root]; // This has no pos right? Need to skip pos on it
SkeletalTransforms[idx++] = Other.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_WRIST_EXT];
SkeletalTransforms[idx++] = Other.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_THUMB_METACARPAL_EXT];
SkeletalTransforms[idx++] = Other.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_THUMB_PROXIMAL_EXT];
SkeletalTransforms[idx++] = Other.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_THUMB_DISTAL_EXT];
if (!bEnableUE4HandRepSavings)
{
SkeletalTransforms[idx++] = Other.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_INDEX_METACARPAL_EXT];
}
SkeletalTransforms[idx++] = Other.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_INDEX_PROXIMAL_EXT];
SkeletalTransforms[idx++] = Other.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_INDEX_INTERMEDIATE_EXT];
SkeletalTransforms[idx++] = Other.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_INDEX_DISTAL_EXT];
if (!bEnableUE4HandRepSavings)
{
SkeletalTransforms[idx++] = Other.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_MIDDLE_METACARPAL_EXT];
}
SkeletalTransforms[idx++] = Other.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_MIDDLE_PROXIMAL_EXT];
SkeletalTransforms[idx++] = Other.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_MIDDLE_INTERMEDIATE_EXT];
SkeletalTransforms[idx++] = Other.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_MIDDLE_DISTAL_EXT];
if (!bEnableUE4HandRepSavings)
{
SkeletalTransforms[idx++] = Other.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_RING_METACARPAL_EXT];
}
SkeletalTransforms[idx++] = Other.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_RING_PROXIMAL_EXT];
SkeletalTransforms[idx++] = Other.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_RING_INTERMEDIATE_EXT];
SkeletalTransforms[idx++] = Other.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_RING_DISTAL_EXT];
if (!bEnableUE4HandRepSavings)
{
SkeletalTransforms[idx++] = Other.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_LITTLE_METACARPAL_EXT];
}
SkeletalTransforms[idx++] = Other.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_LITTLE_PROXIMAL_EXT];
SkeletalTransforms[idx++] = Other.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_LITTLE_INTERMEDIATE_EXT];
SkeletalTransforms[idx++] = Other.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_LITTLE_DISTAL_EXT];
}
void FBPXRSkeletalRepContainer::CopyReplicatedTo(const FBPXRSkeletalRepContainer& Container, FBPOpenXRActionSkeletalData& Other)
{
int32 BoneCountAdjustment = 6 + (Container.bEnableUE4HandRepSavings ? 4 : 0);
if (Container.SkeletalTransforms.Num() < (EHandKeypointCount - BoneCountAdjustment))
{
Other.SkeletalTransforms.Empty();
Other.bHasValidData = false;
return;
}
Other.bAllowDeformingMesh = Container.bAllowDeformingMesh;
Other.bEnableUE4HandRepSavings = Container.bEnableUE4HandRepSavings;
// Instead of doing this, we likely need to lerp but this is for testing
//Other.SkeletalData.SkeletalTransforms = Container.SkeletalTransforms;
if (Other.SkeletalTransforms.Num() != EHandKeypointCount)
{
Other.SkeletalTransforms.Reset(EHandKeypointCount);
Other.SkeletalTransforms.AddUninitialized(EHandKeypointCount);
}
int32 idx = 0;
// Only fill in the ones that we care about
Other.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_PALM_EXT] = FTransform::Identity; // Always identity
Other.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_WRIST_EXT] = Container.SkeletalTransforms[idx++];
Other.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_THUMB_METACARPAL_EXT] = Container.SkeletalTransforms[idx++];
Other.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_THUMB_PROXIMAL_EXT] = Container.SkeletalTransforms[idx++];
Other.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_THUMB_DISTAL_EXT] = Container.SkeletalTransforms[idx++];
Other.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_THUMB_TIP_EXT] = FTransform::Identity;
if (!Container.bEnableUE4HandRepSavings)
{
Other.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_INDEX_METACARPAL_EXT] = Container.SkeletalTransforms[idx++];
}
else
{
Other.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_INDEX_METACARPAL_EXT] = FTransform::Identity;
}
Other.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_INDEX_PROXIMAL_EXT] = Container.SkeletalTransforms[idx++];
Other.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_INDEX_INTERMEDIATE_EXT] = Container.SkeletalTransforms[idx++];
Other.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_INDEX_DISTAL_EXT] = Container.SkeletalTransforms[idx++];
Other.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_INDEX_TIP_EXT] = FTransform::Identity;
if (!Container.bEnableUE4HandRepSavings)
{
Other.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_MIDDLE_METACARPAL_EXT] = Container.SkeletalTransforms[idx++];
}
else
{
Other.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_MIDDLE_METACARPAL_EXT] = FTransform::Identity;
}
Other.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_MIDDLE_PROXIMAL_EXT] = Container.SkeletalTransforms[idx++];
Other.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_MIDDLE_INTERMEDIATE_EXT] = Container.SkeletalTransforms[idx++];
Other.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_MIDDLE_DISTAL_EXT] = Container.SkeletalTransforms[idx++];
Other.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_MIDDLE_TIP_EXT] = FTransform::Identity;
if (!Container.bEnableUE4HandRepSavings)
{
Other.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_RING_METACARPAL_EXT] = Container.SkeletalTransforms[idx++];
}
else
{
Other.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_RING_METACARPAL_EXT] = FTransform::Identity;
}
Other.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_RING_PROXIMAL_EXT] = Container.SkeletalTransforms[idx++];
Other.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_RING_INTERMEDIATE_EXT] = Container.SkeletalTransforms[idx++];
Other.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_RING_DISTAL_EXT] = Container.SkeletalTransforms[idx++];
Other.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_RING_TIP_EXT] = FTransform::Identity;
if (!Container.bEnableUE4HandRepSavings)
{
Other.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_LITTLE_METACARPAL_EXT] = Container.SkeletalTransforms[idx++];
}
else
{
Other.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_LITTLE_METACARPAL_EXT] = FTransform::Identity;
}
Other.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_LITTLE_PROXIMAL_EXT] = Container.SkeletalTransforms[idx++];
Other.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_LITTLE_INTERMEDIATE_EXT] = Container.SkeletalTransforms[idx++];
Other.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_LITTLE_DISTAL_EXT] = Container.SkeletalTransforms[idx++];
Other.SkeletalTransforms[(int32)EXRHandJointType::OXR_HAND_JOINT_LITTLE_TIP_EXT] = FTransform::Identity;
Other.bHasValidData = true;
}
bool FBPXRSkeletalRepContainer::NetSerialize(FArchive& Ar, class UPackageMap* Map, bool& bOutSuccess)
{
bOutSuccess = true;
Ar.SerializeBits(&TargetHand, 1);
Ar.SerializeBits(&bAllowDeformingMesh, 1);
Ar.SerializeBits(&bEnableUE4HandRepSavings, 1);
int32 BoneCountAdjustment = 6 + (bEnableUE4HandRepSavings ? 4 : 0);
uint8 TransformCount = EHandKeypointCount - BoneCountAdjustment;
bool bHasValidData = SkeletalTransforms.Num() >= TransformCount;
Ar.SerializeBits(&bHasValidData, 1);
//Ar << TransformCount;
if (Ar.IsLoading())
{
SkeletalTransforms.Reset(TransformCount);
}
FVector Position = FVector::ZeroVector;
FRotator Rot = FRotator::ZeroRotator;
if (bHasValidData)
{
for (int i = 0; i < TransformCount; i++)
{
if (Ar.IsSaving())
{
if (bAllowDeformingMesh)
Position = SkeletalTransforms[i].GetLocation();
Rot = SkeletalTransforms[i].Rotator();
}
if (bAllowDeformingMesh)
bOutSuccess &= SerializePackedVector<10, 11>(Position, Ar);
Rot.SerializeCompressed(Ar); // Short? 10 bit?
if (Ar.IsLoading())
{
if (bAllowDeformingMesh)
SkeletalTransforms.Add(FTransform(Rot, Position));
else
SkeletalTransforms.Add(FTransform(Rot));
}
}
}
return bOutSuccess;
}
void UOpenXRAnimInstance::NativeBeginPlay()
{
Super::NativeBeginPlay();
AActor* Owner = GetOwningComponent()->GetOwner();
UActorComponent* HandPoseComp = nullptr;
if (Owner)
{
HandPoseComp = Owner->GetComponentByClass(UOpenXRHandPoseComponent::StaticClass());
if (!HandPoseComp)
{
// We are also checking owner->owner in case hand mesh is in a sub actor
if (Owner->GetOwner())
{
HandPoseComp = Owner->GetOwner()->GetComponentByClass(UOpenXRHandPoseComponent::StaticClass());
}
}
}
if (!HandPoseComp)
{
return;
}
if (UOpenXRHandPoseComponent* HandComp = Cast<UOpenXRHandPoseComponent>(HandPoseComp))
{
OwningPoseComp = HandComp;
}
}
/*void UOpenXRAnimInstance::NativeInitializeAnimation()
{
Super::NativeInitializeAnimation();
AActor* Owner = GetOwningComponent()->GetOwner();
UActorComponent* HandPoseComp = nullptr;
if (Owner)
{
HandPoseComp = Owner->GetComponentByClass(UOpenXRHandPoseComponent::StaticClass());
if (!HandPoseComp)
{
// We are also checking owner->owner in case hand mesh is in a sub actor
if (Owner->GetOwner())
{
HandPoseComp = Owner->GetOwner()->GetComponentByClass(UOpenXRHandPoseComponent::StaticClass());
}
}
}
if (!HandPoseComp)
{
return;
}
if (UOpenXRHandPoseComponent* HandComp = Cast<UOpenXRHandPoseComponent>(HandPoseComp))
{
OwningPoseComp = HandComp;
}
}*/
void UOpenXRAnimInstance::InitializeCustomBoneMapping(UPARAM(ref) FBPOpenXRSkeletalMappingData& SkeletalMappingData)
{
USkeleton* AssetSkeleton = this->CurrentSkeleton;//RequiredBones.GetSkeletonAsset();
if (AssetSkeleton)
{
FBoneContainer& RequiredBones = this->GetRequiredBones();
for (FBPOpenXRSkeletalPair& BonePair : SkeletalMappingData.BonePairs)
{
// Fill in the bone name for the reference
BonePair.ReferenceToConstruct.BoneName = BonePair.BoneToTarget;
// Init the reference
BonePair.ReferenceToConstruct.Initialize(AssetSkeleton);
BonePair.ReferenceToConstruct.CachedCompactPoseIndex = BonePair.ReferenceToConstruct.GetCompactPoseIndex(RequiredBones);
if ((BonePair.ReferenceToConstruct.CachedCompactPoseIndex != INDEX_NONE))
{
// Get our parent bones index
BonePair.ParentReference = RequiredBones.GetParentBoneIndex(BonePair.ReferenceToConstruct.CachedCompactPoseIndex);
}
}
if (UObject* OwningAsset = RequiredBones.GetAsset())
{
SkeletalMappingData.LastInitializedName = OwningAsset->GetFName();
}
SkeletalMappingData.bInitialized = true;
return;
}
SkeletalMappingData.bInitialized = false;
}

View file

@ -1,121 +0,0 @@
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "Runtime/AnimGraphRuntime/Public/BoneControllers/AnimNode_SkeletalControlBase.h"
#include "OpenXRExpansionTypes.h"
//#include "Skeleton/BodyStateSkeleton.h"
//#include "BodyStateAnimInstance.h"
#include "AnimNode_ApplyOpenXRHandPose.generated.h"
USTRUCT()
struct OPENXREXPANSIONPLUGIN_API FAnimNode_ApplyOpenXRHandPose : public FAnimNode_SkeletalControlBase
{
GENERATED_USTRUCT_BODY()
public:
FVector WristForwardLS_UE;
FVector WristSideDirectionLS;
// Generally used when not passing in custom bone mappings, defines the auto mapping style
UPROPERTY(EditAnywhere, Category = Skeletal, meta = (PinShownByDefault))
EVROpenXRSkeletonType SkeletonType;
// If your hand is part of a full body or arm skeleton and you don't have a proxy bone to retain the position enable this
UPROPERTY(EditAnywhere, Category = Skeletal, meta = (PinShownByDefault))
bool bSkipRootBone;
// If you only want to use the wrist transform part of this
// This will also automatically add the deform to the wrist as it doesn't make much sense without it
UPROPERTY(EditAnywhere, Category = Skeletal, meta = (PinShownByDefault))
bool bOnlyApplyWristTransform;
// Generally used when not passing in custom bone mappings, defines the auto mapping style
UPROPERTY(EditAnywhere, Category = Skeletal, meta = (PinShownByDefault))
FBPOpenXRActionSkeletalData OptionalStoredActionInfo;
// MappedBonePairs, if you leave it blank then they will auto generate based off of the SkeletonType
// Otherwise, fill out yourself.
UPROPERTY(EditAnywhere, Category = Skeletal, meta = (PinHiddenByDefault))
FBPOpenXRSkeletalMappingData MappedBonePairs;
bool bIsOpenInputAnimationInstance = false;
bool bIsMirroringHand = false;
void ConvertHandTransformsSpace(TArray<FTransform>& OutTransforms, const TArray<FTransform>& WorldTransforms, FTransform AddTrans, bool bMirrorLeftRight, bool bMergeMissingUE4Bones);
void CalculateSkeletalAdjustment(USkeleton* AssetSkeleton);
void CalculateOpenXRAdjustment();
FQuat CalcRotationAboutAxis(const FVector& FromDirection, const FVector& ToDirection, const FVector& Axis)
{
FVector FromDirectionCp = FVector::CrossProduct(Axis, FromDirection);
FVector ToDirectionCp = FVector::CrossProduct(Axis, ToDirection);
return FQuat::FindBetweenVectors(FromDirectionCp, ToDirectionCp);
}
FTransform GetRefBoneInCS(TArray<FTransform>& RefBones, TArray<FMeshBoneInfo>& RefBonesInfo, int32 BoneIndex)
{
FTransform BoneTransform;
if (BoneIndex >= 0)
{
BoneTransform = RefBones[BoneIndex];
if (RefBonesInfo[BoneIndex].ParentIndex >= 0)
{
BoneTransform *= GetRefBoneInCS(RefBones, RefBonesInfo, RefBonesInfo[BoneIndex].ParentIndex);
}
}
return BoneTransform;
}
void SetVectorToMaxElement(FVector& vec)
{
FVector absVal = vec.GetAbs();
if (absVal.X > absVal.Y && absVal.X > absVal.Z)
{
vec = vec.GetSignVector() * FVector(1.0f, 0.f, 0.f);
vec.Normalize();
}
else if (absVal.Y > absVal.X && absVal.Y > absVal.Z)
{
vec = vec.GetSignVector() * FVector(0.0f, 1.f, 0.f);
vec.Normalize();
}
else if (absVal.Z > absVal.X && absVal.Z > absVal.Y)
{
vec = vec.GetSignVector() * FVector(0.0f, 0.f, 1.f);
vec.Normalize();
}
else
{
vec.Normalize();
}
}
// FAnimNode_SkeletalControlBase interface
//virtual void UpdateInternal(const FAnimationUpdateContext& Context) override;
virtual void EvaluateSkeletalControl_AnyThread(FComponentSpacePoseContext& Output, TArray<FBoneTransform>& OutBoneTransforms) override;
virtual bool IsValidToEvaluate(const USkeleton* Skeleton, const FBoneContainer& RequiredBones) override;
// End of FAnimNode_SkeletalControlBase interface
virtual void OnInitializeAnimInstance(const FAnimInstanceProxy* InProxy, const UAnimInstance* InAnimInstance) override;
virtual bool NeedsOnInitializeAnimInstance() const override { return true; }
virtual void InitializeBoneReferences(const FBoneContainer& RequiredBones) override;
virtual void Initialize_AnyThread(const FAnimationInitializeContext& Context) override;
virtual void CacheBones_AnyThread(const FAnimationCacheBonesContext& Context) override;
// Constructor
FAnimNode_ApplyOpenXRHandPose();
protected:
bool WorldIsGame;
AActor* OwningActor;
private:
};

View file

@ -1,102 +0,0 @@
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "UObject/ObjectMacros.h"
#include "Kismet/BlueprintFunctionLibrary.h"
#include "UObject/Object.h"
#include "Engine/EngineTypes.h"
#include "HeadMountedDisplayTypes.h"
#if defined(OPENXR_SUPPORTED)
#include "OpenXRCore.h"
#include "OpenXRHMD.h"
#endif
#include "OpenXRExpansionTypes.h"
#include "HeadMountedDisplayFunctionLibrary.h"
#include "Misc/FileHelper.h"
#include "Misc/Paths.h"
#include "OpenXRExpansionFunctionLibrary.generated.h"
#if !defined(OPENXR_SUPPORTED)
class FOpenXRHMD;
#endif
DECLARE_LOG_CATEGORY_EXTERN(OpenXRExpansionFunctionLibraryLog, Log, All);
// This needs to be updated as the original gets changed, that or hope they make the original blueprint accessible.
UENUM(Blueprintable)
enum class EBPOpenXRControllerDeviceType : uint8
{
DT_SimpleController,
DT_ValveIndexController,
DT_ViveController,
DT_ViveProController,
//DT_CosmosController,
DT_DaydreamController,
DT_OculusTouchController,
DT_OculusGoController,
DT_MicrosoftMotionController,
DT_MicrosoftXboxController,
DT_PicoNeo3Controller,
DT_WMRController,
DT_UnknownController
};
UCLASS(Blueprintable, meta = (BlueprintSpawnableComponent))
class OPENXREXPANSIONPLUGIN_API UOpenXRExpansionFunctionLibrary : public UBlueprintFunctionLibrary
{
//GENERATED_BODY()
GENERATED_BODY()
public:
UOpenXRExpansionFunctionLibrary(const FObjectInitializer& ObjectInitializer);
~UOpenXRExpansionFunctionLibrary();
public:
static FOpenXRHMD* GetOpenXRHMD()
{
#if defined(OPENXR_SUPPORTED)
static FName SystemName(TEXT("OpenXR"));
if (GEngine->XRSystem.IsValid() && (GEngine->XRSystem->GetSystemName() == SystemName))
{
return static_cast<FOpenXRHMD*>(GEngine->XRSystem.Get());
}
#endif
return nullptr;
}
UFUNCTION(BlueprintCallable, Category = "VRExpansionFunctions|OpenXR", meta = (bIgnoreSelf = "true"))
static bool GetOpenXRHandPose(FBPOpenXRActionSkeletalData& HandPoseContainer, UOpenXRHandPoseComponent* HandPoseComponent, bool bGetMockUpPose = false);
//UFUNCTION(BlueprintCallable, Category = "VRExpansionFunctions|OpenXR", meta = (bIgnoreSelf = "true"))
static void GetFingerCurlValues(TArray<FTransform>& TransformArray, TArray<float>& CurlArray);
// Get the estimated curl values from hand tracking
// Will return true if it was able to get the curls, false if it could not (hand tracking not enabled or no data for the tracked index)
UFUNCTION(BlueprintCallable, Category = "VRExpansionFunctions|OpenXR", meta = (WorldContext = "WorldContextObject"))
static bool GetOpenXRFingerCurlValuesForHand(
UObject* WorldContextObject,
EControllerHand TargetHand,
float& ThumbCurl,
float& IndexCurl,
float& MiddleCurl,
float& RingCurl,
float& PinkyCurl);
static float GetCurlValueForBoneRoot(TArray<FTransform>& TransformArray, EHandKeypoint RootBone);
//UFUNCTION(BlueprintCallable, Category = "VRExpansionFunctions|OpenXR", meta = (bIgnoreSelf = "true"))
static void ConvertHandTransformsSpaceAndBack(TArray<FTransform>& OutTransforms, const TArray<FTransform>& WorldTransforms);
UFUNCTION(BlueprintCallable, Category = "VRExpansionFunctions|OpenXR", meta = (bIgnoreSelf = "true"))
static void GetMockUpControllerData(FXRMotionControllerData& MotionControllerData, FBPOpenXRActionSkeletalData& SkeletalMappingData, bool bOpenHand = false);
// Get a list of all currently tracked devices and their types, index in the array is their device index
// Returns failed if the openXR query failed (no interaction profile yet or openXR is not running)
UFUNCTION(BlueprintCallable, Category = "VRExpansionFunctions|OpenXR", meta = (bIgnoreSelf = "true", ExpandEnumAsExecs = "Result"))
static void GetXRMotionControllerType(FString& TrackingSystemName, EBPOpenXRControllerDeviceType& DeviceType, EBPXRResultSwitch &Result);
};

View file

@ -1,18 +0,0 @@
// Copyright 1998-2016 Epic Games, Inc. All Rights Reserved.
#pragma once
#include "Modules/ModuleManager.h"
class FOpenXRExpansionPluginModule : public IModuleInterface
{
public:
FOpenXRExpansionPluginModule()
{
}
/** IModuleInterface implementation */
virtual void StartupModule() override;
virtual void ShutdownModule() override;
};

View file

@ -1,480 +0,0 @@
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "UObject/ObjectMacros.h"
#include "Kismet/BlueprintFunctionLibrary.h"
#include "Animation/BoneReference.h"
#include "UObject/Object.h"
#include "Engine/EngineTypes.h"
#include "OpenXRExpansionTypes.generated.h"
// This makes a lot of the blueprint functions cleaner
UENUM()
enum class EBPXRResultSwitch : uint8
{
// On Success
OnSucceeded,
// On Failure
OnFailed
};
UENUM(BlueprintType)
enum class EVRSkeletalHandIndex : uint8
{
EActionHandIndex_Left = 0,
EActionHandIndex_Right
};
UENUM(BlueprintType)
enum class EXRHandJointType : uint8
{
OXR_HAND_JOINT_PALM_EXT = 0,
OXR_HAND_JOINT_WRIST_EXT = 1,
OXR_HAND_JOINT_THUMB_METACARPAL_EXT = 2,
OXR_HAND_JOINT_THUMB_PROXIMAL_EXT = 3,
OXR_HAND_JOINT_THUMB_DISTAL_EXT = 4,
OXR_HAND_JOINT_THUMB_TIP_EXT = 5,
OXR_HAND_JOINT_INDEX_METACARPAL_EXT = 6,
OXR_HAND_JOINT_INDEX_PROXIMAL_EXT = 7,
OXR_HAND_JOINT_INDEX_INTERMEDIATE_EXT = 8,
OXR_HAND_JOINT_INDEX_DISTAL_EXT = 9,
OXR_HAND_JOINT_INDEX_TIP_EXT = 10,
OXR_HAND_JOINT_MIDDLE_METACARPAL_EXT = 11,
OXR_HAND_JOINT_MIDDLE_PROXIMAL_EXT = 12,
OXR_HAND_JOINT_MIDDLE_INTERMEDIATE_EXT = 13,
OXR_HAND_JOINT_MIDDLE_DISTAL_EXT = 14,
OXR_HAND_JOINT_MIDDLE_TIP_EXT = 15,
OXR_HAND_JOINT_RING_METACARPAL_EXT = 16,
OXR_HAND_JOINT_RING_PROXIMAL_EXT = 17,
OXR_HAND_JOINT_RING_INTERMEDIATE_EXT = 18,
OXR_HAND_JOINT_RING_DISTAL_EXT = 19,
OXR_HAND_JOINT_RING_TIP_EXT = 20,
OXR_HAND_JOINT_LITTLE_METACARPAL_EXT = 21,
OXR_HAND_JOINT_LITTLE_PROXIMAL_EXT = 22,
OXR_HAND_JOINT_LITTLE_INTERMEDIATE_EXT = 23,
OXR_HAND_JOINT_LITTLE_DISTAL_EXT = 24,
OXR_HAND_JOINT_LITTLE_TIP_EXT = 25,
OXR_HAND_JOINT_MAX_ENUM_EXT = 0xFF
};
UENUM(BlueprintType)
enum class EVROpenXRSkeletonType : uint8
{
// UE4 Skeletal Right hand
OXR_SkeletonType_UE4Default_Right,
// UE4 Skeletal Left hand
OXR_SkeletonType_UE4Default_Left,
// OpenVR Skeletal Right hand
OXR_SkeletonType_OpenVRDefault_Right,
// OpenVR Skeletal Left hand
OXR_SkeletonType_OpenVRDefault_Left,
// UE5 Skeletal Right hand
OXR_SkeletonType_UE5Default_Right,
// UE5 Skeletal Left hand
OXR_SkeletonType_UE5Default_Left,
// OpenXR Skeletal Right hand
OXR_SkeletonType_OpenXRDefault_Right,
// OpenXR Skeletal Left hand
OXR_SkeletonType_OpenXRDefault_Left,
OXR_SkeletonType_Custom
};
USTRUCT(BlueprintType, Category = "VRExpansionFunctions|OpenXR|HandSkeleton")
struct OPENXREXPANSIONPLUGIN_API FBPOpenXRActionSkeletalData
{
GENERATED_BODY()
public:
UPROPERTY(EditAnywhere, NotReplicated, BlueprintReadWrite, Category = Default)
EVRSkeletalHandIndex TargetHand;
// A world scale override that will replace the engines current value and force into the tracked data if non zero
UPROPERTY(EditAnywhere, NotReplicated, BlueprintReadWrite, Category = Default)
float WorldScaleOverride;
UPROPERTY(EditAnywhere, NotReplicated, BlueprintReadWrite, Category = Default)
bool bAllowDeformingMesh;
// If true then the bones will be mirrored from left/right, to allow you to swap a hand mesh to the other hand
UPROPERTY(EditAnywhere, NotReplicated, BlueprintReadWrite, Category = Default)
bool bMirrorLeftRight;
// List of aproximated curls for each finger
UPROPERTY(BlueprintReadOnly, NotReplicated, Transient, Category = Default)
TArray<float> FingerCurls;
UPROPERTY(BlueprintReadOnly, NotReplicated, Transient, Category = Default)
TArray<FTransform> SkeletalTransforms;
// If true we will assume that the target skeleton does not have the metacarpal bones and we will not replicate them
// Only really used for the old UE4 skeleton
UPROPERTY(EditAnywhere, NotReplicated, BlueprintReadWrite, Category = Default)
bool bEnableUE4HandRepSavings;
//UPROPERTY(BlueprintReadOnly, NotReplicated, Transient, Category = Default)
//TArray<FTransform> OldSkeletalTransforms;
// The rotation required to rotate the finger bones back to X+
// The animation node attempts to auto calculate it, if you have a non standard hand you may need to fill
// This in by yourself
UPROPERTY(EditAnywhere, NotReplicated, BlueprintReadWrite, Category = Default)
FTransform AdditionTransform;
UPROPERTY(NotReplicated, BlueprintReadOnly, Category = Default)
bool bHasValidData;
FName LastHandGesture;
int32 LastHandGestureIndex;
FBPOpenXRActionSkeletalData()
{
//bGetTransformsInParentSpace = false;
AdditionTransform = FTransform::Identity;// FTransform(FRotator(180.f, 0.f, -90.f), FVector::ZeroVector, FVector(1.f));//FTransform(FRotator(0.f, 90.f, 90.f), FVector::ZeroVector, FVector(1.f));
WorldScaleOverride = 0.0f;
bAllowDeformingMesh = true;
bMirrorLeftRight = false;
bEnableUE4HandRepSavings = false;
TargetHand = EVRSkeletalHandIndex::EActionHandIndex_Right;
bHasValidData = false;
LastHandGestureIndex = INDEX_NONE;
LastHandGesture = NAME_None;
}
};
USTRUCT(BlueprintType, Category = "VRExpansionFunctions|SteamVR|HandSkeleton")
struct OPENXREXPANSIONPLUGIN_API FBPOpenXRSkeletalPair
{
GENERATED_BODY()
public:
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Default")
EXRHandJointType OpenXRBone;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Default")
FName BoneToTarget;
FBoneReference ReferenceToConstruct;
FCompactPoseBoneIndex ParentReference;
FQuat RetargetRot;
FBPOpenXRSkeletalPair() :
ParentReference(INDEX_NONE)
{
OpenXRBone = EXRHandJointType::OXR_HAND_JOINT_WRIST_EXT;
BoneToTarget = NAME_None;
RetargetRot = FQuat::Identity;
}
FBPOpenXRSkeletalPair(EXRHandJointType Bone, FString TargetBone) :
ParentReference(INDEX_NONE)
{
OpenXRBone = Bone;
BoneToTarget = FName(*TargetBone);
ReferenceToConstruct.BoneName = BoneToTarget;
RetargetRot = FQuat::Identity;
}
FORCEINLINE bool operator==(const int32& Other) const
{
return ReferenceToConstruct.CachedCompactPoseIndex.GetInt() == Other;
//return ReferenceToConstruct.BoneIndex == Other;
}
};
USTRUCT(BlueprintType, Category = "VRExpansionFunctions|SteamVR|HandSkeleton")
struct OPENXREXPANSIONPLUGIN_API FBPOpenXRSkeletalMappingData
{
GENERATED_BODY()
public:
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Default")
TArray<FBPOpenXRSkeletalPair> BonePairs;
TArray<int32> ReverseBonePairMap;
// Merge the transforms of bones that are missing from the OpenVR skeleton to the UE4 one.
// This should be always enabled for UE4 skeletons generally.
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Default")
bool bMergeMissingBonesUE4;
// The hand data to get, if not using a custom bone mapping then this value will be auto filled
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Default")
EVRSkeletalHandIndex TargetHand;
FQuat AdjustmentQuat;
bool bInitialized;
FName LastInitializedName;
EVROpenXRSkeletonType LastInitializedSkeleton;
void ClearMapping()
{
bInitialized = false;
LastInitializedName = NAME_None;
AdjustmentQuat = FQuat::Identity;
LastInitializedSkeleton = EVROpenXRSkeletonType::OXR_SkeletonType_Custom;
BonePairs.Empty();
ReverseBonePairMap.Empty();
}
void ConstructReverseMapping()
{
int32 MaxElements = ((uint8)EXRHandJointType::OXR_HAND_JOINT_LITTLE_TIP_EXT) + 1;
ReverseBonePairMap.Empty(MaxElements);
ReverseBonePairMap.AddUninitialized(MaxElements);
FMemory::Memset(ReverseBonePairMap.GetData(), 0, MaxElements * sizeof(int32));
for (int i = 0; i < BonePairs.Num(); ++i)
{
// Just in case someone messed up the mapping file
if (i < MaxElements)
{
ReverseBonePairMap[(uint8)BonePairs[i].OpenXRBone] = i;
}
}
}
void ConstructDefaultMappings(EVROpenXRSkeletonType SkeletonType, bool bSkipRootBone)
{
switch (SkeletonType)
{
case EVROpenXRSkeletonType::OXR_SkeletonType_OpenVRDefault_Left:
case EVROpenXRSkeletonType::OXR_SkeletonType_OpenVRDefault_Right:
{
bMergeMissingBonesUE4 = false;
SetDefaultOpenVRInputs(SkeletonType, bSkipRootBone);
}break;
case EVROpenXRSkeletonType::OXR_SkeletonType_UE4Default_Left:
case EVROpenXRSkeletonType::OXR_SkeletonType_UE4Default_Right:
{
bMergeMissingBonesUE4 = true;
SetDefaultUE4Inputs(SkeletonType, bSkipRootBone);
}break;
case EVROpenXRSkeletonType::OXR_SkeletonType_UE5Default_Left:
case EVROpenXRSkeletonType::OXR_SkeletonType_UE5Default_Right:
{
bMergeMissingBonesUE4 = false;
SetDefaultUE5Inputs(SkeletonType, bSkipRootBone);
}break;
case EVROpenXRSkeletonType::OXR_SkeletonType_OpenXRDefault_Left:
case EVROpenXRSkeletonType::OXR_SkeletonType_OpenXRDefault_Right:
{
bMergeMissingBonesUE4 = false;
SetDefaultOpenXRInputs(SkeletonType, bSkipRootBone);
}break;
}
}
void SetDefaultOpenVRInputs(EVROpenXRSkeletonType cSkeletonType, bool bSkipRootBone)
{
// Don't map anything if the end user already has
if (BonePairs.Num())
return;
bool bIsRightHand = cSkeletonType != EVROpenXRSkeletonType::OXR_SkeletonType_OpenVRDefault_Left;
FString HandDelimiterS = !bIsRightHand ? "l" : "r";
const TCHAR* HandDelimiter = *HandDelimiterS;
TargetHand = bIsRightHand ? EVRSkeletalHandIndex::EActionHandIndex_Right : EVRSkeletalHandIndex::EActionHandIndex_Left;
// Default OpenVR bones mapping
//if (!bSkipRootBone)
//{
//BonePairs.Add(FBPOpenVRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_PALM_EXT, FString::Printf(TEXT("Root"), HandDelimiter)));
//}
//if (!bSkipRootBone)
{
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_WRIST_EXT, FString::Printf(TEXT("wrist_%s"), HandDelimiter)));
}
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_THUMB_METACARPAL_EXT, FString::Printf(TEXT("finger_thumb_0_%s"), HandDelimiter)));
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_THUMB_PROXIMAL_EXT, FString::Printf(TEXT("finger_thumb_1_%s"), HandDelimiter)));
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_THUMB_DISTAL_EXT, FString::Printf(TEXT("finger_thumb_2_%s"), HandDelimiter)));
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_THUMB_TIP_EXT, FString::Printf(TEXT("finger_thumb_%s_end"), HandDelimiter)));
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_INDEX_METACARPAL_EXT, FString::Printf(TEXT("finger_index_meta_%s"), HandDelimiter)));
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_INDEX_PROXIMAL_EXT, FString::Printf(TEXT("finger_index_0_%s"), HandDelimiter)));
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_INDEX_INTERMEDIATE_EXT, FString::Printf(TEXT("finger_index_1_%s"), HandDelimiter)));
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_INDEX_DISTAL_EXT, FString::Printf(TEXT("finger_index_2_%s"), HandDelimiter)));
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_INDEX_TIP_EXT, FString::Printf(TEXT("finger_index_%s_end"), HandDelimiter)));
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_MIDDLE_METACARPAL_EXT, FString::Printf(TEXT("finger_middle_meta_%s"), HandDelimiter)));
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_MIDDLE_PROXIMAL_EXT, FString::Printf(TEXT("finger_middle_0_%s"), HandDelimiter)));
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_MIDDLE_INTERMEDIATE_EXT, FString::Printf(TEXT("finger_middle_1_%s"), HandDelimiter)));
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_MIDDLE_DISTAL_EXT, FString::Printf(TEXT("finger_middle_2_%s"), HandDelimiter)));
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_MIDDLE_TIP_EXT, FString::Printf(TEXT("finger_middle_%s_end"), HandDelimiter)));
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_RING_METACARPAL_EXT, FString::Printf(TEXT("finger_ring_meta_%s"), HandDelimiter)));
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_RING_PROXIMAL_EXT, FString::Printf(TEXT("finger_ring_0_%s"), HandDelimiter)));
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_RING_INTERMEDIATE_EXT, FString::Printf(TEXT("finger_ring_1_%s"), HandDelimiter)));
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_RING_DISTAL_EXT, FString::Printf(TEXT("finger_ring_2_%s"), HandDelimiter)));
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_RING_TIP_EXT, FString::Printf(TEXT("finger_ring_%s_end"), HandDelimiter)));
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_LITTLE_METACARPAL_EXT, FString::Printf(TEXT("finger_pinky_meta_%s"), HandDelimiter)));
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_LITTLE_PROXIMAL_EXT, FString::Printf(TEXT("finger_pinky_0_%s"), HandDelimiter)));
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_LITTLE_INTERMEDIATE_EXT, FString::Printf(TEXT("finger_pinky_1_%s"), HandDelimiter)));
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_LITTLE_DISTAL_EXT, FString::Printf(TEXT("finger_pinky_2_%s"), HandDelimiter)));
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_LITTLE_TIP_EXT, FString::Printf(TEXT("finger_pinky_%s_end"), HandDelimiter)));
// Aux bones share the final knuckles location / rotation
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_THUMB_DISTAL_EXT, FString::Printf(TEXT("finger_thumb_%s_aux"), HandDelimiter)));
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_INDEX_DISTAL_EXT, FString::Printf(TEXT("finger_index_%s_aux"), HandDelimiter)));
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_MIDDLE_DISTAL_EXT, FString::Printf(TEXT("finger_middle_%s_aux"), HandDelimiter)));
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_RING_DISTAL_EXT, FString::Printf(TEXT("finger_ring_%s_aux"), HandDelimiter)));
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_LITTLE_DISTAL_EXT, FString::Printf(TEXT("finger_pinky_%s_aux"), HandDelimiter)));
}
void SetDefaultUE4Inputs(EVROpenXRSkeletonType cSkeletonType, bool bSkipRootBone)
{
// Don't map anything if the end user already has
if (BonePairs.Num())
return;
bool bIsRightHand = cSkeletonType != EVROpenXRSkeletonType::OXR_SkeletonType_UE4Default_Left;
FString HandDelimiterS = !bIsRightHand ? "l" : "r";
const TCHAR* HandDelimiter = *HandDelimiterS;
TargetHand = bIsRightHand ? EVRSkeletalHandIndex::EActionHandIndex_Right : EVRSkeletalHandIndex::EActionHandIndex_Left;
// Default ue4 skeleton hand to the OpenVR bones, skipping the extra joint and the aux joints
//if (!bSkipRootBone)
{
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_WRIST_EXT, FString::Printf(TEXT("hand_%s"), HandDelimiter)));
}
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_INDEX_PROXIMAL_EXT, FString::Printf(TEXT("index_01_%s"), HandDelimiter)));
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_INDEX_INTERMEDIATE_EXT, FString::Printf(TEXT("index_02_%s"), HandDelimiter)));
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_INDEX_DISTAL_EXT, FString::Printf(TEXT("index_03_%s"), HandDelimiter)));
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_MIDDLE_PROXIMAL_EXT, FString::Printf(TEXT("middle_01_%s"), HandDelimiter)));
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_MIDDLE_INTERMEDIATE_EXT, FString::Printf(TEXT("middle_02_%s"), HandDelimiter)));
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_MIDDLE_DISTAL_EXT, FString::Printf(TEXT("middle_03_%s"), HandDelimiter)));
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_LITTLE_PROXIMAL_EXT, FString::Printf(TEXT("pinky_01_%s"), HandDelimiter)));
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_LITTLE_INTERMEDIATE_EXT, FString::Printf(TEXT("pinky_02_%s"), HandDelimiter)));
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_LITTLE_DISTAL_EXT, FString::Printf(TEXT("pinky_03_%s"), HandDelimiter)));
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_RING_PROXIMAL_EXT, FString::Printf(TEXT("ring_01_%s"), HandDelimiter)));
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_RING_INTERMEDIATE_EXT, FString::Printf(TEXT("ring_02_%s"), HandDelimiter)));
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_RING_DISTAL_EXT, FString::Printf(TEXT("ring_03_%s"), HandDelimiter)));
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_THUMB_METACARPAL_EXT, FString::Printf(TEXT("thumb_01_%s"), HandDelimiter)));
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_THUMB_PROXIMAL_EXT, FString::Printf(TEXT("thumb_02_%s"), HandDelimiter)));
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_THUMB_DISTAL_EXT, FString::Printf(TEXT("thumb_03_%s"), HandDelimiter)));
}
void SetDefaultUE5Inputs(EVROpenXRSkeletonType cSkeletonType, bool bSkipRootBone)
{
// Don't map anything if the end user already has
if (BonePairs.Num())
return;
bool bIsRightHand = cSkeletonType != EVROpenXRSkeletonType::OXR_SkeletonType_UE5Default_Left;
FString HandDelimiterS = !bIsRightHand ? "l" : "r";
const TCHAR* HandDelimiter = *HandDelimiterS;
TargetHand = bIsRightHand ? EVRSkeletalHandIndex::EActionHandIndex_Right : EVRSkeletalHandIndex::EActionHandIndex_Left;
// Default ue5 skeleton hand to the OpenVR bones, skipping the extra joint and the aux joints
//if (!bSkipRootBone)
{
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_WRIST_EXT, FString::Printf(TEXT("hand_%s"), HandDelimiter)));
}
// There are inner and outer wrist elements to this, going to be anoying to map that to a single wrist index....
//OXR_HAND_JOINT_WRIST_EXT = 1,
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_INDEX_PROXIMAL_EXT, FString::Printf(TEXT("index_01_%s"), HandDelimiter)));
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_INDEX_INTERMEDIATE_EXT, FString::Printf(TEXT("index_02_%s"), HandDelimiter)));
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_INDEX_DISTAL_EXT, FString::Printf(TEXT("index_03_%s"), HandDelimiter)));
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_MIDDLE_PROXIMAL_EXT, FString::Printf(TEXT("middle_01_%s"), HandDelimiter)));
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_MIDDLE_INTERMEDIATE_EXT, FString::Printf(TEXT("middle_02_%s"), HandDelimiter)));
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_MIDDLE_DISTAL_EXT, FString::Printf(TEXT("middle_03_%s"), HandDelimiter)));
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_LITTLE_PROXIMAL_EXT, FString::Printf(TEXT("pinky_01_%s"), HandDelimiter)));
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_LITTLE_INTERMEDIATE_EXT, FString::Printf(TEXT("pinky_02_%s"), HandDelimiter)));
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_LITTLE_DISTAL_EXT, FString::Printf(TEXT("pinky_03_%s"), HandDelimiter)));
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_RING_PROXIMAL_EXT, FString::Printf(TEXT("ring_01_%s"), HandDelimiter)));
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_RING_INTERMEDIATE_EXT, FString::Printf(TEXT("ring_02_%s"), HandDelimiter)));
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_RING_DISTAL_EXT, FString::Printf(TEXT("ring_03_%s"), HandDelimiter)));
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_THUMB_METACARPAL_EXT, FString::Printf(TEXT("thumb_01_%s"), HandDelimiter)));
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_THUMB_PROXIMAL_EXT, FString::Printf(TEXT("thumb_02_%s"), HandDelimiter)));
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_THUMB_DISTAL_EXT, FString::Printf(TEXT("thumb_03_%s"), HandDelimiter)));
}
void SetDefaultOpenXRInputs(EVROpenXRSkeletonType cSkeletonType, bool bSkipRootBone)
{
// Don't map anything if the end user already has
if (BonePairs.Num())
return;
bool bIsRightHand = cSkeletonType != EVROpenXRSkeletonType::OXR_SkeletonType_OpenXRDefault_Left;
FString HandDelimiterS = !bIsRightHand ? "l" : "r";
const TCHAR* HandDelimiter = *HandDelimiterS;
TargetHand = bIsRightHand ? EVRSkeletalHandIndex::EActionHandIndex_Right : EVRSkeletalHandIndex::EActionHandIndex_Left;
// Default ue5 skeleton hand to the OpenVR bones, skipping the extra joint and the aux joints
//if (!bSkipRootBone)
{
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_WRIST_EXT, FString::Printf(TEXT("hand_%s"), HandDelimiter)));
}
// There are inner and outer wrist elements to this, going to be anoyying to map that to a single wrist index....
//OXR_HAND_JOINT_WRIST_EXT = 1,
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_INDEX_PROXIMAL_EXT, FString::Printf(TEXT("index_01_%s"), HandDelimiter)));
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_INDEX_INTERMEDIATE_EXT, FString::Printf(TEXT("index_02_%s"), HandDelimiter)));
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_INDEX_DISTAL_EXT, FString::Printf(TEXT("index_03_%s"), HandDelimiter)));
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_MIDDLE_PROXIMAL_EXT, FString::Printf(TEXT("middle_01_%s"), HandDelimiter)));
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_MIDDLE_INTERMEDIATE_EXT, FString::Printf(TEXT("middle_02_%s"), HandDelimiter)));
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_MIDDLE_DISTAL_EXT, FString::Printf(TEXT("middle_03_%s"), HandDelimiter)));
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_LITTLE_PROXIMAL_EXT, FString::Printf(TEXT("pinky_01_%s"), HandDelimiter)));
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_LITTLE_INTERMEDIATE_EXT, FString::Printf(TEXT("pinky_02_%s"), HandDelimiter)));
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_LITTLE_DISTAL_EXT, FString::Printf(TEXT("pinky_03_%s"), HandDelimiter)));
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_RING_PROXIMAL_EXT, FString::Printf(TEXT("ring_01_%s"), HandDelimiter)));
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_RING_INTERMEDIATE_EXT, FString::Printf(TEXT("ring_02_%s"), HandDelimiter)));
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_RING_DISTAL_EXT, FString::Printf(TEXT("ring_03_%s"), HandDelimiter)));
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_THUMB_METACARPAL_EXT, FString::Printf(TEXT("thumb_01_%s"), HandDelimiter)));
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_THUMB_PROXIMAL_EXT, FString::Printf(TEXT("thumb_02_%s"), HandDelimiter)));
BonePairs.Add(FBPOpenXRSkeletalPair(EXRHandJointType::OXR_HAND_JOINT_THUMB_DISTAL_EXT, FString::Printf(TEXT("thumb_03_%s"), HandDelimiter)));
}
FBPOpenXRSkeletalMappingData()
{
AdjustmentQuat = FQuat::Identity;
bInitialized = false;
bMergeMissingBonesUE4 = false;
TargetHand = EVRSkeletalHandIndex::EActionHandIndex_Right;
LastInitializedName = NAME_None;
LastInitializedSkeleton = EVROpenXRSkeletonType::OXR_SkeletonType_Custom;
}
};

View file

@ -1,377 +0,0 @@
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "UObject/Object.h"
#include "Engine/Texture.h"
#include "Engine/EngineTypes.h"
#include "HeadMountedDisplayTypes.h"
//#include "Runtime/Launch/Resources/Version.h"
#include "Animation/AnimInstanceProxy.h"
#include "OpenXRExpansionTypes.h"
#include "Engine/DataAsset.h"
#include "OpenXRHandPoseComponent.generated.h"
USTRUCT(BlueprintType, Category = "VRExpansionFunctions|OpenXR|HandSkeleton")
struct OPENXREXPANSIONPLUGIN_API FBPXRSkeletalRepContainer
{
GENERATED_BODY()
public:
UPROPERTY(Transient, NotReplicated)
EVRSkeletalHandIndex TargetHand;
UPROPERTY(Transient, NotReplicated)
bool bAllowDeformingMesh;
// If true we will skip sending the 4 metacarpal bones that ue4 doesn't need, (STEAMVR skeletons need this disabled!)
// Only really used for the older UE4 skeleton
UPROPERTY(Transient, NotReplicated)
bool bEnableUE4HandRepSavings;
UPROPERTY(Transient, NotReplicated)
TArray<FTransform> SkeletalTransforms;
UPROPERTY(Transient, NotReplicated)
uint8 BoneCount;
FBPXRSkeletalRepContainer()
{
TargetHand = EVRSkeletalHandIndex::EActionHandIndex_Left;
bAllowDeformingMesh = false;
bEnableUE4HandRepSavings = false;
BoneCount = 0;
}
bool bHasValidData()
{
return SkeletalTransforms.Num() > 0;
}
void CopyForReplication(FBPOpenXRActionSkeletalData& Other);
static void CopyReplicatedTo(const FBPXRSkeletalRepContainer& Container, FBPOpenXRActionSkeletalData& Other);
bool NetSerialize(FArchive& Ar, class UPackageMap* Map, bool& bOutSuccess);
};
template<>
struct TStructOpsTypeTraits< FBPXRSkeletalRepContainer > : public TStructOpsTypeTraitsBase2<FBPXRSkeletalRepContainer>
{
enum
{
WithNetSerializer = true
};
};
USTRUCT(BlueprintType, Category = "VRGestures")
struct OPENXREXPANSIONPLUGIN_API FOpenXRGestureFingerPosition
{
GENERATED_BODY()
public:
// The Finger index, not editable
UPROPERTY(BlueprintReadOnly, VisibleAnywhere, Category = "VRGesture")
EXRHandJointType IndexType;
// The locational value of this element 0.f - 1.f
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "VRGesture")
FVector Value;
// The threshold within which this finger value will be detected as matching (1.0 would be always matching, IE: finger doesn't count)
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "VRGesture", meta = (ClampMin = "0.0", ClampMax = "100.0", UIMin = "0.0", UIMax = "100.0"))
float Threshold;
FOpenXRGestureFingerPosition(FVector TipLoc, EXRHandJointType Type)
{
IndexType = Type;
Value = TipLoc;
Threshold = 5.0f;
}
FOpenXRGestureFingerPosition()
{
IndexType = EXRHandJointType::OXR_HAND_JOINT_INDEX_TIP_EXT;
Value = FVector(0.f);
Threshold = 5.0f;
}
};
USTRUCT(BlueprintType, Category = "VRGestures")
struct OPENXREXPANSIONPLUGIN_API FOpenXRGesture
{
GENERATED_BODY()
public:
// Name of the recorded gesture
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "VRGesture")
FName Name;
// Samples in the recorded gesture
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "VRGesture")
TArray<FOpenXRGestureFingerPosition> FingerValues;
FOpenXRGesture()
{
InitPoseValues();
Name = NAME_None;
}
void InitPoseValues()
{
FingerValues.Add(FOpenXRGestureFingerPosition(FVector::ZeroVector, EXRHandJointType::OXR_HAND_JOINT_THUMB_TIP_EXT));
FingerValues.Add(FOpenXRGestureFingerPosition(FVector::ZeroVector, EXRHandJointType::OXR_HAND_JOINT_INDEX_TIP_EXT));
FingerValues.Add(FOpenXRGestureFingerPosition(FVector::ZeroVector, EXRHandJointType::OXR_HAND_JOINT_MIDDLE_TIP_EXT));
FingerValues.Add(FOpenXRGestureFingerPosition(FVector::ZeroVector, EXRHandJointType::OXR_HAND_JOINT_RING_TIP_EXT));
FingerValues.Add(FOpenXRGestureFingerPosition(FVector::ZeroVector, EXRHandJointType::OXR_HAND_JOINT_LITTLE_TIP_EXT));
}
};
/**
* Items Database DataAsset, here we can save all of our game items
*/
UCLASS(BlueprintType, Category = "VRGestures")
class OPENXREXPANSIONPLUGIN_API UOpenXRGestureDatabase : public UDataAsset
{
GENERATED_BODY()
public:
// Gestures in this database
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "VRGestures")
TArray <FOpenXRGesture> Gestures;
UOpenXRGestureDatabase()
{
}
};
DECLARE_DYNAMIC_MULTICAST_DELEGATE_ThreeParams(FOpenXRGestureDetected, const FName &, GestureDetected, int32, GestureIndex, EVRSkeletalHandIndex, ActionHandType);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_ThreeParams(FOpenXRGestureEnded, const FName &, GestureEnded, int32, GestureIndex, EVRSkeletalHandIndex, ActionHandType);
UCLASS(Blueprintable, meta = (BlueprintSpawnableComponent))
class OPENXREXPANSIONPLUGIN_API UOpenXRHandPoseComponent : public UActorComponent
{
GENERATED_BODY()
public:
UOpenXRHandPoseComponent(const FObjectInitializer& ObjectInitializer);
// Says whether we should run gesture detection
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "VRGestures")
bool bDetectGestures;
UFUNCTION(BlueprintCallable, Category = "VRGestures")
void SetDetectGestures(bool bNewDetectGestures)
{
bDetectGestures = bNewDetectGestures;
}
UPROPERTY(BlueprintAssignable, Category = "VRGestures")
FOpenXRGestureDetected OnNewGestureDetected;
UPROPERTY(BlueprintAssignable, Category = "VRGestures")
FOpenXRGestureEnded OnGestureEnded;
// Known sequences
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "VRGestures")
UOpenXRGestureDatabase *GesturesDB;
UFUNCTION(BlueprintCallable, Category = "VRGestures")
bool SaveCurrentPose(FName RecordingName, EVRSkeletalHandIndex HandToSave = EVRSkeletalHandIndex::EActionHandIndex_Right);
UFUNCTION(BlueprintCallable, Category = "VRGestures", meta = (DisplayName = "DetectCurrentPose"))
bool K2_DetectCurrentPose(UPARAM(ref) FBPOpenXRActionSkeletalData& SkeletalAction, FOpenXRGesture & GestureOut);
// This version throws events
bool DetectCurrentPose(FBPOpenXRActionSkeletalData& SkeletalAction);
// Need this as I can't think of another way for an actor component to make sure it isn't on the server
inline bool IsLocallyControlled() const
{
//#if ENGINE_MAJOR_VERSION > 4 || (ENGINE_MAJOR_VERSION == 4 && ENGINE_MINOR_VERSION >= 22)
const AActor* MyOwner = GetOwner();
return MyOwner->HasLocalNetOwner();
//#else
// I like epics new authority check more than mine
/* const AActor* MyOwner = GetOwner();
const APawn* MyPawn = Cast<APawn>(MyOwner);
return MyPawn ? MyPawn->IsLocallyControlled() : (MyOwner && MyOwner->GetLocalRole() == ENetRole::ROLE_Authority);*/
//#endif
}
// Using tick and not timers because skeletal components tick anyway, kind of a waste to make another tick by adding a timer over that
void TickComponent(float DeltaTime, enum ELevelTick TickType, FActorComponentTickFunction *ThisTickFunction) override;
//virtual void OnUnregister() override;
virtual void BeginPlay() override;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "SkeletalData|Actions")
bool bGetMockUpPoseForDebugging;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "SkeletalData|Actions")
TArray<FBPOpenXRActionSkeletalData> HandSkeletalActions;
UPROPERTY(Replicated, Transient, ReplicatedUsing = OnRep_SkeletalTransformLeft)
FBPXRSkeletalRepContainer LeftHandRep;
UPROPERTY(Replicated, Transient, ReplicatedUsing = OnRep_SkeletalTransformRight)
FBPXRSkeletalRepContainer RightHandRep;
UFUNCTION(Unreliable, Server, WithValidation)
void Server_SendSkeletalTransforms(const FBPXRSkeletalRepContainer& SkeletalInfo);
bool bLerpingPositionLeft;
bool bLerpingPositionRight;
struct FTransformLerpManager
{
bool bReplicatedOnce;
bool bLerping;
float UpdateCount;
float UpdateRate;
TArray<FTransform> OldTransforms;
TArray<FTransform> NewTransforms;
FTransformLerpManager();
void PreCopyNewData(FBPOpenXRActionSkeletalData& ActionInfo, int NetUpdateRate, bool bExponentialSmoothing);
void NotifyNewData(FBPOpenXRActionSkeletalData& ActionInfo, int NetUpdateRate, bool bExponentialSmoothing);
FORCEINLINE void BlendBone(uint8 BoneToBlend, FBPOpenXRActionSkeletalData& ActionInfo, float& LerpVal, bool bExponentialSmoothing)
{
ActionInfo.SkeletalTransforms[BoneToBlend].Blend(OldTransforms[BoneToBlend], NewTransforms[BoneToBlend], LerpVal);
if (bExponentialSmoothing)
{
// Saving base back out for exponential
OldTransforms[BoneToBlend] = ActionInfo.SkeletalTransforms[BoneToBlend];
}
}
void UpdateManager(float DeltaTime, FBPOpenXRActionSkeletalData& ActionInfo, UOpenXRHandPoseComponent * ParentComp);
};
FTransformLerpManager LeftHandRepManager;
FTransformLerpManager RightHandRepManager;
UFUNCTION()
virtual void OnRep_SkeletalTransformLeft()
{
for (int i = 0; i < HandSkeletalActions.Num(); i++)
{
if (HandSkeletalActions[i].TargetHand == LeftHandRep.TargetHand)
{
if (bSmoothReplicatedSkeletalData)
{
LeftHandRepManager.PreCopyNewData(HandSkeletalActions[i], ReplicationRateForSkeletalAnimations, bUseExponentialSmoothing);
}
FBPXRSkeletalRepContainer::CopyReplicatedTo(LeftHandRep, HandSkeletalActions[i]);
if (bSmoothReplicatedSkeletalData)
{
LeftHandRepManager.NotifyNewData(HandSkeletalActions[i], ReplicationRateForSkeletalAnimations, bUseExponentialSmoothing);
}
break;
}
}
}
UFUNCTION()
virtual void OnRep_SkeletalTransformRight()
{
for (int i = 0; i < HandSkeletalActions.Num(); i++)
{
if (HandSkeletalActions[i].TargetHand == RightHandRep.TargetHand)
{
if (bSmoothReplicatedSkeletalData)
{
RightHandRepManager.PreCopyNewData(HandSkeletalActions[i], ReplicationRateForSkeletalAnimations, bUseExponentialSmoothing);
}
FBPXRSkeletalRepContainer::CopyReplicatedTo(RightHandRep, HandSkeletalActions[i]);
if (bSmoothReplicatedSkeletalData)
{
RightHandRepManager.NotifyNewData(HandSkeletalActions[i], ReplicationRateForSkeletalAnimations, bUseExponentialSmoothing);
}
break;
}
}
}
// If we should replicate the skeletal transform data
UPROPERTY(EditAnywhere, Category = SkeletalData)
bool bReplicateSkeletalData;
// If true we will lerp between updates of the skeletal mesh transforms and smooth the result
UPROPERTY(EditAnywhere, Category = SkeletalData)
bool bSmoothReplicatedSkeletalData;
// If true then we will use exponential smoothing with buffered correction
UPROPERTY(EditAnywhere, Category = "SkeletalData", meta = (editcondition = "bSmoothReplicatedSkeletalData"))
bool bUseExponentialSmoothing = false;
// Timestep of smoothing translation
UPROPERTY(EditAnywhere, Category = "SkeletalData", meta = (editcondition = "bUseExponentialSmoothing"))
float InterpolationSpeed = 25.0f;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = SkeletalData)
float ReplicationRateForSkeletalAnimations;
// Used in Tick() to accumulate before sending updates, didn't want to use a timer in this case, also used for remotes to lerp position
float SkeletalNetUpdateCount;
// Used in Tick() to accumulate before sending updates, didn't want to use a timer in this case, also used for remotes to lerp position
float SkeletalUpdateCount;
};
USTRUCT()
struct OPENXREXPANSIONPLUGIN_API FOpenXRAnimInstanceProxy : public FAnimInstanceProxy
{
public:
GENERATED_BODY()
FOpenXRAnimInstanceProxy() {}
FOpenXRAnimInstanceProxy(UAnimInstance* InAnimInstance);
/** Called before update so we can copy any data we need */
virtual void PreUpdate(UAnimInstance* InAnimInstance, float DeltaSeconds) override;
public:
EVRSkeletalHandIndex TargetHand;
TArray<FBPOpenXRActionSkeletalData> HandSkeletalActionData;
};
UCLASS(transient, Blueprintable, hideCategories = AnimInstance, BlueprintType)
class OPENXREXPANSIONPLUGIN_API UOpenXRAnimInstance : public UAnimInstance
{
GENERATED_BODY()
public:
UPROPERTY(transient)
UOpenXRHandPoseComponent * OwningPoseComp;
FOpenXRAnimInstanceProxy AnimInstanceProxy;
virtual FAnimInstanceProxy* CreateAnimInstanceProxy() override
{
return new FOpenXRAnimInstanceProxy(this);
//return &AnimInstanceProxy;
}
virtual void NativeBeginPlay() override;
//virtual void NativeInitializeAnimation() override;
UFUNCTION(BlueprintCallable, Category = "BoneMappings")
void InitializeCustomBoneMapping(UPARAM(ref) FBPOpenXRSkeletalMappingData & SkeletalMappingData);
};

View file

@ -1,48 +0,0 @@
UE Forums Thread
https://forums.unrealengine.com/development-discussion/vr-ar-development/89050-vr-openvr-expansion-plugin
Example Template Project
https://github.com/mordentral/VRExpPluginExample
Website:
www.vreue4.com
***
### Use Of This Plugin ###
This Plugin is intended to add additional functionality to Open/SteamVR/(All VR now) in UE4.
### Plugin Website ###
[VREUE4.com](https://vreue4.com)
### How do I install it? ###
https://vreue4.com/documentation?section=installation
**Guides for migrating between different engine versions of the plugin:**
View the patch notes at www.vreue4.com for migration guides as well.
**Option 1:**
Go to www.vreue4.com and downloaded the pre-built binary version for the engine version you are using (not updated with every daily change, only weekly or with large patches).
Install it into your ProjectName/Plugins Directory (Engine level hasn't worked since 4.25 or so when it stopped letting me reference other plugins when compiling for that).
**Option 2 (More up to date - preferred if possible):**
* Clone Or Download Zip and extract this repository to a folder named "VRExpansionPlugin" in your "ProjectName/Plugins" directory, create this directory if it is missing.
* Add the VRExpansionPlugin to your projects PublicDependencyModuleNames in the projects build.cs if you have c++ code included.
* IF you do not have c++ code, use the Add New button in the editor and add a blank c++ class to your project.
* Open up the generated project .SLN file and build the project from the build menu.
You need to have visual studio installed and follow the UE4 setup guide for it: https://docs.unrealengine.com/latest/INT/Programming/Development/VisualStudioSetup/
### How do I use it? ###
### How do I VR? ###
The template project contains use examples of most of the features of the plugin as well as locomotion modes, interaction methods, and basic multiplayer.

View file

@ -1257,6 +1257,55 @@ void UGripMotionControllerComponent::SetGripStiffnessAndDamping(
}
}
void UGripMotionControllerComponent::SetGripAdvancedGripSettings(
const FBPActorGripInformation& Grip,
EBPVRResultSwitch& Result,
uint8 GripPriority,
bool bSetOwnerOnGrip,
bool bDisallowLerping,
bool bDisallowSettingPositionOnClientAuthDrop
)
{
Result = EBPVRResultSwitch::OnFailed;
int fIndex = GrippedObjects.Find(Grip);
if (fIndex != INDEX_NONE)
{
GrippedObjects[fIndex].AdvancedGripSettings.GripPriority = GripPriority;
GrippedObjects[fIndex].AdvancedGripSettings.bSetOwnerOnGrip = bSetOwnerOnGrip;
GrippedObjects[fIndex].AdvancedGripSettings.bDisallowLerping = bDisallowLerping;
GrippedObjects[fIndex].AdvancedGripSettings.bDisallowSettingPositionOnClientAuthDrop = bDisallowSettingPositionOnClientAuthDrop;
DIRTY_GRIPPED_OBJECTS();
Result = EBPVRResultSwitch::OnSucceeded;
}
else
{
fIndex = LocallyGrippedObjects.Find(Grip);
if (fIndex != INDEX_NONE)
{
GrippedObjects[fIndex].AdvancedGripSettings.GripPriority = GripPriority;
GrippedObjects[fIndex].AdvancedGripSettings.bSetOwnerOnGrip = bSetOwnerOnGrip;
GrippedObjects[fIndex].AdvancedGripSettings.bDisallowLerping = bDisallowLerping;
GrippedObjects[fIndex].AdvancedGripSettings.bDisallowSettingPositionOnClientAuthDrop = bDisallowSettingPositionOnClientAuthDrop;
if (IsLocallyControlled() && !IsServer() && !IsTornOff() && LocallyGrippedObjects[fIndex].GripMovementReplicationSetting == EGripMovementReplicationSettings::ClientSide_Authoritive)
{
FBPActorGripInformation GripInfo = LocallyGrippedObjects[fIndex];
Server_NotifyLocalGripAddedOrChanged(GripInfo);
}
DIRTY_LOCALLY_GRIPPED_OBJECTS();
Result = EBPVRResultSwitch::OnSucceeded;
}
}
}
FTransform UGripMotionControllerComponent::CreateGripRelativeAdditionTransform_BP(
const FBPActorGripInformation &GripToSample,
const FTransform & AdditionTransform,
@ -7976,7 +8025,17 @@ void UGripMotionControllerComponent::Server_NotifyLocalGripRemoved_Implementatio
{
if (IsValid(DroppingActor) && TransformAtDrop.IsValid())
{
DroppingActor->SetActorTransform(TransformAtDrop, false, nullptr, ETeleportType::None);
bool bSkipSetTransform = false;
if (DroppingActor->GetClass()->ImplementsInterface(UVRGripInterface::StaticClass()))
{
FBPAdvGripSettings AdvSettings = IVRGripInterface::Execute_AdvancedGripSettings(DroppingActor);
bSkipSetTransform = AdvSettings.bDisallowSettingPositionOnClientAuthDrop;
}
if (!bSkipSetTransform)
{
DroppingActor->SetActorTransform(TransformAtDrop, false, nullptr, ETeleportType::None);
}
}
}
}break;
@ -7986,7 +8045,17 @@ void UGripMotionControllerComponent::Server_NotifyLocalGripRemoved_Implementatio
{
if (IsValid(DroppingComp) && TransformAtDrop.IsValid())
{
DroppingComp->SetWorldTransform(TransformAtDrop, false, nullptr, ETeleportType::None);
bool bSkipSetTransform = false;
if (DroppingComp->GetClass()->ImplementsInterface(UVRGripInterface::StaticClass()))
{
FBPAdvGripSettings AdvSettings = IVRGripInterface::Execute_AdvancedGripSettings(DroppingComp);
bSkipSetTransform = AdvSettings.bDisallowSettingPositionOnClientAuthDrop;
}
if (!bSkipSetTransform)
{
DroppingComp->SetWorldTransform(TransformAtDrop, false, nullptr, ETeleportType::None);
}
}
}
}break;

View file

@ -50,7 +50,7 @@ AGrippableActor::AGrippableActor(const FObjectInitializer& ObjectInitializer)
// #TODO we can register them maybe in the future
// Don't use the replicated list, use our custom replication instead
bReplicateUsingRegisteredSubObjectList = false;
bReplicateUsingRegisteredSubObjectList = true;
// Setting a minimum of every 3rd frame (VR 90fps) for replication consideration
// Otherwise we will get some massive slow downs if the replication is allowed to hit the 2 per second minimum default
@ -756,22 +756,6 @@ void AGrippableActor::BeginDestroy()
GripLogicScripts.Empty();
}
void AGrippableActor::GetSubobjectsWithStableNamesForNetworking(TArray<UObject*>& ObjList)
{
Super::GetSubobjectsWithStableNamesForNetworking(ObjList);
if (bReplicateGripScripts)
{
for (int32 i = 0; i < GripLogicScripts.Num(); ++i)
{
if (UObject* SubObject = GripLogicScripts[i])
{
ObjList.Add(SubObject);
}
}
}
}
/////////////////////////////////////////////////
//- Push networking getter / setter functions
/////////////////////////////////////////////////

View file

@ -44,7 +44,7 @@ UGrippableBoxComponent::UGrippableBoxComponent(const FObjectInitializer& ObjectI
// #TODO we can register them maybe in the future
// Don't use the replicated list, use our custom replication instead
bReplicateUsingRegisteredSubObjectList = false;
bReplicateUsingRegisteredSubObjectList = true;
}
void UGrippableBoxComponent::GetLifetimeReplicatedProps(TArray< class FLifetimeProperty > & OutLifetimeProps) const

View file

@ -40,7 +40,7 @@ UGrippableCapsuleComponent::UGrippableCapsuleComponent(const FObjectInitializer&
// #TODO we can register them maybe in the future
// Don't use the replicated list, use our custom replication instead
bReplicateUsingRegisteredSubObjectList = false;
bReplicateUsingRegisteredSubObjectList = true;
}
void UGrippableCapsuleComponent::GetLifetimeReplicatedProps(TArray< class FLifetimeProperty > & OutLifetimeProps) const

View file

@ -5,8 +5,7 @@
#include "GameFramework/Actor.h"
bool FRepAttachmentWithWeld::NetSerialize(FArchive& Ar, class UPackageMap* Map, bool& bOutSuccess)
/*bool FRepAttachmentWithWeld::NetSerialize(FArchive& Ar, class UPackageMap* Map, bool& bOutSuccess)
{
// Our additional weld bit is here
Ar.SerializeBits(&bIsWelded, 1);
@ -17,9 +16,9 @@ bool FRepAttachmentWithWeld::NetSerialize(FArchive& Ar, class UPackageMap* Map,
Ar << AttachSocket;
Ar << AttachComponent;
return true;
}
}*/
FRepAttachmentWithWeld::FRepAttachmentWithWeld()
{
bIsWelded = false;
}
}

View file

@ -48,6 +48,7 @@ namespace VRPhysicsReplicationStatics
namespace RenderInterpolationCVars
{
bool bRenderInterpDebugDrawResimTrigger = false;
float RenderInterpDebugDrawResimBoxScale = 1.0f;
}
namespace PhysicsReplicationCVars
@ -81,6 +82,7 @@ namespace PhysicsReplicationCVars
float VelStabilityMultiplier = 0.5f;
float AngVelStabilityMultiplier = 0.5f;
bool bDrawDebug = false;
float LogOutOfBoundsTimeLimit = 5.0f;
// Inside of NetworkPhysicsComponent - UPDATE AS CHANGE
int32 RedundantInputs = 2;
@ -92,6 +94,11 @@ namespace PhysicsReplicationCVars
bool bCompareInputToTriggerRewind = false;
bool bEnableUnreliableFlow = true;
bool bEnableReliableFlow = false;
bool bTriggerResimOnInputReceive = false;
bool bApplyInputDecayOverSetTime = false;
float InputDecaySetTime = 0.15f;
bool bApplyDataInsteadOfMergeData = false;
bool bAllowInputExtrapolation = true;
bool bValidateDataOnGameThread = false;
@ -542,12 +549,13 @@ bool FPhysicsReplicationVR::ApplyRigidBodyState(float DeltaSeconds, FBodyInstanc
bool bCorrectConnectedBodiesFriction = CVarCorrectConnectedBodiesFriction->GetBool();
// Assign per-actor settings from NetworkPhysicSettingsComponent if this actor has one
if (SettingsCurrent.Get())
if (SettingsCurrent.IsValid())
{
MaxLinearHardSnapDistance = SettingsCurrent.Get()->DefaultReplicationSettings.GetMaxLinearHardSnapDistance(MaxLinearHardSnapDistance);
bHardsnapLegacyInPT = SettingsCurrent.Get()->DefaultReplicationSettings.GetHardsnapDefaultLegacyInPT();
bCorrectConnectedBodies = SettingsCurrent.Get()->DefaultReplicationSettings.GetCorrectConnectedBodies();
bCorrectConnectedBodiesFriction = SettingsCurrent.Get()->DefaultReplicationSettings.GetCorrectConnectedBodiesFriction();
const FNetworkPhysicsSettingsData& SettingsData = SettingsCurrent.Pin()->GetSettings();
MaxLinearHardSnapDistance = SettingsData.DefaultReplicationSettings.GetMaxLinearHardSnapDistance(MaxLinearHardSnapDistance);
bHardsnapLegacyInPT = SettingsData.DefaultReplicationSettings.GetHardsnapDefaultLegacyInPT();
bCorrectConnectedBodies = SettingsData.DefaultReplicationSettings.GetCorrectConnectedBodies();
bCorrectConnectedBodiesFriction = SettingsData.DefaultReplicationSettings.GetCorrectConnectedBodiesFriction();
}
// Get Current state
@ -1042,19 +1050,19 @@ void FPhysicsReplicationAsyncVR::OnPhysicsObjectUnregistered_Internal(Chaos::FCo
ObjectToSettings.Remove(PhysicsObject);
}
void FPhysicsReplicationAsyncVR::RegisterSettings(Chaos::FConstPhysicsObjectHandle PhysicsObject, FNetworkPhysicsSettingsAsync InSettings)
void FPhysicsReplicationAsyncVR::RegisterSettings(Chaos::FConstPhysicsObjectHandle PhysicsObject, TWeakPtr<const FNetworkPhysicsSettingsData> InSettings)
{
if (PhysicsObject != nullptr)
{
FNetworkPhysicsSettingsAsync& Settings = ObjectToSettings.FindOrAdd(PhysicsObject);
TWeakPtr<const FNetworkPhysicsSettingsData>& Settings = ObjectToSettings.FindOrAdd(PhysicsObject);
Settings = InSettings;
}
}
void FPhysicsReplicationAsyncVR::FetchObjectSettings(Chaos::FConstPhysicsObjectHandle PhysicsObject)
{
FNetworkPhysicsSettingsAsync* CustomSettings = ObjectToSettings.Find(PhysicsObject);
SettingsCurrent = (CustomSettings != nullptr) ? *CustomSettings : SettingsDefault;
TWeakPtr<const FNetworkPhysicsSettingsData>* CustomSettings = ObjectToSettings.Find(PhysicsObject);
SettingsCurrent = (CustomSettings && (*CustomSettings).IsValid()) ? *(*CustomSettings).Pin().Get() : SettingsDefault;
}
void FPhysicsReplicationAsyncVR::OnPostInitialize_Internal()
@ -1080,6 +1088,8 @@ void FPhysicsReplicationAsyncVR::OnPreSimulate_Internal()
Chaos::FPBDRigidsSolver* RigidsSolver = static_cast<Chaos::FPBDRigidsSolver*>(GetSolver());
check(RigidsSolver);
ResimErrorLogTimer += RigidsSolver->GetAsyncDeltaTime();
// Early out if this is a resim frame
Chaos::FRewindData* RewindData = RigidsSolver->GetRewindData();
const bool bRewindDataExist = RewindData != nullptr;
@ -1215,7 +1225,7 @@ void FPhysicsReplicationAsyncVR::UpdateRewindDataTarget(const FPhysicsRepAsyncIn
{
// Cache all target states inside RewindData
const int32 LocalFrame = Input.ServerFrame - *Input.FrameOffset;
RewindData->SetTargetStateAtFrame(*Handle, LocalFrame, Chaos::FFrameAndPhase::EParticleHistoryPhase::PostPushData,
RewindData->SetTargetStateAtFrame(*Handle, LocalFrame, Chaos::FFrameAndPhase::EParticleHistoryPhase::PrePushData,
Input.TargetState.Position, Input.TargetState.Quaternion,
Input.TargetState.LinVel, FMath::DegreesToRadians(Input.TargetState.AngVel), (Input.TargetState.Flags & ERigidBodyFlags::Sleeping));
}
@ -1565,8 +1575,28 @@ void FPhysicsReplicationAsyncVR::CheckTargetResimValidity(FReplicatedPhysicsTarg
Target.RepMode = EPhysicsReplicationMode::PredictiveInterpolation;
}
UE_LOG(LogPhysics, Warning, TEXT("FPhysicsReplication received target frame (%d) out of rewind data bounds (%d, %d) - %s - Target will use EPhysicsReplicationMode: %s"),
LocalFrame, RewindData->GetEarliestFrame_Internal(), RewindData->CurrentFrame(), (LocalFrame < RewindData->GetEarliestFrame_Internal()) ? TEXT("Client is far ahead of the server, server might be dropping frames.") : TEXT("Client is behind the server, client might be dropping frames."), *UEnum::GetValueAsString(Target.RepMode));
if (ResimOutOfBoundsCounter == 0)
{
UE_LOG(LogPhysics, Warning, TEXT("FPhysicsReplication DESYNCED - received target frame (%d) out of rewind data bounds (%d, %d) - %s - Target will use %s")
, LocalFrame, RewindData->GetEarliestFrame_Internal(), RewindData->CurrentFrame()
, (LocalFrame < RewindData->GetEarliestFrame_Internal())
? TEXT("Client is far ahead of the server, server might be dropping frames.")
: TEXT("Client is behind the server, client might be dropping frames."), *UEnum::GetValueAsString(Target.RepMode));
}
ResimOutOfBoundsCounter++;
ResimErrorLogTimer = 0;
}
else
{
static const auto CVarLogOutOfBoundsTimeLimit = IConsoleManager::Get().FindConsoleVariable(TEXT("np2.Resim.LogOutOfBoundsTimeLimit"));
if (ResimOutOfBoundsCounter > 0 && ResimErrorLogTimer > CVarLogOutOfBoundsTimeLimit->GetFloat())
{
UE_LOG(LogPhysics, Log, TEXT("FPhysicsReplication IN-SYNC - Received targets have now been within rewind data bounds again for at least %f seconds"), ResimErrorLogTimer);
ResimOutOfBoundsCounter = 0;
}
}
}
@ -2429,7 +2459,7 @@ bool FPhysicsReplicationAsyncVR::ResimulationReplication(Chaos::FPBDRigidParticl
const bool bShouldSleep = (Target.TargetState.Flags & ERigidBodyFlags::Sleeping) != 0;
bool bClearTarget = true;
static constexpr Chaos::FFrameAndPhase::EParticleHistoryPhase RewindPhase = Chaos::FFrameAndPhase::EParticleHistoryPhase::PostPushData;
static constexpr Chaos::FFrameAndPhase::EParticleHistoryPhase RewindPhase = Chaos::FFrameAndPhase::EParticleHistoryPhase::PrePushData;
// Get state from locally cached history for frame corresponding to received data
const Chaos::FGeometryParticleState PastState = RewindData->GetPastStateAtFrame(*Handle, LocalFrame, RewindPhase);
@ -2515,12 +2545,15 @@ bool FPhysicsReplicationAsyncVR::ResimulationReplication(Chaos::FPBDRigidParticl
static const auto CVarResimDrawDebug = IConsoleManager::Get().FindConsoleVariable(TEXT("np2.Resim.DrawDebug"));
static const auto CVarRenderInterpDebugDrawResimTrigger = IConsoleManager::Get().FindConsoleVariable(TEXT("p.RenderInterp.DebugDraw.ResimTrigger"));
static const auto CVarRenderInterpDebugDrawResimBoxScale = IConsoleManager::Get().FindConsoleVariable(TEXT("p.RenderInterp.DebugDraw.ResimBoxScale"));
if (CVarResimDrawDebug->GetBool() || CVarRenderInterpDebugDrawResimTrigger->GetBool())
{
if (bShouldTriggerResim)
{
FVector Box = CVarRenderInterpDebugDrawResimTrigger->GetBool() ? FVector(6, 3, 2) : FVector(40, 20, 10);
const float DrawThickness = CVarRenderInterpDebugDrawResimTrigger->GetBool() ? 0.5f : 1.5f;
Box *= CVarRenderInterpDebugDrawResimBoxScale->GetFloat();
const float DrawThickness = (CVarRenderInterpDebugDrawResimTrigger->GetBool() ? 0.5f : 1.5f) * CVarRenderInterpDebugDrawResimBoxScale->GetFloat();
static const auto CVarDebugdrawLifetime = IConsoleManager::Get().FindConsoleVariable(TEXT("p.Net.DebugDraw.LifeTime"));
if (CVarRenderInterpDebugDrawResimTrigger->GetBool()) // Resim debug draw extension for render interpolation

View file

@ -152,7 +152,7 @@ AGrippableSkeletalMeshActor::AGrippableSkeletalMeshActor(const FObjectInitialize
// #TODO we can register them maybe in the future
// Don't use the replicated list, use our custom replication instead
bReplicateUsingRegisteredSubObjectList = false;
bReplicateUsingRegisteredSubObjectList = true;
bAllowIgnoringAttachOnOwner = true;
@ -857,22 +857,6 @@ void AGrippableSkeletalMeshActor::BeginDestroy()
GripLogicScripts.Empty();
}
void AGrippableSkeletalMeshActor::GetSubobjectsWithStableNamesForNetworking(TArray<UObject*>& ObjList)
{
Super::GetSubobjectsWithStableNamesForNetworking(ObjList);
if (bReplicateGripScripts)
{
for (int32 i = 0; i < GripLogicScripts.Num(); ++i)
{
if (UObject* SubObject = GripLogicScripts[i])
{
ObjList.Add(SubObject);
}
}
}
}
/////////////////////////////////////////////////
//- Push networking getter / setter functions
/////////////////////////////////////////////////

View file

@ -41,7 +41,7 @@ UGrippableSkeletalMeshComponent::UGrippableSkeletalMeshComponent(const FObjectIn
// #TODO we can register them maybe in the future
// Don't use the replicated list, use our custom replication instead
bReplicateUsingRegisteredSubObjectList = false;
bReplicateUsingRegisteredSubObjectList = true;
}
void UGrippableSkeletalMeshComponent::GetLifetimeReplicatedProps(TArray< class FLifetimeProperty > & OutLifetimeProps) const

View file

@ -40,7 +40,7 @@ UGrippableSphereComponent::UGrippableSphereComponent(const FObjectInitializer& O
// #TODO we can register them maybe in the future
// Don't use the replicated list, use our custom replication instead
bReplicateUsingRegisteredSubObjectList = false;
bReplicateUsingRegisteredSubObjectList = true;
}
void UGrippableSphereComponent::GetLifetimeReplicatedProps(TArray< class FLifetimeProperty > & OutLifetimeProps) const

View file

@ -97,7 +97,7 @@ AGrippableStaticMeshActor::AGrippableStaticMeshActor(const FObjectInitializer& O
// #TODO we can register them maybe in the future
// Don't use the replicated list, use our custom replication instead
bReplicateUsingRegisteredSubObjectList = false;
bReplicateUsingRegisteredSubObjectList = true;
bAllowIgnoringAttachOnOwner = true;
@ -801,22 +801,6 @@ void AGrippableStaticMeshActor::BeginDestroy()
GripLogicScripts.Empty();
}
void AGrippableStaticMeshActor::GetSubobjectsWithStableNamesForNetworking(TArray<UObject*>& ObjList)
{
Super::GetSubobjectsWithStableNamesForNetworking(ObjList);
if (bReplicateGripScripts)
{
for (int32 i = 0; i < GripLogicScripts.Num(); ++i)
{
if (UObject* SubObject = GripLogicScripts[i])
{
ObjList.Add(SubObject);
}
}
}
}
/////////////////////////////////////////////////
//- Push networking getter / setter functions
/////////////////////////////////////////////////

View file

@ -39,7 +39,7 @@ UGrippableStaticMeshComponent::UGrippableStaticMeshComponent(const FObjectInitia
// #TODO we can register them maybe in the future
// Don't use the replicated list, use our custom replication instead
bReplicateUsingRegisteredSubObjectList = false;
bReplicateUsingRegisteredSubObjectList = true;
}
void UGrippableStaticMeshComponent::GetLifetimeReplicatedProps(TArray< class FLifetimeProperty > & OutLifetimeProps) const

View file

@ -399,7 +399,9 @@ void UVRDialComponent::GetGripStiffnessAndDamping_Implementation(float &GripStif
FBPAdvGripSettings UVRDialComponent::AdvancedGripSettings_Implementation()
{
return FBPAdvGripSettings(GripPriority);
FBPAdvGripSettings GripSettings(GripPriority);
GripSettings.bDisallowSettingPositionOnClientAuthDrop = true;
return GripSettings;
}
float UVRDialComponent::GripBreakDistance_Implementation()

View file

@ -541,7 +541,9 @@ void UVRLeverComponent::GetGripStiffnessAndDamping_Implementation(float &GripSti
FBPAdvGripSettings UVRLeverComponent::AdvancedGripSettings_Implementation()
{
return FBPAdvGripSettings(GripPriority);
FBPAdvGripSettings GripSettings(GripPriority);
GripSettings.bDisallowSettingPositionOnClientAuthDrop = true;
return GripSettings;
}
float UVRLeverComponent::GripBreakDistance_Implementation()

View file

@ -514,7 +514,9 @@ void UVRMountComponent::GetGripStiffnessAndDamping_Implementation(float &GripSti
FBPAdvGripSettings UVRMountComponent::AdvancedGripSettings_Implementation()
{
return FBPAdvGripSettings(GripPriority);
FBPAdvGripSettings GripSettings(GripPriority);
GripSettings.bDisallowSettingPositionOnClientAuthDrop = true;
return GripSettings;
}
float UVRMountComponent::GripBreakDistance_Implementation()

View file

@ -644,7 +644,9 @@ void UVRSliderComponent::GetGripStiffnessAndDamping_Implementation(float &GripSt
FBPAdvGripSettings UVRSliderComponent::AdvancedGripSettings_Implementation()
{
return FBPAdvGripSettings(GripPriority);
FBPAdvGripSettings GripSettings(GripPriority);
GripSettings.bDisallowSettingPositionOnClientAuthDrop = true;
return GripSettings;
}
float UVRSliderComponent::GripBreakDistance_Implementation()

View file

@ -25,6 +25,19 @@
#include "Materials/Material.h"
#include "Net/UnrealNetwork.h"
// Iris
#include "Serializers/SerializerHelpers.h"
#include "Iris/Serialization/ObjectNetSerializer.h"
#include "Iris/Core/NetObjectReference.h"
#include "Iris/Serialization/NetSerializerDelegates.h"
#include "Iris/Serialization/NetSerializers.h"
#include "Iris/ReplicationState/PropertyNetSerializerInfoRegistry.h"
#include "Iris/ReplicationState/ReplicationStateDescriptorBuilder.h"
#include "Templates/IsPODType.h"
#include "Iris/Serialization/SoftObjectNetSerializers.h"
#include "Iris/Serialization/NetSerializerArrayStorage.h"
namespace RLE_Funcs
{
enum RLE_Flags
@ -1741,7 +1754,6 @@ bool FRenderManagerOperation::NetSerialize(FArchive& Ar, class UPackageMap* Map,
{
bOutSuccess = true;
Ar.SerializeIntPacked(OwnerID);
Ar.SerializeBits(&OperationType, 3);
@ -1813,3 +1825,725 @@ bool FRenderManagerOperation::NetSerialize(FArchive& Ar, class UPackageMap* Map,
return bOutSuccess;
}
// SERIALIZER SETUP FOR IRIS
namespace UE::Net
{
struct alignas(8) FQuantizedRenderTargetTextureStoreData
{
uint32 Width;
uint32 Height;
uint32 bIsZipped;
// I don't think people are going to go over the 512 default generally, and def shouldn't hit 2048
// We'll accept the slightly larger memory usage as this isn't a common operation
//static constexpr uint32 MaxBlobStorage = 2048;
//, AllocationPolicies::TInlinedElementAllocationPolicy<MaxBlobStorage>
typedef FNetSerializerArrayStorage<uint8> DataStorage;
DataStorage Data;
};
}
template <> struct TIsPODType<UE::Net::FQuantizedRenderTargetTextureStoreData> { enum { Value = true }; };
namespace UE::Net
{
// -----------------------------------------------------------------------------
// Iris serializer for FBPVRReplicatedTextureStore
// -----------------------------------------------------------------------------
struct FBPVRReplicatedTextureStoreNetSerializer
{
// Version is required.
static constexpr uint32 Version = 0;
//Set to false when a same value delta compression method is undesirable, for example when the serializer only writes a single bit for the state.
static constexpr bool bUseDefaultDelta = true;
// Not doing delta, the majority of the time a single bit (bool) controls the serialization of the entirity
static constexpr bool bHasDynamicState = true;
class FNetSerializerRegistryDelegates final : private UE::Net::FNetSerializerRegistryDelegates
{
public:
virtual ~FNetSerializerRegistryDelegates();
private:
virtual void OnPreFreezeNetSerializerRegistry() override;
//virtual void OnPostFreezeNetSerializerRegistry() override;
};
inline static FBPVRReplicatedTextureStoreNetSerializer::FNetSerializerRegistryDelegates NetSerializerRegistryDelegates;
typedef FBPVRReplicatedTextureStore SourceType;
typedef FQuantizedRenderTargetTextureStoreData QuantizedType;
typedef FBPVRReplicatedTextureStoreSerializerConfig ConfigType;
inline static const ConfigType DefaultConfig;
// Called to create a "quantized snapshot" of the struct
static void Quantize(FNetSerializationContext& Context, const FNetQuantizeArgs& Args)
{
// Actually do the real quantization step here next instead of just in serialize, will save on memory overall
const SourceType& Source = *reinterpret_cast<const SourceType*>(Args.Source);
QuantizedType& Target = *reinterpret_cast<QuantizedType*>(Args.Target);
Target.bIsZipped = Source.bIsZipped ? 1 : 0;
Target.Width = Source.Width;
Target.Height = Source.Height;
Target.Data.AdjustSize(Context, Source.PackedData.Num());
if (Target.Data.Num() > 0)
{
FMemory::Memcpy(Target.Data.GetData(), Source.PackedData.GetData(), Target.Data.Num());
}
}
// Called to apply the quantized snapshot back to gameplay memory
static void Dequantize(FNetSerializationContext& Context, const FNetDequantizeArgs& Args)
{
const QuantizedType& Source = *reinterpret_cast<const QuantizedType*>(Args.Source);
SourceType& Target = *reinterpret_cast<SourceType*>(Args.Target);
Target.bIsZipped = Source.bIsZipped != 0;
Target.Width = Source.Width;
Target.Height = Source.Height;
//Target.Size = Source.PackedData.Num();
Target.PackedData.Reset(Source.Data.Num());
if (Source.Data.Num() > 0)
{
Target.PackedData.AddUninitialized(Source.Data.Num());
FMemory::Memcpy(Target.PackedData.GetData(), Source.Data.GetData(), Source.Data.Num());
}
}
// Serialize into bitstream
static void Serialize(FNetSerializationContext& Context, const FNetSerializeArgs& Args)
{
const QuantizedType& Source = *reinterpret_cast<const QuantizedType*>(Args.Source);
FNetBitStreamWriter* Writer = Context.GetBitStreamWriter();
Writer->WriteBits(Source.bIsZipped, 1);
Writer->WriteBits(Source.Width, 32);
Writer->WriteBits(Source.Height, 32);
// Write array size first
uint32 Size = Source.Data.Num();
Writer->WriteBits(Size, 32);
if (Size > 0)
{
Writer->WriteBitStream((uint32*)Source.Data.GetData(), 0, Source.Data.Num() * 8);
}
}
// Deserialize from bitstream
static void Deserialize(FNetSerializationContext& Context, const FNetDeserializeArgs& Args)
{
QuantizedType& Target = *reinterpret_cast<QuantizedType*>(Args.Target);
FNetBitStreamReader* Reader = Context.GetBitStreamReader();
Target.bIsZipped = Reader->ReadBits(1);
Target.Width = Reader->ReadBits(32);
Target.Height = Reader->ReadBits(32);
uint32 Size = Reader->ReadBits(32);
Target.Data.AdjustSize(Context, Size);
if (Size > 0)
{
Reader->ReadBitStream((uint32*)Target.Data.GetData(), Target.Data.Num() * 8);
}
}
// Compare two instances to see if they differ
static bool IsEqual(FNetSerializationContext& Context, const FNetIsEqualArgs& Args)
{
// This struct is never stored, it is only packed and sent once to catch a client up
return false;
}
static void CloneDynamicState(FNetSerializationContext& Context, const FNetCloneDynamicStateArgs& Args)
{
const QuantizedType* Source = reinterpret_cast<const QuantizedType*>(Args.Source);
QuantizedType* Target = reinterpret_cast<QuantizedType*>(Args.Target);
// copy small fields
Target->bIsZipped = Source->bIsZipped;
Target->Width = Source->Width;
Target->Height = Source->Height;
Target->Data.Clone(Context, Source->Data);
}
static void FreeDynamicState(FNetSerializationContext& Context, const FNetFreeDynamicStateArgs& Args)
{
QuantizedType& Target = *reinterpret_cast<QuantizedType*>(Args.Source);
Target.Data.Free(Context);
}
};
}
namespace UE::Net
{
struct alignas(8) FQuantizedTriData
{
uint16 P1[2];
uint16 P2[2];
uint16 P3[2];
};
// TODO check on this if it changes over time.....
// Really needs to be a macro for getting struct sizes outside of the private files....
struct FQuantizedSoftObjectRef
{
alignas(8) uint8 Storage[64]; // Iris FSoftObjectNetSerializerQuantizedType is 48 bytes
// Keeping some overhead
};
struct alignas(8) FQuantizedRenderManagerOperationData
{
uint32 OwnerID;
uint8 OperationType;
uint32 Color;
uint16 P1[2];
uint16 P2[2];
uint32 Thickness;
typedef FNetSerializerArrayStorage<FQuantizedTriData> TriStorage;
TriStorage Tris;
//String data
FQuantizedSoftObjectRef Texture;
FQuantizedSoftObjectRef Material;
};
}
template <> struct TIsPODType<UE::Net::FQuantizedRenderManagerOperationData> { enum { Value = true }; };
namespace UE::Net
{
// -----------------------------------------------------------------------------
// Iris serializer for FRenderManagerOperation
// -----------------------------------------------------------------------------
struct FRenderManagerOperationNetSerializer
{
inline static const FSoftObjectNetSerializerConfig ObjectPtrNetSerializerConfig;
inline static const FNetSerializerConfig* FObjectPtrSerializerConfigPtr = &ObjectPtrNetSerializerConfig;
inline static const FNetSerializer* FObjectPtrNetSerializerPtr;
// Version is required.
static constexpr uint32 Version = 0;
//Set to false when a same value delta compression method is undesirable, for example when the serializer only writes a single bit for the state.
static constexpr bool bUseDefaultDelta = true;
// Not doing delta, the majority of the time a single bit (bool) controls the serialization of the entirity
static constexpr bool bHasDynamicState = true;
class FNetSerializerRegistryDelegates final : private UE::Net::FNetSerializerRegistryDelegates
{
public:
virtual ~FNetSerializerRegistryDelegates();
void InitNetSerializer()
{
FRenderManagerOperationNetSerializer::FObjectPtrNetSerializerPtr = &UE_NET_GET_SERIALIZER(FSoftObjectNetSerializer);
}
private:
virtual void OnPreFreezeNetSerializerRegistry() override;
//virtual void OnPostFreezeNetSerializerRegistry() override;
};
inline static FRenderManagerOperationNetSerializer::FNetSerializerRegistryDelegates NetSerializerRegistryDelegates;
typedef FRenderManagerOperation SourceType;
typedef FQuantizedRenderManagerOperationData QuantizedType;
typedef FRenderManagerOperationSerializerConfig ConfigType;
inline static const ConfigType DefaultConfig;
// Called to create a "quantized snapshot" of the struct
static void Quantize(FNetSerializationContext& Context, const FNetQuantizeArgs& Args)
{
// Actually do the real quantization step here next instead of just in serialize, will save on memory overall
const SourceType& Source = *reinterpret_cast<const SourceType*>(Args.Source);
QuantizedType& Target = *reinterpret_cast<QuantizedType*>(Args.Target);
Target.OwnerID = Source.OwnerID;
Target.OperationType = (uint8)Source.OperationType;
switch ((ERenderManagerOperationType)Source.OperationType)
{
case ERenderManagerOperationType::Op_LineDraw:
{
// Color
uint8* ColorPtr = ((uint8*)&Target.Color);
ColorPtr[0] = Source.Color.R;
ColorPtr[1] = Source.Color.G;
ColorPtr[2] = Source.Color.B;
ColorPtr[3] = Source.Color.A;
// Thickness
Target.Thickness = Source.Thickness;
//P 1 1, 20
//P 2 2, 20
Target.P1[0] = (uint16)GetCompressedFloat<10000, 16>(Source.P1.X);
Target.P1[1] = (uint16)GetCompressedFloat<10000, 16>(Source.P1.Y);
Target.P2[0] = (uint16)GetCompressedFloat<10000, 16>(Source.P2.X);
Target.P2[1] = (uint16)GetCompressedFloat<10000, 16>(Source.P2.Y);
}break;
case ERenderManagerOperationType::Op_TexDraw:
{
// Texture
const FNetSerializer* ObjSerializer = FObjectPtrNetSerializerPtr;
const FNetSerializerConfig* ObjSerializerConfig = FObjectPtrSerializerConfigPtr;
//Target.Texture
FNetQuantizeArgs MemberArgsObj = Args;
MemberArgsObj.NetSerializerConfig = NetSerializerConfigParam(ObjSerializerConfig);
MemberArgsObj.Source = NetSerializerValuePointer(&Source.Texture);
MemberArgsObj.Target = NetSerializerValuePointer(&Target.Texture);
ObjSerializer->Quantize(Context, MemberArgsObj);
// P1 1, 20
Target.P1[0] = (uint16)GetCompressedFloat<10000, 16>(Source.P1.X);
Target.P1[1] = (uint16)GetCompressedFloat<10000, 16>(Source.P1.Y);
}break;
case ERenderManagerOperationType::Op_TriDraw:
{
// Color
uint8* ColorPtr = ((uint8*)&Target.Color);
ColorPtr[0] = Source.Color.R;
ColorPtr[1] = Source.Color.G;
ColorPtr[2] = Source.Color.B;
ColorPtr[3] = Source.Color.A;
// Material
const FNetSerializer* ObjSerializer = FObjectPtrNetSerializerPtr;
const FNetSerializerConfig* ObjSerializerConfig = FObjectPtrSerializerConfigPtr;
//Target.Material
FNetQuantizeArgs MemberArgsObj = Args;
MemberArgsObj.NetSerializerConfig = NetSerializerConfigParam(ObjSerializerConfig);
MemberArgsObj.Source = NetSerializerValuePointer(&Source.Material);
MemberArgsObj.Target = NetSerializerValuePointer(&Target.Material);
ObjSerializer->Quantize(Context, MemberArgsObj);
// Tris
Target.Tris.AdjustSize(Context, Source.Tris.Num());
FQuantizedTriData* TargetTriData = Target.Tris.GetData();
if (Target.Tris.Num() > 0)
{
for (uint32 i = 0; i < Target.Tris.Num(); ++i)
{
TargetTriData[i].P1[0] = (uint16)GetCompressedFloat<10000, 16>(Source.Tris[i].P1.X);
TargetTriData[i].P1[1] = (uint16)GetCompressedFloat<10000, 16>(Source.Tris[i].P1.Y);
TargetTriData[i].P2[0] = (uint16)GetCompressedFloat<10000, 16>(Source.Tris[i].P2.X);
TargetTriData[i].P2[1] = (uint16)GetCompressedFloat<10000, 16>(Source.Tris[i].P2.Y);
TargetTriData[i].P3[0] = (uint16)GetCompressedFloat<10000, 16>(Source.Tris[i].P3.X);
TargetTriData[i].P3[1] = (uint16)GetCompressedFloat<10000, 16>(Source.Tris[i].P3.Y);
}
}
}break;
}
}
// Called to apply the quantized snapshot back to gameplay memory
static void Dequantize(FNetSerializationContext& Context, const FNetDequantizeArgs& Args)
{
const QuantizedType& Source = *reinterpret_cast<const QuantizedType*>(Args.Source);
SourceType& Target = *reinterpret_cast<SourceType*>(Args.Target);
Target.OwnerID = Source.OwnerID;
Target.OperationType = (ERenderManagerOperationType)Source.OperationType;
switch ((ERenderManagerOperationType)Source.OperationType)
{
case ERenderManagerOperationType::Op_LineDraw:
{
// Color
uint8* ColorPtr = ((uint8*)&Source.Color);
Target.Color.R = ColorPtr[0];
Target.Color.G = ColorPtr[1];
Target.Color.B = ColorPtr[2];
Target.Color.A = ColorPtr[3];
// Thickness
Target.Thickness = Source.Thickness;
//P 1 1, 20
//P 2 2, 20
Target.P1.X = GetDecompressedFloat<10000, 16>(Source.P1[0]);
Target.P1.Y = GetDecompressedFloat<10000, 16>(Source.P1[1]);
Target.P2.X = GetDecompressedFloat<10000, 16>(Source.P2[0]);
Target.P2.Y = GetDecompressedFloat<10000, 16>(Source.P2[1]);
}break;
case ERenderManagerOperationType::Op_TexDraw:
{
// Texture
const FNetSerializer* ObjSerializer = FObjectPtrNetSerializerPtr;
const FNetSerializerConfig* ObjSerializerConfig = FObjectPtrSerializerConfigPtr;
//Target.Texture
FNetDequantizeArgs MemberArgsObj = Args;
MemberArgsObj.NetSerializerConfig = NetSerializerConfigParam(ObjSerializerConfig);
MemberArgsObj.Source = NetSerializerValuePointer(&Source.Texture);
MemberArgsObj.Target = NetSerializerValuePointer(&Target.Texture);
ObjSerializer->Dequantize(Context, MemberArgsObj);
// P1 1, 20
Target.P1.X = GetDecompressedFloat<10000, 16>(Source.P1[0]);
Target.P1.Y = GetDecompressedFloat<10000, 16>(Source.P1[1]);
}break;
case ERenderManagerOperationType::Op_TriDraw:
{
// Color
uint8* ColorPtr = ((uint8*)&Source.Color);
Target.Color.R = ColorPtr[0];
Target.Color.G = ColorPtr[1];
Target.Color.B = ColorPtr[2];
Target.Color.A = ColorPtr[3];
// Material
const FNetSerializer* ObjSerializer = FObjectPtrNetSerializerPtr;
const FNetSerializerConfig* ObjSerializerConfig = FObjectPtrSerializerConfigPtr;
//Target.Material
FNetDequantizeArgs MemberArgsObj = Args;
MemberArgsObj.NetSerializerConfig = NetSerializerConfigParam(ObjSerializerConfig);
MemberArgsObj.Source = NetSerializerValuePointer(&Source.Material);
MemberArgsObj.Target = NetSerializerValuePointer(&Target.Material);
ObjSerializer->Dequantize(Context, MemberArgsObj);
// Tris
Target.Tris.Reset(Source.Tris.Num());
Target.Tris.AddUninitialized(Source.Tris.Num());
const FQuantizedTriData* SourceTriData = Source.Tris.GetData();
if (Target.Tris.Num() > 0)
{
for (int32 i = 0; i < Target.Tris.Num(); ++i)
{
Target.Tris[i].P1.X = GetDecompressedFloat<10000, 16>(SourceTriData[i].P1[0]);
Target.Tris[i].P1.Y = GetDecompressedFloat<10000, 16>(SourceTriData[i].P1[1]);
Target.Tris[i].P2.X = GetDecompressedFloat<10000, 16>(SourceTriData[i].P2[0]);
Target.Tris[i].P2.Y = GetDecompressedFloat<10000, 16>(SourceTriData[i].P2[1]);
Target.Tris[i].P3.X = GetDecompressedFloat<10000, 16>(SourceTriData[i].P3[0]);
Target.Tris[i].P3.Y = GetDecompressedFloat<10000, 16>(SourceTriData[i].P3[1]);
}
}
}break;
}
}
// Serialize into bitstream
static void Serialize(FNetSerializationContext& Context, const FNetSerializeArgs& Args)
{
const QuantizedType& Source = *reinterpret_cast<const QuantizedType*>(Args.Source);
FNetBitStreamWriter* Writer = Context.GetBitStreamWriter();
Writer->WriteBits(Source.OwnerID, 32);
Writer->WriteBits(Source.OperationType, 8);
switch ((ERenderManagerOperationType)Source.OperationType)
{
case ERenderManagerOperationType::Op_LineDraw:
{
// Color
Writer->WriteBits(Source.Color, 32);
// Thickness
Writer->WriteBits(Source.Thickness, 32);
//P 1 1, 20
//P 2 2, 20
if (Writer->WriteBool(Source.P1[0] != 0)) {Writer->WriteBits(Source.P1[0], 16);}
if (Writer->WriteBool(Source.P1[1] != 0)) { Writer->WriteBits(Source.P1[1], 16);}
if (Writer->WriteBool(Source.P2[0] != 0)) { Writer->WriteBits(Source.P2[0], 16);}
if (Writer->WriteBool(Source.P2[1] != 0)) { Writer->WriteBits(Source.P2[1], 16);}
}break;
case ERenderManagerOperationType::Op_TexDraw:
{
// Texture
const FNetSerializer* ObjSerializer = FObjectPtrNetSerializerPtr;
const FNetSerializerConfig* ObjSerializerConfig = FObjectPtrSerializerConfigPtr;
//Target.Texture
FNetSerializeArgs MemberArgsObj = Args;
MemberArgsObj.NetSerializerConfig = NetSerializerConfigParam(ObjSerializerConfig);
MemberArgsObj.Source = NetSerializerValuePointer(&Source.Texture);
ObjSerializer->Serialize(Context, MemberArgsObj);
// P1 1, 20
if (Writer->WriteBool(Source.P1[0] != 0)) { Writer->WriteBits(Source.P1[0], 16); }
if (Writer->WriteBool(Source.P1[1] != 0)) { Writer->WriteBits(Source.P1[1], 16); }
}break;
case ERenderManagerOperationType::Op_TriDraw:
{
// Color
Writer->WriteBits(Source.Color, 32);
// Material
const FNetSerializer* ObjSerializer = FObjectPtrNetSerializerPtr;
const FNetSerializerConfig* ObjSerializerConfig = FObjectPtrSerializerConfigPtr;
//Target.Material
FNetSerializeArgs MemberArgsObj = Args;
MemberArgsObj.NetSerializerConfig = NetSerializerConfigParam(ObjSerializerConfig);
MemberArgsObj.Source = NetSerializerValuePointer(&Source.Material);
ObjSerializer->Serialize(Context, MemberArgsObj);
// Tris
const FQuantizedTriData* TargetTriData = Source.Tris.GetData();
uint32 TriSize = Source.Tris.Num();
Writer->WriteBits(TriSize, 32);
if (Source.Tris.Num() > 0)
{
for (uint32 i = 0; i < Source.Tris.Num(); ++i)
{
Writer->WriteBits(TargetTriData[i].P1[0], 16);
Writer->WriteBits(TargetTriData[i].P1[1], 16);
Writer->WriteBits(TargetTriData[i].P2[0], 16);
Writer->WriteBits(TargetTriData[i].P2[1], 16);
Writer->WriteBits(TargetTriData[i].P3[0], 16);
Writer->WriteBits(TargetTriData[i].P3[1], 16);
}
//Writer->WriteBitStream((uint32*)TargetTriData, 0, (Source.Tris.Num() * sizeof(FQuantizedTriData)) * 8);
}
}break;
}
}
// Deserialize from bitstream
static void Deserialize(FNetSerializationContext& Context, const FNetDeserializeArgs& Args)
{
QuantizedType& Target = *reinterpret_cast<QuantizedType*>(Args.Target);
FNetBitStreamReader* Reader = Context.GetBitStreamReader();
Target.OwnerID = Reader->ReadBits(32);
Target.OperationType = Reader->ReadBits(8);
switch ((ERenderManagerOperationType)Target.OperationType)
{
case ERenderManagerOperationType::Op_LineDraw:
{
// Color
Target.Color = Reader->ReadBits(32);
// Thickness
Target.Thickness = Reader->ReadBits(32);
//P 1 1, 20
//P 2 2, 20
Target.P1[0] = Reader->ReadBool() ? Reader->ReadBits(16) : 0;
Target.P1[1] = Reader->ReadBool() ? Reader->ReadBits(16) : 0;
Target.P2[0] = Reader->ReadBool() ? Reader->ReadBits(16) : 0;
Target.P2[1] = Reader->ReadBool() ? Reader->ReadBits(16) : 0;
}break;
case ERenderManagerOperationType::Op_TexDraw:
{
// Texture
const FNetSerializer* ObjSerializer = FObjectPtrNetSerializerPtr;
const FNetSerializerConfig* ObjSerializerConfig = FObjectPtrSerializerConfigPtr;
//Target.Texture
FNetDeserializeArgs MemberArgsObj = Args;
MemberArgsObj.NetSerializerConfig = NetSerializerConfigParam(ObjSerializerConfig);
MemberArgsObj.Target = NetSerializerValuePointer(&Target.Texture);
ObjSerializer->Deserialize(Context, MemberArgsObj);
// P1 1, 20
Target.P1[0] = Reader->ReadBool() ? Reader->ReadBits(16) : 0;
Target.P1[1] = Reader->ReadBool() ? Reader->ReadBits(16) : 0;
}break;
case ERenderManagerOperationType::Op_TriDraw:
{
// Color
Target.Color = Reader->ReadBits(32);
// Material
const FNetSerializer* ObjSerializer = FObjectPtrNetSerializerPtr;
const FNetSerializerConfig* ObjSerializerConfig = FObjectPtrSerializerConfigPtr;
//Target.Material
FNetDeserializeArgs MemberArgsObj = Args;
MemberArgsObj.NetSerializerConfig = NetSerializerConfigParam(ObjSerializerConfig);
MemberArgsObj.Target = NetSerializerValuePointer(&Target.Material);
ObjSerializer->Deserialize(Context, MemberArgsObj);
// Tris
uint32 TriSize = Reader->ReadBits(32);
Target.Tris.AdjustSize(Context, TriSize);
FQuantizedTriData* TargetTriData = Target.Tris.GetData();
if (Target.Tris.Num() > 0)
{
for (uint32 i = 0; i < TriSize; ++i)
{
TargetTriData[i].P1[0] = Reader->ReadBits(16);
TargetTriData[i].P1[1] = Reader->ReadBits(16);
TargetTriData[i].P2[0] = Reader->ReadBits(16);
TargetTriData[i].P2[1] = Reader->ReadBits(16);
TargetTriData[i].P3[0] = Reader->ReadBits(16);
TargetTriData[i].P3[1] = Reader->ReadBits(16);
}
//Reader->ReadBitStream((uint32*)&TargetTriData, (TriSize * sizeof(FQuantizedTriData)) * 8);
}
}break;
}
}
// Compare two instances to see if they differ
static bool IsEqual(FNetSerializationContext& Context, const FNetIsEqualArgs& Args)
{
// This struct is never stored, it is only packed and sent once to catch a client up
return false;
}
static void CloneDynamicState(FNetSerializationContext& Context, const FNetCloneDynamicStateArgs& Args)
{
const QuantizedType* Source = reinterpret_cast<const QuantizedType*>(Args.Source);
QuantizedType* Target = reinterpret_cast<QuantizedType*>(Args.Target);
Target->OwnerID = Source->OwnerID;
Target->OperationType = Source->OperationType;
switch ((ERenderManagerOperationType)Target->OperationType)
{
case ERenderManagerOperationType::Op_LineDraw:
{
// Color
Target->Color = Source->Color;
// Thickness
Target->Thickness = Source->Thickness;
//P 1 1, 20
//P 2 2, 20
Target->P1[0] = Source->P1[0];
Target->P1[1] = Source->P1[1];
Target->P2[0] = Source->P2[0];
Target->P2[1] = Source->P2[1];
}break;
case ERenderManagerOperationType::Op_TexDraw:
{
// Texture
const FNetSerializer* ObjSerializer = FObjectPtrNetSerializerPtr;
const FNetSerializerConfig* ObjSerializerConfig = FObjectPtrSerializerConfigPtr;
FNetCloneDynamicStateArgs ObjMemberArgs = Args;
ObjMemberArgs.NetSerializerConfig = NetSerializerConfigParam(ObjSerializerConfig);
ObjMemberArgs.Target = NetSerializerValuePointer(&Target->Texture);
ObjMemberArgs.Source = NetSerializerValuePointer(&Source->Texture);
ObjSerializer->CloneDynamicState(Context, ObjMemberArgs);
// P1 1, 20
Target->P1[0] = Source->P1[0];
Target->P1[1] = Source->P1[1];
}break;
case ERenderManagerOperationType::Op_TriDraw:
{
// Color
Target->Color = Source->Color;
// Material
const FNetSerializer* ObjSerializer = FObjectPtrNetSerializerPtr;
const FNetSerializerConfig* ObjSerializerConfig = FObjectPtrSerializerConfigPtr;
FNetCloneDynamicStateArgs ObjMemberArgs = Args;
ObjMemberArgs.NetSerializerConfig = NetSerializerConfigParam(ObjSerializerConfig);
ObjMemberArgs.Target = NetSerializerValuePointer(&Target->Material);
ObjMemberArgs.Source = NetSerializerValuePointer(&Source->Material);
ObjSerializer->CloneDynamicState(Context, ObjMemberArgs);
// Tris
Target->Tris.Clone(Context, Source->Tris);
}break;
}
}
static void FreeDynamicState(FNetSerializationContext& Context, const FNetFreeDynamicStateArgs& Args)
{
QuantizedType& Target = *reinterpret_cast<QuantizedType*>(Args.Source);
Target.Tris.Free(Context);
}
};
static const FName PropertyNetSerializerRegistry_NAME_BPVRReplicatedTextureStore("BPVRReplicatedTextureStore");
UE_NET_IMPLEMENT_NAMED_STRUCT_NETSERIALIZER_INFO(PropertyNetSerializerRegistry_NAME_BPVRReplicatedTextureStore, FBPVRReplicatedTextureStoreNetSerializer);
FBPVRReplicatedTextureStoreNetSerializer::FNetSerializerRegistryDelegates::~FNetSerializerRegistryDelegates()
{
UE_NET_UNREGISTER_NETSERIALIZER_INFO(PropertyNetSerializerRegistry_NAME_BPVRReplicatedTextureStore);
}
void FBPVRReplicatedTextureStoreNetSerializer::FNetSerializerRegistryDelegates::OnPreFreezeNetSerializerRegistry()
{
UE_NET_REGISTER_NETSERIALIZER_INFO(PropertyNetSerializerRegistry_NAME_BPVRReplicatedTextureStore);
}
UE_NET_IMPLEMENT_SERIALIZER(FBPVRReplicatedTextureStoreNetSerializer);
static const FName PropertyNetSerializerRegistry_NAME_RenderManagerOperation("RenderManagerOperation");
UE_NET_IMPLEMENT_NAMED_STRUCT_NETSERIALIZER_INFO(PropertyNetSerializerRegistry_NAME_RenderManagerOperation, FRenderManagerOperationNetSerializer);
FRenderManagerOperationNetSerializer::FNetSerializerRegistryDelegates::~FNetSerializerRegistryDelegates()
{
UE_NET_UNREGISTER_NETSERIALIZER_INFO(PropertyNetSerializerRegistry_NAME_RenderManagerOperation);
}
void FRenderManagerOperationNetSerializer::FNetSerializerRegistryDelegates::OnPreFreezeNetSerializerRegistry()
{
InitNetSerializer();
UE_NET_REGISTER_NETSERIALIZER_INFO(PropertyNetSerializerRegistry_NAME_RenderManagerOperation);
}
UE_NET_IMPLEMENT_SERIALIZER(FRenderManagerOperationNetSerializer);
}

View file

@ -29,7 +29,7 @@ FLayeredMove_VRMovement::FLayeredMove_VRMovement()
DurationMs = -1.0f;
}
bool FLayeredMove_VRMovement::IsFinished(float CurrentSimTimeMs) const
bool FLayeredMove_VRMovement::IsFinished(double CurrentSimTimeMs) const
{
// We never end the VR velocity injection
return false;

View file

@ -0,0 +1,258 @@
#include "Serializers/FBPAdvGripPhysicsSettingsNetSerializer.h"
#include "Serializers/SerializerHelpers.h"
#include "Iris/Serialization/NetSerializerDelegates.h"
#include "Iris/Serialization/NetSerializers.h"
#include "Iris/ReplicationState/PropertyNetSerializerInfoRegistry.h"
#include "Iris/ReplicationState/ReplicationStateDescriptorBuilder.h"
namespace UE::Net
{
// -----------------------------------------------------------------------------
// Iris serializer for FBPAdvGripPhysicsSettings
// -----------------------------------------------------------------------------
struct FBPAdvGripPhysicsSettingsNetSerializer
{
class FNetSerializerRegistryDelegates final : private UE::Net::FNetSerializerRegistryDelegates
{
public:
virtual ~FNetSerializerRegistryDelegates();
private:
virtual void OnPreFreezeNetSerializerRegistry() override;
//virtual void OnPostFreezeNetSerializerRegistry() override;
};
inline static FBPAdvGripPhysicsSettingsNetSerializer::FNetSerializerRegistryDelegates NetSerializerRegistryDelegates;
/** Version is required. */
static constexpr uint32 Version = 0;
struct FQuantizedData
{
uint32 bUsePhysicsSettings : 1;
uint32 PhysicsConstraintType : 1; // This only has two elements
uint32 PhysicsGripLocationSettings : 3; // This only has five states
uint32 bTurnOffGravityDuringGrip : 1;
uint32 bSkipSettingSimulating : 1;
uint32 bUseCustomAngularValues : 1;
uint32 Reserved : 25; // pad out to full 32 bits
// Quantized ranges (0512 with ~0.01 precision)
uint32 LinearMaxForceCoefficient;
uint32 AngularMaxForceCoefficient;
float AngularStiffness;
float AngularDamping;
};
typedef FBPAdvGripPhysicsSettings SourceType;
typedef FQuantizedData QuantizedType;
typedef FBPAdvGripPhysicsSettingsNetSerializerConfig ConfigType;
inline static const ConfigType DefaultConfig;
/** Set to false when a same value delta compression method is undesirable, for example when the serializer only writes a single bit for the state. */
static constexpr bool bUseDefaultDelta = true;
// Not doing delta, the majority of the time a single bit (bool) controls the serialization of the entirity
/**
* Optional. Same as Serialize but where an acked previous state is provided for bitpacking purposes.
* This is implemented by default to do same value optimization, at the cost of a bit. If implemented
* then DeserializeDelta is required.
*/
/*static void SerializeDelta(FNetSerializationContext&, const FNetSerializeDeltaArgs&)
{
}*/
/**
* Optional. Same as Deserialize but where an acked previous state is provided for bitpacking purposes.
* This is implemented by default to do same value optimization, at the cost of a bit. If implemented
* then SerializeDelta is required.
*/
/*static void DeserializeDelta(FNetSerializationContext&, const FNetDeserializeDeltaArgs&)
{
}*/
// Called to create a "quantized snapshot" of the struct
static void Quantize(FNetSerializationContext& Context, const FNetQuantizeArgs& Args)
{
// Actually do the real quantization step here next instead of just in serialize, will save on memory overall
const SourceType& Source = *reinterpret_cast<const SourceType*>(Args.Source);
QuantizedType& Target = *reinterpret_cast<QuantizedType*>(Args.Target);
// Copy flags
Target.bUsePhysicsSettings = Source.bUsePhysicsSettings;
if (Target.bUsePhysicsSettings)
{
Target.PhysicsConstraintType = (uint32)Source.PhysicsConstraintType;
Target.PhysicsGripLocationSettings = (uint32)Source.PhysicsGripLocationSettings;
Target.bTurnOffGravityDuringGrip = Source.bTurnOffGravityDuringGrip;
Target.bSkipSettingSimulating = Source.bSkipSettingSimulating;
// Quantize forces
Target.LinearMaxForceCoefficient = GetCompressedFloat<512, 17>(Source.LinearMaxForceCoefficient);
Target.AngularMaxForceCoefficient = GetCompressedFloat<512, 17>(Source.AngularMaxForceCoefficient);
Target.bUseCustomAngularValues = Source.bUseCustomAngularValues;
if (Target.bUseCustomAngularValues)
{
// Copy angular floats as-is
Target.AngularStiffness = Source.AngularStiffness;
Target.AngularDamping = Source.AngularDamping;
}
}
}
// Called to apply the quantized snapshot back to gameplay memory
static void Dequantize(FNetSerializationContext& Context, const FNetDequantizeArgs& Args)
{
const QuantizedType& Source = *reinterpret_cast<const QuantizedType*>(Args.Source);
SourceType& Target = *reinterpret_cast<SourceType*>(Args.Target);
Target.bUsePhysicsSettings = Source.bUsePhysicsSettings != 0;
if (Source.bUsePhysicsSettings)
{
Target.AngularDamping = Source.AngularDamping;
Target.AngularStiffness = Source.AngularStiffness;
Target.bSkipSettingSimulating = Source.bSkipSettingSimulating != 0;
Target.bTurnOffGravityDuringGrip = Source.bTurnOffGravityDuringGrip != 0;
Target.PhysicsConstraintType = (EPhysicsGripConstraintType)Source.PhysicsConstraintType;
Target.PhysicsGripLocationSettings = (EPhysicsGripCOMType)Source.PhysicsGripLocationSettings;
Target.bUseCustomAngularValues = Source.bUseCustomAngularValues != 0;
if (Target.bUseCustomAngularValues)
{
Target.AngularMaxForceCoefficient = GetDecompressedFloat<512, 17>(Source.AngularMaxForceCoefficient);
Target.LinearMaxForceCoefficient = GetDecompressedFloat<512, 17>(Source.LinearMaxForceCoefficient);
}
}
}
// Serialize into bitstream
static void Serialize(FNetSerializationContext& Context, const FNetSerializeArgs& Args)
{
const QuantizedType& Source = *reinterpret_cast<const QuantizedType*>(Args.Source);
FNetBitStreamWriter* Writer = Context.GetBitStreamWriter();
Writer->WriteBool(Source.bUsePhysicsSettings);
if (Source.bUsePhysicsSettings)
{
Writer->WriteBits(static_cast<uint32>(Source.PhysicsGripLocationSettings), 3);
Writer->WriteBits(static_cast<uint32>(Source.PhysicsConstraintType), 1);
Writer->WriteBool(Source.bTurnOffGravityDuringGrip);
Writer->WriteBool(Source.bSkipSettingSimulating);
// Compressed floats
Writer->WriteBits(Source.LinearMaxForceCoefficient, 17);
Writer->WriteBits(Source.AngularMaxForceCoefficient, 17);
Writer->WriteBool(Source.bUseCustomAngularValues);
if (Source.bUseCustomAngularValues)
{
Writer->WriteBits(Source.AngularStiffness, 32);
Writer->WriteBits(Source.AngularDamping, 32);
}
}
}
// Deserialize from bitstream
static void Deserialize(FNetSerializationContext& Context, const FNetDeserializeArgs& Args)
{
QuantizedType& Target = *reinterpret_cast<QuantizedType*>(Args.Target);
FNetBitStreamReader* Reader = Context.GetBitStreamReader();
Target.bUsePhysicsSettings = Reader->ReadBool();
if (Target.bUsePhysicsSettings)
{
Target.PhysicsGripLocationSettings = Reader->ReadBits(3);
Target.PhysicsConstraintType = Reader->ReadBits(1);
Target.bTurnOffGravityDuringGrip = Reader->ReadBool();
Target.bSkipSettingSimulating = Reader->ReadBool();
// Decompress floats
Target.LinearMaxForceCoefficient = Reader->ReadBits(17);
Target.AngularMaxForceCoefficient = Reader->ReadBits(17);
Target.bUseCustomAngularValues = Reader->ReadBool();
if (Target.bUseCustomAngularValues)
{
Target.AngularStiffness = Reader->ReadBits(32);
Target.AngularDamping = Reader->ReadBits(32);
}
}
}
// Compare two instances to see if they differ
static bool IsEqual(FNetSerializationContext& Context, const FNetIsEqualArgs& Args)
{
if (Args.bStateIsQuantized)
{
const QuantizedType& QuantizedValue0 = *reinterpret_cast<const QuantizedType*>(Args.Source0);
const QuantizedType& QuantizedValue1 = *reinterpret_cast<const QuantizedType*>(Args.Source1);
return FPlatformMemory::Memcmp(&QuantizedValue0, &QuantizedValue1, sizeof(QuantizedType)) == 0;
}
else
{
const SourceType& L = *reinterpret_cast<const SourceType*>(Args.Source0);
const SourceType& R = *reinterpret_cast<const SourceType*>(Args.Source1);
if (L.bUsePhysicsSettings != R.bUsePhysicsSettings) return false;
if (!L.bUsePhysicsSettings) return true;
if (L.PhysicsGripLocationSettings != R.PhysicsGripLocationSettings) return false;
if (L.PhysicsConstraintType != R.PhysicsConstraintType) return false;
if (L.bTurnOffGravityDuringGrip != R.bTurnOffGravityDuringGrip) return false;
if (L.bSkipSettingSimulating != R.bSkipSettingSimulating) return false;
if (!FMath::IsNearlyEqual(L.LinearMaxForceCoefficient, R.LinearMaxForceCoefficient)) return false;
if (!FMath::IsNearlyEqual(L.AngularMaxForceCoefficient, R.AngularMaxForceCoefficient)) return false;
if (L.bUseCustomAngularValues != R.bUseCustomAngularValues) return false;
if (L.bUseCustomAngularValues)
{
if (!FMath::IsNearlyEqual(L.AngularStiffness, R.AngularStiffness)) return false;
if (!FMath::IsNearlyEqual(L.AngularDamping, R.AngularDamping)) return false;
}
return true;
}
}
};
static const FName PropertyNetSerializerRegistry_NAME_BPAdvGripPhysicsSettings("BPAdvGripPhysicsSettings");
UE_NET_IMPLEMENT_NAMED_STRUCT_NETSERIALIZER_INFO(PropertyNetSerializerRegistry_NAME_BPAdvGripPhysicsSettings, FBPAdvGripPhysicsSettingsNetSerializer);
FBPAdvGripPhysicsSettingsNetSerializer::FNetSerializerRegistryDelegates::~FNetSerializerRegistryDelegates()
{
UE_NET_UNREGISTER_NETSERIALIZER_INFO(PropertyNetSerializerRegistry_NAME_BPAdvGripPhysicsSettings);
}
void FBPAdvGripPhysicsSettingsNetSerializer::FNetSerializerRegistryDelegates::OnPreFreezeNetSerializerRegistry()
{
UE_NET_REGISTER_NETSERIALIZER_INFO(PropertyNetSerializerRegistry_NAME_BPAdvGripPhysicsSettings);
}
/*void FBPAdvGripPhysicsSettingsNetSerializer::FNetSerializerRegistryDelegates::OnPostFreezeNetSerializerRegistry()
{
}*/
UE_NET_IMPLEMENT_SERIALIZER(FBPAdvGripPhysicsSettingsNetSerializer);
}

View file

@ -0,0 +1,423 @@
#include "Serializers/FBPSecondaryGripInfoNetSerializer.h"
#include "Serializers/SerializerHelpers.h"
#include "Iris/Serialization/NetSerializerDelegates.h"
#include "Iris/Serialization/NetSerializers.h"
#include "Iris/ReplicationState/PropertyNetSerializerInfoRegistry.h"
#include "Iris/ReplicationState/ReplicationStateDescriptorBuilder.h"
#include "Serializers/FTransformNetQuantizeNetSerializer.h"
namespace UE::Net
{
// -----------------------------------------------------------------------------
// Iris serializer for FBPSecondaryGripInfo
// -----------------------------------------------------------------------------
struct FBPSecondaryGripInfoNetSerializer
{
inline static const FVectorNetQuantize100NetSerializerConfig FTransformQuantizeSerializerConfig;
inline static const FObjectPtrNetSerializerConfig ObjectPtrNetSerializerConfig;
inline static const FNameNetSerializerConfig NameNetSerializerConfig;
inline static const FNetSerializerConfig* FTransformQuantizeSerializerConfigPtr = &FTransformQuantizeSerializerConfig;
inline static const FNetSerializer* FTransformQuantizeNetSerializerPtr;
inline static const FNetSerializerConfig* FObjectPtrSerializerConfigPtr = &ObjectPtrNetSerializerConfig;
inline static const FNetSerializer* FObjectPtrNetSerializerPtr;
inline static const FNetSerializerConfig* FNameSerializerConfigPtr = &NameNetSerializerConfig;
inline static const FNetSerializer* FNameNetSerializerPtr;
class FNetSerializerRegistryDelegates final : private UE::Net::FNetSerializerRegistryDelegates
{
public:
virtual ~FNetSerializerRegistryDelegates();
void InitNetSerializer()
{
FBPSecondaryGripInfoNetSerializer::FTransformQuantizeNetSerializerPtr = &UE_NET_GET_SERIALIZER(FTransformNetQuantizeNetSerializer);
FBPSecondaryGripInfoNetSerializer::FObjectPtrNetSerializerPtr = &UE_NET_GET_SERIALIZER(FObjectPtrNetSerializer);
FBPSecondaryGripInfoNetSerializer::FNameNetSerializerPtr = &UE_NET_GET_SERIALIZER(FNameNetSerializer);
}
private:
virtual void OnPreFreezeNetSerializerRegistry() override;
//virtual void OnPostFreezeNetSerializerRegistry() override;
};
inline static FBPSecondaryGripInfoNetSerializer::FNetSerializerRegistryDelegates NetSerializerRegistryDelegates;
/** Version is required. */
static constexpr uint32 Version = 0;
struct alignas(8) FQuantizedData
{
uint8 bHasSecondaryAttachment;
FObjectNetSerializerQuantizedReferenceStorage SecondaryAttachment;
FTransformNetQuantizeQuantizedData SecondaryRelativeTransform;
uint8 bIsSlotGrip;
alignas(8) uint8 SecondarySlotName[GetNameNetSerializerSafeQuantizedSize()];
uint16 LerpToRate;
// Not Replicated
// SecondaryGripDistance
//EGripLerpState GripLerpState;
//float curLerp;
//FVector LastRelativeLocation;
};
typedef FBPSecondaryGripInfo SourceType;
typedef FQuantizedData QuantizedType;
typedef FBPSecondaryGripInfoNetSerializerConfig ConfigType;
inline static const ConfigType DefaultConfig;
// TODO: This is actually a struct that could use some delta serialization implementations.
/** Set to false when a same value delta compression method is undesirable, for example when the serializer only writes a single bit for the state. */
static constexpr bool bUseDefaultDelta = true;
static constexpr bool bHasDynamicState = true;
//static constexpr bool bHasCustomNetReference = true;
// Called to create a "quantized snapshot" of the struct
static void Quantize(FNetSerializationContext& Context, const FNetQuantizeArgs& Args)
{
// Actually do the real quantization step here next instead of just in serialize, will save on memory overall
const SourceType& Source = *reinterpret_cast<const SourceType*>(Args.Source);
QuantizedType& Target = *reinterpret_cast<QuantizedType*>(Args.Target);
Target.bHasSecondaryAttachment = Source.bHasSecondaryAttachment ? 1 : 0;
if (Target.bHasSecondaryAttachment)
{
// ObjectPtr
const FNetSerializer* ObjSerializer = FObjectPtrNetSerializerPtr;
const FNetSerializerConfig* ObjSerializerConfig = FObjectPtrSerializerConfigPtr;
//Target.SecondaryAttachment
FNetQuantizeArgs MemberArgsObj = Args;
MemberArgsObj.NetSerializerConfig = NetSerializerConfigParam(ObjSerializerConfig);
MemberArgsObj.Source = NetSerializerValuePointer(&Source.SecondaryAttachment);
MemberArgsObj.Target = NetSerializerValuePointer(&Target.SecondaryAttachment);
ObjSerializer->Quantize(Context, MemberArgsObj);
// Transform
const FNetSerializer* TransformSerializer = FTransformQuantizeNetSerializerPtr;
const FNetSerializerConfig* TransformSerializerConfig = FTransformQuantizeSerializerConfigPtr;
//SecondaryRelativeTransform
FNetQuantizeArgs MemberArgsTransform = Args;
MemberArgsTransform.NetSerializerConfig = NetSerializerConfigParam(TransformSerializerConfig);
MemberArgsTransform.Source = NetSerializerValuePointer(&Source.SecondaryRelativeTransform);
MemberArgsTransform.Target = NetSerializerValuePointer(&Target.SecondaryRelativeTransform);
TransformSerializer->Quantize(Context, MemberArgsTransform);
Target.bIsSlotGrip = Source.bIsSlotGrip ? 1 : 0;
// FName
const FNetSerializer* NameSerializer = FNameNetSerializerPtr;
const FNetSerializerConfig* NameSerializerConfig = FNameSerializerConfigPtr;
//SecondarySlotName
FNetQuantizeArgs MemberArgs = Args;
MemberArgs.NetSerializerConfig = NetSerializerConfigParam(NameSerializerConfig);
MemberArgs.Source = NetSerializerValuePointer(&Source.SecondarySlotName);
MemberArgs.Target = NetSerializerValuePointer(&Target.SecondarySlotName);
NameSerializer->Quantize(Context, MemberArgs);
}
// This is 0.0 - 16.0, using compression to get it smaller, 4 bits = max 16 + 1 bit for sign and 7 bits precision for 128 / full 2 digit precision
Target.LerpToRate = GetCompressedFloat<16, 12>(Source.LerpToRate);
}
// Called to apply the quantized snapshot back to gameplay memory
static void Dequantize(FNetSerializationContext& Context, const FNetDequantizeArgs& Args)
{
const QuantizedType& Source = *reinterpret_cast<const QuantizedType*>(Args.Source);
SourceType& Target = *reinterpret_cast<SourceType*>(Args.Target);
Target.bHasSecondaryAttachment = Source.bHasSecondaryAttachment != 0;
if (Target.bHasSecondaryAttachment)
{
// ObjectPtr
const FNetSerializer* ObjSerializer = FObjectPtrNetSerializerPtr;
const FNetSerializerConfig* ObjSerializerConfig = FObjectPtrSerializerConfigPtr;
//Target.SecondaryAttachment
FNetDequantizeArgs MemberArgsObj = Args;
MemberArgsObj.NetSerializerConfig = NetSerializerConfigParam(ObjSerializerConfig);
MemberArgsObj.Source = NetSerializerValuePointer(&Source.SecondaryAttachment);
MemberArgsObj.Target = NetSerializerValuePointer(&Target.SecondaryAttachment);
ObjSerializer->Dequantize(Context, MemberArgsObj);
// Transform
const FNetSerializer* TransformSerializer = FTransformQuantizeNetSerializerPtr;
const FNetSerializerConfig* TransformSerializerConfig = FTransformQuantizeSerializerConfigPtr;
//SecondaryRelativeTransform
FNetDequantizeArgs MemberArgsTransform = Args;
MemberArgsTransform.NetSerializerConfig = NetSerializerConfigParam(TransformSerializerConfig);
MemberArgsTransform.Source = NetSerializerValuePointer(&Source.SecondaryRelativeTransform);
MemberArgsTransform.Target = NetSerializerValuePointer(&Target.SecondaryRelativeTransform);
TransformSerializer->Dequantize(Context, MemberArgsTransform);
Target.bIsSlotGrip = Source.bIsSlotGrip != 0;
// FName
const FNetSerializer* NameSerializer = FNameNetSerializerPtr;
const FNetSerializerConfig* NameSerializerConfig = FNameSerializerConfigPtr;
//SecondarySlotName
FNetDequantizeArgs MemberArgs = Args;
MemberArgs.NetSerializerConfig = NetSerializerConfigParam(NameSerializerConfig);
MemberArgs.Source = NetSerializerValuePointer(&Source.SecondarySlotName);
MemberArgs.Target = NetSerializerValuePointer(&Target.SecondarySlotName);
NameSerializer->Dequantize(Context, MemberArgs);
}
// This is 0.0 - 16.0, using compression to get it smaller, 4 bits = max 16 + 1 bit for sign and 7 bits precision for 128 / full 2 digit precision
Target.LerpToRate = GetDecompressedFloat<16, 12>(Source.LerpToRate);
}
// Serialize into bitstream
static void Serialize(FNetSerializationContext& Context, const FNetSerializeArgs& Args)
{
const QuantizedType& Source = *reinterpret_cast<const QuantizedType*>(Args.Source);
FNetBitStreamWriter* Writer = Context.GetBitStreamWriter();
Writer->WriteBits(static_cast<uint32>(Source.bHasSecondaryAttachment), 1);
if (Source.bHasSecondaryAttachment != 0)
{
// ObjectPtr
const FNetSerializer* ObjSerializer = FObjectPtrNetSerializerPtr;
const FNetSerializerConfig* ObjSerializerConfig = FObjectPtrSerializerConfigPtr;
//Target.SecondaryAttachment
FNetSerializeArgs MemberArgsObj = Args;
MemberArgsObj.NetSerializerConfig = NetSerializerConfigParam(ObjSerializerConfig);
MemberArgsObj.Source = NetSerializerValuePointer(&Source.SecondaryAttachment);
ObjSerializer->Serialize(Context, MemberArgsObj);
// Transform
const FNetSerializer* TransformSerializer = FTransformQuantizeNetSerializerPtr;
const FNetSerializerConfig* TransformSerializerConfig = FTransformQuantizeSerializerConfigPtr;
//SecondaryRelativeTransform
FNetSerializeArgs MemberArgsTransform = Args;
MemberArgsTransform.NetSerializerConfig = NetSerializerConfigParam(TransformSerializerConfig);
MemberArgsTransform.Source = NetSerializerValuePointer(&Source.SecondaryRelativeTransform);
TransformSerializer->Serialize(Context, MemberArgsTransform);
Writer->WriteBits(static_cast<uint32>(Source.bIsSlotGrip), 1);
// FName
const FNetSerializer* NameSerializer = FNameNetSerializerPtr;
const FNetSerializerConfig* NameSerializerConfig = FNameSerializerConfigPtr;
//SecondarySlotName
FNetSerializeArgs MemberArgs = Args;
MemberArgs.NetSerializerConfig = NetSerializerConfigParam(NameSerializerConfig);
MemberArgs.Source = NetSerializerValuePointer(&Source.SecondarySlotName);
NameSerializer->Serialize(Context, MemberArgs);
}
// This is 0.0 - 16.0, using compression to get it smaller, 4 bits = max 16 + 1 bit for sign and 7 bits precision for 128 / full 2 digit precision
Writer->WriteBits(static_cast<uint32>(Source.LerpToRate), 12);
}
// Deserialize from bitstream
static void Deserialize(FNetSerializationContext& Context, const FNetDeserializeArgs& Args)
{
QuantizedType& Target = *reinterpret_cast<QuantizedType*>(Args.Target);
FNetBitStreamReader* Reader = Context.GetBitStreamReader();
Target.bHasSecondaryAttachment = Reader->ReadBits(1);
if (Target.bHasSecondaryAttachment != 0)
{
// ObjectPtr
const FNetSerializer* ObjSerializer = FObjectPtrNetSerializerPtr;
const FNetSerializerConfig* ObjSerializerConfig = FObjectPtrSerializerConfigPtr;
//Target.SecondaryAttachment
FNetDeserializeArgs MemberArgsObj = Args;
MemberArgsObj.NetSerializerConfig = NetSerializerConfigParam(ObjSerializerConfig);
MemberArgsObj.Target = NetSerializerValuePointer(&Target.SecondaryAttachment);
ObjSerializer->Deserialize(Context, MemberArgsObj);
// Transform
const FNetSerializer* TransformSerializer = FTransformQuantizeNetSerializerPtr;
const FNetSerializerConfig* TransformSerializerConfig = FTransformQuantizeSerializerConfigPtr;
//SecondaryRelativeTransform
FNetDeserializeArgs MemberArgsTransform = Args;
MemberArgsTransform.NetSerializerConfig = NetSerializerConfigParam(TransformSerializerConfig);
MemberArgsTransform.Target = NetSerializerValuePointer(&Target.SecondaryRelativeTransform);
TransformSerializer->Deserialize(Context, MemberArgsTransform);
Target.bIsSlotGrip = Reader->ReadBits(1);
// FName
const FNetSerializer* NameSerializer = FNameNetSerializerPtr;
const FNetSerializerConfig* NameSerializerConfig = FNameSerializerConfigPtr;
//SecondarySlotName
FNetDeserializeArgs MemberArgs = Args;
MemberArgs.NetSerializerConfig = NetSerializerConfigParam(NameSerializerConfig);
MemberArgs.Target = NetSerializerValuePointer(&Target.SecondarySlotName);
NameSerializer->Deserialize(Context, MemberArgs);
}
// This is 0.0 - 16.0, using compression to get it smaller, 4 bits = max 16 + 1 bit for sign and 7 bits precision for 128 / full 2 digit precision
Target.LerpToRate = Reader->ReadBits(12);
}
// Compare two instances to see if they differ
static bool IsEqual(FNetSerializationContext& Context, const FNetIsEqualArgs& Args)
{
if (Args.bStateIsQuantized)
{
const QuantizedType& QuantizedValue0 = *reinterpret_cast<const QuantizedType*>(Args.Source0);
const QuantizedType& QuantizedValue1 = *reinterpret_cast<const QuantizedType*>(Args.Source1);
return FPlatformMemory::Memcmp(&QuantizedValue0, &QuantizedValue1, sizeof(QuantizedType)) == 0;
}
else
{
const SourceType& L = *reinterpret_cast<const SourceType*>(Args.Source0);
const SourceType& R = *reinterpret_cast<const SourceType*>(Args.Source1);
if (L.bHasSecondaryAttachment != R.bHasSecondaryAttachment) return false;
if (L.bHasSecondaryAttachment)
{
if (L.SecondaryAttachment != R.SecondaryAttachment) return false;
if (!L.SecondaryRelativeTransform.Equals(R.SecondaryRelativeTransform)) return false;
if (L.bIsSlotGrip != R.bIsSlotGrip) return false;
if (L.SecondarySlotName != R.SecondarySlotName) return false;
}
if (L.LerpToRate != R.LerpToRate) return false;
return true;
}
}
static void CloneDynamicState(FNetSerializationContext& Context, const FNetCloneDynamicStateArgs& Args)
{
const QuantizedType* Source = reinterpret_cast<const QuantizedType*>(Args.Source);
QuantizedType& Target = *reinterpret_cast<QuantizedType*>(Args.Target);
const FNetSerializer* NameSerializer = FNameNetSerializerPtr;
const FNetSerializerConfig* NameSerializerConfig = FNameSerializerConfigPtr;
FNetCloneDynamicStateArgs MemberArgs = Args;
MemberArgs.NetSerializerConfig = NetSerializerConfigParam(NameSerializerConfig);
MemberArgs.Target = NetSerializerValuePointer(&Target.SecondarySlotName);
MemberArgs.Source = NetSerializerValuePointer(&Source->SecondarySlotName);
NameSerializer->CloneDynamicState(Context, MemberArgs);
const FNetSerializer* ObjSerializer = FObjectPtrNetSerializerPtr;
const FNetSerializerConfig* ObjSerializerConfig = FObjectPtrSerializerConfigPtr;
FNetCloneDynamicStateArgs ObjMemberArgs = Args;
ObjMemberArgs.NetSerializerConfig = NetSerializerConfigParam(ObjSerializerConfig);
ObjMemberArgs.Target = NetSerializerValuePointer(&Target.SecondaryAttachment);
ObjMemberArgs.Source = NetSerializerValuePointer(&Source->SecondaryAttachment);
ObjSerializer->CloneDynamicState(Context, MemberArgs);
}
static void FreeDynamicState(FNetSerializationContext& Context, const FNetFreeDynamicStateArgs& Args)
{
QuantizedType& Source = *reinterpret_cast<QuantizedType*>(Args.Source);
const FNetSerializer* NameSerializer = FNameNetSerializerPtr;
const FNetSerializerConfig* NameSerializerConfig = FNameSerializerConfigPtr;
FNetFreeDynamicStateArgs MemberArgs = Args;
MemberArgs.NetSerializerConfig = NetSerializerConfigParam(NameSerializerConfig);
MemberArgs.Source = NetSerializerValuePointer(&Source.SecondarySlotName);
NameSerializer->FreeDynamicState(Context, MemberArgs);
const FNetSerializer* ObjSerializer = FObjectPtrNetSerializerPtr;
const FNetSerializerConfig* ObjSerializerConfig = FObjectPtrSerializerConfigPtr;
FNetFreeDynamicStateArgs ObjMemberArgs = Args;
ObjMemberArgs.NetSerializerConfig = NetSerializerConfigParam(ObjSerializerConfig);
ObjMemberArgs.Source = NetSerializerValuePointer(&Source.SecondaryAttachment);
ObjSerializer->FreeDynamicState(Context, MemberArgs);
}
static void Apply(FNetSerializationContext&, const FNetApplyArgs& Args)
{
const SourceType& Source = *reinterpret_cast<const SourceType*>(Args.Source);
SourceType& Target = *reinterpret_cast<SourceType*>(Args.Target);
Target.bHasSecondaryAttachment = Source.bHasSecondaryAttachment;
if (Target.bHasSecondaryAttachment)
{
Target.SecondaryAttachment = Source.SecondaryAttachment;
Target.SecondaryRelativeTransform = Source.SecondaryRelativeTransform;
Target.bIsSlotGrip = Source.bIsSlotGrip;
Target.SecondarySlotName = Source.SecondarySlotName;
}
else
{
// Clear non repped values
Target.SecondaryAttachment = nullptr;
Target.SecondaryRelativeTransform = FTransform::Identity;
Target.bIsSlotGrip = false;
Target.SecondarySlotName = NAME_None;
}
Target.LerpToRate = Source.LerpToRate;
// Not Replicated
// SecondaryGripDistance
//EGripLerpState GripLerpState;
//float curLerp;
//FVector LastRelativeLocation;
}
/*static void CollectNetReferences(FNetSerializationContext& Context, const FNetCollectReferencesArgs& Args)
{
const QuantizedType& Source = *reinterpret_cast<const QuantizedType*>(Args.Source);
const FNetSerializer* ObjSerializer = FObjectPtrNetSerializerPtr;
const FNetSerializerConfig* ObjSerializerConfig = FObjectPtrSerializerConfigPtr;
FNetCollectReferencesArgs CollectNetReferencesArgs = Args;
CollectNetReferencesArgs.NetSerializerConfig = FObjectPtrSerializerConfigPtr;
CollectNetReferencesArgs.Source = NetSerializerValuePointer(&Source.SecondaryAttachment);
ObjSerializer->CollectNetReferences(Context, CollectNetReferencesArgs);
}*/
};
static const FName PropertyNetSerializerRegistry_NAME_BPSecondaryGripInfo("BPSecondaryGripInfo");
UE_NET_IMPLEMENT_NAMED_STRUCT_NETSERIALIZER_INFO(PropertyNetSerializerRegistry_NAME_BPSecondaryGripInfo, FBPSecondaryGripInfoNetSerializer);
FBPSecondaryGripInfoNetSerializer::FNetSerializerRegistryDelegates::~FNetSerializerRegistryDelegates()
{
UE_NET_UNREGISTER_NETSERIALIZER_INFO(PropertyNetSerializerRegistry_NAME_BPSecondaryGripInfo);
}
void FBPSecondaryGripInfoNetSerializer::FNetSerializerRegistryDelegates::OnPreFreezeNetSerializerRegistry()
{
InitNetSerializer();
UE_NET_REGISTER_NETSERIALIZER_INFO(PropertyNetSerializerRegistry_NAME_BPSecondaryGripInfo);
}
UE_NET_IMPLEMENT_SERIALIZER(FBPSecondaryGripInfoNetSerializer);
}

View file

@ -0,0 +1,277 @@
#include "Serializers/FBPVRComponentPosRepNetSerializer.h"
#include "Serializers/SerializerHelpers.h"
#include "Iris/Serialization/NetSerializerDelegates.h"
#include "Iris/Serialization/NetSerializers.h"
#include "Iris/Serialization/PackedVectorNetSerializers.h"
#include "Iris/ReplicationState/PropertyNetSerializerInfoRegistry.h"
#include "Iris/ReplicationState/ReplicationStateDescriptorBuilder.h"
namespace UE::Net
{
// -----------------------------------------------------------------------------
// Iris serializer for FBPVRComponentPosRep
// -----------------------------------------------------------------------------
struct FBPVRComponentPosRepNetSerializer
{
inline static const FVectorNetQuantize10NetSerializerConfig Quantize10SerializerConfig;
inline static const FVectorNetQuantize100NetSerializerConfig Quantize100SerializerConfig;
inline static const FNetSerializerConfig* VectorNetQuantizeNetSerializerConfigs[2] = { &Quantize10SerializerConfig, &Quantize100SerializerConfig};
inline static const FNetSerializer* VectorNetQuantizeNetSerializers[2] = {};
class FNetSerializerRegistryDelegates final : private UE::Net::FNetSerializerRegistryDelegates
{
public:
virtual ~FNetSerializerRegistryDelegates();
void InitNetSerializer()
{
FBPVRComponentPosRepNetSerializer::VectorNetQuantizeNetSerializers[0] = &UE_NET_GET_SERIALIZER(FVectorNetQuantize10NetSerializer);
FBPVRComponentPosRepNetSerializer::VectorNetQuantizeNetSerializers[1] = &UE_NET_GET_SERIALIZER(FVectorNetQuantize100NetSerializer);
}
private:
virtual void OnPreFreezeNetSerializerRegistry() override;
//virtual void OnPostFreezeNetSerializerRegistry() override;
};
inline static FBPVRComponentPosRepNetSerializer::FNetSerializerRegistryDelegates NetSerializerRegistryDelegates;
/** Version is required. */
static constexpr uint32 Version = 0;
struct alignas(8) FQuantizedData
{
uint64 Position[4]; // We don't need to store double for tracked device positions, but their forwarded serializer uses it
uint16 Rotation[3];
uint8 QuantizationLevel : 1;
uint8 RotationQuantizationLevel : 1;
};
typedef FBPVRComponentPosRep SourceType;
typedef FQuantizedData QuantizedType;
typedef FBPVRComponentPosRepNetSerializerConfig ConfigType;
inline static const ConfigType DefaultConfig;
/** Set to false when a same value delta compression method is undesirable, for example when the serializer only writes a single bit for the state. */
static constexpr bool bUseDefaultDelta = true;
// Not doing delta, the majority of the time a single bit (bool) controls the serialization of the entirity
/**
* Optional. Same as Serialize but where an acked previous state is provided for bitpacking purposes.
* This is implemented by default to do same value optimization, at the cost of a bit. If implemented
* then DeserializeDelta is required.
*/
/*static void SerializeDelta(FNetSerializationContext&, const FNetSerializeDeltaArgs&)
{
}*/
/**
* Optional. Same as Deserialize but where an acked previous state is provided for bitpacking purposes.
* This is implemented by default to do same value optimization, at the cost of a bit. If implemented
* then SerializeDelta is required.
*/
/*static void DeserializeDelta(FNetSerializationContext&, const FNetDeserializeDeltaArgs&)
{
}*/
// Called to create a "quantized snapshot" of the struct
static void Quantize(FNetSerializationContext& Context, const FNetQuantizeArgs& Args)
{
// Actually do the real quantization step here next instead of just in serialize, will save on memory overall
const SourceType& Source = *reinterpret_cast<const SourceType*>(Args.Source);
QuantizedType& Target = *reinterpret_cast<QuantizedType*>(Args.Target);
Target.QuantizationLevel = (uint8)Source.QuantizationLevel;
Target.RotationQuantizationLevel = (uint8)Source.RotationQuantizationLevel;
const FNetSerializer* Serializer = VectorNetQuantizeNetSerializers[Target.QuantizationLevel] ;
const FNetSerializerConfig* SerializerConfig = VectorNetQuantizeNetSerializerConfigs[Target.QuantizationLevel] ;
FNetQuantizeArgs MemberArgs = Args;
MemberArgs.NetSerializerConfig = NetSerializerConfigParam(SerializerConfig);
MemberArgs.Source = NetSerializerValuePointer(&Source.Position);
MemberArgs.Target = NetSerializerValuePointer(&Target.Position[0]);
Serializer->Quantize(Context, MemberArgs);
// Manually quantize these
switch (Source.RotationQuantizationLevel)
{
case EVRRotationQuantization::RoundTo10Bits:
{
Target.Rotation[0] = FBPVRComponentPosRep::CompressAxisTo10BitShort(Source.Rotation.Pitch);
Target.Rotation[1] = FBPVRComponentPosRep::CompressAxisTo10BitShort(Source.Rotation.Yaw);
Target.Rotation[2] = FBPVRComponentPosRep::CompressAxisTo10BitShort(Source.Rotation.Roll);
}break;
case EVRRotationQuantization::RoundToShort:
{
Target.Rotation[0] = FRotator::CompressAxisToShort(Source.Rotation.Pitch);
Target.Rotation[1] = FRotator::CompressAxisToShort(Source.Rotation.Yaw);
Target.Rotation[2] = FRotator::CompressAxisToShort(Source.Rotation.Roll);
}break;
}
}
// Called to apply the quantized snapshot back to gameplay memory
static void Dequantize(FNetSerializationContext& Context, const FNetDequantizeArgs& Args)
{
const QuantizedType& Source = *reinterpret_cast<const QuantizedType*>(Args.Source);
SourceType& Target = *reinterpret_cast<SourceType*>(Args.Target);
Target.QuantizationLevel = (EVRVectorQuantization)Source.QuantizationLevel;
Target.RotationQuantizationLevel = (EVRRotationQuantization)Source.RotationQuantizationLevel;
const FNetSerializer* Serializer = VectorNetQuantizeNetSerializers[Source.QuantizationLevel];
const FNetSerializerConfig* SerializerConfig = VectorNetQuantizeNetSerializerConfigs[Source.QuantizationLevel];
FNetDequantizeArgs MemberArgs = Args;
MemberArgs.NetSerializerConfig = NetSerializerConfigParam(SerializerConfig);
MemberArgs.Source = NetSerializerValuePointer(&Source.Position[0]);
MemberArgs.Target = NetSerializerValuePointer(&Target.Position);
Serializer->Dequantize(Context, MemberArgs);
// Manually quantize these
switch (Target.RotationQuantizationLevel)
{
case EVRRotationQuantization::RoundTo10Bits:
{
Target.Rotation.Pitch = FBPVRComponentPosRep::DecompressAxisFrom10BitShort(Source.Rotation[0]);
Target.Rotation.Yaw = FBPVRComponentPosRep::DecompressAxisFrom10BitShort(Source.Rotation[1]);
Target.Rotation.Roll = FBPVRComponentPosRep::DecompressAxisFrom10BitShort(Source.Rotation[2]);
}break;
case EVRRotationQuantization::RoundToShort:
{
Target.Rotation.Pitch = FRotator::DecompressAxisFromShort(Source.Rotation[0]);
Target.Rotation.Yaw = FRotator::DecompressAxisFromShort(Source.Rotation[1]);
Target.Rotation.Roll = FRotator::DecompressAxisFromShort(Source.Rotation[2]);
}break;
}
}
// Serialize into bitstream
static void Serialize(FNetSerializationContext& Context, const FNetSerializeArgs& Args)
{
const QuantizedType& Source = *reinterpret_cast<const QuantizedType*>(Args.Source);
FNetBitStreamWriter* Writer = Context.GetBitStreamWriter();
Writer->WriteBits(static_cast<uint32>(Source.QuantizationLevel), 1);
Writer->WriteBits(static_cast<uint32>(Source.RotationQuantizationLevel), 1);
const FNetSerializer* Serializer = VectorNetQuantizeNetSerializers[Source.QuantizationLevel] ;
const FNetSerializerConfig* SerializerConfig = VectorNetQuantizeNetSerializerConfigs[Source.QuantizationLevel];
FNetSerializeArgs MemberArgs = Args;
MemberArgs.NetSerializerConfig = NetSerializerConfigParam(SerializerConfig);
MemberArgs.Source = NetSerializerValuePointer(&Source.Position[0]);
Serializer->Serialize(Context, MemberArgs);
switch ((EVRRotationQuantization)Source.RotationQuantizationLevel)
{
case EVRRotationQuantization::RoundTo10Bits:
{
Writer->WriteBits(static_cast<uint32>(Source.Rotation[0]), 10);
Writer->WriteBits(static_cast<uint32>(Source.Rotation[1]), 10);
Writer->WriteBits(static_cast<uint32>(Source.Rotation[2]), 10);
}break;
case EVRRotationQuantization::RoundToShort:
{
Writer->WriteBits(static_cast<uint32>(Source.Rotation[0]), 16);
Writer->WriteBits(static_cast<uint32>(Source.Rotation[1]), 16);
Writer->WriteBits(static_cast<uint32>(Source.Rotation[2]), 16);
}break;
}
}
// Deserialize from bitstream
static void Deserialize(FNetSerializationContext& Context, const FNetDeserializeArgs& Args)
{
QuantizedType& Target = *reinterpret_cast<QuantizedType*>(Args.Target);
FNetBitStreamReader* Reader = Context.GetBitStreamReader();
Target.QuantizationLevel = Reader->ReadBits(1);
Target.RotationQuantizationLevel = Reader->ReadBits(1);
const FNetSerializer* Serializer = VectorNetQuantizeNetSerializers[Target.QuantizationLevel] ;
const FNetSerializerConfig* SerializerConfig = VectorNetQuantizeNetSerializerConfigs[Target.QuantizationLevel];
FNetDeserializeArgs MemberArgs = Args;
MemberArgs.NetSerializerConfig = NetSerializerConfigParam(SerializerConfig);
MemberArgs.Target = NetSerializerValuePointer(&Target.Position[0]);
Serializer->Deserialize(Context, MemberArgs);
switch ((EVRRotationQuantization)Target.RotationQuantizationLevel)
{
case EVRRotationQuantization::RoundTo10Bits:
{
Target.Rotation[0] = Reader->ReadBits(10);
Target.Rotation[1] = Reader->ReadBits(10);
Target.Rotation[2] = Reader->ReadBits(10);
}break;
case EVRRotationQuantization::RoundToShort:
{
Target.Rotation[0] = Reader->ReadBits(16);
Target.Rotation[1] = Reader->ReadBits(16);
Target.Rotation[2] = Reader->ReadBits(16);
}break;
}
}
// Compare two instances to see if they differ
static bool IsEqual(FNetSerializationContext& Context, const FNetIsEqualArgs& Args)
{
if (Args.bStateIsQuantized)
{
const QuantizedType& QuantizedValue0 = *reinterpret_cast<const QuantizedType*>(Args.Source0);
const QuantizedType& QuantizedValue1 = *reinterpret_cast<const QuantizedType*>(Args.Source1);
return FPlatformMemory::Memcmp(&QuantizedValue0, &QuantizedValue1, sizeof(QuantizedType)) == 0;
}
else
{
const SourceType& L = *reinterpret_cast<const SourceType*>(Args.Source0);
const SourceType& R = *reinterpret_cast<const SourceType*>(Args.Source1);
if (L.QuantizationLevel != R.QuantizationLevel) return false;
if (L.RotationQuantizationLevel != R.RotationQuantizationLevel) return false;
if (!L.Position.Equals(R.Position)) return false;
if (!L.Rotation.Equals(R.Rotation)) return false;
return true;
}
}
};
static const FName PropertyNetSerializerRegistry_NAME_BPVRComponentPosRep("BPVRComponentPosRep");
UE_NET_IMPLEMENT_NAMED_STRUCT_NETSERIALIZER_INFO(PropertyNetSerializerRegistry_NAME_BPVRComponentPosRep, FBPVRComponentPosRepNetSerializer);
FBPVRComponentPosRepNetSerializer::FNetSerializerRegistryDelegates::~FNetSerializerRegistryDelegates()
{
UE_NET_UNREGISTER_NETSERIALIZER_INFO(PropertyNetSerializerRegistry_NAME_BPVRComponentPosRep);
}
void FBPVRComponentPosRepNetSerializer::FNetSerializerRegistryDelegates::OnPreFreezeNetSerializerRegistry()
{
InitNetSerializer();
UE_NET_REGISTER_NETSERIALIZER_INFO(PropertyNetSerializerRegistry_NAME_BPVRComponentPosRep);
}
/*void FBPVRComponentPosRepNetSerializer::FNetSerializerRegistryDelegates::OnPostFreezeNetSerializerRegistry()
{
}*/
UE_NET_IMPLEMENT_SERIALIZER(FBPVRComponentPosRepNetSerializer);
}

View file

@ -0,0 +1,402 @@
#include "Serializers/FRepMovementVRNetSerializer.h"
#include "Serializers/SerializerHelpers.h"
#include "Iris/Serialization/NetSerializerDelegates.h"
#include "Iris/Serialization/NetSerializers.h"
#include "Iris/Serialization/PackedVectorNetSerializers.h"
#include "Iris/ReplicationState/PropertyNetSerializerInfoRegistry.h"
#include "Iris/ReplicationState/ReplicationStateDescriptorBuilder.h"
#include "VRBaseCharacter.h"
#include "RepMovementNetSerializer.h"
namespace UE::Net
{
// -----------------------------------------------------------------------------
// Iris serializer for FRepMovementVRCharacter
// -----------------------------------------------------------------------------
struct FRepMovementVRCharacterNetSerializer
{
inline static const FVectorNetQuantize100NetSerializerConfig Quantize100SerializerConfig;
inline static const FRepMovementNetSerializerConfig RepMovementPtrNetSerializerConfig;
inline static const FNetSerializerConfig* VectorNetQuantizeNetSerializerConfig = &Quantize100SerializerConfig;
inline static const FNetSerializer* VectorNetQuantizeNetSerializer;
inline static const FNetSerializerConfig* FRepMovementNetSerializerConfig = &RepMovementPtrNetSerializerConfig;
inline static const FNetSerializer* FRepMovementNetSerializer;
class FNetSerializerRegistryDelegates final : private UE::Net::FNetSerializerRegistryDelegates
{
public:
virtual ~FNetSerializerRegistryDelegates();
void InitNetSerializer()
{
FRepMovementVRCharacterNetSerializer::VectorNetQuantizeNetSerializer = &UE_NET_GET_SERIALIZER(FVectorNetQuantize100NetSerializer);
FRepMovementVRCharacterNetSerializer::FRepMovementNetSerializer = &UE_NET_GET_SERIALIZER(FRepMovementNetSerializer);
}
private:
virtual void OnPreFreezeNetSerializerRegistry() override;
//virtual void OnPostFreezeNetSerializerRegistry() override;
};
inline static FRepMovementVRCharacterNetSerializer::FNetSerializerRegistryDelegates NetSerializerRegistryDelegates;
/** Version is required. */
static constexpr uint32 Version = 0;
// TODO: Keep up to date with the source struct
// Copy of the rep movement quant data struct, needs to be kept up to date with engine changes
struct FRepMovementQuantizedDataCopy
{
uint64 AngularVelocity[4];
uint64 LinearVelocity[4];
uint64 Location[4];
uint16 Rotation[4];
uint64 Acceleration[4];
int32 ServerFrame;
int32 ServerPhysicsHandle;
uint16 Flags : 4;
uint16 VelocityQuantizationLevel : 2;
uint16 LocationQuantizationLevel : 2;
uint16 RotationQuantizationLevel : 1;
uint16 RepAcceleration : 1;
uint16 Unused : 6;
uint16 Padding[3];
};
struct alignas(8) FRepMovementVRCharacterQuantizedData
{
uint8 bJustTeleported;
uint8 bJustTeleportedGrips;
uint8 bPausedTracking;
uint64 PausedTrackingLoc[4];
uint16 PausedTrackingRot;
// Not Replicated, is stored as a reference
//FObjectNetSerializerQuantizedReferenceStorage Owner;
FRepMovementQuantizedDataCopy RepMovementData;
};
typedef FRepMovementVRCharacter SourceType;
typedef FRepMovementVRCharacterQuantizedData QuantizedType;
typedef FRepMovementVRCharacterNetSerializerConfig ConfigType;
inline static const ConfigType DefaultConfig;
/** Set to false when a same value delta compression method is undesirable, for example when the serializer only writes a single bit for the state. */
static constexpr bool bUseDefaultDelta = true;
// Should likely handle delta custom eventually
// Called to create a "quantized snapshot" of the struct
static void Quantize(FNetSerializationContext& Context, const FNetQuantizeArgs& Args)
{
// Actually do the real quantization step here next instead of just in serialize, will save on memory overall
const SourceType& Source = *reinterpret_cast<const SourceType*>(Args.Source);
QuantizedType& Target = *reinterpret_cast<QuantizedType*>(Args.Target);
Target.bJustTeleported = Source.bJustTeleported ? 1 : 0;
Target.bJustTeleportedGrips = Source.bJustTeleportedGrips ? 1 : 0;
Target.bPausedTracking = Source.bPausedTracking ? 1 : 0;
if (Source.bPausedTracking)
{
const FNetSerializer* Serializer = VectorNetQuantizeNetSerializer;
const FNetSerializerConfig* SerializerConfig = VectorNetQuantizeNetSerializerConfig;
FNetQuantizeArgs MemberArgs = Args;
MemberArgs.NetSerializerConfig = NetSerializerConfigParam(SerializerConfig);
MemberArgs.Source = NetSerializerValuePointer(&Source.PausedTrackingLoc);
MemberArgs.Target = NetSerializerValuePointer(&Target.PausedTrackingLoc[0]);
Serializer->Quantize(Context, MemberArgs);
Target.PausedTrackingRot = FRotator::CompressAxisToShort(Source.PausedTrackingRot);
}
// FRepMovementQuantizedDataCopy RepMovementData;
const FNetSerializer* Serializer = FRepMovementNetSerializer;
const FNetSerializerConfig* SerializerConfig = FRepMovementNetSerializerConfig;
FNetQuantizeArgs MemberArgs = Args;
MemberArgs.NetSerializerConfig = NetSerializerConfigParam(SerializerConfig);
MemberArgs.Source = NetSerializerValuePointer(&Source);
MemberArgs.Target = NetSerializerValuePointer(&Target.RepMovementData);
Serializer->Quantize(Context, MemberArgs);
}
// Called to apply the quantized snapshot back to gameplay memory
static void Dequantize(FNetSerializationContext& Context, const FNetDequantizeArgs& Args)
{
const QuantizedType& Source = *reinterpret_cast<const QuantizedType*>(Args.Source);
SourceType& Target = *reinterpret_cast<SourceType*>(Args.Target);
Target.bJustTeleported = Source.bJustTeleported != 0;
Target.bJustTeleportedGrips = Source.bJustTeleportedGrips != 0;
Target.bPausedTracking = Source.bPausedTracking != 0;
if (Target.bPausedTracking)
{
const FNetSerializer* Serializer = VectorNetQuantizeNetSerializer;
const FNetSerializerConfig* SerializerConfig = VectorNetQuantizeNetSerializerConfig;
FNetDequantizeArgs MemberArgs = Args;
MemberArgs.NetSerializerConfig = NetSerializerConfigParam(SerializerConfig);
MemberArgs.Source = NetSerializerValuePointer(&Source.PausedTrackingLoc[0]);
MemberArgs.Target = NetSerializerValuePointer(&Target.PausedTrackingLoc);
Serializer->Dequantize(Context, MemberArgs);
Target.PausedTrackingRot = FRotator::DecompressAxisFromShort(Source.PausedTrackingRot);
}
// FRepMovementQuantizedDataCopy RepMovementData;
const FNetSerializer* Serializer = FRepMovementNetSerializer;
const FNetSerializerConfig* SerializerConfig = FRepMovementNetSerializerConfig;
FNetDequantizeArgs MemberArgs = Args;
MemberArgs.NetSerializerConfig = NetSerializerConfigParam(SerializerConfig);
MemberArgs.Source = NetSerializerValuePointer(&Source.RepMovementData);
MemberArgs.Target = NetSerializerValuePointer(&Target);
Serializer->Dequantize(Context, MemberArgs);
}
// Serialize into bitstream
static void Serialize(FNetSerializationContext& Context, const FNetSerializeArgs& Args)
{
const QuantizedType& Source = *reinterpret_cast<const QuantizedType*>(Args.Source);
FNetBitStreamWriter* Writer = Context.GetBitStreamWriter();
Writer->WriteBits(static_cast<uint32>(Source.bJustTeleported), 1);
Writer->WriteBits(static_cast<uint32>(Source.bJustTeleportedGrips), 1);
Writer->WriteBits(static_cast<uint32>(Source.bPausedTracking), 1);
if (Source.bPausedTracking != 0)
{
const FNetSerializer* Serializer = VectorNetQuantizeNetSerializer;
const FNetSerializerConfig* SerializerConfig = VectorNetQuantizeNetSerializerConfig;
FNetSerializeArgs MemberArgs = Args;
MemberArgs.NetSerializerConfig = NetSerializerConfigParam(SerializerConfig);
MemberArgs.Source = NetSerializerValuePointer(&Source.PausedTrackingLoc[0]);
Serializer->Serialize(Context, MemberArgs);
Writer->WriteBits(static_cast<uint32>(Source.PausedTrackingRot), 16);
}
// FRepMovementQuantizedDataCopy RepMovementData;
const FNetSerializer* Serializer = FRepMovementNetSerializer;
const FNetSerializerConfig* SerializerConfig = FRepMovementNetSerializerConfig;
FNetSerializeArgs MemberArgs = Args;
MemberArgs.NetSerializerConfig = NetSerializerConfigParam(SerializerConfig);
MemberArgs.Source = NetSerializerValuePointer(&Source.RepMovementData);
Serializer->Serialize(Context, MemberArgs);
}
// Deserialize from bitstream
static void Deserialize(FNetSerializationContext& Context, const FNetDeserializeArgs& Args)
{
QuantizedType& Target = *reinterpret_cast<QuantizedType*>(Args.Target);
FNetBitStreamReader* Reader = Context.GetBitStreamReader();
Target.bJustTeleported = Reader->ReadBits(1);
Target.bJustTeleportedGrips = Reader->ReadBits(1);
Target.bPausedTracking = Reader->ReadBits(1);
if (Target.bPausedTracking != 0)
{
const FNetSerializer* Serializer = VectorNetQuantizeNetSerializer;
const FNetSerializerConfig* SerializerConfig = VectorNetQuantizeNetSerializerConfig;
FNetDeserializeArgs MemberArgs = Args;
MemberArgs.NetSerializerConfig = NetSerializerConfigParam(SerializerConfig);
MemberArgs.Target = NetSerializerValuePointer(&Target.PausedTrackingLoc[0]);
Serializer->Deserialize(Context, MemberArgs);
Target.PausedTrackingRot = Reader->ReadBits(16);
}
// FRepMovementQuantizedDataCopy RepMovementData;
const FNetSerializer* Serializer = FRepMovementNetSerializer;
const FNetSerializerConfig* SerializerConfig = FRepMovementNetSerializerConfig;
FNetDeserializeArgs MemberArgs = Args;
MemberArgs.NetSerializerConfig = NetSerializerConfigParam(SerializerConfig);
MemberArgs.Target = NetSerializerValuePointer(&Target.RepMovementData);
Serializer->Deserialize(Context, MemberArgs);
}
static void SerializeDelta(FNetSerializationContext& Context, const FNetSerializeDeltaArgs& Args)
{
const QuantizedType& Source = *reinterpret_cast<const QuantizedType*>(Args.Source);
FNetBitStreamWriter* Writer = Context.GetBitStreamWriter();
Writer->WriteBits(static_cast<uint32>(Source.bJustTeleported), 1);
Writer->WriteBits(static_cast<uint32>(Source.bJustTeleportedGrips), 1);
Writer->WriteBits(static_cast<uint32>(Source.bPausedTracking), 1);
if (Source.bPausedTracking != 0)
{
const FNetSerializer* Serializer = VectorNetQuantizeNetSerializer;
const FNetSerializerConfig* SerializerConfig = VectorNetQuantizeNetSerializerConfig;
FNetSerializeArgs MemberArgs = Args;
MemberArgs.NetSerializerConfig = NetSerializerConfigParam(SerializerConfig);
MemberArgs.Source = NetSerializerValuePointer(&Source.PausedTrackingLoc[0]);
Serializer->Serialize(Context, MemberArgs);
Writer->WriteBits(static_cast<uint32>(Source.PausedTrackingRot), 16);
}
// FRepMovementQuantizedDataCopy RepMovementData;
const FNetSerializer* Serializer = FRepMovementNetSerializer;
const FNetSerializerConfig* SerializerConfig = FRepMovementNetSerializerConfig;
FNetSerializeDeltaArgs MemberArgs = Args;
MemberArgs.NetSerializerConfig = NetSerializerConfigParam(SerializerConfig);
MemberArgs.Source = NetSerializerValuePointer(&Source.RepMovementData);
Serializer->SerializeDelta(Context, MemberArgs);
}
static void DeserializeDelta(FNetSerializationContext& Context, const FNetDeserializeDeltaArgs& Args)
{
QuantizedType& Target = *reinterpret_cast<QuantizedType*>(Args.Target);
FNetBitStreamReader* Reader = Context.GetBitStreamReader();
Target.bJustTeleported = Reader->ReadBits(1);
Target.bJustTeleportedGrips = Reader->ReadBits(1);
Target.bPausedTracking = Reader->ReadBits(1);
if (Target.bPausedTracking != 0)
{
const FNetSerializer* Serializer = VectorNetQuantizeNetSerializer;
const FNetSerializerConfig* SerializerConfig = VectorNetQuantizeNetSerializerConfig;
FNetDeserializeArgs MemberArgs = Args;
MemberArgs.NetSerializerConfig = NetSerializerConfigParam(SerializerConfig);
MemberArgs.Target = NetSerializerValuePointer(&Target.PausedTrackingLoc[0]);
Serializer->Deserialize(Context, MemberArgs);
Target.PausedTrackingRot = Reader->ReadBits(16);
}
// FRepMovementQuantizedDataCopy RepMovementData;
const FNetSerializer* Serializer = FRepMovementNetSerializer;
const FNetSerializerConfig* SerializerConfig = FRepMovementNetSerializerConfig;
FNetDeserializeDeltaArgs MemberArgs = Args;
MemberArgs.NetSerializerConfig = NetSerializerConfigParam(SerializerConfig);
MemberArgs.Target = NetSerializerValuePointer(&Target.RepMovementData);
Serializer->DeserializeDelta(Context, MemberArgs);
}
// Compare two instances to see if they differ
static bool IsEqual(FNetSerializationContext& Context, const FNetIsEqualArgs& Args)
{
if (Args.bStateIsQuantized)
{
const QuantizedType& QuantizedValue0 = *reinterpret_cast<const QuantizedType*>(Args.Source0);
const QuantizedType& QuantizedValue1 = *reinterpret_cast<const QuantizedType*>(Args.Source1);
return FPlatformMemory::Memcmp(&QuantizedValue0, &QuantizedValue1, sizeof(QuantizedType)) == 0;
}
else
{
const SourceType& L = *reinterpret_cast<const SourceType*>(Args.Source0);
const SourceType& R = *reinterpret_cast<const SourceType*>(Args.Source1);
if (L.bJustTeleported != R.bJustTeleported) return false;
if (L.bJustTeleportedGrips != R.bJustTeleportedGrips) return false;
if (L.bPausedTracking != R.bPausedTracking) return false;
if (L.bPausedTracking)
{
if (!L.PausedTrackingLoc.Equals(R.PausedTrackingLoc)) return false;
if (!FMath::IsNearlyEqual(L.PausedTrackingRot, R.PausedTrackingRot)) return false;
}
const FNetSerializer* Serializer = FRepMovementNetSerializer;
const FNetSerializerConfig* SerializerConfig = FRepMovementNetSerializerConfig;
FNetIsEqualArgs MemberArgs = Args;
MemberArgs.NetSerializerConfig = NetSerializerConfigParam(SerializerConfig);
MemberArgs.Source0 = NetSerializerValuePointer(&L);
MemberArgs.Source1 = NetSerializerValuePointer(&R);
return Serializer->IsEqual(Context, MemberArgs);
}
}
static bool Validate(FNetSerializationContext& Context, const FNetValidateArgs& Args)
{
const SourceType& Source = *reinterpret_cast<SourceType*>(Args.Source);
const FNetSerializer* Serializer = FRepMovementNetSerializer;
const FNetSerializerConfig* SerializerConfig = FRepMovementNetSerializerConfig;
FNetValidateArgs MemberArgs = Args;
MemberArgs.NetSerializerConfig = NetSerializerConfigParam(SerializerConfig);
MemberArgs.Source = NetSerializerValuePointer(&Source);
return Serializer->Validate(Context, MemberArgs);
}
static void Apply(FNetSerializationContext& Context, const FNetApplyArgs& Args)
{
const SourceType& Source = *reinterpret_cast<const SourceType*>(Args.Source);
SourceType& Target = *reinterpret_cast<SourceType*>(Args.Target);
Target.bJustTeleported = Source.bJustTeleported;
Target.bJustTeleportedGrips = Source.bJustTeleportedGrips;
Target.bPausedTracking = Source.bPausedTracking;
if (Target.bPausedTracking)
{
Target.PausedTrackingLoc = Source.PausedTrackingLoc;
Target.PausedTrackingRot = Source.PausedTrackingRot;
}
else
{
Target.PausedTrackingLoc = FVector::ZeroVector;
Target.PausedTrackingRot = 0.0f;
}
const FNetSerializer* Serializer = FRepMovementNetSerializer;
const FNetSerializerConfig* SerializerConfig = FRepMovementNetSerializerConfig;
FNetApplyArgs MemberArgs = Args;
MemberArgs.NetSerializerConfig = NetSerializerConfigParam(SerializerConfig);
MemberArgs.Source = NetSerializerValuePointer(&Source);
MemberArgs.Target = NetSerializerValuePointer(&Target);
Serializer->Apply(Context, MemberArgs);
}
};
static const FName PropertyNetSerializerRegistry_NAME_RepMovementVRCharacter("RepMovementVRCharacter");
UE_NET_IMPLEMENT_NAMED_STRUCT_NETSERIALIZER_INFO(PropertyNetSerializerRegistry_NAME_RepMovementVRCharacter, FRepMovementVRCharacterNetSerializer);
FRepMovementVRCharacterNetSerializer::FNetSerializerRegistryDelegates::~FNetSerializerRegistryDelegates()
{
UE_NET_UNREGISTER_NETSERIALIZER_INFO(PropertyNetSerializerRegistry_NAME_RepMovementVRCharacter);
}
void FRepMovementVRCharacterNetSerializer::FNetSerializerRegistryDelegates::OnPreFreezeNetSerializerRegistry()
{
InitNetSerializer();
UE_NET_REGISTER_NETSERIALIZER_INFO(PropertyNetSerializerRegistry_NAME_RepMovementVRCharacter);
}
UE_NET_IMPLEMENT_SERIALIZER(FRepMovementVRCharacterNetSerializer);
}

View file

@ -0,0 +1,250 @@
#include "Serializers/FTransformNetQuantizeNetSerializer.h"
#include "Serializers/SerializerHelpers.h"
#include "Iris/Serialization/NetSerializerDelegates.h"
#include "Iris/Serialization/NetSerializers.h"
#include "Iris/Serialization/PackedVectorNetSerializers.h"
#include "Iris/ReplicationState/PropertyNetSerializerInfoRegistry.h"
#include "Iris/ReplicationState/ReplicationStateDescriptorBuilder.h"
#include "VRBPDatatypes.h"
namespace UE::Net
{
// -----------------------------------------------------------------------------
// Iris serializer for FTransformNetQuantize
// -----------------------------------------------------------------------------
struct FTransformNetQuantizeNetSerializer
{
inline static const FVectorNetQuantize100NetSerializerConfig Quantize100SerializerConfig;
inline static const FNetSerializerConfig* VectorNetQuantizeNetSerializerConfig = &Quantize100SerializerConfig;
inline static const FNetSerializer* VectorNetQuantizeNetSerializer;
class FNetSerializerRegistryDelegates final : private UE::Net::FNetSerializerRegistryDelegates
{
public:
virtual ~FNetSerializerRegistryDelegates();
void InitNetSerializer()
{
FTransformNetQuantizeNetSerializer::VectorNetQuantizeNetSerializer = &UE_NET_GET_SERIALIZER(FVectorNetQuantize100NetSerializer);
}
private:
virtual void OnPreFreezeNetSerializerRegistry() override;
//virtual void OnPostFreezeNetSerializerRegistry() override;
};
inline static FTransformNetQuantizeNetSerializer::FNetSerializerRegistryDelegates NetSerializerRegistryDelegates;
/** Version is required. */
static constexpr uint32 Version = 0;
/*struct alignas(8) FQuantizedData
{
uint64 Position[4]; // We don't need to store double for tracked device positions, but their forwarded serializer uses it
uint16 Rotation[3];
uint64 Scale[4]; // We don't need to store double for tracked device positions, but their forwarded serializer uses it
uint8 UseHighPrecision : 1;
};*/
typedef FTransform_NetQuantize SourceType;
typedef FTransformNetQuantizeQuantizedData QuantizedType;
typedef FTransformNetQuantizeNetSerializerConfig ConfigType;
inline static const ConfigType DefaultConfig;
/** Set to false when a same value delta compression method is undesirable, for example when the serializer only writes a single bit for the state. */
static constexpr bool bUseDefaultDelta = true;
// Should likely handle delta custom eventually
// Called to create a "quantized snapshot" of the struct
static void Quantize(FNetSerializationContext& Context, const FNetQuantizeArgs& Args)
{
// Actually do the real quantization step here next instead of just in serialize, will save on memory overall
const SourceType& Source = *reinterpret_cast<const SourceType*>(Args.Source);
QuantizedType& Target = *reinterpret_cast<QuantizedType*>(Args.Target);
static const auto CVarRepHighPrecisionTransforms = IConsoleManager::Get().FindConsoleVariable(TEXT("vrexp.RepHighPrecisionTransforms"));
bool bUseHighPrecision = CVarRepHighPrecisionTransforms->GetBool();
Target.UseHighPrecision = bUseHighPrecision;
FVector rTranslation = Source.GetTranslation();
FVector rScale3D = Source.GetScale3D();
FRotator rRotation = Source.Rotator();
if (bUseHighPrecision)
{
Target.Position[0] = rTranslation.X;
Target.Position[1] = rTranslation.Y;
Target.Position[2] = rTranslation.Z;
Target.Scale[0] = rScale3D.X;
Target.Scale[1] = rScale3D.Y;
Target.Scale[2] = rScale3D.Z;
Target.Rotation[0] = FRotator::CompressAxisToShort(rRotation.Pitch);
Target.Rotation[1] = FRotator::CompressAxisToShort(rRotation.Yaw);
Target.Rotation[2] = FRotator::CompressAxisToShort(rRotation.Roll);
}
else
{
const FNetSerializer* Serializer = VectorNetQuantizeNetSerializer;
const FNetSerializerConfig* SerializerConfig = VectorNetQuantizeNetSerializerConfig;
FNetQuantizeArgs MemberArgs = Args;
MemberArgs.NetSerializerConfig = NetSerializerConfigParam(SerializerConfig);
MemberArgs.Source = NetSerializerValuePointer(&rTranslation);
MemberArgs.Target = NetSerializerValuePointer(&Target.Position[0]);
Serializer->Quantize(Context, MemberArgs);
MemberArgs.Source = NetSerializerValuePointer(&rScale3D);
MemberArgs.Target = NetSerializerValuePointer(&Target.Scale[0]);
Serializer->Quantize(Context, MemberArgs);
Target.Rotation[0] = FRotator::CompressAxisToShort(rRotation.Pitch);
Target.Rotation[1] = FRotator::CompressAxisToShort(rRotation.Yaw);
Target.Rotation[2] = FRotator::CompressAxisToShort(rRotation.Roll);
}
}
// Called to apply the quantized snapshot back to gameplay memory
static void Dequantize(FNetSerializationContext& Context, const FNetDequantizeArgs& Args)
{
const QuantizedType& Source = *reinterpret_cast<const QuantizedType*>(Args.Source);
SourceType& Target = *reinterpret_cast<SourceType*>(Args.Target);
bool bUseHighPrecision = Source.UseHighPrecision != 0;
FVector rTranslation;
FVector rScale3D;
FRotator rRotation;
if (bUseHighPrecision)
{
rTranslation.X = Source.Position[0];
rTranslation.Y = Source.Position[1];
rTranslation.Z = Source.Position[2];
rScale3D.X = Source.Scale[0];
rScale3D.Y = Source.Scale[1];
rScale3D.Z = Source.Scale[2];
rRotation.Pitch = FRotator::DecompressAxisFromShort(Source.Rotation[0]);
rRotation.Yaw = FRotator::DecompressAxisFromShort(Source.Rotation[1]);
rRotation.Roll = FRotator::DecompressAxisFromShort(Source.Rotation[2]);
}
else
{
const FNetSerializer* Serializer = VectorNetQuantizeNetSerializer;
const FNetSerializerConfig* SerializerConfig = VectorNetQuantizeNetSerializerConfig;
FNetDequantizeArgs MemberArgs = Args;
MemberArgs.NetSerializerConfig = NetSerializerConfigParam(SerializerConfig);
MemberArgs.Source = NetSerializerValuePointer(&Source.Position[0]);
MemberArgs.Target = NetSerializerValuePointer(&rTranslation);
Serializer->Dequantize(Context, MemberArgs);
MemberArgs.Source = NetSerializerValuePointer(&Source.Scale[0]);
MemberArgs.Target = NetSerializerValuePointer(&rScale3D);
Serializer->Dequantize(Context, MemberArgs);
rRotation.Pitch = FRotator::DecompressAxisFromShort(Source.Rotation[0]);
rRotation.Yaw = FRotator::DecompressAxisFromShort(Source.Rotation[1]);
rRotation.Roll = FRotator::DecompressAxisFromShort(Source.Rotation[2]);
}
Target.SetComponents(rRotation.Quaternion(), rTranslation, rScale3D);
Target.NormalizeRotation();
}
// Serialize into bitstream
static void Serialize(FNetSerializationContext& Context, const FNetSerializeArgs& Args)
{
const QuantizedType& Source = *reinterpret_cast<const QuantizedType*>(Args.Source);
FNetBitStreamWriter* Writer = Context.GetBitStreamWriter();
Writer->WriteBits(static_cast<uint32>(Source.UseHighPrecision), 1);
const FNetSerializer* Serializer = VectorNetQuantizeNetSerializer;
const FNetSerializerConfig* SerializerConfig = VectorNetQuantizeNetSerializerConfig;
FNetSerializeArgs MemberArgs = Args;
MemberArgs.NetSerializerConfig = NetSerializerConfigParam(SerializerConfig);
MemberArgs.Source = NetSerializerValuePointer(&Source.Position[0]);
Serializer->Serialize(Context, MemberArgs);
MemberArgs.Source = NetSerializerValuePointer(&Source.Scale[0]);
Serializer->Serialize(Context, MemberArgs);
Writer->WriteBits(static_cast<uint32>(Source.Rotation[0]), 16);
Writer->WriteBits(static_cast<uint32>(Source.Rotation[1]), 16);
Writer->WriteBits(static_cast<uint32>(Source.Rotation[2]), 16);
}
// Deserialize from bitstream
static void Deserialize(FNetSerializationContext& Context, const FNetDeserializeArgs& Args)
{
QuantizedType& Target = *reinterpret_cast<QuantizedType*>(Args.Target);
FNetBitStreamReader* Reader = Context.GetBitStreamReader();
Target.UseHighPrecision = Reader->ReadBits(1) != 0;
const FNetSerializer* Serializer = VectorNetQuantizeNetSerializer;
const FNetSerializerConfig* SerializerConfig = VectorNetQuantizeNetSerializerConfig;
FNetDeserializeArgs MemberArgs = Args;
MemberArgs.NetSerializerConfig = NetSerializerConfigParam(SerializerConfig);
MemberArgs.Target = NetSerializerValuePointer(&Target.Position[0]);
Serializer->Deserialize(Context, MemberArgs);
MemberArgs.NetSerializerConfig = NetSerializerConfigParam(SerializerConfig);
MemberArgs.Target = NetSerializerValuePointer(&Target.Scale[0]);
Serializer->Deserialize(Context, MemberArgs);
Target.Rotation[0] = Reader->ReadBits(16);
Target.Rotation[1] = Reader->ReadBits(16);
Target.Rotation[2] = Reader->ReadBits(16);
}
// Compare two instances to see if they differ
static bool IsEqual(FNetSerializationContext& Context, const FNetIsEqualArgs& Args)
{
if (Args.bStateIsQuantized)
{
const QuantizedType& QuantizedValue0 = *reinterpret_cast<const QuantizedType*>(Args.Source0);
const QuantizedType& QuantizedValue1 = *reinterpret_cast<const QuantizedType*>(Args.Source1);
return FPlatformMemory::Memcmp(&QuantizedValue0, &QuantizedValue1, sizeof(QuantizedType)) == 0;
}
else
{
const SourceType& L = *reinterpret_cast<const SourceType*>(Args.Source0);
const SourceType& R = *reinterpret_cast<const SourceType*>(Args.Source1);
if (!L.Equals(R)) return false;
return true;
}
}
};
static const FName PropertyNetSerializerRegistry_NAME_TransformNetQuantize("Transform_NetQuantize");
UE_NET_IMPLEMENT_NAMED_STRUCT_NETSERIALIZER_INFO(PropertyNetSerializerRegistry_NAME_TransformNetQuantize, FTransformNetQuantizeNetSerializer);
FTransformNetQuantizeNetSerializer::FNetSerializerRegistryDelegates::~FNetSerializerRegistryDelegates()
{
UE_NET_UNREGISTER_NETSERIALIZER_INFO(PropertyNetSerializerRegistry_NAME_TransformNetQuantize);
}
void FTransformNetQuantizeNetSerializer::FNetSerializerRegistryDelegates::OnPreFreezeNetSerializerRegistry()
{
InitNetSerializer();
UE_NET_REGISTER_NETSERIALIZER_INFO(PropertyNetSerializerRegistry_NAME_TransformNetQuantize);
}
UE_NET_IMPLEMENT_SERIALIZER(FTransformNetQuantizeNetSerializer);
}

View file

@ -16,6 +16,16 @@
#include "XRMotionControllerBase.h"
#include "NavFilters/NavigationQueryFilter.h"
#include "Misc/EngineNetworkCustomVersion.h"
#include "Serializers/SerializerHelpers.h"
#include "Iris/Serialization/NetSerializerDelegates.h"
#include "Iris/Serialization/NetSerializers.h"
#include "Iris/Serialization/PackedVectorNetSerializers.h"
#include "Iris/ReplicationState/PropertyNetSerializerInfoRegistry.h"
#include "Serializers/FTransformNetQuantizeNetSerializer.h"
//#include "Iris/ReplicationState/ReplicationStateDescriptorBuilder.h"
//#include "Runtime/Engine/Private/EnginePrivate.h"
#if WITH_PUSH_MODEL
@ -1354,4 +1364,441 @@ bool FRepMovementVRCharacter::NetSerialize(FArchive& Ar, class UPackageMap* Map,
}
return true;
}
}
// IRIS NET SERIALIZERS
namespace UE::Net
{
// -----------------------------------------------------------------------------
// Iris serializer for FVRReplicatedCapsuleHeight
// -----------------------------------------------------------------------------
struct FVRReplicatedCapsuleHeightNetSerializer
{
class FNetSerializerRegistryDelegates final : private UE::Net::FNetSerializerRegistryDelegates
{
public:
virtual ~FNetSerializerRegistryDelegates();
private:
virtual void OnPreFreezeNetSerializerRegistry() override;
//virtual void OnPostFreezeNetSerializerRegistry() override;
};
inline static FVRReplicatedCapsuleHeightNetSerializer::FNetSerializerRegistryDelegates NetSerializerRegistryDelegates;
/** Version is required. */
static constexpr uint32 Version = 0;
struct alignas(8) FQuantizedData
{
uint32 CompressedFloat;
};
typedef FVRReplicatedCapsuleHeight SourceType;
typedef FQuantizedData QuantizedType;
typedef FVRReplicatedCapsuleHeightNetSerializerConfig ConfigType;
inline static const ConfigType DefaultConfig;
/** Set to false when a same value delta compression method is undesirable, for example when the serializer only writes a single bit for the state. */
static constexpr bool bUseDefaultDelta = true;
// Not doing delta, the majority of the time a single bit (bool) controls the serialization of the entirity
// Called to create a "quantized snapshot" of the struct
static void Quantize(FNetSerializationContext& Context, const FNetQuantizeArgs& Args)
{
// Actually do the real quantization step here next instead of just in serialize, will save on memory overall
const SourceType& Source = *reinterpret_cast<const SourceType*>(Args.Source);
QuantizedType& Target = *reinterpret_cast<QuantizedType*>(Args.Target);
Target.CompressedFloat = GetCompressedFloat<1024, 18>(Source.CapsuleHeight);
}
// Called to apply the quantized snapshot back to gameplay memory
static void Dequantize(FNetSerializationContext& Context, const FNetDequantizeArgs& Args)
{
const QuantizedType& Source = *reinterpret_cast<const QuantizedType*>(Args.Source);
SourceType& Target = *reinterpret_cast<SourceType*>(Args.Target);
Target.CapsuleHeight = GetDecompressedFloat<1024, 18>(Source.CompressedFloat);
}
// Serialize into bitstream
static void Serialize(FNetSerializationContext& Context, const FNetSerializeArgs& Args)
{
const QuantizedType& Source = *reinterpret_cast<const QuantizedType*>(Args.Source);
FNetBitStreamWriter* Writer = Context.GetBitStreamWriter();
Writer->WriteBits(static_cast<uint32>(Source.CompressedFloat), 18);
}
// Deserialize from bitstream
static void Deserialize(FNetSerializationContext& Context, const FNetDeserializeArgs& Args)
{
QuantizedType& Target = *reinterpret_cast<QuantizedType*>(Args.Target);
FNetBitStreamReader* Reader = Context.GetBitStreamReader();
Target.CompressedFloat = Reader->ReadBits(18);
}
// Compare two instances to see if they differ
static bool IsEqual(FNetSerializationContext& Context, const FNetIsEqualArgs& Args)
{
if (Args.bStateIsQuantized)
{
const QuantizedType& QuantizedValue0 = *reinterpret_cast<const QuantizedType*>(Args.Source0);
const QuantizedType& QuantizedValue1 = *reinterpret_cast<const QuantizedType*>(Args.Source1);
return FPlatformMemory::Memcmp(&QuantizedValue0, &QuantizedValue1, sizeof(QuantizedType)) == 0;
}
else
{
const SourceType& L = *reinterpret_cast<const SourceType*>(Args.Source0);
const SourceType& R = *reinterpret_cast<const SourceType*>(Args.Source1);
return FMath::IsNearlyEqual(L.CapsuleHeight, R.CapsuleHeight);
}
}
};
static const FName PropertyNetSerializerRegistry_NAME_FVRReplicatedCapsuleHeight("VRReplicatedCapsuleHeight");
UE_NET_IMPLEMENT_NAMED_STRUCT_NETSERIALIZER_INFO(PropertyNetSerializerRegistry_NAME_FVRReplicatedCapsuleHeight, FVRReplicatedCapsuleHeightNetSerializer);
FVRReplicatedCapsuleHeightNetSerializer::FNetSerializerRegistryDelegates::~FNetSerializerRegistryDelegates()
{
UE_NET_UNREGISTER_NETSERIALIZER_INFO(PropertyNetSerializerRegistry_NAME_FVRReplicatedCapsuleHeight);
}
void FVRReplicatedCapsuleHeightNetSerializer::FNetSerializerRegistryDelegates::OnPreFreezeNetSerializerRegistry()
{
UE_NET_REGISTER_NETSERIALIZER_INFO(PropertyNetSerializerRegistry_NAME_FVRReplicatedCapsuleHeight);
}
UE_NET_IMPLEMENT_SERIALIZER(FVRReplicatedCapsuleHeightNetSerializer);
// -----------------------------------------------------------------------------
// Iris serializer for FVRSeatedCharacterInfo
// -----------------------------------------------------------------------------
struct FVRSeatedCharacterInfoNetSerializer
{
inline static const FVectorNetQuantize100NetSerializerConfig FTransformQuantizeSerializerConfig;
inline static const FObjectPtrNetSerializerConfig ObjectPtrNetSerializerConfig;
inline static const FNetSerializerConfig* FTransformQuantizeSerializerConfigPtr = &FTransformQuantizeSerializerConfig;
inline static const FNetSerializer* FTransformQuantizeNetSerializerPtr;
inline static const FNetSerializerConfig* FObjectPtrSerializerConfigPtr = &ObjectPtrNetSerializerConfig;
inline static const FNetSerializer* FObjectPtrNetSerializerPtr;
class FNetSerializerRegistryDelegates final : private UE::Net::FNetSerializerRegistryDelegates
{
public:
virtual ~FNetSerializerRegistryDelegates();
void InitNetSerializer()
{
FVRSeatedCharacterInfoNetSerializer::FTransformQuantizeNetSerializerPtr = &UE_NET_GET_SERIALIZER(FTransformNetQuantizeNetSerializer);
FVRSeatedCharacterInfoNetSerializer::FObjectPtrNetSerializerPtr = &UE_NET_GET_SERIALIZER(FObjectPtrNetSerializer);
}
private:
virtual void OnPreFreezeNetSerializerRegistry() override;
//virtual void OnPostFreezeNetSerializerRegistry() override;
};
inline static FVRSeatedCharacterInfoNetSerializer::FNetSerializerRegistryDelegates NetSerializerRegistryDelegates;
// Version is required.
static constexpr uint32 Version = 0;
struct alignas(8) FQuantizedData
{
uint8 bSitting : 1;
uint8 bZeroToHead : 1;
FTransformNetQuantizeQuantizedData StoredTargetTransform;
FObjectNetSerializerQuantizedReferenceStorage SeatParent;
uint8 PostSeatedMovementMode;
// Only if bSitting is true
FTransformNetQuantizeQuantizedData InitialRelCameraTransform;
uint32 AllowedRadius; // Flt 256, 16
uint32 AllowedRadiusThreshold; // Flt 256, 16
//uint8 bIsOverThreshold : 1; // Not Replicated
//uint32 CurrentThresholdScaler; // Not Replicated
};
typedef FVRSeatedCharacterInfo SourceType;
typedef FQuantizedData QuantizedType;
typedef FVRSeatedCharacterInfoNetSerializerConfig ConfigType;
inline static const ConfigType DefaultConfig;
// Set to false when a same value delta compression method is undesirable, for example when the serializer only writes a single bit for the state.
static constexpr bool bUseDefaultDelta = true;
// TODO: This is actually a struct that could use some delta serialization implementations.
// Called to create a "quantized snapshot" of the struct
static void Quantize(FNetSerializationContext& Context, const FNetQuantizeArgs& Args)
{
// Actually do the real quantization step here next instead of just in serialize, will save on memory overall
const SourceType& Source = *reinterpret_cast<const SourceType*>(Args.Source);
QuantizedType& Target = *reinterpret_cast<QuantizedType*>(Args.Target);
Target.bSitting = Source.bSitting;
Target.bZeroToHead = Source.bZeroToHead;
const FNetSerializer* Serializer = FTransformQuantizeNetSerializerPtr;
const FNetSerializerConfig* SerializerConfig = FTransformQuantizeSerializerConfigPtr;
//FTransformNetQuantizeQuantizedData StoredTargetTransform;
FNetQuantizeArgs MemberArgs = Args;
MemberArgs.NetSerializerConfig = NetSerializerConfigParam(SerializerConfig);
MemberArgs.Source = NetSerializerValuePointer(&Source.StoredTargetTransform);
MemberArgs.Target = NetSerializerValuePointer(&Target.StoredTargetTransform);
Serializer->Quantize(Context, MemberArgs);
// Only if bSitting is true
//FTransformNetQuantizeQuantizedData InitialRelCameraTransform;
//uint32 AllowedRadius; // Flt 256, 16
//uint32 AllowedRadiusThreshold; // Flt 256, 16
if (Source.bSitting)
{
// Initial relative transform doesn't need to be touched or set if not bsitting
MemberArgs.Source = NetSerializerValuePointer(&Source.InitialRelCameraTransform);
MemberArgs.Target = NetSerializerValuePointer(&Target.InitialRelCameraTransform);
Serializer->Quantize(Context, MemberArgs);
Target.AllowedRadius = GetCompressedFloat<256,16>(Source.AllowedRadius);
Target.AllowedRadiusThreshold = GetCompressedFloat<256, 16>(Source.AllowedRadiusThreshold);
}
const FNetSerializer* ObjSerializer = FObjectPtrNetSerializerPtr;
const FNetSerializerConfig* ObjSerializerConfig = FObjectPtrSerializerConfigPtr;
//FObjectNetSerializerQuantizedReferenceStorage SeatParent;
FNetQuantizeArgs MemberArgsObj = Args;
MemberArgsObj.NetSerializerConfig = NetSerializerConfigParam(ObjSerializerConfig);
MemberArgsObj.Source = NetSerializerValuePointer(&Source.SeatParent);
MemberArgsObj.Target = NetSerializerValuePointer(&Target.SeatParent);
ObjSerializer->Quantize(Context, MemberArgsObj);
// Store full 8 bits
Target.PostSeatedMovementMode = (uint8)Source.PostSeatedMovementMode;
}
// Called to apply the quantized snapshot back to gameplay memory
static void Dequantize(FNetSerializationContext& Context, const FNetDequantizeArgs& Args)
{
const QuantizedType& Source = *reinterpret_cast<const QuantizedType*>(Args.Source);
SourceType& Target = *reinterpret_cast<SourceType*>(Args.Target);
Target.bSitting = Source.bSitting != 0;
Target.bZeroToHead = Source.bZeroToHead != 0;
const FNetSerializer* Serializer = FTransformQuantizeNetSerializerPtr;
const FNetSerializerConfig* SerializerConfig = FTransformQuantizeSerializerConfigPtr;
//FTransformNetQuantizeQuantizedData StoredTargetTransform;
FNetDequantizeArgs MemberArgs = Args;
MemberArgs.NetSerializerConfig = NetSerializerConfigParam(SerializerConfig);
MemberArgs.Source = NetSerializerValuePointer(&Source.StoredTargetTransform);
MemberArgs.Target = NetSerializerValuePointer(&Target.StoredTargetTransform);
Serializer->Dequantize(Context, MemberArgs);
// Only if bSitting is true
//FTransformNetQuantizeQuantizedData InitialRelCameraTransform;
//uint32 AllowedRadius; // Flt 256, 16
//uint32 AllowedRadiusThreshold; // Flt 256, 16
if (Target.bSitting != 0)
{
// Initial relative transform doesn't need to be touched or set if not bsitting
MemberArgs.Source = NetSerializerValuePointer(&Source.InitialRelCameraTransform);
MemberArgs.Target = NetSerializerValuePointer(&Target.InitialRelCameraTransform);
Serializer->Dequantize(Context, MemberArgs);
Target.AllowedRadius = GetDecompressedFloat<256, 16>(Source.AllowedRadius);
Target.AllowedRadiusThreshold = GetDecompressedFloat<256, 16>(Source.AllowedRadiusThreshold);
}
const FNetSerializer* ObjSerializer = FObjectPtrNetSerializerPtr;
const FNetSerializerConfig* ObjSerializerConfig = FObjectPtrSerializerConfigPtr;
//FObjectNetSerializerQuantizedReferenceStorage SeatParent;
FNetDequantizeArgs MemberArgsObj = Args;
MemberArgsObj.NetSerializerConfig = NetSerializerConfigParam(ObjSerializerConfig);
MemberArgsObj.Source = NetSerializerValuePointer(&Source.SeatParent);
MemberArgsObj.Target = NetSerializerValuePointer(&Target.SeatParent);
ObjSerializer->Dequantize(Context, MemberArgsObj);
// Store full 8 bits
Target.PostSeatedMovementMode = (EVRConjoinedMovementModes)Source.PostSeatedMovementMode;
}
// Serialize into bitstream
static void Serialize(FNetSerializationContext& Context, const FNetSerializeArgs& Args)
{
const QuantizedType& Source = *reinterpret_cast<const QuantizedType*>(Args.Source);
FNetBitStreamWriter* Writer = Context.GetBitStreamWriter();
Writer->WriteBits(static_cast<uint32>(Source.bSitting), 1);
Writer->WriteBits(static_cast<uint32>(Source.bZeroToHead), 1);
const FNetSerializer* Serializer = FTransformQuantizeNetSerializerPtr;
const FNetSerializerConfig* SerializerConfig = FTransformQuantizeSerializerConfigPtr;
//FTransformNetQuantizeQuantizedData StoredTargetTransform;
FNetSerializeArgs MemberArgs = Args;
MemberArgs.NetSerializerConfig = NetSerializerConfigParam(SerializerConfig);
MemberArgs.Source = NetSerializerValuePointer(&Source.StoredTargetTransform);
Serializer->Serialize(Context, MemberArgs);
if (Source.bSitting != 0)
{
// Initial relative transform doesn't need to be touched or set if not bsitting
MemberArgs.NetSerializerConfig = NetSerializerConfigParam(SerializerConfig);
MemberArgs.Source = NetSerializerValuePointer(&Source.InitialRelCameraTransform);
Serializer->Serialize(Context, MemberArgs);
Writer->WriteBits(static_cast<uint32>(Source.AllowedRadius), 16);
Writer->WriteBits(static_cast<uint32>(Source.AllowedRadiusThreshold), 16);
}
const FNetSerializer* ObjSerializer = FObjectPtrNetSerializerPtr;
const FNetSerializerConfig* ObjSerializerConfig = FObjectPtrSerializerConfigPtr;
//FObjectNetSerializerQuantizedReferenceStorage SeatParent;
FNetSerializeArgs MemberArgsObj = Args;
MemberArgsObj.NetSerializerConfig = NetSerializerConfigParam(ObjSerializerConfig);
MemberArgsObj.Source = NetSerializerValuePointer(&Source.SeatParent);
ObjSerializer->Serialize(Context, MemberArgsObj);
Writer->WriteBits(static_cast<uint32>(Source.PostSeatedMovementMode), 8);
}
// Deserialize from bitstream
static void Deserialize(FNetSerializationContext& Context, const FNetDeserializeArgs& Args)
{
QuantizedType& Target = *reinterpret_cast<QuantizedType*>(Args.Target);
FNetBitStreamReader* Reader = Context.GetBitStreamReader();
Target.bSitting = Reader->ReadBits(1) != 0;
Target.bZeroToHead = Reader->ReadBits(1) != 0;
const FNetSerializer* Serializer = FTransformQuantizeNetSerializerPtr;
const FNetSerializerConfig* SerializerConfig = FTransformQuantizeSerializerConfigPtr;
//FTransformNetQuantizeQuantizedData StoredTargetTransform;
FNetDeserializeArgs MemberArgs = Args;
MemberArgs.NetSerializerConfig = NetSerializerConfigParam(SerializerConfig);
MemberArgs.Target = NetSerializerValuePointer(&Target.StoredTargetTransform);
Serializer->Deserialize(Context, MemberArgs);
if (Target.bSitting != 0)
{
// Initial relative transform doesn't need to be touched or set if not bsitting
MemberArgs.NetSerializerConfig = NetSerializerConfigParam(SerializerConfig);
MemberArgs.Target = NetSerializerValuePointer(&Target.InitialRelCameraTransform);
Serializer->Deserialize(Context, MemberArgs);
Target.AllowedRadius = Reader->ReadBits(16);
Target.AllowedRadiusThreshold = Reader->ReadBits(16);
}
const FNetSerializer* ObjSerializer = FObjectPtrNetSerializerPtr;
const FNetSerializerConfig* ObjSerializerConfig = FObjectPtrSerializerConfigPtr;
//FObjectNetSerializerQuantizedReferenceStorage SeatParent;
FNetDeserializeArgs MemberArgsObj = Args;
MemberArgsObj.NetSerializerConfig = NetSerializerConfigParam(ObjSerializerConfig);
MemberArgsObj.Target = NetSerializerValuePointer(&Target.SeatParent);
ObjSerializer->Deserialize(Context, MemberArgsObj);
Target.PostSeatedMovementMode = Reader->ReadBits(8);
}
// Compare two instances to see if they differ
static bool IsEqual(FNetSerializationContext& Context, const FNetIsEqualArgs& Args)
{
if (Args.bStateIsQuantized)
{
const QuantizedType& QuantizedValue0 = *reinterpret_cast<const QuantizedType*>(Args.Source0);
const QuantizedType& QuantizedValue1 = *reinterpret_cast<const QuantizedType*>(Args.Source1);
return FPlatformMemory::Memcmp(&QuantizedValue0, &QuantizedValue1, sizeof(QuantizedType)) == 0;
}
else
{
const SourceType& L = *reinterpret_cast<const SourceType*>(Args.Source0);
const SourceType& R = *reinterpret_cast<const SourceType*>(Args.Source1);
if (L.bSitting != R.bSitting) return false;
if (L.SeatParent != R.SeatParent) return false;
if(!FMath::IsNearlyEqual(L.AllowedRadius, R.AllowedRadius)) return false;
if (!FMath::IsNearlyEqual(L.AllowedRadiusThreshold, R.AllowedRadiusThreshold)) return false;
if (L.bZeroToHead != R.bZeroToHead) return false;
if (!L.StoredTargetTransform.Equals(R.StoredTargetTransform)) return false;
if (L.bSitting && !L.InitialRelCameraTransform.Equals(R.InitialRelCameraTransform)) return false;
return true;
}
}
static void Apply(FNetSerializationContext&, const FNetApplyArgs& Args)
{
const SourceType& Source = *reinterpret_cast<const SourceType*>(Args.Source);
SourceType& Target = *reinterpret_cast<SourceType*>(Args.Target);
Target.bSitting = Source.bSitting;
Target.bZeroToHead = Source.bZeroToHead;
Target.StoredTargetTransform = Source.StoredTargetTransform;
if (Target.bSitting)
{
Target.InitialRelCameraTransform = Source.InitialRelCameraTransform;
Target.AllowedRadius = Source.AllowedRadius;
Target.AllowedRadiusThreshold = Source.AllowedRadiusThreshold;
}
else
{
// Clear non repped values
Target.InitialRelCameraTransform = FTransform::Identity;
Target.AllowedRadius = 0.0f;
Target.AllowedRadiusThreshold = 0.0f;
}
Target.SeatParent = Source.SeatParent;
Target.PostSeatedMovementMode = Source.PostSeatedMovementMode;
}
};
static const FName PropertyNetSerializerRegistry_NAME_FVRSeatedCharacterInfo("VRSeatedCharacterInfo");
UE_NET_IMPLEMENT_NAMED_STRUCT_NETSERIALIZER_INFO(PropertyNetSerializerRegistry_NAME_FVRSeatedCharacterInfo, FVRSeatedCharacterInfoNetSerializer);
FVRSeatedCharacterInfoNetSerializer::FNetSerializerRegistryDelegates::~FNetSerializerRegistryDelegates()
{
UE_NET_UNREGISTER_NETSERIALIZER_INFO(PropertyNetSerializerRegistry_NAME_FVRSeatedCharacterInfo);
}
void FVRSeatedCharacterInfoNetSerializer::FNetSerializerRegistryDelegates::OnPreFreezeNetSerializerRegistry()
{
InitNetSerializer();
UE_NET_REGISTER_NETSERIALIZER_INFO(PropertyNetSerializerRegistry_NAME_FVRSeatedCharacterInfo);
}
UE_NET_IMPLEMENT_SERIALIZER(FVRSeatedCharacterInfoNetSerializer);
}

View file

@ -919,6 +919,12 @@ void UVRCharacterMovementComponent::PhysWalking(float deltaTime, int32 Iteration
const float timeTick = GetSimulationTimeStep(remainingTime, Iterations);
remainingTime -= timeTick;
#if UE_WITH_REMOTE_OBJECT_HANDLE
//Scale down impact force if CharacterMoveComponent is taking multiple substeps.
const float LastFrameDt = GetWorld()->GetDeltaSeconds();
PhysicsForceSubsteppingFactor = timeTick / LastFrameDt;
#endif
// Save current values
UPrimitiveComponent * const OldBase = GetMovementBase();
const FVector PreviousBaseLocation = (OldBase != NULL) ? OldBase->GetComponentLocation() : FVector::ZeroVector;
@ -3223,8 +3229,8 @@ void UVRCharacterMovementComponent::PhysNavWalking(float deltaTime, int32 Iterat
Acceleration = ProjectToGravityFloor(Acceleration);
//if (!HasRootMotion())
//{
CalcVelocity(deltaTime, GroundFriction, false, BrakingDecelerationWalking);
devCodeVR(ensureMsgf(!Velocity.ContainsNaN(), TEXT("PhysNavWalking: Velocity contains NaN after CalcVelocity (%s)\n%s"), *GetPathNameSafe(this), *Velocity.ToString()));
CalcVelocity(deltaTime, GroundFriction, false, BrakingDecelerationWalking);
devCodeVR(ensureMsgf(!Velocity.ContainsNaN(), TEXT("PhysNavWalking: Velocity contains NaN after CalcVelocity (%s)\n%s"), *GetPathNameSafe(this), *Velocity.ToString()));
//}
ApplyRootMotionToVelocity(deltaTime);
@ -3270,7 +3276,7 @@ void UVRCharacterMovementComponent::PhysNavWalking(float deltaTime, int32 Iterat
if (bDeltaMoveNearlyZero && bSameNavLocation)
{
if (const INavigationDataInterface * NavData = GetNavData())
if (const INavigationDataInterface* NavData = GetNavData())
{
if (!NavData->IsNodeRefValid(CachedNavLocation.NodeRef))
{
@ -3281,6 +3287,7 @@ void UVRCharacterMovementComponent::PhysNavWalking(float deltaTime, int32 Iterat
}
}
if (bDeltaMoveNearlyZero && bSameNavLocation)
{
DestNavLocation = CachedNavLocation;
@ -3298,9 +3305,51 @@ void UVRCharacterMovementComponent::PhysNavWalking(float deltaTime, int32 Iterat
SetGravitySpaceZ(AdjustedDest, GetGravitySpaceZ(CachedNavLocation.Location));
}
// Find the point on the NavMesh
const bool bHasNavigationData = FindNavFloor(AdjustedDest, DestNavLocation);
if (!bHasNavigationData)
bool bFoundPointOnNavMesh = false;
if (bSlideAlongNavMeshEdge)
{
if (const INavigationDataInterface* NavDataInterface = GetNavData())
{
const IPathFollowingAgentInterface* PathFollowingAgent = GetPathFollowingAgent();
const bool bIsOnNavLink = PathFollowingAgent && PathFollowingAgent->IsFollowingNavLink();
if (!bIsOnNavLink)
{
FNavLocation StartingNavFloorLocation;
bool bHasValidCachedNavLocation = NavDataInterface->IsNodeRefValid(CachedNavLocation.NodeRef);
// If we don't have a valid CachedNavLocation lets try finding the NavFloor where we're currently at and use that
if (!bHasValidCachedNavLocation)
{
bHasValidCachedNavLocation = FindNavFloor(OldLocation, OUT StartingNavFloorLocation);
}
else
{
StartingNavFloorLocation = CachedNavLocation;
}
if (bHasValidCachedNavLocation)
{
bFoundPointOnNavMesh = NavDataInterface->FindMoveAlongSurface(StartingNavFloorLocation, AdjustedDest, OUT DestNavLocation);
if (bFoundPointOnNavMesh)
{
AdjustedDest = ProjectToGravityFloor(DestNavLocation.Location) + GetGravitySpaceComponentZ(AdjustedDest);
}
}
}
else
{
bFoundPointOnNavMesh = FindNavFloor(AdjustedDest, DestNavLocation);
}
}
}
else
{
bFoundPointOnNavMesh = FindNavFloor(AdjustedDest, DestNavLocation);
}
if (!bFoundPointOnNavMesh)
{
RestorePreAdditiveVRMotionVelocity();
SetMovementMode(MOVE_Walking);
@ -3329,7 +3378,7 @@ void UVRCharacterMovementComponent::PhysNavWalking(float deltaTime, int32 Iterat
// 4.16 UNCOMMENT
FHitResult HitResult;
SafeMoveUpdatedComponent(AdjustedDelta, UpdatedComponent->GetComponentQuat(), bSweepWhileNavWalking, HitResult);
/* 4.16 Delete*/
//const bool bSweep = UpdatedPrimitive ? UpdatedPrimitive->bGenerateOverlapEvents : false;
//FHitResult HitResult;
@ -3354,6 +3403,7 @@ void UVRCharacterMovementComponent::PhysNavWalking(float deltaTime, int32 Iterat
RestorePreAdditiveVRMotionVelocity();
}
void UVRCharacterMovementComponent::PhysSwimming(float deltaTime, int32 Iterations)
{
if (deltaTime < MIN_TICK_TIME)

View file

@ -1374,7 +1374,7 @@ bool UVRRootComponent::UpdateOverlapsImpl(const TOverlapArrayView* NewPendingOve
const bool bCheckOverlapFlags = false; // Already checked above
if (!ShouldIgnoreOverlapResult(MyWorld, MyActor, *this, Result.OverlapObjectHandle.FetchActor(), *HitComp, bCheckOverlapFlags))
{
OverlapMultiResult.Emplace(HitComp, Result.ItemIndex); // don't need to add unique unless the overlap check can return dupes
OverlapMultiResult.Emplace(HitComp, Result.GetItemIndex()); // don't need to add unique unless the overlap check can return dupes
}
}
}

View file

@ -728,6 +728,15 @@ void UVRStereoWidgetComponent::TickComponent(float DeltaTime, enum ELevelTick Ti
}*/
}
// Implement the correct facing stereo layers cvar option
static const auto CVarXRUseLegacyFacing = IConsoleManager::Get().FindConsoleVariable(TEXT("XR.StereoLayers.UseLegacyFacing"));
if (!CVarXRUseLegacyFacing->GetBool())
{
// The default Open Xr Stereo layer object faces the camera at no rotation where as the unreal object faces away from it at zero rotation
LayerDsec.Transform.SetRotation(Transform.GetRotation() * FQuat(0., 0., 1., 0.));
}
if (RenderTarget)
{
// TODO 5.7 need to figure out how to replace this in some way that isn't so fing slow
@ -888,7 +897,7 @@ public:
if (MaterialInstance)
{
MaterialRelevance = MaterialInstance->GetRelevance(GetScene().GetFeatureLevel());
MaterialRelevance = MaterialInstance->GetRelevance(GetScene().GetShaderPlatform());
}
}

View file

@ -1207,6 +1207,18 @@ public:
float NewStiffness, float NewDamping, bool bAlsoSetAngularValues = false, float OptionalAngularStiffness = 0.0f, float OptionalAngularDamping = 0.0f
);
// Set the grips advanced settings, may need to recreate grip locally if altering something important (non owning clients will always re-create the grip)
// Can check HasGripAuthority to decide if callable locally
UFUNCTION(BlueprintCallable, Category = "GripMotionController", meta = (ExpandEnumAsExecs = "Result"))
void SetGripAdvancedGripSettings(
const FBPActorGripInformation& Grip,
EBPVRResultSwitch& Result,
uint8 GripPriority = 0,
bool bSetOwnerOnGrip = true,
bool bDisallowLerping = false,
bool bDisallowSettingPositionOnClientAuthDrop = false
);
// Used to convert an offset transform to grip relative, useful for storing an initial offset and then lerping back to 0 without re-calculating every tick
UFUNCTION(BlueprintPure, Category = "GripMotionController", meta = (DisplayName = "CreateGripRelativeAdditionTransform"))
FTransform CreateGripRelativeAdditionTransform_BP(

Some files were not shown because too many files have changed in this diff Show more