Up to Unreal 5.7
This commit is contained in:
parent
8a63902c06
commit
1a64805d43
239 changed files with 3947 additions and 296 deletions
|
|
@ -1,2 +0,0 @@
|
|||
* text=auto
|
||||
*.bat eol=crlf
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
|
||||
.hg/
|
||||
binaries/
|
||||
deriveddatacache/
|
||||
.vs/
|
||||
build/
|
||||
intermediate/
|
||||
PACKPLUGIN/
|
||||
saved/
|
||||
*.orig
|
||||
|
|
@ -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.
|
||||
|
|
@ -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
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
|
|
@ -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 ...
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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()
|
||||
{
|
||||
}
|
||||
|
|
@ -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
|
||||
|
||||
};
|
||||
|
|
@ -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;
|
||||
};
|
||||
|
|
@ -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");
|
||||
// }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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)
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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:
|
||||
};
|
||||
|
|
@ -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);
|
||||
};
|
||||
|
|
@ -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;
|
||||
};
|
||||
|
|
@ -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;
|
||||
}
|
||||
};
|
||||
|
|
@ -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);
|
||||
|
||||
|
||||
};
|
||||
|
|
@ -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.
|
||||
|
|
@ -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;
|
||||
|
|
@ -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
|
||||
/////////////////////////////////////////////////
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
/////////////////////////////////////////////////
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
/////////////////////////////////////////////////
|
||||
|
|
@ -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
|
||||
|
|
@ -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()
|
||||
|
|
@ -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()
|
||||
|
|
@ -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()
|
||||
|
|
@ -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()
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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;
|
||||
|
|
@ -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 (0–512 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);
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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)
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -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
Loading…
Add table
Add a link
Reference in a new issue