diff --git a/Content/Proteus_Multi/MotionControllerBP.uasset b/Content/Proteus_Multi/MotionControllerBP.uasset index c43de4c..a82b494 100644 Binary files a/Content/Proteus_Multi/MotionControllerBP.uasset and b/Content/Proteus_Multi/MotionControllerBP.uasset differ diff --git a/Content/Proteus_Multi/ProteusInteractions.uasset b/Content/Proteus_Multi/ProteusInteractions.uasset index 3ab3918..0a871ff 100644 Binary files a/Content/Proteus_Multi/ProteusInteractions.uasset and b/Content/Proteus_Multi/ProteusInteractions.uasset differ diff --git a/Content/Proteus_Multi/ProteusPawn.uasset b/Content/Proteus_Multi/ProteusPawn.uasset index 2365fea..37f06da 100644 Binary files a/Content/Proteus_Multi/ProteusPawn.uasset and b/Content/Proteus_Multi/ProteusPawn.uasset differ diff --git a/Plugins/ProteusAvatars/Source/ProteusAvatars/Private/ProteusLocalAvatar.cpp b/Plugins/ProteusAvatars/Source/ProteusAvatars/Private/ProteusLocalAvatar.cpp index 17fdb75..f07c3a1 100644 --- a/Plugins/ProteusAvatars/Source/ProteusAvatars/Private/ProteusLocalAvatar.cpp +++ b/Plugins/ProteusAvatars/Source/ProteusAvatars/Private/ProteusLocalAvatar.cpp @@ -2,7 +2,6 @@ #include "ProteusLocalAvatar.h" #include "Object.h" #include "OVRLipSyncLiveActorComponent.h" -#include "OVRLipSyncPlaybackActorComponent.h" #include "Components/AudioComponent.h" #include "Sound/SoundWave.h" #include "IConsoleManager.h" @@ -22,7 +21,6 @@ AProteusLocalAvatar::AProteusLocalAvatar() { RootComponent = CreateDefaultSubobject(TEXT("LocalAvatarRoot")); AvatarComponent = CreateDefaultSubobject(TEXT("LocalAvatar")); - PlayBackLipSyncComponent = CreateDefaultSubobject(TEXT("CannedLipSync")); AudioComponent = CreateDefaultSubobject(TEXT("LocalAvatarAudio")); LipSyncComponent = CreateDefaultSubobject(TEXT("LocalLipSync")); @@ -40,21 +38,6 @@ void AProteusLocalAvatar::PreInitializeComponents() { Super::PreInitializeComponents(); UE_LOG(LogTemp, Warning, TEXT("FCT: PREINITIALIZE COMPONENTS")); - /*if (UseCannedLipSyncPlayback) - { - FString playbackAssetPath = TEXT("/Game/Proteus_Multi/vox_lp_01_LipSyncSequence"); - auto sequence = LoadObject(nullptr, *playbackAssetPath, nullptr, LOAD_None, nullptr); - PlayBackLipSyncComponent->Sequence = sequence; - - FString AudioClip = TEXT("/Game/Proteus_Multi/vox_lp_01"); - auto SoundWave = LoadObject(nullptr, *AudioClip, nullptr, LOAD_None, nullptr); - - if (SoundWave) - { - SoundWave->bLooping = 1; - AudioComponent->Sound = SoundWave; - } - }*/ #if PLATFORM_WINDOWS auto SilenceDetectionThresholdCVar = IConsoleManager::Get().FindConsoleVariable(TEXT("voice.SilenceDetectionThreshold")); SilenceDetectionThresholdCVar->Set(0.f); @@ -74,11 +57,7 @@ void AProteusLocalAvatar::BeginDestroy() void AProteusLocalAvatar::EndPlay(const EEndPlayReason::Type EndPlayReason) { - if /*(UseCannedLipSyncPlayback) - { - PlayBackLipSyncComponent->OnVisemesReady.RemoveDynamic(this, &AProteusLocalAvatar::LipSyncVismesReady); - } - else if */ (UseLocalMicrophone) + if (UseLocalMicrophone) { LipSyncComponent->OnVisemesReady.RemoveDynamic(this, &AProteusLocalAvatar::LipSyncVismesReady); } @@ -109,18 +88,9 @@ void AProteusLocalAvatar::InitializeLocalAvatar(FString OculusUserID) UE_LOG(LogTemp, Warning, TEXT("InitializeLocalAvatar")); UE_LOG(LogTemp, Warning, TEXT("InitializeLocalAvatar: PacketKey is %s"), *OculusUserID); -/*#if PLATFORM_ANDROID - ovrAvatarAssetLevelOfDetail lod = ovrAvatarAssetLevelOfDetail_Three; -#else - ovrAvatarAssetLevelOfDetail lod = ovrAvatarAssetLevelOfDetail_Five; -#endif*/ if (IsUsingAvatars) { - if /*(UseCannedLipSyncPlayback) - { - PlayBackLipSyncComponent->OnVisemesReady.AddDynamic(this, &AProteusLocalAvatar::LipSyncVismesReady); - } - else if */ (UseLocalMicrophone) + if (UseLocalMicrophone) { LipSyncComponent->OnVisemesReady.AddDynamic(this, &AProteusLocalAvatar::LipSyncVismesReady); LipSyncComponent->Start(); @@ -130,12 +100,9 @@ void AProteusLocalAvatar::InitializeLocalAvatar(FString OculusUserID) UE_LOG(LogTemp, Warning, TEXT("InitializeLocalAvatar: Request Avatar %llu"), OculusID64); AvatarComponent->SetPlayerType(UOvrAvatar::ePlayerType::Local); AvatarComponent->StartPacketRecording(); - //AvatarComponent->SetVisibilityType(ovrAvatarVisibilityFlag_ThirdPerson); AvatarComponent->SetVisibilityType(AvatarVisibilityType == AvatarVisibility::FirstPerson? ovrAvatarVisibilityFlag_FirstPerson: ovrAvatarVisibilityFlag_ThirdPerson); AvatarComponent->SetBodyMaterial(GetOvrAvatarMaterialFromType(BodyMaterial)); AvatarComponent->SetHandMaterial(GetOvrAvatarMaterialFromType(HandsMaterial)); - //AvatarHands[UOvrAvatar::HandType_Left] = nullptr; - //AvatarHands[UOvrAvatar::HandType_Right] = nullptr; } UE_LOG(LogTemp, Warning, TEXT("Local Avatar: StartPacketRecording has fired!")); if (UseLocalMicrophone) @@ -233,7 +200,6 @@ void AProteusLocalAvatar::OnRep_PacketData() } } - // Replication List void AProteusLocalAvatar::GetLifetimeReplicatedProps(TArray< FLifetimeProperty > & OutLifetimeProps) const { @@ -356,14 +322,7 @@ void AProteusLocalAvatar::SetControllersVisibility(bool ControllersVisible) void AProteusLocalAvatar::LipSyncVismesReady() { - /*if (UseCannedLipSyncPlayback) - { - AvatarComponent->UpdateVisemeValues(PlayBackLipSyncComponent->GetVisemes(), PlayBackLipSyncComponent->GetLaughterScore()); - } - else - {*/ AvatarComponent->UpdateVisemeValues(LipSyncComponent->GetVisemes(), LipSyncComponent->GetLaughterScore()); - //} } UOvrAvatar::MaterialType AProteusLocalAvatar::GetOvrAvatarMaterialFromType(AvatarMaterial material) diff --git a/Plugins/ProteusAvatars/Source/ProteusAvatars/Public/NetworkTypes.h b/Plugins/ProteusAvatars/Source/ProteusAvatars/Public/NetworkTypes.h deleted file mode 100644 index d49f8d2..0000000 --- a/Plugins/ProteusAvatars/Source/ProteusAvatars/Public/NetworkTypes.h +++ /dev/null @@ -1,104 +0,0 @@ - -#pragma once - -#include "CoreMinimal.h" -#include "Engine/NetSerialization.h" -#include "NetworkTypes.generated.h" - -/** -* FTransform_NetQuantize10 compresses vectors to one decimal place -* accuracy and rotations to byte per component -*/ -USTRUCT(BlueprintType) -struct FTransform_NetQuantize10 -{ - GENERATED_BODY() - - UPROPERTY(BlueprintReadWrite) - FVector Location; - - UPROPERTY(BlueprintReadWrite) - FRotator Rotation; - - FTransform_NetQuantize10() : Location(0.f), Rotation(0.f) {}; - - FTransform_NetQuantize10(const FVector& InLocation, const FRotator& InRotation) - : Location(InLocation), Rotation(InRotation) {}; - - FTransform_NetQuantize10(const FTransform& InTransform) - : Location(InTransform.GetLocation()), Rotation(InTransform.Rotator()) {}; - - bool NetSerialize(FArchive& Ar, class UPackageMap* Map, bool& bOutSuccess) - { - bOutSuccess = SerializePackedVector<10, 27>(Location, Ar); - - // Rotation compressed to byte per component - Rotation.SerializeCompressed(Ar); - - return true; - }; -}; - -// Required for our custom NetSerialize function to be used by Unreal's network replication system -template<> -struct TStructOpsTypeTraits : public TStructOpsTypeTraitsBase2 -{ - enum - { - WithNetSerializer = true - }; -}; - - - -/** -* FTransform_NetQuantize100 compresses vectors to two decimal places -* accuracy and rotations to short per component -*/ -USTRUCT(BlueprintType) -struct FTransform_NetQuantize100 -{ - GENERATED_BODY() - - UPROPERTY(BlueprintReadWrite) - FVector Location; - - UPROPERTY(BlueprintReadWrite) - FRotator Rotation; - - FTransform_NetQuantize100() : Location(0.f), Rotation(0.f) {}; - - FTransform_NetQuantize100(const FVector& InLocation, const FRotator& InRotation) - : Location(InLocation), Rotation(InRotation) {}; - - FTransform_NetQuantize100(const FTransform& InTransform) - : Location(InTransform.GetLocation()), Rotation(InTransform.Rotator()) {}; - - bool NetSerialize(FArchive& Ar, class UPackageMap* Map, bool& bOutSuccess) - { - bOutSuccess = SerializePackedVector<100, 30>(Location, Ar); - - // Rotation compressed to short per component - Rotation.SerializeCompressedShort(Ar); - - return true; - }; -}; - -template<> -struct TStructOpsTypeTraits : public TStructOpsTypeTraitsBase2 -{ - enum - { - WithNetSerializer = true - }; -}; - -/** -* -*/ -UCLASS(abstract) -class PROTEUSAVATARS_API UNetworkTypes : public UObject -{ - GENERATED_BODY() -}; \ No newline at end of file diff --git a/Plugins/ProteusAvatars/Source/ProteusAvatars/Public/ProteusLocalAvatar.h b/Plugins/ProteusAvatars/Source/ProteusAvatars/Public/ProteusLocalAvatar.h index 8a0f305..f030bda 100644 --- a/Plugins/ProteusAvatars/Source/ProteusAvatars/Public/ProteusLocalAvatar.h +++ b/Plugins/ProteusAvatars/Source/ProteusAvatars/Public/ProteusLocalAvatar.h @@ -59,14 +59,6 @@ struct AvatarLevelOfDetailHash } }; -/*UENUM(BlueprintType) -enum class EAvatarLevelOfDetail : uint8 -{ - AvatarLevelOfDetail_One, // low LOD, which conserves even more resources - AvatarLevelOfDetail_Three, // medium LOD for mobile; his improves performance on Oculus Go and Samsung Gear VR by using lower resolution meshes and textures - AvatarLevelOfDetail_Five // Oculus Rift -};*/ - UCLASS() class PROTEUSAVATARS_API AProteusLocalAvatar : public APawn { @@ -193,7 +185,6 @@ class PROTEUSAVATARS_API AProteusLocalAvatar : public APawn TWeakObjectPtr AvatarHands[UOvrAvatar::HandType_Count]; float CurrentPacketLength = 0.f; - //bool UseCannedLipSyncPlayback = false; static std::unordered_map LODMap; }; \ No newline at end of file diff --git a/Plugins/ProteusAvatars/Source/ProteusAvatars/Public/ProteusReplication.cpp b/Plugins/ProteusAvatars/Source/ProteusAvatars/Public/ProteusReplication.cpp index aa0cd1f..df20ab7 100644 --- a/Plugins/ProteusAvatars/Source/ProteusAvatars/Public/ProteusReplication.cpp +++ b/Plugins/ProteusAvatars/Source/ProteusAvatars/Public/ProteusReplication.cpp @@ -6,259 +6,155 @@ #include "MotionControllerComponent.h" #include "XRMotionControllerBase.h" - UProteusReplication::UProteusReplication() { - PrimaryComponentTick.bCanEverTick = true; - PrimaryComponentTick.bStartWithTickEnabled = false; - PrimaryComponentTick.TickGroup = ETickingGroup::TG_PrePhysics; - bAutoActivate = false; + PrimaryComponentTick.bCanEverTick = true; + PrimaryComponentTick.bStartWithTickEnabled = false; + //This is the tick group to use if your actor is intended to interact with physics objects, including physics-based attachments. + //This way, the actor's movement is complete and can be factored into physics simulation. + //Physics simulation data during this tick will be one frame old - i.e.the data that was rendered to the screen last frame. + PrimaryComponentTick.TickGroup = ETickingGroup::TG_PrePhysics; + bAutoActivate = false; - bReplicates = true; - bIsRemote = false; + bReplicates = true; + bIsRemote = false; } -void UProteusReplication::GetLifetimeReplicatedProps(TArray& OutLifetimeProps) const -{ - Super::GetLifetimeReplicatedProps(OutLifetimeProps); - - DOREPLIFETIME_CONDITION(UProteusReplication, ReplicatedVRBodyTransforms, COND_SkipOwner); -} - - void UProteusReplication::BeginPlay() { - Super::BeginPlay(); + Super::BeginPlay(); - OwningPawn = Cast(GetOwner()); + //Pointer to the Owning Pawn + OwningPawn = Cast(GetOwner()); - if (OwningPawn == nullptr) - { - HandleSetupError(TEXT("ProteusReplication component does not work with non-Pawn Actors!")); - return; - } - - bIsRemote = OwningPawn->Role != ROLE_Authority; - - if (!bHasRegisteredBody && bAutoRegisterBodyComponents) - { - const bool bRegisterSuccess = AutoRegisterProteusBody(); - - if (!bRegisterSuccess) - { - HandleSetupError( - TEXT("ProteusReplication could not find valid VR components to replicate! \ - Ensure your Pawn has a camera and left and right motion controllers \ - You can set them manually by disabling AutoRegisterBodyComponents and calling RegisterProteusForReplication.") - ); - } - } + //Is remote only NOT when the Owning Pawn only the server replicates actors to connected clients (clients will never replicate actors to the server) + bIsRemote = OwningPawn->Role != ROLE_Authority; } - +//Will enable component tick when head and 2 hands will be registered void UProteusReplication::SetComponentTickEnabled(bool bTickEnabled) { - // Only enable tick if the vr body is registered or a crash may occur - if (!bTickEnabled || bHasRegisteredBody) - { - Super::SetComponentTickEnabled(bTickEnabled); - } -} - -void UProteusReplication::Activate(bool bReset) -{ - if (bHasRegisteredBody) - { - Super::Activate(bReset); - } + if (!bTickEnabled || bHasRegisteredBody) + { + Super::SetComponentTickEnabled(bTickEnabled); + } } void UProteusReplication::TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) { - Super::TickComponent(DeltaTime, TickType, ThisTickFunction); + Super::TickComponent(DeltaTime, TickType, ThisTickFunction); - if (IsAuthoritative()) - { - SendTransformsToServer(); - } - else - { - if (bLerpToTransform) - { - LerpReplicatedComponents(DeltaTime); - } - // !bLerpToTransform handled by OnRep_ReplicatedTransforms - } + if (IsAuthoritative()) + { + SendTransformsToServer(); + } + else + { + LerpReplicatedComponents(DeltaTime); + } } bool UProteusReplication::IsAuthoritative() const { - return OwningPawn->IsLocallyControlled(); + return OwningPawn->IsLocallyControlled(); } -bool UProteusReplication::RegisterProteusForReplication(USceneComponent* Head, USceneComponent* LeftHand, USceneComponent* RightHand) +//Called when RegisterProteusForReplication fires +void UProteusReplication::Activate(bool bReset) { - if (Head == nullptr || LeftHand == nullptr || RightHand == nullptr) - { - UE_LOG(LogTemp, Warning, TEXT("Invalid VR Body components! Please check that you properly connected the pins for RegisterVRBody. Body will not replicate.")); - return false; - } - - HeadComponent = Head; - LeftHandComponent = LeftHand; - RightHandComponent = RightHand; - - bHasRegisteredBody = true; - Activate(true); - - return true; + //The activation will happen even if ShouldActivate returns fals + Super::Activate(bReset); } -bool UProteusReplication::AutoRegisterProteusBody() +//CALLED IN BP +bool UProteusReplication::RegisterProteusForReplication(USceneComponent* Head, USceneComponent* LeftHand, USceneComponent* RightHand) { - if (const AActor* Owner = GetOwner()) - { - TArray Cameras; - TArray MotionControllers; - - Owner->GetComponents(Cameras); - Owner->GetComponents(MotionControllers); - - UCameraComponent* RegisteredCamera = nullptr; - UMotionControllerComponent* RegisteredLeftHand = nullptr; - UMotionControllerComponent* RegisteredRightHand = nullptr; + if (Head == nullptr || LeftHand == nullptr || RightHand == nullptr) + { + return false; + } - for (UCameraComponent* Camera : Cameras) - { - if (Camera->bLockToHmd) - { - RegisteredCamera = Camera; - break; - } - } + HeadComponent = Head; + LeftHandComponent = LeftHand; + RightHandComponent = RightHand; - // If there are cameras but none with bLockToHMD enabled by default - // Then we'll just use one that isn't lock to an HMD and assume it will be later - if (RegisteredCamera == nullptr && Cameras.Num() > 0) - { - RegisteredCamera = Cameras[0]; - } + bHasRegisteredBody = true; + Activate(true); - for (UMotionControllerComponent* MotionController : MotionControllers) - { - const FName& MotionSource = MotionController->MotionSource; - if (MotionSource == FXRMotionControllerBase::LeftHandSourceId) - { - RegisteredLeftHand = MotionController; - } - else if (MotionSource == FXRMotionControllerBase::RightHandSourceId) - { - RegisteredRightHand = MotionController; - } - } - - RegisterProteusForReplication(RegisteredCamera, RegisteredLeftHand, RegisteredRightHand); - } - - return bHasRegisteredBody; + return true; } +//TICK +//Only for locally controlled pawns, on every tick void UProteusReplication::SendTransformsToServer() { - ReplicatedVRBodyTransforms.HeadTransform = HeadComponent->GetRelativeTransform(); - ReplicatedVRBodyTransforms.LeftHandTransform = LeftHandComponent->GetRelativeTransform(); - ReplicatedVRBodyTransforms.RightHandTransform = RightHandComponent->GetRelativeTransform(); + //Define relative transforms for head and 2 hands + ReplicatedVRBodyTransforms.HeadTransform = HeadComponent->GetRelativeTransform(); + ReplicatedVRBodyTransforms.LeftHandTransform = LeftHandComponent->GetRelativeTransform(); + ReplicatedVRBodyTransforms.RightHandTransform = RightHandComponent->GetRelativeTransform(); - if (bIsRemote) - { - Server_UpdateReplicationData(ReplicatedVRBodyTransforms); - } + //If the locally controlled pawns is remote, it will send to server the relative transforms data + if (bIsRemote) + { + Server_UpdateReplicationData(ReplicatedVRBodyTransforms); + //NO DEFINITION + } } -void UProteusReplication::LerpReplicatedComponents(float DeltaTime) +//Called from SendTransformsToServer +bool UProteusReplication::Server_UpdateReplicationData_Validate(const FProteusReplicationData& UpdatedData) { - const float LerpAlpha = NetworkLocationLerpRate * DeltaTime; - LerpReplicatedComponent(HeadComponent, ReplicatedVRBodyTransforms.HeadTransform, DeltaTime, LerpAlpha); - LerpReplicatedComponent(LeftHandComponent, ReplicatedVRBodyTransforms.LeftHandTransform, DeltaTime, LerpAlpha); - LerpReplicatedComponent(RightHandComponent, ReplicatedVRBodyTransforms.RightHandTransform, DeltaTime, LerpAlpha); + return true; } -void UProteusReplication::SetToServerTransforms() +void UProteusReplication::Server_UpdateReplicationData_Implementation(const FProteusReplicationData& UpdatedData) { - const FTransform_NetQuantize100& HeadTransform = ReplicatedVRBodyTransforms.HeadTransform; - HeadComponent->SetRelativeLocationAndRotation(HeadTransform.Location, HeadTransform.Rotation); - - const FTransform_NetQuantize100& LeftHandTransform = ReplicatedVRBodyTransforms.LeftHandTransform; - LeftHandComponent->SetRelativeLocationAndRotation(LeftHandTransform.Location, LeftHandTransform.Rotation); - - const FTransform_NetQuantize100& RightHandTransform = ReplicatedVRBodyTransforms.RightHandTransform; - RightHandComponent->SetRelativeLocationAndRotation(RightHandTransform.Location, RightHandTransform.Rotation); + ReplicatedVRBodyTransforms = UpdatedData; } -void UProteusReplication::LerpReplicatedComponent(USceneComponent* Component, const FTransform_NetQuantize100& TargetTransform, float DeltaTime, float LerpAlpha) +//TICK +//Linearly interpolates between the simulated transforms and the server transforms based on Alpha (100% of A when Alpha=0 and 100% of B when Alpha=1) +//Alpha = rate per second, = 10*DeltaTime +void UProteusReplication::LerpReplicatedComponents(float DeltaTime) { - const FTransform_NetQuantize100& OldTransform = Component->GetRelativeTransform(); - const FTransform_NetQuantize100& NewTransform = LerpTransform(OldTransform, TargetTransform, DeltaTime, LerpAlpha); - Component->SetRelativeLocationAndRotation(NewTransform.Location, NewTransform.Rotation); + const float Alpha = 10.f * DeltaTime; + LerpReplicatedComponent(HeadComponent, ReplicatedVRBodyTransforms.HeadTransform, DeltaTime, Alpha); + LerpReplicatedComponent(LeftHandComponent, ReplicatedVRBodyTransforms.LeftHandTransform, DeltaTime, Alpha); + LerpReplicatedComponent(RightHandComponent, ReplicatedVRBodyTransforms.RightHandTransform, DeltaTime, Alpha); } -FTransform_NetQuantize100 UProteusReplication::LerpTransform(const FTransform_NetQuantize100& Start, const FTransform_NetQuantize100& End, float DeltaTime, float Alpha) const +//Called from LerpReplicatedComponents +void UProteusReplication::LerpReplicatedComponent(USceneComponent* Component, const FTransform_NetQuantize100& TargetTransform, float DeltaTime, float LerpAlpha) { - // Mixes both regular and constant rate interpolation for smooth updates during fast hand movement and accuracy in small movements - const FVector FirstLerpLocation = FMath::Lerp(Start.Location, End.Location, Alpha); - const FVector FinalLerpLocation = FMath::VInterpConstantTo(FirstLerpLocation, End.Location, DeltaTime, NetworkLocationConstInterpRate); - - const FQuat StartQuat(Start.Rotation); - const FQuat EndQuat(End.Rotation); - const FQuat LerpRotation = FQuat::FastLerp(StartQuat, EndQuat, Alpha + .1f); - - return FTransform_NetQuantize100(FinalLerpLocation, LerpRotation.Rotator()); + const FTransform_NetQuantize100& OldTransform = Component->GetRelativeTransform(); + const FTransform_NetQuantize100& NewTransform = LerpTransform(OldTransform, TargetTransform, DeltaTime, LerpAlpha); + Component->SetRelativeLocationAndRotation(NewTransform.Location, NewTransform.Rotation); } -void UProteusReplication::HandleSetupError(const FString& ErrorMessage) -{ - FString FinalErrorMessage(ErrorMessage); - FinalErrorMessage.Append(TEXT(" Destroying component.")); - -#if UE_BUILD_DEBUG - if (GEngine) - { - GEngine->AddOnScreenDebugMessage(-1, 15.0f, FColor::Red, FinalErrorMessage); - } -#endif - UE_LOG(LogTemp, Warning, TEXT("%s"), *FinalErrorMessage); - - DestroyComponent(); -} - - - -bool UProteusReplication::Server_UpdateReplicationData_Validate(const FProteusReplicationData& UpdatedData) +//Called from LerpReplicatedComponent +// The constant interpolation rate to the authority location, helps keep small movements exact +// We const interp from the simulated transforms to the server transforms at this rate per second +FTransform_NetQuantize100 UProteusReplication::LerpTransform(const FTransform_NetQuantize100& Start, const FTransform_NetQuantize100& End, float DeltaTime, float Alpha) const { - return true; -} + // Mixes both regular and constant rate interpolation for smooth updates during fast hand movement and accuracy in small movements + const FVector FirstLerpLocation = FMath::Lerp(Start.Location, End.Location, Alpha); + const FVector FinalLerpLocation = FMath::VInterpConstantTo(FirstLerpLocation, End.Location, DeltaTime, 100.f); -void UProteusReplication::Server_UpdateReplicationData_Implementation(const FProteusReplicationData& UpdatedData) -{ - ReplicatedVRBodyTransforms = UpdatedData; + const FQuat StartQuat(Start.Rotation); + const FQuat EndQuat(End.Rotation); + const FQuat LerpRotation = FQuat::FastLerp(StartQuat, EndQuat, Alpha + .1f); - OnRep_ReplicatedTransforms(); + return FTransform_NetQuantize100(FinalLerpLocation, LerpRotation.Rotator()); } -bool UProteusReplication::Server_JumpToAuthorityTransforms_Validate() +void UProteusReplication::GetLifetimeReplicatedProps(TArray& OutLifetimeProps) const { - return true; -} + Super::GetLifetimeReplicatedProps(OutLifetimeProps); -void UProteusReplication::Server_JumpToAuthorityTransforms_Implementation() -{ - SetToServerTransforms(); + DOREPLIFETIME_CONDITION(UProteusReplication, ReplicatedVRBodyTransforms, COND_SkipOwner); } void UProteusReplication::OnRep_ReplicatedTransforms() { - if (!bLerpToTransform) - { - SetToServerTransforms(); - } + return; } \ No newline at end of file diff --git a/Plugins/ProteusAvatars/Source/ProteusAvatars/Public/ProteusReplication.h b/Plugins/ProteusAvatars/Source/ProteusAvatars/Public/ProteusReplication.h index 48f280a..5452a9f 100644 --- a/Plugins/ProteusAvatars/Source/ProteusAvatars/Public/ProteusReplication.h +++ b/Plugins/ProteusAvatars/Source/ProteusAvatars/Public/ProteusReplication.h @@ -3,126 +3,123 @@ #include "CoreMinimal.h" #include "Components/ActorComponent.h" -#include "NetworkTypes.h" +#include "Engine/NetSerialization.h" #include "ProteusReplication.generated.h" -USTRUCT() -struct FProteusReplicationData +//To gain speed, we want to serialize the transforms of the pawn (head & controllers) +USTRUCT(BlueprintType) +struct FTransform_NetQuantize100 { - GENERATED_BODY() + GENERATED_BODY() - UPROPERTY() - FTransform_NetQuantize100 HeadTransform; + UPROPERTY(BlueprintReadWrite) + FVector Location; - UPROPERTY() - FTransform_NetQuantize100 LeftHandTransform; + UPROPERTY(BlueprintReadWrite) + FRotator Rotation; - UPROPERTY() - FTransform_NetQuantize100 RightHandTransform; -}; + //Initialize Transform + FTransform_NetQuantize100() : Location(0.f), Rotation(0.f) {}; -/** -* Efficiently replicates VR Head and Hands positions from Client to Server and other clients. -* Only to be used in VR Pawns. -*/ -UCLASS(BlueprintType, ClassGroup = (Network), meta = (BlueprintSpawnableComponent), HideCategories = (Activation, Cooking, ComponentReplication, Collision)) -class PROTEUSAVATARS_API UProteusReplication : public UActorComponent -{ - GENERATED_BODY() + //Get vectors values + FTransform_NetQuantize100(const FVector& InLocation, const FRotator& InRotation) + : Location(InLocation), Rotation(InRotation) {}; -public: + FTransform_NetQuantize100(const FTransform& InTransform) + : Location(InTransform.GetLocation()), Rotation(InTransform.Rotator()) {}; - // If true, the component will automatically discover the owning pawn's VR camera and left and right motion controllers to replicate - // If false, RegisterVRBodyForReplication must be called to setup the replicated components (should be called on BeginPlay) - UPROPERTY(BlueprintReadOnly, EditAnywhere, Category = "Parameters") - bool bAutoRegisterBodyComponents = true; + bool NetSerialize(FArchive& Ar, class UPackageMap* Map, bool& bOutSuccess) + { + //Serialize vectors + //2 decimal place of precision; up to 30 bits per component + bOutSuccess = SerializePackedVector<100, 30>(Location, Ar); - // If false, body components will snap to their latest replicated transform - // If true, body components will lerp to their latest replicated transform - UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Parameters") - bool bLerpToTransform = true; + //Serialize rotators + // Rotation compressed to short per component + Rotation.SerializeCompressedShort(Ar); - // The constant interpolation rate to the authority location, helps keep small movements exact - // We const interp from the simulated transforms to the server transforms at this rate per second - UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Parameters") - float NetworkLocationConstInterpRate = 100.f; - - // The interpolation rate to the current network transforms, for smoothing when jumping long distances - // We lerp from the simulated transforms to the server transforms at this rate per second - UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Parameters") - float NetworkLocationLerpRate = 10.f; + return true; + }; +}; -protected: +//Tell the engine that struct FTransform_NetQuantize100 defines a custom NetSerializer function +//you have to set to true the type trait WithNetSerializer for the struct +//If you don’t add that piece of code, the NetSerialize method will never be called +template<> +struct TStructOpsTypeTraits : public TStructOpsTypeTraitsBase2 +{ + enum + { + WithNetSerializer = true + }; +}; - bool bIsRemote = false; +USTRUCT() +struct FProteusReplicationData +{ + GENERATED_BODY() - bool bHasRegisteredBody = false; + UPROPERTY() + FTransform_NetQuantize100 HeadTransform; + UPROPERTY() + FTransform_NetQuantize100 LeftHandTransform; -protected: + UPROPERTY() + FTransform_NetQuantize100 RightHandTransform; +}; - UPROPERTY() - APawn* OwningPawn = nullptr; +UCLASS(BlueprintType, ClassGroup = (Network), meta = (BlueprintSpawnableComponent), HideCategories = (Activation, Cooking, ComponentReplication, Collision)) +class PROTEUSAVATARS_API UProteusReplication : public UActorComponent +{ + GENERATED_BODY() +public: - UPROPERTY(BlueprintReadOnly, Category = "Replication", Meta=(AllowPrivateAcess="true")) - USceneComponent* HeadComponent = nullptr; + UProteusReplication(); - UPROPERTY(BlueprintReadOnly, Category = "Replication", Meta = (AllowPrivateAcess = "true")) - USceneComponent* LeftHandComponent = nullptr; + UFUNCTION(BlueprintCallable, Category = "Network") + bool IsAuthoritative() const; - UPROPERTY(BlueprintReadOnly, Category = "Replication", Meta = (AllowPrivateAcess = "true")) - USceneComponent* RightHandComponent = nullptr; + UFUNCTION(BlueprintCallable, Category = "Network") + bool RegisterProteusForReplication(USceneComponent* Head, USceneComponent* LeftHand, USceneComponent* RightHand); - UPROPERTY(ReplicatedUsing = OnRep_ReplicatedTransforms) - FProteusReplicationData ReplicatedVRBodyTransforms; + virtual void SetComponentTickEnabled(bool bTickEnabled) override; + virtual void Activate(bool bReset = false) override; + virtual void TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) override; -public: - UProteusReplication(); protected: - virtual void BeginPlay() override; -public: - virtual void SetComponentTickEnabled(bool bTickEnabled) override; - virtual void Activate(bool bReset = false) override; - virtual void TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) override; - - -public: - UFUNCTION(BlueprintCallable, Category = "Network") - bool IsAuthoritative() const; + UPROPERTY() + APawn* OwningPawn = nullptr; - /** - * Set the hand and hands components and starts replicating their transforms - * across the network - * Typically this will be the Camera, LeftMotionController, and RightMotionController - * @return Success - */ - UFUNCTION(BlueprintCallable, Category = "Network") - bool RegisterProteusForReplication(USceneComponent* Head, USceneComponent* LeftHand, USceneComponent* RightHand); + UPROPERTY(BlueprintReadOnly, Category = "Replication", Meta = (AllowPrivateAcess = "true")) + USceneComponent* HeadComponent = nullptr; -protected: + UPROPERTY(BlueprintReadOnly, Category = "Replication", Meta = (AllowPrivateAcess = "true")) + USceneComponent* LeftHandComponent = nullptr; - bool AutoRegisterProteusBody(); + UPROPERTY(BlueprintReadOnly, Category = "Replication", Meta = (AllowPrivateAcess = "true")) + USceneComponent* RightHandComponent = nullptr; - void SendTransformsToServer(); + UPROPERTY(ReplicatedUsing = OnRep_ReplicatedTransforms) + FProteusReplicationData ReplicatedVRBodyTransforms; - void LerpReplicatedComponents(float DeltaTime); + UFUNCTION(Server, Unreliable, WithValidation, Category = "Network") + void Server_UpdateReplicationData(const FProteusReplicationData& UpdatedData); - void SetToServerTransforms(); + UFUNCTION() + void OnRep_ReplicatedTransforms(); - void LerpReplicatedComponent(USceneComponent* Component, const FTransform_NetQuantize100& TargetTransform, float DeltaTime, float LerpAlpha); - FTransform_NetQuantize100 LerpTransform(const FTransform_NetQuantize100& Start, const FTransform_NetQuantize100& End, float DeltaTime, float LerpAlpha) const; + bool bIsRemote = false; - void HandleSetupError(const FString& ErrorMessage); + bool bHasRegisteredBody = false; -protected: + virtual void BeginPlay() override; - UFUNCTION(Server, Unreliable, WithValidation, Category = "Network") - void Server_UpdateReplicationData(const FProteusReplicationData& UpdatedData); + void SendTransformsToServer(); - UFUNCTION(Server, Reliable, WithValidation, Category = "Network") - void Server_JumpToAuthorityTransforms(); + void LerpReplicatedComponents(float DeltaTime); - UFUNCTION() - void OnRep_ReplicatedTransforms(); -}; + void LerpReplicatedComponent(USceneComponent* Component, const FTransform_NetQuantize100& TargetTransform, float DeltaTime, float LerpAlpha); + FTransform_NetQuantize100 LerpTransform(const FTransform_NetQuantize100& Start, const FTransform_NetQuantize100& End, float DeltaTime, float LerpAlpha) const; +}; \ No newline at end of file