Switch to VRExpansionPlugin Example 2

This commit is contained in:
Simeon "Waldo" Wallrath 2023-11-03 13:45:36 +01:00
parent 3e6ff514ff
commit 6c02da9299
837 changed files with 78100 additions and 0 deletions

View file

@ -0,0 +1,8 @@
[FilterPlugin]
; This section lists additional files which will be packaged along with your plugin. Paths should be listed relative to the root plugin directory, and
; may include "...", "*", and "?" wildcards to match directories, files, and individual characters respectively.
;
; Examples:
; /README.txt
; /Extras/...
; /Binaries/ThirdParty/*.dll

View file

@ -0,0 +1,849 @@
// Copyright Epic Games, Inc. All Rights Reserved.
#include "HandSocketComponentDetails.h"
#include "HandSocketVisualizer.h"
//#include "PropertyEditing.h"
#include "Widgets/Text/STextBlock.h"
#include "Widgets/Input/SButton.h"
#include "PropertyHandle.h"
#include "DetailLayoutBuilder.h"
#include "DetailWidgetRow.h"
#include "DetailCategoryBuilder.h"
#include "IDetailsView.h"
#include "Developer/AssetTools/Public/IAssetTools.h"
#include "Developer/AssetTools/Public/AssetToolsModule.h"
#include "Editor/ContentBrowser/Public/IContentBrowserSingleton.h"
#include "Editor/ContentBrowser/Public/ContentBrowserModule.h"
#include "AnimationUtils.h"
#include "AssetRegistry/AssetRegistryModule.h"
#include "UObject/SavePackage.h"
#include "Misc/MessageDialog.h"
#include "Widgets/Layout/SBorder.h"
#include "Widgets/Layout/SSeparator.h"
#include "Widgets/Layout/SUniformGridPanel.h"
#include "Widgets/Input/SEditableTextBox.h"
#include "Editor.h"
#include "EditorStyleSet.h"
#include "Styling/CoreStyle.h"
#include "Animation/AnimData/AnimDataModel.h"
#include "Editor/UnrealEdEngine.h"
#include "UnrealEdGlobals.h"
#define LOCTEXT_NAMESPACE "HandSocketComponentDetails"
FText SCreateHandAnimationDlg::LastUsedAssetPath;
static bool PromptUserForAssetPath(FString& AssetPath, FString& AssetName)
{
TSharedRef<SCreateHandAnimationDlg> NewAnimDlg = SNew(SCreateHandAnimationDlg);
if (NewAnimDlg->ShowModal() != EAppReturnType::Cancel)
{
AssetPath = NewAnimDlg->GetFullAssetPath();
AssetName = NewAnimDlg->GetAssetName();
return true;
}
return false;
}
TWeakObjectPtr<UAnimSequence> FHandSocketComponentDetails::SaveAnimationAsset(const FString& InAssetPath, const FString& InAssetName)
{
TWeakObjectPtr<UAnimSequence> FinalAnimation;
// Replace when this moves to custom display
if (!HandSocketComponent.IsValid())
return FinalAnimation;
/*if (!HandSocketComponent->HandVisualizerComponent)// || !HandSocketComponent->HandVisualizerComponent->SkeletalMesh || !HandSocketComponent->HandVisualizerComponent->SkeletalMesh->Skeleton)
{
return false;
}*/
if (!HandSocketComponent->HandTargetAnimation && (!HandSocketComponent->VisualizationMesh || !HandSocketComponent->VisualizationMesh->GetSkeleton()))
{
return FinalAnimation;
}
// create the asset
FText InvalidPathReason;
bool const bValidPackageName = FPackageName::IsValidLongPackageName(InAssetPath, false, &InvalidPathReason);
if (bValidPackageName == false)
{
UE_LOG(LogAnimation, Log, TEXT("%s is an invalid asset path, prompting user for new asset path. Reason: %s"), *InAssetPath, *InvalidPathReason.ToString());
}
FString ValidatedAssetPath = InAssetPath;
FString ValidatedAssetName = InAssetName;
UObject* Parent = bValidPackageName ? CreatePackage(*ValidatedAssetPath) : nullptr;
if (Parent == nullptr)
{
// bad or no path passed in, do the popup
if (PromptUserForAssetPath(ValidatedAssetPath, ValidatedAssetName) == false)
{
return FinalAnimation;
}
Parent = CreatePackage(*ValidatedAssetPath);
}
UObject* const Object = LoadObject<UObject>(Parent, *ValidatedAssetName, nullptr, LOAD_Quiet, nullptr);
// if object with same name exists, warn user
if (Object)
{
EAppReturnType::Type ReturnValue = FMessageDialog::Open(EAppMsgType::YesNo, NSLOCTEXT("UnrealEd", "Error_AssetExist", "Asset with same name exists. Do you wish to overwrite it?"));
if (ReturnValue == EAppReturnType::No)
{
return FinalAnimation; // failed
}
}
UAnimSequence* BaseAnimation = HandSocketComponent->HandTargetAnimation;
TArray<FTransform> LocalPoses;
if (!BaseAnimation)
{
LocalPoses = HandSocketComponent->VisualizationMesh->GetSkeleton()->GetRefLocalPoses();
}
// If not, create new one now.
UAnimSequence* const NewSeq = NewObject<UAnimSequence>(Parent, *ValidatedAssetName, RF_Public | RF_Standalone);
if (NewSeq)
{
// set skeleton
if (BaseAnimation)
{
NewSeq->SetSkeleton(BaseAnimation->GetSkeleton());
}
else
{
NewSeq->SetSkeleton(HandSocketComponent->VisualizationMesh->GetSkeleton());
}
// Notify the asset registry
FAssetRegistryModule::AssetCreated(NewSeq);
//StartRecord(Component, NewSeq);
//return true;
UAnimSequence* AnimationObject = NewSeq;
IAnimationDataController& AnimController = AnimationObject->GetController();
{
IAnimationDataController::FScopedBracket ScopedBracket(AnimController, LOCTEXT("SaveAnimationAsset_VRE", "Creating Animation Sequence based on hand pose"));
AnimationObject->ResetAnimation();
if (BaseAnimation)
{
AnimationObject->BoneCompressionSettings = BaseAnimation->BoneCompressionSettings;
}
else
{
AnimationObject->BoneCompressionSettings = FAnimationUtils::GetDefaultAnimationBoneCompressionSettings();
}
AnimController.InitializeModel();
AnimController.RemoveAllBoneTracks(false);
//checkf(MovieScene, TEXT("No Movie Scene found for SequencerDataModel"));
//AnimController.SetPlayLength(4.f);
AnimController.SetNumberOfFrames(FFrameNumber(1), false);
// Set frame rate to 1 to 1 as we don't animate
AnimController.SetFrameRate(FFrameRate(1, 1));
TArray<FName> TrackNames;
const IAnimationDataModel* BaseDataModel = BaseAnimation ? BaseAnimation->GetController().GetModel() : nullptr;
if (BaseAnimation)
{
if (BaseDataModel)
{
BaseDataModel->GetBoneTrackNames(TrackNames);
for (FName TrackName : TrackNames)
{
AnimController.AddBoneCurve(TrackName);
}
}
else
{
return FinalAnimation;
}
}
else
{
int numBones = HandSocketComponent->VisualizationMesh->GetRefSkeleton().GetNum();
for (int i = 0; i < LocalPoses.Num() && i < numBones; ++i)
{
AnimController.AddBoneCurve(HandSocketComponent->VisualizationMesh->GetRefSkeleton().GetBoneName(i));
//AnimController.AddBoneTrack(HandSocketComponent->VisualizationMesh->GetRefSkeleton().GetBoneName(i));
}
}
if (BaseAnimation)
{
AnimationObject->RetargetSource = BaseAnimation->RetargetSource;
}
else
{
AnimationObject->RetargetSource = HandSocketComponent->VisualizationMesh ? HandSocketComponent->VisualizationMesh->GetSkeleton()->GetRetargetSourceForMesh(HandSocketComponent->VisualizationMesh) : NAME_None;
}
const IAnimationDataModel* DataModel = AnimController.GetModel();
/// SAVE POSE
if (BaseAnimation && DataModel && BaseDataModel)
{
for (int32 TrackIndex = 0; TrackIndex < /*DataModel->GetBoneAnimationTracks().Num()*/BaseDataModel->GetNumBoneTracks(); ++TrackIndex)
{
FName TrackName = TrackIndex < TrackNames.Num() ? TrackNames[TrackIndex] : NAME_None;
if (!BaseDataModel->IsValidBoneTrackName(TrackName))
{
continue;
}
FTransform FinalTrans = BaseDataModel->GetBoneTrackTransform(TrackName, 0);
//FTransform FinalTrans(Rot, Loc, Scale);
FQuat DeltaQuat = FQuat::Identity;
for (FBPVRHandPoseBonePair& HandPair : HandSocketComponent->CustomPoseDeltas)
{
if (HandPair.BoneName == TrackName)
{
DeltaQuat = HandPair.DeltaPose;
break;
}
}
FinalTrans.ConcatenateRotation(DeltaQuat);
FinalTrans.NormalizeRotation();
//FRawAnimSequenceTrack& RawNewTrack = DataModel->GetBoneTrackByIndex(TrackIndex).InternalTrackData;
AnimController.SetBoneTrackKeys(TrackName, { FinalTrans.GetTranslation() }, { FinalTrans.GetRotation() }, { FinalTrans.GetScale3D() });
}
}
else if(DataModel)
{
USkeletalMesh* SkeletalMesh = HandSocketComponent->VisualizationMesh;
FReferenceSkeleton RefSkeleton = SkeletalMesh->GetRefSkeleton();
USkeleton* AnimSkeleton = SkeletalMesh->GetSkeleton();
for (int32 TrackIndex = 0; TrackIndex < RefSkeleton.GetNum(); ++TrackIndex)
{
FName TrackName = RefSkeleton.GetBoneName(TrackIndex);
if (!DataModel->IsValidBoneTrackName(TrackName))
{
continue;
}
int32 BoneTreeIndex = RefSkeleton.FindBoneIndex(TrackName);
// verify if this bone exists in skeleton
//int32 BoneTreeIndex = DataModel->GetBoneTrackByIndex(TrackIndex).BoneTreeIndex;
if (BoneTreeIndex != INDEX_NONE)
{
int32 BoneIndex = BoneTreeIndex;//AnimSkeleton->GetMeshBoneIndexFromSkeletonBoneIndex(SkeletalMesh, BoneTreeIndex);
//int32 ParentIndex = SkeletalMesh->RefSkeleton.GetParentIndex(BoneIndex);
FTransform LocalTransform = LocalPoses[BoneIndex];
FName BoneName = AnimSkeleton->GetReferenceSkeleton().GetBoneName(BoneIndex);
FQuat DeltaQuat = FQuat::Identity;
for (FBPVRHandPoseBonePair& HandPair : HandSocketComponent->CustomPoseDeltas)
{
if (HandPair.BoneName == BoneName)
{
DeltaQuat = HandPair.DeltaPose;
}
}
LocalTransform.ConcatenateRotation(DeltaQuat);
LocalTransform.NormalizeRotation();
AnimController.SetBoneTrackKeys(BoneName, { LocalTransform.GetTranslation() }, { LocalTransform.GetRotation() }, { LocalTransform.GetScale3D() });
}
}
}
AnimController.NotifyPopulated();
}
/// END SAVE POSE
///
///
///
// init notifies
AnimationObject->InitializeNotifyTrack();
//#TODO: 5.1, need to figure out what they replaced this with
//PRAGMA_DISABLE_DEPRECATION_WARNINGS
//AnimationObject->PostProcessSequence();
//PRAGMA_ENABLE_DEPRECATION_WARNINGS
AnimationObject->MarkPackageDirty();
//if (bAutoSaveAsset)
{
UPackage* const Package = AnimationObject->GetOutermost();
FString const PackageName = Package->GetName();
FString const PackageFileName = FPackageName::LongPackageNameToFilename(PackageName, FPackageName::GetAssetPackageExtension());
double StartTime = FPlatformTime::Seconds();
FSavePackageArgs PackageArguments;
PackageArguments.SaveFlags = RF_Standalone;
PackageArguments.SaveFlags = SAVE_NoError;
UPackage::SavePackage(Package, NULL, *PackageFileName, PackageArguments);
//UPackage::SavePackage(Package, NULL, RF_Standalone, *PackageFileName, GError, nullptr, false, true, SAVE_NoError);
double ElapsedTime = FPlatformTime::Seconds() - StartTime;
UE_LOG(LogAnimation, Log, TEXT("Animation Recorder saved %s in %0.2f seconds"), *PackageName, ElapsedTime);
}
FinalAnimation = AnimationObject;
return FinalAnimation;
}
return FinalAnimation;
}
TSharedRef< IDetailCustomization > FHandSocketComponentDetails::MakeInstance()
{
return MakeShareable(new FHandSocketComponentDetails);
}
void FHandSocketComponentDetails::OnHandRelativeUpdated(IDetailLayoutBuilder* LayoutBuilder)
{
if (!HandSocketComponent.IsValid())
{
return;
}
HandSocketComponent->Modify();
if (AActor* Owner = HandSocketComponent->GetOwner())
{
Owner->Modify();
}
TSharedPtr<FComponentVisualizer> Visualizer = GUnrealEd->FindComponentVisualizer(HandSocketComponent->GetClass());
FHandSocketVisualizer* HandVisualizer = (FHandSocketVisualizer*)Visualizer.Get();
if (HandVisualizer)
{
if (UHandSocketComponent* RefHand = HandVisualizer->GetCurrentlyEditingComponent())
{
RefHand->HandRelativePlacement = HandSocketComponent->HandRelativePlacement;
}
}
FComponentVisualizer::NotifyPropertyModified(HandSocketComponent.Get(), FindFProperty<FProperty>(UHandSocketComponent::StaticClass(), GET_MEMBER_NAME_CHECKED(UHandSocketComponent, HandRelativePlacement)));
}
void FHandSocketComponentDetails::OnLeftDominantUpdated(IDetailLayoutBuilder* LayoutBuilder)
{
if (!HandSocketComponent.IsValid())
{
return;
}
// Default to always flipping this
//if (HandSocketComponent->bFlipForLeftHand)
{
FTransform relTrans = HandSocketComponent->GetRelativeTransform();
FTransform HandPlacement = HandSocketComponent->GetHandRelativePlacement();
if (HandSocketComponent->bDecoupleMeshPlacement)
{
relTrans = FTransform::Identity;
}
FTransform ReturnTrans = (HandPlacement * relTrans);
HandSocketComponent->MirrorHandTransform(ReturnTrans, relTrans);
HandSocketComponent->Modify();
if (AActor* Owner = HandSocketComponent->GetOwner())
{
Owner->Modify();
}
ReturnTrans = ReturnTrans.GetRelativeTransform(relTrans);
HandSocketComponent->HandRelativePlacement = ReturnTrans;
TSharedPtr<FComponentVisualizer> Visualizer = GUnrealEd->FindComponentVisualizer(HandSocketComponent->GetClass());
FHandSocketVisualizer* HandVisualizer = (FHandSocketVisualizer*)Visualizer.Get();
if (HandVisualizer)
{
if (UHandSocketComponent* RefHand = HandVisualizer->GetCurrentlyEditingComponent())
{
RefHand->HandRelativePlacement = HandSocketComponent->HandRelativePlacement;
//FComponentVisualizer::NotifyPropertyModified(RefHand, FindFProperty<FProperty>(UHandSocketComponent::StaticClass(), GET_MEMBER_NAME_CHECKED(UHandSocketComponent, HandRelativePlacement)));
}
}
FComponentVisualizer::NotifyPropertyModified(HandSocketComponent.Get(), FindFProperty<FProperty>(UHandSocketComponent::StaticClass(), GET_MEMBER_NAME_CHECKED(UHandSocketComponent, HandRelativePlacement)));
}
}
void FHandSocketComponentDetails::OnLockedStateUpdated(IDetailLayoutBuilder* LayoutBuilder)
{
if (!HandSocketComponent.IsValid())
{
return;
}
if (HandSocketComponent->bDecoupleMeshPlacement)
{
//FTransform RelTrans = HandSocketComponent->GetRelativeTransform();
//FTransform WorldTrans = HandSocketComponent->GetComponentTransform();
//if (USceneComponent* ParentComp = HandSocketComponent->GetAttachParent())
{
HandSocketComponent->Modify();
if (AActor* Owner = HandSocketComponent->GetOwner())
{
Owner->Modify();
}
HandSocketComponent->HandRelativePlacement = HandSocketComponent->HandRelativePlacement * HandSocketComponent->GetRelativeTransform();// HandSocketComponent->GetComponentTransform();
HandSocketComponent->bDecoupled = true;
TSharedPtr<FComponentVisualizer> Visualizer = GUnrealEd->FindComponentVisualizer(HandSocketComponent->GetClass());
FHandSocketVisualizer* HandVisualizer = (FHandSocketVisualizer*)Visualizer.Get();
if (HandVisualizer)
{
if (UHandSocketComponent* RefHand = HandVisualizer->GetCurrentlyEditingComponent())
{
RefHand->HandRelativePlacement = HandSocketComponent->HandRelativePlacement;
RefHand->bDecoupled = true;
//FComponentVisualizer::NotifyPropertyModified(RefHand, FindFProperty<FProperty>(UHandSocketComponent::StaticClass(), GET_MEMBER_NAME_CHECKED(UHandSocketComponent, HandRelativePlacement)));
}
}
}
}
else
{
//if (USceneComponent* ParentComp = HandSocketComponent->GetAttachParent())
{
HandSocketComponent->Modify();
if (AActor* Owner = HandSocketComponent->GetOwner())
{
Owner->Modify();
}
HandSocketComponent->HandRelativePlacement = HandSocketComponent->HandRelativePlacement.GetRelativeTransform(HandSocketComponent->GetRelativeTransform());
HandSocketComponent->bDecoupled = false;
TSharedPtr<FComponentVisualizer> Visualizer = GUnrealEd->FindComponentVisualizer(HandSocketComponent->GetClass());
FHandSocketVisualizer* HandVisualizer = (FHandSocketVisualizer*)Visualizer.Get();
if (HandVisualizer)
{
if (UHandSocketComponent* RefHand = HandVisualizer->GetCurrentlyEditingComponent())
{
RefHand->HandRelativePlacement = HandSocketComponent->HandRelativePlacement;
RefHand->bDecoupled = false;
//FComponentVisualizer::NotifyPropertyModified(RefHand, FindFProperty<FProperty>(UHandSocketComponent::StaticClass(), GET_MEMBER_NAME_CHECKED(UHandSocketComponent, HandRelativePlacement)));
}
}
}
}
TArray<FProperty*> PropertiesToModify;
PropertiesToModify.Add(FindFProperty<FProperty>(UHandSocketComponent::StaticClass(), GET_MEMBER_NAME_CHECKED(UHandSocketComponent, HandRelativePlacement)));
PropertiesToModify.Add(FindFProperty<FProperty>(UHandSocketComponent::StaticClass(), GET_MEMBER_NAME_CHECKED(UHandSocketComponent, bDecoupled)));
FComponentVisualizer::NotifyPropertiesModified(HandSocketComponent.Get(), PropertiesToModify);
}
void FHandSocketComponentDetails::CustomizeDetails(IDetailLayoutBuilder& DetailBuilder)
{
// Hide the SplineCurves property
//TSharedPtr<IPropertyHandle> HandPlacementProperty = DetailBuilder.GetProperty(GET_MEMBER_NAME_CHECKED(UHandSocketComponent, HandRelativePlacement));
//HandPlacementProperty->MarkHiddenByCustomization();
TArray<TWeakObjectPtr<UObject>> ObjectsBeingCustomized;
DetailBuilder.GetObjectsBeingCustomized(ObjectsBeingCustomized);
if (ObjectsBeingCustomized.Num() == 1)
{
UHandSocketComponent* CurrentHandSocket = Cast<UHandSocketComponent>(ObjectsBeingCustomized[0]);
if (CurrentHandSocket != NULL)
{
if (HandSocketComponent != CurrentHandSocket)
{
TSharedPtr<FComponentVisualizer> Visualizer = GUnrealEd->FindComponentVisualizer(CurrentHandSocket->GetClass());
FHandSocketVisualizer* HandVisualizer = (FHandSocketVisualizer*)Visualizer.Get();
if (HandVisualizer)
{
HandVisualizer->CurrentlySelectedBoneIdx = INDEX_NONE;
HandVisualizer->CurrentlySelectedBone = NAME_None;
HandVisualizer->HandPropertyPath = FComponentPropertyPath();
//HandVisualizer->OldHandSocketComp = CurrentHandSocket;
}
HandSocketComponent = CurrentHandSocket;
}
}
}
/*const TArray< TWeakObjectPtr<UObject> >& SelectedObjects = DetailBuilder.GetSelectedObjects();
for (int32 ObjectIndex = 0; ObjectIndex < SelectedObjects.Num(); ++ObjectIndex)
{
const TWeakObjectPtr<UObject>& CurrentObject = SelectedObjects[ObjectIndex];
if (CurrentObject.IsValid())
{
UHandSocketComponent* CurrentHandSocket = Cast<UHandSocketComponent>(CurrentObject.Get());
if (CurrentHandSocket != NULL)
{
if (HandSocketComponent != CurrentHandSocket)
{
TSharedPtr<FComponentVisualizer> Visualizer = GUnrealEd->FindComponentVisualizer(CurrentHandSocket->GetClass());
FHandSocketVisualizer* HandVisualizer = (FHandSocketVisualizer*)Visualizer.Get();
if (HandVisualizer)
{
HandVisualizer->CurrentlySelectedBoneIdx = INDEX_NONE;
HandVisualizer->CurrentlySelectedBone = NAME_None;
HandVisualizer->HandPropertyPath = FComponentPropertyPath();
//HandVisualizer->OldHandSocketComp = CurrentHandSocket;
}
HandSocketComponent = CurrentHandSocket;
}
break;
}
}
}*/
DetailBuilder.HideCategory(FName("ComponentTick"));
DetailBuilder.HideCategory(FName("GameplayTags"));
DetailBuilder.HideCategory(FName("VRGripInterface"));
DetailBuilder.HideCategory(FName("VRGripInterface|Replication"));
DetailBuilder.HideCategory(FName("Tags"));
DetailBuilder.HideCategory(FName("AssetUserData"));
DetailBuilder.HideCategory(FName("Events"));
DetailBuilder.HideCategory(FName("Activation"));
DetailBuilder.HideCategory(FName("Cooking"));
DetailBuilder.HideCategory(FName("ComponentReplication"));
TSharedPtr<IPropertyHandle> LockedLocationProperty = DetailBuilder.GetProperty(GET_MEMBER_NAME_CHECKED(UHandSocketComponent, bDecoupleMeshPlacement));
TSharedPtr<IPropertyHandle> HandRelativePlacementProperty = DetailBuilder.GetProperty(GET_MEMBER_NAME_CHECKED(UHandSocketComponent, HandRelativePlacement));
TSharedPtr<IPropertyHandle> LeftHandDominateProperty = DetailBuilder.GetProperty(GET_MEMBER_NAME_CHECKED(UHandSocketComponent, bLeftHandDominant));
FSimpleDelegate OnHandRelativeChangedDelegate = FSimpleDelegate::CreateSP(this, &FHandSocketComponentDetails::OnHandRelativeUpdated, &DetailBuilder);
HandRelativePlacementProperty->SetOnPropertyValueChanged(OnHandRelativeChangedDelegate);
FSimpleDelegate OnLockedStateChangedDelegate = FSimpleDelegate::CreateSP(this, &FHandSocketComponentDetails::OnLockedStateUpdated, &DetailBuilder);
LockedLocationProperty->SetOnPropertyValueChanged(OnLockedStateChangedDelegate);
FSimpleDelegate OnLeftDominateChangedDelegate = FSimpleDelegate::CreateSP(this, &FHandSocketComponentDetails::OnLeftDominantUpdated, &DetailBuilder);
LeftHandDominateProperty->SetOnPropertyValueChanged(OnLeftDominateChangedDelegate);
TSharedPtr<IPropertyHandle> ShowVisualizationProperty = DetailBuilder.GetProperty(GET_MEMBER_NAME_CHECKED(UHandSocketComponent, bShowVisualizationMesh));
FSimpleDelegate OnShowVisChangedDelegate = FSimpleDelegate::CreateSP(this, &FHandSocketComponentDetails::OnUpdateShowMesh, &DetailBuilder);
ShowVisualizationProperty->SetOnPropertyValueChanged(OnShowVisChangedDelegate);
DetailBuilder.EditCategory("Hand Animation")
.AddCustomRow(NSLOCTEXT("HandSocketDetails", "UpdateHandSocket", "Save Current Pose"))
.NameContent()
[
SNew(STextBlock)
.Font(IDetailLayoutBuilder::GetDetailFont())
.Text(NSLOCTEXT("HandSocketDetails", "UpdateHandSocket", "Save Current Pose"))
]
.ValueContent()
.MaxDesiredWidth(125.f)
.MinDesiredWidth(125.f)
[
SNew(SButton)
.ContentPadding(2)
.VAlign(VAlign_Center)
.HAlign(HAlign_Center)
.OnClicked(this, &FHandSocketComponentDetails::OnUpdateSavePose)
[
SNew(STextBlock)
.Font(IDetailLayoutBuilder::GetDetailFont())
.Text(NSLOCTEXT("HandSocketDetails", "UpdateHandSocket", "Save"))
]
];
}
void FHandSocketComponentDetails::OnUpdateShowMesh(IDetailLayoutBuilder* LayoutBuilder)
{
if (!HandSocketComponent.IsValid())
return;
TSharedPtr<FComponentVisualizer> Visualizer = GUnrealEd->FindComponentVisualizer(HandSocketComponent->GetClass());
FHandSocketVisualizer* HandVisualizer = (FHandSocketVisualizer*)Visualizer.Get();
if (HandVisualizer)
{
HandVisualizer->CurrentlySelectedBoneIdx = INDEX_NONE;
HandVisualizer->CurrentlySelectedBone = NAME_None;
HandVisualizer->HandPropertyPath = FComponentPropertyPath();
}
}
FReply FHandSocketComponentDetails::OnUpdateSavePose()
{
if (HandSocketComponent.IsValid() && HandSocketComponent->CustomPoseDeltas.Num() > 0)
{
if (HandSocketComponent->HandTargetAnimation || HandSocketComponent->VisualizationMesh)
{
// Save Animation Pose here
FString AssetPath;
FString AssetName;
PromptUserForAssetPath(AssetPath, AssetName);
TWeakObjectPtr<UAnimSequence> NewAnim = SaveAnimationAsset(AssetPath, AssetName);
// Finally remove the deltas
if (NewAnim.IsValid())
{
HandSocketComponent->Modify();
if (AActor* Owner = HandSocketComponent->GetOwner())
{
Owner->Modify();
}
HandSocketComponent->HandTargetAnimation = NewAnim.Get();
HandSocketComponent->CustomPoseDeltas.Empty();
HandSocketComponent->bUseCustomPoseDeltas = false;
TSharedPtr<FComponentVisualizer> Visualizer = GUnrealEd->FindComponentVisualizer(HandSocketComponent->GetClass());
FHandSocketVisualizer* HandVisualizer = (FHandSocketVisualizer*)Visualizer.Get();
if (HandVisualizer)
{
if (UHandSocketComponent* RefHand = HandVisualizer->GetCurrentlyEditingComponent())
{
RefHand->HandTargetAnimation = NewAnim.Get();
RefHand->CustomPoseDeltas.Empty();
RefHand->bUseCustomPoseDeltas = false;
/*TArray<FProperty*> PropertiesToModify;
PropertiesToModify.Add(FindFProperty<FProperty>(UHandSocketComponent::StaticClass(), GET_MEMBER_NAME_CHECKED(UHandSocketComponent, HandTargetAnimation)));
PropertiesToModify.Add(FindFProperty<FProperty>(UHandSocketComponent::StaticClass(), GET_MEMBER_NAME_CHECKED(UHandSocketComponent, bUseCustomPoseDeltas)));
PropertiesToModify.Add(FindFProperty<FProperty>(UHandSocketComponent::StaticClass(), GET_MEMBER_NAME_CHECKED(UHandSocketComponent, CustomPoseDeltas)));
FComponentVisualizer::NotifyPropertiesModified(RefHand, PropertiesToModify);*/
}
}
// Modify all of the properties at once
TArray<FProperty*> PropertiesToModify;
PropertiesToModify.Add(FindFProperty<FProperty>(UHandSocketComponent::StaticClass(), GET_MEMBER_NAME_CHECKED(UHandSocketComponent, HandTargetAnimation)));
PropertiesToModify.Add(FindFProperty<FProperty>(UHandSocketComponent::StaticClass(), GET_MEMBER_NAME_CHECKED(UHandSocketComponent, bUseCustomPoseDeltas)));
PropertiesToModify.Add(FindFProperty<FProperty>(UHandSocketComponent::StaticClass(), GET_MEMBER_NAME_CHECKED(UHandSocketComponent, CustomPoseDeltas)));
FComponentVisualizer::NotifyPropertiesModified(HandSocketComponent.Get(), PropertiesToModify);
}
}
}
return FReply::Handled();
}
void SCreateHandAnimationDlg::Construct(const FArguments& InArgs)
{
AssetPath = FText::FromString(FPackageName::GetLongPackagePath(InArgs._DefaultAssetPath.ToString()));
AssetName = FText::FromString(FPackageName::GetLongPackageAssetName(InArgs._DefaultAssetPath.ToString()));
if (AssetPath.IsEmpty())
{
AssetPath = LastUsedAssetPath;
// still empty?
if (AssetPath.IsEmpty())
{
AssetPath = FText::FromString(TEXT("/Game"));
}
}
else
{
LastUsedAssetPath = AssetPath;
}
if (AssetName.IsEmpty())
{
// find default name for them
FAssetToolsModule& AssetToolsModule = FModuleManager::Get().LoadModuleChecked<FAssetToolsModule>("AssetTools");
FString OutPackageName, OutAssetName;
FString PackageName = AssetPath.ToString() + TEXT("/NewAnimation");
AssetToolsModule.Get().CreateUniqueAssetName(PackageName, TEXT(""), OutPackageName, OutAssetName);
AssetName = FText::FromString(OutAssetName);
}
FPathPickerConfig PathPickerConfig;
PathPickerConfig.DefaultPath = AssetPath.ToString();
PathPickerConfig.OnPathSelected = FOnPathSelected::CreateSP(this, &SCreateHandAnimationDlg::OnPathChange);
PathPickerConfig.bAddDefaultPath = true;
FContentBrowserModule& ContentBrowserModule = FModuleManager::LoadModuleChecked<FContentBrowserModule>("ContentBrowser");
SWindow::Construct(SWindow::FArguments()
.Title(LOCTEXT("SCreateHandAnimationDlg_Title", "Create New Animation Object"))
.SupportsMinimize(false)
.SupportsMaximize(false)
//.SizingRule( ESizingRule::Autosized )
.ClientSize(FVector2D(450, 450))
[
SNew(SVerticalBox)
+ SVerticalBox::Slot() // Add user input block
.Padding(2)
[
SNew(SBorder)
.BorderImage(FAppStyle::GetBrush("ToolPanel.GroupBorder"))
[
SNew(SVerticalBox)
+ SVerticalBox::Slot()
.AutoHeight()
[
SNew(STextBlock)
.Text(LOCTEXT("SelectPath", "Select Path to create animation"))
.Font(FCoreStyle::GetDefaultFontStyle("Regular", 14))
]
+ SVerticalBox::Slot()
.FillHeight(1)
.Padding(3)
[
ContentBrowserModule.Get().CreatePathPicker(PathPickerConfig)
]
+ SVerticalBox::Slot()
.AutoHeight()
[
SNew(SSeparator)
]
+ SVerticalBox::Slot()
.AutoHeight()
.Padding(3)
[
SNew(SHorizontalBox)
+ SHorizontalBox::Slot()
.AutoWidth()
.Padding(0, 0, 10, 0)
.VAlign(VAlign_Center)
[
SNew(STextBlock)
.Text(LOCTEXT("AnimationName", "Animation Name"))
]
+ SHorizontalBox::Slot()
[
SNew(SEditableTextBox)
.Text(AssetName)
.OnTextCommitted(this, &SCreateHandAnimationDlg::OnNameChange)
.MinDesiredWidth(250)
]
]
]
]
+ SVerticalBox::Slot()
.AutoHeight()
.HAlign(HAlign_Right)
.Padding(5)
[
SNew(SUniformGridPanel)
.SlotPadding(FAppStyle::GetMargin("StandardDialog.SlotPadding"))
.MinDesiredSlotWidth(FAppStyle::GetFloat("StandardDialog.MinDesiredSlotWidth"))
.MinDesiredSlotHeight(FAppStyle::GetFloat("StandardDialog.MinDesiredSlotHeight"))
+ SUniformGridPanel::Slot(0, 0)
[
SNew(SButton)
.HAlign(HAlign_Center)
.ContentPadding(FAppStyle::GetMargin("StandardDialog.ContentPadding"))
.Text(LOCTEXT("OK", "OK"))
.OnClicked(this, &SCreateHandAnimationDlg::OnButtonClick, EAppReturnType::Ok)
]
+ SUniformGridPanel::Slot(1, 0)
[
SNew(SButton)
.HAlign(HAlign_Center)
.ContentPadding(FAppStyle::GetMargin("StandardDialog.ContentPadding"))
.Text(LOCTEXT("Cancel", "Cancel"))
.OnClicked(this, &SCreateHandAnimationDlg::OnButtonClick, EAppReturnType::Cancel)
]
]
]);
}
void SCreateHandAnimationDlg::OnNameChange(const FText& NewName, ETextCommit::Type CommitInfo)
{
AssetName = NewName;
}
void SCreateHandAnimationDlg::OnPathChange(const FString& NewPath)
{
AssetPath = FText::FromString(NewPath);
LastUsedAssetPath = AssetPath;
}
FReply SCreateHandAnimationDlg::OnButtonClick(EAppReturnType::Type ButtonID)
{
UserResponse = ButtonID;
if (ButtonID != EAppReturnType::Cancel)
{
if (!ValidatePackage())
{
// reject the request
return FReply::Handled();
}
}
RequestDestroyWindow();
return FReply::Handled();
}
/** Ensures supplied package name information is valid */
bool SCreateHandAnimationDlg::ValidatePackage()
{
FText Reason;
FString FullPath = GetFullAssetPath();
if (!FPackageName::IsValidLongPackageName(FullPath, false, &Reason)
|| !FName(*AssetName.ToString()).IsValidObjectName(Reason))
{
FMessageDialog::Open(EAppMsgType::Ok, Reason);
return false;
}
return true;
}
EAppReturnType::Type SCreateHandAnimationDlg::ShowModal()
{
GEditor->EditorAddModalWindow(SharedThis(this));
return UserResponse;
}
FString SCreateHandAnimationDlg::GetAssetPath()
{
return AssetPath.ToString();
}
FString SCreateHandAnimationDlg::GetAssetName()
{
return AssetName.ToString();
}
FString SCreateHandAnimationDlg::GetFullAssetPath()
{
return AssetPath.ToString() + "/" + AssetName.ToString();
}
#undef LOCTEXT_NAMESPACE

View file

@ -0,0 +1,465 @@
// Copyright 1998-2018 Epic Games, Inc. All Rights Reserved.
#include "HandSocketVisualizer.h"
#include "CanvasItem.h"
#include "CanvasTypes.h"
#include "SceneManagement.h"
//#include "UObject/Field.h"
#include "VRBPDatatypes.h"
#include "ScopedTransaction.h"
#include "Modules/ModuleManager.h"
#include "EditorViewportClient.h"
#include "Components/PoseableMeshComponent.h"
#include "Misc/PackageName.h"
//#include "Persona.h"
IMPLEMENT_HIT_PROXY(HHandSocketVisProxy, HComponentVisProxy);
#define LOCTEXT_NAMESPACE "HandSocketVisualizer"
bool FHandSocketVisualizer::VisProxyHandleClick(FEditorViewportClient* InViewportClient, HComponentVisProxy* VisProxy, const FViewportClick& Click)
{
bool bEditing = false;
if (VisProxy && VisProxy->Component.IsValid())
{
bEditing = true;
if (VisProxy->IsA(HHandSocketVisProxy::StaticGetType()))
{
if( const UHandSocketComponent * HandComp = UpdateSelectedHandComponent(VisProxy))
{
HHandSocketVisProxy* Proxy = (HHandSocketVisProxy*)VisProxy;
if (Proxy)
{
CurrentlySelectedBone = Proxy->TargetBoneName;
CurrentlySelectedBoneIdx = Proxy->BoneIdx;
TargetViewport = InViewportClient->Viewport;
}
}
}
}
return bEditing;
}
bool FHandSocketVisualizer::GetCustomInputCoordinateSystem(const FEditorViewportClient* ViewportClient, FMatrix& OutMatrix) const
{
if (TargetViewport == nullptr || TargetViewport != ViewportClient->Viewport)
{
return false;
}
if (HandPropertyPath.IsValid() && CurrentlySelectedBone != NAME_None/* && CurrentlySelectedBone != "HandSocket"*/)
{
if (CurrentlySelectedBone == "HandSocket")
{
UHandSocketComponent* CurrentlyEditingComponent = GetCurrentlyEditingComponent();
if (CurrentlyEditingComponent)
{
if (CurrentlyEditingComponent->bMirrorVisualizationMesh)
{
FTransform NewTrans = CurrentlyEditingComponent->GetRelativeTransform();
NewTrans.Mirror(CurrentlyEditingComponent->GetAsEAxis(CurrentlyEditingComponent->MirrorAxis), CurrentlyEditingComponent->GetAsEAxis(CurrentlyEditingComponent->FlipAxis));
if (USceneComponent* ParentComp = CurrentlyEditingComponent->GetAttachParent())
{
NewTrans = NewTrans * ParentComp->GetComponentTransform();
}
OutMatrix = FRotationMatrix::Make(NewTrans.GetRotation());
}
}
return false;
}
else if (CurrentlySelectedBone == "Visualizer")
{
if (UHandSocketComponent* CurrentlyEditingComponent = GetCurrentlyEditingComponent())
{
FTransform newTrans = FTransform::Identity;
if (CurrentlyEditingComponent->bDecoupleMeshPlacement)
{
if (USceneComponent* ParentComp = CurrentlyEditingComponent->GetAttachParent())
{
newTrans = CurrentlyEditingComponent->HandRelativePlacement * ParentComp->GetComponentTransform();
}
}
else
{
newTrans = CurrentlyEditingComponent->GetHandRelativePlacement() * CurrentlyEditingComponent->GetComponentTransform();
}
OutMatrix = FRotationMatrix::Make(newTrans.GetRotation());
}
}
else
{
if (UHandSocketComponent* CurrentlyEditingComponent = GetCurrentlyEditingComponent())
{
FTransform newTrans = CurrentlyEditingComponent->HandVisualizerComponent->GetBoneTransform(CurrentlySelectedBoneIdx);
OutMatrix = FRotationMatrix::Make(newTrans.GetRotation());
}
}
return true;
}
return false;
}
bool FHandSocketVisualizer::IsVisualizingArchetype() const
{
return (HandPropertyPath.IsValid() && HandPropertyPath.GetParentOwningActor() && FActorEditorUtils::IsAPreviewOrInactiveActor(HandPropertyPath.GetParentOwningActor()));
}
void FHandSocketVisualizer::DrawVisualizationHUD(const UActorComponent* Component, const FViewport* Viewport, const FSceneView* View, FCanvas* Canvas)
{
if (TargetViewport == nullptr || TargetViewport != Viewport)
{
return;
}
if (const UHandSocketComponent* HandComp = Cast<const UHandSocketComponent>(Component))
{
if (CurrentlySelectedBone != NAME_None)
{
if (UHandSocketComponent* CurrentlyEditingComponent = GetCurrentlyEditingComponent())
{
if (!CurrentlyEditingComponent->HandVisualizerComponent)
{
return;
}
int32 XL;
int32 YL;
const FIntRect CanvasRect = Canvas->GetViewRect();
FPlane location = View->Project(CurrentlyEditingComponent->HandVisualizerComponent->GetBoneTransform(CurrentlySelectedBoneIdx).GetLocation());
StringSize(GEngine->GetLargeFont(), XL, YL, *CurrentlySelectedBone.ToString());
//const float DrawPositionX = location.X - XL;
//const float DrawPositionY = location.Y - YL;
const float DrawPositionX = FMath::FloorToFloat(CanvasRect.Min.X + (CanvasRect.Width() - XL) * 0.5f);
const float DrawPositionY = CanvasRect.Min.Y + 50.0f;
Canvas->DrawShadowedString(DrawPositionX, DrawPositionY, *CurrentlySelectedBone.ToString(), GEngine->GetLargeFont(), FLinearColor::Yellow);
}
}
}
}
void FHandSocketVisualizer::DrawVisualization(const UActorComponent* Component, const FSceneView* View, FPrimitiveDrawInterface* PDI)
{
//UWorld* World = Component->GetWorld();
//return World && (World->WorldType == EWorldType::EditorPreview || World->WorldType == EWorldType::Inactive);
//cast the component into the expected component type
if (const UHandSocketComponent* HandComponent = Cast<UHandSocketComponent>(Component))
{
if (!HandComponent->HandVisualizerComponent)
return;
//This is an editor only uproperty of our targeting component, that way we can change the colors if we can't see them against the background
const FLinearColor SelectedColor = FLinearColor::Yellow;//TargetingComponent->EditorSelectedColor;
const FLinearColor UnselectedColor = FLinearColor::White;//TargetingComponent->EditorUnselectedColor;
const FVector Location = HandComponent->HandVisualizerComponent->GetComponentLocation();
float BoneScale = 1.0f - ((View->ViewLocation - Location).SizeSquared() / FMath::Square(100.0f));
BoneScale = FMath::Clamp(BoneScale, 0.2f, 1.0f);
HHandSocketVisProxy* newHitProxy = new HHandSocketVisProxy(Component);
newHitProxy->TargetBoneName = "Visualizer";
PDI->SetHitProxy(newHitProxy);
PDI->DrawPoint(Location, CurrentlySelectedBone == newHitProxy->TargetBoneName ? SelectedColor : FLinearColor::Red, 20.f * BoneScale, SDPG_Foreground);
PDI->SetHitProxy(NULL);
newHitProxy = nullptr;
newHitProxy = new HHandSocketVisProxy(Component);
newHitProxy->TargetBoneName = "HandSocket";
BoneScale = 1.0f - ((View->ViewLocation - HandComponent->GetComponentLocation()).SizeSquared() / FMath::Square(100.0f));
BoneScale = FMath::Clamp(BoneScale, 0.2f, 1.0f);
PDI->SetHitProxy(newHitProxy);
PDI->DrawPoint(HandComponent->GetComponentLocation(), FLinearColor::Green, 20.f * BoneScale, SDPG_Foreground);
PDI->SetHitProxy(NULL);
newHitProxy = nullptr;
if (HandComponent->bUseCustomPoseDeltas)
{
TArray<FTransform> BoneTransforms = HandComponent->HandVisualizerComponent->GetBoneSpaceTransforms();
FTransform ParentTrans = HandComponent->HandVisualizerComponent->GetComponentTransform();
// We skip root bone, moving the visualizer itself handles that
for (int i = 1; i < HandComponent->HandVisualizerComponent->GetNumBones(); i++)
{
FName BoneName = HandComponent->HandVisualizerComponent->GetBoneName(i);
if (HandComponent->bFilterBonesByPostfix)
{
if (BoneName.ToString().Right(2) != HandComponent->FilterPostfix)
{
// Skip visualizing this bone its the incorrect side
continue;
}
}
if (HandComponent->BonesToSkip.Contains(BoneName))
{
// Skip visualizing this bone as its in the ignore array
continue;
}
FTransform BoneTransform = HandComponent->HandVisualizerComponent->GetBoneTransform(i);
FVector BoneLoc = BoneTransform.GetLocation();
BoneScale = 1.0f - ((View->ViewLocation - BoneLoc).SizeSquared() / FMath::Square(100.0f));
BoneScale = FMath::Clamp(BoneScale, 0.1f, 0.9f);
newHitProxy = new HHandSocketVisProxy(Component);
newHitProxy->TargetBoneName = BoneName;
newHitProxy->BoneIdx = i;
PDI->SetHitProxy(newHitProxy);
PDI->DrawPoint(BoneLoc, CurrentlySelectedBone == newHitProxy->TargetBoneName ? SelectedColor : UnselectedColor, 20.f * BoneScale, SDPG_Foreground);
PDI->SetHitProxy(NULL);
newHitProxy = nullptr;
}
}
if (HandComponent->bShowRangeVisualization)
{
float RangeVisualization = HandComponent->OverrideDistance;
if (RangeVisualization <= 0.0f)
{
if (USceneComponent* Parent = Cast<USceneComponent>(HandComponent->GetAttachParent()))
{
FStructProperty* ObjectProperty = CastField<FStructProperty>(Parent->GetClass()->FindPropertyByName("VRGripInterfaceSettings"));
AActor* ParentsActor = nullptr;
if (!ObjectProperty)
{
ParentsActor = Parent->GetOwner();
if (ParentsActor)
{
ObjectProperty = CastField<FStructProperty>(Parent->GetOwner()->GetClass()->FindPropertyByName("VRGripInterfaceSettings"));
}
}
if (ObjectProperty)
{
UObject* Target = ParentsActor;
if (Target == nullptr)
{
Target = Parent;
}
if (const FBPInterfaceProperties* Curve = ObjectProperty->ContainerPtrToValuePtr<FBPInterfaceProperties>(Target))
{
if (HandComponent->SlotPrefix == "VRGripS")
{
RangeVisualization = Curve->SecondarySlotRange;
}
else
{
RangeVisualization = Curve->PrimarySlotRange;
}
}
}
}
}
// Scale into our parents space as that is actually what the range is based on
FBox BoxToDraw = FBox::BuildAABB(FVector::ZeroVector, FVector(RangeVisualization) * HandComponent->GetAttachParent()->GetComponentScale());
BoxToDraw.Min += HandComponent->GetComponentLocation();
BoxToDraw.Max += HandComponent->GetComponentLocation();
DrawWireBox(PDI, BoxToDraw, FColor::Green, 0.0f);
}
}
}
bool FHandSocketVisualizer::GetWidgetLocation(const FEditorViewportClient* ViewportClient, FVector& OutLocation) const
{
if (TargetViewport == nullptr || TargetViewport != ViewportClient->Viewport)
{
return false;
}
if (HandPropertyPath.IsValid() && CurrentlySelectedBone != NAME_None && CurrentlySelectedBone != "HandSocket")
{
if (CurrentlySelectedBone == "HandSocket")
{
return false;
}
else if (CurrentlySelectedBone == "Visualizer")
{
if (UHandSocketComponent* CurrentlyEditingComponent = GetCurrentlyEditingComponent())
{
FTransform newTrans = FTransform::Identity;
if (CurrentlyEditingComponent->bDecoupleMeshPlacement)
{
if (USceneComponent* ParentComp = CurrentlyEditingComponent->GetAttachParent())
{
newTrans = CurrentlyEditingComponent->HandRelativePlacement * ParentComp->GetComponentTransform();
}
}
else
{
newTrans = CurrentlyEditingComponent->GetHandRelativePlacement() * CurrentlyEditingComponent->GetComponentTransform();
}
OutLocation = newTrans.GetLocation();
}
}
else
{
if (UHandSocketComponent* CurrentlyEditingComponent = GetCurrentlyEditingComponent())
{
OutLocation = CurrentlyEditingComponent->HandVisualizerComponent->GetBoneTransform(CurrentlySelectedBoneIdx).GetLocation();
}
}
return true;
}
return false;
}
bool FHandSocketVisualizer::HandleInputDelta(FEditorViewportClient* ViewportClient, FViewport* Viewport, FVector& DeltaTranslate, FRotator& DeltaRotate, FVector& DeltaScale)
{
if (TargetViewport == nullptr || TargetViewport != Viewport)
{
return false;
}
bool bHandled = false;
if (HandPropertyPath.IsValid())
{
if (CurrentlySelectedBone == "HandSocket" || CurrentlySelectedBone == NAME_None)
{
bHandled = false;
}
else if (CurrentlySelectedBone == "Visualizer")
{
const FScopedTransaction Transaction(LOCTEXT("ChangingComp", "ChangingComp"));
UHandSocketComponent* CurrentlyEditingComponent = GetCurrentlyEditingComponent();
if (!CurrentlyEditingComponent)
{
return false;
}
CurrentlyEditingComponent->Modify();
if (AActor* Owner = CurrentlyEditingComponent->GetOwner())
{
Owner->Modify();
}
bool bLevelEdit = ViewportClient->IsLevelEditorClient();
FTransform CurrentTrans = FTransform::Identity;
if (CurrentlyEditingComponent->bDecoupleMeshPlacement)
{
if (USceneComponent* ParentComp = CurrentlyEditingComponent->GetAttachParent())
{
CurrentTrans = CurrentlyEditingComponent->HandRelativePlacement * ParentComp->GetComponentTransform();
}
}
else
{
CurrentTrans = CurrentlyEditingComponent->GetHandRelativePlacement() * CurrentlyEditingComponent->GetComponentTransform();
}
if (!DeltaTranslate.IsNearlyZero())
{
CurrentTrans.AddToTranslation(DeltaTranslate);
}
if (!DeltaRotate.IsNearlyZero())
{
CurrentTrans.SetRotation(DeltaRotate.Quaternion() * CurrentTrans.GetRotation());
}
if (!DeltaScale.IsNearlyZero())
{
CurrentTrans.MultiplyScale3D(DeltaScale);
}
if (CurrentlyEditingComponent->bDecoupleMeshPlacement)
{
if (USceneComponent* ParentComp = CurrentlyEditingComponent->GetAttachParent())
{
CurrentlyEditingComponent->HandRelativePlacement = CurrentTrans.GetRelativeTransform(ParentComp->GetComponentTransform());
}
}
else
{
CurrentlyEditingComponent->HandRelativePlacement = CurrentTrans.GetRelativeTransform(CurrentlyEditingComponent->GetComponentTransform());
}
NotifyPropertyModified(CurrentlyEditingComponent, FindFProperty<FProperty>(UHandSocketComponent::StaticClass(), GET_MEMBER_NAME_CHECKED(UHandSocketComponent, HandRelativePlacement)));
//GEditor->RedrawLevelEditingViewports(true);
bHandled = true;
}
else
{
UHandSocketComponent* CurrentlyEditingComponent = GetCurrentlyEditingComponent();
if (!CurrentlyEditingComponent || !CurrentlyEditingComponent->HandVisualizerComponent)
{
return false;
}
const FScopedTransaction Transaction(LOCTEXT("ChangingComp", "ChangingComp"));
CurrentlyEditingComponent->Modify();
if (AActor* Owner = CurrentlyEditingComponent->GetOwner())
{
Owner->Modify();
}
bool bLevelEdit = ViewportClient->IsLevelEditorClient();
FTransform BoneTrans = CurrentlyEditingComponent->HandVisualizerComponent->GetBoneTransform(CurrentlySelectedBoneIdx);
FTransform NewTrans = BoneTrans;
NewTrans.SetRotation(DeltaRotate.Quaternion() * NewTrans.GetRotation());
FQuat DeltaRotateMod = NewTrans.GetRelativeTransform(BoneTrans).GetRotation();
bool bFoundBone = false;
for (FBPVRHandPoseBonePair& BonePair : CurrentlyEditingComponent->CustomPoseDeltas)
{
if (BonePair.BoneName == CurrentlySelectedBone)
{
bFoundBone = true;
BonePair.DeltaPose *= DeltaRotateMod;
break;
}
}
if (!bFoundBone)
{
FBPVRHandPoseBonePair newBonePair;
newBonePair.BoneName = CurrentlySelectedBone;
newBonePair.DeltaPose *= DeltaRotateMod;
CurrentlyEditingComponent->CustomPoseDeltas.Add(newBonePair);
bFoundBone = true;
}
if (bFoundBone)
{
NotifyPropertyModified(CurrentlyEditingComponent, FindFProperty<FProperty>(UHandSocketComponent::StaticClass(), GET_MEMBER_NAME_CHECKED(UHandSocketComponent, CustomPoseDeltas)));
}
//GEditor->RedrawLevelEditingViewports(true);
bHandled = true;
}
}
return bHandled;
}
void FHandSocketVisualizer::EndEditing()
{
HandPropertyPath = FComponentPropertyPath();
CurrentlySelectedBone = NAME_None;
CurrentlySelectedBoneIdx = INDEX_NONE;
TargetViewport = nullptr;
}
#undef LOCTEXT_NAMESPACE

View file

@ -0,0 +1,73 @@
// Copyright 1998-2018 Epic Games, Inc. All Rights Reserved.
#include "VRExpansionEditor.h"
#include "Editor/UnrealEdEngine.h"
#include "UnrealEdGlobals.h"
#include "Grippables/HandSocketComponent.h"
#include "PropertyEditorModule.h"
#include "HandSocketVisualizer.h"
#include "HandSocketComponentDetails.h"
#include "VRGlobalSettingsDetails.h"
#include "VRGlobalSettings.h"
IMPLEMENT_MODULE(FVRExpansionEditorModule, VRExpansionEditor);
void FVRExpansionEditorModule::StartupModule()
{
RegisterComponentVisualizer(UHandSocketComponent::StaticClass()->GetFName(), MakeShareable(new FHandSocketVisualizer));
// Register detail customizations
{
auto& PropertyModule = FModuleManager::LoadModuleChecked< FPropertyEditorModule >("PropertyEditor");
// Register our customization to be used by a class 'UMyClass' or 'AMyClass'. Note the prefix must be dropped.
PropertyModule.RegisterCustomClassLayout(
UHandSocketComponent::StaticClass()->GetFName(),
FOnGetDetailCustomizationInstance::CreateStatic(&FHandSocketComponentDetails::MakeInstance)
);
PropertyModule.RegisterCustomClassLayout(
UVRGlobalSettings::StaticClass()->GetFName(),
FOnGetDetailCustomizationInstance::CreateStatic(&FVRGlobalSettingsDetails::MakeInstance)
);
PropertyModule.NotifyCustomizationModuleChanged();
}
}
void FVRExpansionEditorModule::ShutdownModule()
{
if (GUnrealEd != NULL)
{
// Iterate over all class names we registered for
for (FName ClassName : RegisteredComponentClassNames)
{
GUnrealEd->UnregisterComponentVisualizer(ClassName);
}
}
if (FModuleManager::Get().IsModuleLoaded("PropertyEditor"))
{
auto& PropertyModule = FModuleManager::LoadModuleChecked<FPropertyEditorModule>("PropertyEditor");
PropertyModule.UnregisterCustomClassLayout(UHandSocketComponent::StaticClass()->GetFName());
PropertyModule.UnregisterCustomClassLayout(UVRGlobalSettings::StaticClass()->GetFName());
}
}
void FVRExpansionEditorModule::RegisterComponentVisualizer(FName ComponentClassName, TSharedPtr<FComponentVisualizer> Visualizer)
{
if (GUnrealEd != NULL)
{
GUnrealEd->RegisterComponentVisualizer(ComponentClassName, Visualizer);
}
RegisteredComponentClassNames.Add(ComponentClassName);
if (Visualizer.IsValid())
{
Visualizer->OnRegister();
}
}

View file

@ -0,0 +1,151 @@
// Copyright Epic Games, Inc. All Rights Reserved.
#include "VRGlobalSettingsDetails.h"
#include "VRGlobalSettings.h"
//#include "PropertyEditing.h"
#include "Widgets/Text/STextBlock.h"
#include "Widgets/Input/SButton.h"
//#include "PropertyHandle.h"
#include "DetailLayoutBuilder.h"
#include "DetailWidgetRow.h"
#include "DetailCategoryBuilder.h"
#include "IDetailsView.h"
//#include "Developer/AssetTools/Public/IAssetTools.h"
//#include "Developer/AssetTools/Public/AssetToolsModule.h"
//#include "Editor/ContentBrowser/Public/IContentBrowserSingleton.h"
//#include "Editor/ContentBrowser/Public/ContentBrowserModule.h"
//#include "AnimationUtils.h"
#include "UObject/SavePackage.h"
#include "Misc/MessageDialog.h"
//#include "Widgets/Layout/SBorder.h"
//#include "Widgets/Layout/SSeparator.h"
//#include "Widgets/Layout/SUniformGridPanel.h"
//#include "Widgets/Input/SEditableTextBox.h"
#include "Editor.h"
#include "EditorStyleSet.h"
#include "Styling/CoreStyle.h"
#include "Animation/AnimData/AnimDataModel.h"
#include "AssetRegistry/AssetRegistryModule.h"
#include "AssetRegistry/ARFilter.h"
#include "Editor/UnrealEdEngine.h"
#include "UnrealEdGlobals.h"
#define LOCTEXT_NAMESPACE "VRGlobalSettingsDetails"
TSharedRef< IDetailCustomization > FVRGlobalSettingsDetails::MakeInstance()
{
return MakeShareable(new FVRGlobalSettingsDetails);
}
void FVRGlobalSettingsDetails::CustomizeDetails(IDetailLayoutBuilder& DetailBuilder)
{
//DetailBuilder.HideCategory(FName("ComponentTick"));
//DetailBuilder.HideCategory(FName("GameplayTags"));
//TSharedPtr<IPropertyHandle> LockedLocationProperty = DetailBuilder.GetProperty(GET_MEMBER_NAME_CHECKED(UHandSocketComponent, bDecoupleMeshPlacement));
//FSimpleDelegate OnLockedStateChangedDelegate = FSimpleDelegate::CreateSP(this, &FVRGlobalSettingsDetails::OnLockedStateUpdated, &DetailBuilder);
//LockedLocationProperty->SetOnPropertyValueChanged(OnLockedStateChangedDelegate);
DetailBuilder.EditCategory("Utilities")
.AddCustomRow(NSLOCTEXT("VRGlobalSettingsDetails", "Tools", "Fix Invalid 5.2 Animation Assets"))
.NameContent()
[
SNew(STextBlock)
.Font(IDetailLayoutBuilder::GetDetailFont())
.Text(NSLOCTEXT("VRGlobalSettingsDetails", "Tools", "Fix Invalid 5.2 Animation Assets"))
]
.ValueContent()
.MaxDesiredWidth(125.f)
.MinDesiredWidth(125.f)
[
SNew(SButton)
.ContentPadding(2)
.VAlign(VAlign_Center)
.HAlign(HAlign_Center)
.OnClicked(this, &FVRGlobalSettingsDetails::OnCorrectInvalidAnimationAssets)
[
SNew(STextBlock)
.Font(IDetailLayoutBuilder::GetDetailFont())
.Text(NSLOCTEXT("VRGlobalSettingsDetails", "Tools", "Fix Animation Assets"))
]
];
}
FReply FVRGlobalSettingsDetails::OnCorrectInvalidAnimationAssets()
{
// Load the asset registry module
FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked< FAssetRegistryModule >(FName("AssetRegistry"));
IAssetRegistry& AssetRegistry = AssetRegistryModule.Get();
TArray< FString > ContentPaths;
ContentPaths.Add(TEXT("/Game"));
AssetRegistry.ScanPathsSynchronous(ContentPaths);
FARFilter Filter;
Filter.ClassPaths.Add(UAnimSequence::StaticClass()->GetClassPathName());
//Filter.ClassNames.Add(UAnimSequence::StaticClass()->GetFName());
Filter.bRecursiveClasses = true;
//if (!Path.IsEmpty())
//{
// Filter.PackagePaths.Add(*Path);
//}
Filter.bRecursivePaths = true;
TArray< FAssetData > AssetList;
AssetRegistry.GetAssets(Filter, AssetList);
// Iterate over retrieved blueprint assets
for (auto& Asset : AssetList)
{
// Check the anim sequence for invalid data
if (UAnimSequence* AnimSeq = Cast<UAnimSequence>(Asset.GetAsset()))
{
IAnimationDataController& AnimController = AnimSeq->GetController();
{
IAnimationDataController::FScopedBracket ScopedBracket(AnimController, LOCTEXT("FixAnimationAsset_VRE", "Fixing invalid anim sequences"));
const IAnimationDataModel* AnimModel = AnimController.GetModel();
FFrameRate FrameRate = AnimModel->GetFrameRate();
//int32 NumFrames = AnimModel->GetNumberOfFrames();
double FrameRateD = FrameRate.AsDecimal();
// I was saving with a below 1.0 frame rate and 1 frame
if (FrameRateD < 1.0f)
{
// We have an invalid frame rate for 5.2
AnimController.SetFrameRate(FFrameRate(1, 1));
AnimSeq->MarkPackageDirty();
UPackage* const Package = AnimSeq->GetOutermost();
FString const PackageName = Package->GetName();
FString const PackageFileName = FPackageName::LongPackageNameToFilename(PackageName, FPackageName::GetAssetPackageExtension());
double StartTime = FPlatformTime::Seconds();
FSavePackageArgs PackageArguments;
PackageArguments.SaveFlags = RF_Standalone;
PackageArguments.SaveFlags = SAVE_NoError;
UPackage::SavePackage(Package, NULL, *PackageFileName, PackageArguments);
//UPackage::SavePackage(Package, NULL, RF_Standalone, *PackageFileName, GError, nullptr, false, true, SAVE_NoError);
double ElapsedTime = FPlatformTime::Seconds() - StartTime;
UE_LOG(LogAnimation, Log, TEXT("Animation re-saved %s in %0.2f seconds"), *PackageName, ElapsedTime);
}
}
}
}
return FReply::Handled();
}
#undef LOCTEXT_NAMESPACE

View file

@ -0,0 +1,83 @@
// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "Grippables/HandSocketComponent.h"
#include "IDetailCustomization.h"
#include "Input/Reply.h"
#include "Widgets/DeclarativeSyntaxSupport.h"
#include "Widgets/SWindow.h"
class IDetailLayoutBuilder;
class FHandSocketComponentDetails : public IDetailCustomization
{
public:
/** Makes a new instance of this detail layout class for a specific detail view requesting it */
static TSharedRef<IDetailCustomization> MakeInstance();
/** IDetailCustomization interface */
virtual void CustomizeDetails(IDetailLayoutBuilder& DetailBuilder) override;
//void SortCategories(const TMap<FName, IDetailCategoryBuilder*>& AllCategoryMap);
// The selected hand component
TWeakObjectPtr<UHandSocketComponent> HandSocketComponent;
FReply OnUpdateSavePose();
TWeakObjectPtr<UAnimSequence> SaveAnimationAsset(const FString& InAssetPath, const FString& InAssetName);
void OnLockedStateUpdated(IDetailLayoutBuilder* LayoutBuilder);
void OnLeftDominantUpdated(IDetailLayoutBuilder* LayoutBuilder);
void OnHandRelativeUpdated(IDetailLayoutBuilder* LayoutBuilder);
void OnUpdateShowMesh(IDetailLayoutBuilder* LayoutBuilder);
FHandSocketComponentDetails()
{
}
};
class SCreateHandAnimationDlg : public SWindow
{
public:
SLATE_BEGIN_ARGS(SCreateHandAnimationDlg)
{
}
SLATE_ARGUMENT(FText, DefaultAssetPath)
SLATE_END_ARGS()
SCreateHandAnimationDlg()
: UserResponse(EAppReturnType::Cancel)
{
}
void Construct(const FArguments& InArgs);
public:
/** Displays the dialog in a blocking fashion */
EAppReturnType::Type ShowModal();
/** Gets the resulting asset path */
FString GetAssetPath();
/** Gets the resulting asset name */
FString GetAssetName();
/** Gets the resulting full asset path (path+'/'+name) */
FString GetFullAssetPath();
protected:
void OnPathChange(const FString& NewPath);
void OnNameChange(const FText& NewName, ETextCommit::Type CommitInfo);
FReply OnButtonClick(EAppReturnType::Type ButtonID);
bool ValidatePackage();
EAppReturnType::Type UserResponse;
FText AssetPath;
FText AssetName;
static FText LastUsedAssetPath;
};

View file

@ -0,0 +1,95 @@
#pragma once
#include "ComponentVisualizer.h"
#include "Grippables/HandSocketComponent.h"
#include "ActorEditorUtils.h"
class FEditorViewportClient;
/**Base class for clickable targeting editing proxies*/
struct VREXPANSIONEDITOR_API HHandSocketVisProxy : public HComponentVisProxy
{
DECLARE_HIT_PROXY();
HHandSocketVisProxy(const UActorComponent* InComponent)
: HComponentVisProxy(InComponent, HPP_Wireframe)
{
BoneIdx = 0;
TargetBoneName = NAME_None;
}
uint32 BoneIdx;
FName TargetBoneName;
};
class VREXPANSIONEDITOR_API FHandSocketVisualizer : public FComponentVisualizer
{
public:
FHandSocketVisualizer()
{
CurrentlySelectedBone = NAME_None;
CurrentlySelectedBoneIdx = INDEX_NONE;
HandPropertyPath = FComponentPropertyPath();
TargetViewport = nullptr;
}
virtual ~FHandSocketVisualizer()
{
}
UPROPERTY()
FComponentPropertyPath HandPropertyPath;
FName CurrentlySelectedBone;
uint32 CurrentlySelectedBoneIdx;
UPROPERTY()
FViewport* TargetViewport;
UHandSocketComponent* GetCurrentlyEditingComponent() const
{
return Cast<UHandSocketComponent>(HandPropertyPath.GetComponent());;
}
const UHandSocketComponent* UpdateSelectedHandComponent(HComponentVisProxy* VisProxy)
{
const UHandSocketComponent* HandComp = CastChecked<const UHandSocketComponent>(VisProxy->Component.Get());
UHandSocketComponent* OldHandComp = Cast<UHandSocketComponent>(HandPropertyPath.GetComponent());
AActor* OldOwningActor = HandPropertyPath.GetParentOwningActor();
HandPropertyPath = FComponentPropertyPath(HandComp);
AActor* NewOwningActor = HandPropertyPath.GetParentOwningActor();
if (HandPropertyPath.IsValid())
{
if (OldOwningActor != NewOwningActor || OldHandComp != HandComp)
{
// Reset selection state if we are selecting a different actor to the one previously selected
CurrentlySelectedBoneIdx = INDEX_NONE;
CurrentlySelectedBone = NAME_None;
}
return HandComp;
}
HandPropertyPath = FComponentPropertyPath();
return nullptr;
}
bool SaveAnimationAsset(const FString& InAssetPath, const FString& InAssetName);
bool GetCustomInputCoordinateSystem(const FEditorViewportClient* ViewportClient, FMatrix& OutMatrix) const override;
bool IsVisualizingArchetype() const override;
virtual void DrawVisualization(const UActorComponent* Component, const FSceneView* View, FPrimitiveDrawInterface* PDI) override;
virtual void DrawVisualizationHUD(const UActorComponent* Component, const FViewport* Viewport, const FSceneView* View, FCanvas* Canvas) override;
virtual bool VisProxyHandleClick(FEditorViewportClient* InViewportClient, HComponentVisProxy* VisProxy, const FViewportClick& Click) override;
bool GetWidgetLocation(const FEditorViewportClient* ViewportClient, FVector& OutLocation) const override;
bool HandleInputDelta(FEditorViewportClient* ViewportClient, FViewport* Viewport, FVector& DeltaTranslate, FRotator& DeltaRotate, FVector& DeltaScale) override;
virtual void EndEditing() override;
private:
};

View file

@ -0,0 +1,19 @@
// Copyright 1998-2018 Epic Games, Inc. All Rights Reserved.
#pragma once
#include "Runtime/Core/Public/Modules/ModuleInterface.h"
#include "ComponentVisualizer.h"
class FVRExpansionEditorModule : public IModuleInterface
{
public:
virtual void StartupModule() override;
virtual void ShutdownModule() override;
void RegisterComponentVisualizer(FName ComponentClassName, TSharedPtr<FComponentVisualizer> Visualizer);
/** Array of component class names we have registered, so we know what to unregister afterwards */
TArray<FName> RegisteredComponentClassNames;
};

View file

@ -0,0 +1,31 @@
// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "IDetailCustomization.h"
#include "Input/Reply.h"
#include "Widgets/DeclarativeSyntaxSupport.h"
#include "Widgets/SWindow.h"
class IDetailLayoutBuilder;
class FVRGlobalSettingsDetails : public IDetailCustomization
{
public:
/** Makes a new instance of this detail layout class for a specific detail view requesting it */
static TSharedRef<IDetailCustomization> MakeInstance();
/** IDetailCustomization interface */
virtual void CustomizeDetails(IDetailLayoutBuilder& DetailBuilder) override;
FReply OnCorrectInvalidAnimationAssets();
void OnLockedStateUpdated(IDetailLayoutBuilder* LayoutBuilder);
FVRGlobalSettingsDetails()
{
}
};

View file

@ -0,0 +1,62 @@
// Copyright 1998-2018 Epic Games, Inc. All Rights Reserved.
using System.IO;
namespace UnrealBuildTool.Rules
{
public class VRExpansionEditor : ModuleRules
{
public VRExpansionEditor(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",
"VRExpansionPlugin",
}
);
PrivateDependencyModuleNames.AddRange(
new string[]
{
"UnrealEd",
"BlueprintGraph",
"AnimGraph",
"AnimGraphRuntime",
"SlateCore",
"Slate",
"InputCore",
"Engine",
"UnrealEd",
"EditorStyle",
"AssetRegistry"
}
);
DynamicallyLoadedModuleNames.AddRange(
new string[]
{
// ... add any modules that your module loads dynamically here ...
}
);
}
}
}

View file

@ -0,0 +1,504 @@
// Copyright 1998-2016 Epic Games, Inc. All Rights Reserved.
/*=============================================================================
Movement.cpp: Character movement implementation
=============================================================================*/
#include "CharacterMovementCompTypes.h"
#include UE_INLINE_GENERATED_CPP_BY_NAME(CharacterMovementCompTypes)
#include "VRBaseCharacterMovementComponent.h"
#include "VRBPDatatypes.h"
#include "VRBaseCharacter.h"
#include "VRRootComponent.h"
#include "VRPlayerController.h"
FSavedMove_VRBaseCharacter::FSavedMove_VRBaseCharacter() : FSavedMove_Character()
{
VRCapsuleLocation = FVector::ZeroVector;
LFDiff = FVector::ZeroVector;
VRCapsuleRotation = FRotator::ZeroRotator;
VRReplicatedMovementMode = EVRConjoinedMovementModes::C_MOVE_MAX;// _None;
}
uint8 FSavedMove_VRBaseCharacter::GetCompressedFlags() const
{
// Fills in 01 and 02 for Jump / Crouch
uint8 Result = FSavedMove_Character::GetCompressedFlags();
// Not supporting custom movement mode directly at this time by replicating custom index
// We use 4 bits for this so a maximum of 16 elements
//Result |= (uint8)VRReplicatedMovementMode << 2;
// This takes up custom_2
/*if (bWantsToSnapTurn)
{
Result |= FLAG_SnapTurn;
}*/
// Reserved_1, and Reserved_2, Flag_Custom_0 and Flag_Custom_1 are used up
// By the VRReplicatedMovementMode packing
// only custom_2 and custom_3 are left currently
return Result;
}
bool FSavedMove_VRBaseCharacter::CanCombineWith(const FSavedMovePtr& NewMove, ACharacter* Character, float MaxDelta) const
{
FSavedMove_VRBaseCharacter* nMove = (FSavedMove_VRBaseCharacter*)NewMove.Get();
if (!nMove || (VRReplicatedMovementMode != nMove->VRReplicatedMovementMode))
return false;
if (!ConditionalValues.MoveActionArray.CanCombine() || !nMove->ConditionalValues.MoveActionArray.CanCombine())
return false;
if (!ConditionalValues.CustomVRInputVector.IsZero() || !nMove->ConditionalValues.CustomVRInputVector.IsZero())
return false;
if (!ConditionalValues.RequestedVelocity.IsZero() || !nMove->ConditionalValues.RequestedVelocity.IsZero())
return false;
// Hate this but we really can't combine if I am sending a new capsule height
if (!FMath::IsNearlyEqual(CapsuleHeight, nMove->CapsuleHeight))
return false;
if (!LFDiff.IsZero() && !nMove->LFDiff.IsZero() && !FVector::Coincident(LFDiff.GetSafeNormal(), nMove->LFDiff.GetSafeNormal(), AccelDotThresholdCombine))
return false;
return FSavedMove_Character::CanCombineWith(NewMove, Character, MaxDelta);
}
bool FSavedMove_VRBaseCharacter::IsImportantMove(const FSavedMovePtr& LastAckedMove) const
{
// Auto important if toggled climbing
if (VRReplicatedMovementMode != EVRConjoinedMovementModes::C_MOVE_MAX)//_None)
return true;
if (!ConditionalValues.CustomVRInputVector.IsZero())
return true;
if (!ConditionalValues.RequestedVelocity.IsZero())
return true;
if (ConditionalValues.MoveActionArray.MoveActions.Num() > 0)
return true;
// #TODO: What to do here?
// This is debatable, however it will ALWAYS be non zero realistically and only really effects step ups for the most part
//if (!LFDiff.IsNearlyZero())
//return true;
// Else check parent class
return FSavedMove_Character::IsImportantMove(LastAckedMove);
}
void FSavedMove_VRBaseCharacter::SetInitialPosition(ACharacter* C)
{
// See if we can get the VR capsule location
//if (AVRBaseCharacter * VRC = Cast<AVRBaseCharacter>(C))
//{
if (UVRBaseCharacterMovementComponent* moveComp = Cast<UVRBaseCharacterMovementComponent>(C->GetMovementComponent()))
{
// Saving this out early because it will be wiped before the PostUpdate gets the values
//ConditionalValues.MoveAction.MoveAction = moveComp->MoveAction.MoveAction;
VRReplicatedMovementMode = moveComp->VRReplicatedMovementMode;
if (moveComp->HasRequestedVelocity())
ConditionalValues.RequestedVelocity = moveComp->RequestedVelocity;
else
ConditionalValues.RequestedVelocity = FVector::ZeroVector;
// Throw out the Z value of the headset, its not used anyway for movement
// Instead, re-purpose it to be the capsule half height
if (AVRBaseCharacter* BaseChar = Cast<AVRBaseCharacter>(C))
{
if (BaseChar->VRReplicateCapsuleHeight)
CapsuleHeight = BaseChar->GetCapsuleComponent()->GetUnscaledCapsuleHalfHeight();
else
CapsuleHeight = 0.0f;
}
else
CapsuleHeight = 0.0f;
}
else
{
VRReplicatedMovementMode = EVRConjoinedMovementModes::C_MOVE_MAX;//None;
ConditionalValues.CustomVRInputVector = FVector::ZeroVector;
ConditionalValues.RequestedVelocity = FVector::ZeroVector;
}
//}
//else
//{
// VRReplicatedMovementMode = EVRConjoinedMovementModes::C_MOVE_MAX;//None;
// ConditionalValues.CustomVRInputVector = FVector::ZeroVector;
//}
FSavedMove_Character::SetInitialPosition(C);
}
void FSavedMove_VRBaseCharacter::CombineWith(const FSavedMove_Character* OldMove, ACharacter* InCharacter, APlayerController* PC, const FVector& OldStartLocation)
{
UCharacterMovementComponent* CharMovement = InCharacter->GetCharacterMovement();
// to combine move, first revert pawn position to PendingMove start position, before playing combined move on client
CharMovement->UpdatedComponent->SetWorldLocationAndRotation(OldStartLocation, OldMove->StartRotation, false, nullptr, CharMovement->GetTeleportType());
CharMovement->Velocity = OldMove->StartVelocity;
CharMovement->SetBase(OldMove->StartBase.Get(), OldMove->StartBoneName);
CharMovement->CurrentFloor = OldMove->StartFloor;
// Now that we have reverted to the old position, prepare a new move from that position,
// using our current velocity, acceleration, and rotation, but applied over the combined time from the old and new move.
// Combine times for both moves
DeltaTime += OldMove->DeltaTime;
//FSavedMove_VRBaseCharacter * BaseSavedMove = (FSavedMove_VRBaseCharacter *)NewMove.Get();
FSavedMove_VRBaseCharacter* BaseSavedMovePending = (FSavedMove_VRBaseCharacter*)OldMove;
if (/*BaseSavedMove && */BaseSavedMovePending)
{
LFDiff.X += BaseSavedMovePending->LFDiff.X;
LFDiff.Y += BaseSavedMovePending->LFDiff.Y;
}
// Roll back jump force counters. SetInitialPosition() below will copy them to the saved move.
// Changes in certain counters like JumpCurrentCount don't allow move combining, so no need to roll those back (they are the same).
InCharacter->JumpForceTimeRemaining = OldMove->JumpForceTimeRemaining;
InCharacter->JumpKeyHoldTime = OldMove->JumpKeyHoldTime;
// Merge if we had valid mergable move actions
ConditionalValues.MoveActionArray.MoveActions.Append(BaseSavedMovePending->ConditionalValues.MoveActionArray.MoveActions);
}
void FSavedMove_VRBaseCharacter::PostUpdate(ACharacter* C, EPostUpdateMode PostUpdateMode)
{
FSavedMove_Character::PostUpdate(C, PostUpdateMode);
// See if we can get the VR capsule location
//if (AVRBaseCharacter * VRC = Cast<AVRBaseCharacter>(C))
//{
if (UVRBaseCharacterMovementComponent* moveComp = Cast<UVRBaseCharacterMovementComponent>(C->GetMovementComponent()))
{
ConditionalValues.CustomVRInputVector = moveComp->CustomVRInputVector;
ConditionalValues.MoveActionArray = moveComp->MoveActionArray;
moveComp->MoveActionArray.Clear();
}
//}
/*if (ConditionalValues.MoveAction.MoveAction != EVRMoveAction::VRMOVEACTION_None)
{
// See if we can get the VR capsule location
if (AVRBaseCharacter * VRC = Cast<AVRBaseCharacter>(C))
{
if (UVRBaseCharacterMovementComponent * moveComp = Cast<UVRBaseCharacterMovementComponent>(VRC->GetMovementComponent()))
{
// This is cleared out in perform movement so I need to save it before applying below
EVRMoveAction tempAction = ConditionalValues.MoveAction.MoveAction;
ConditionalValues.MoveAction = moveComp->MoveAction;
ConditionalValues.MoveAction.MoveAction = tempAction;
}
else
{
ConditionalValues.MoveAction.Clear();
}
}
else
{
ConditionalValues.MoveAction.Clear();
}
}*/
}
void FSavedMove_VRBaseCharacter::Clear()
{
VRReplicatedMovementMode = EVRConjoinedMovementModes::C_MOVE_MAX;// None;
VRCapsuleLocation = FVector::ZeroVector;
VRCapsuleRotation = FRotator::ZeroRotator;
LFDiff = FVector::ZeroVector;
CapsuleHeight = 0.0f;
ConditionalValues.CustomVRInputVector = FVector::ZeroVector;
ConditionalValues.RequestedVelocity = FVector::ZeroVector;
ConditionalValues.MoveActionArray.Clear();
//ConditionalValues.MoveAction.Clear();
FSavedMove_Character::Clear();
}
void FSavedMove_VRBaseCharacter::PrepMoveFor(ACharacter* Character)
{
UVRBaseCharacterMovementComponent* BaseCharMove = Cast<UVRBaseCharacterMovementComponent>(Character->GetCharacterMovement());
if (BaseCharMove)
{
BaseCharMove->MoveActionArray = ConditionalValues.MoveActionArray;
//BaseCharMove->MoveAction = ConditionalValues.MoveAction;
BaseCharMove->CustomVRInputVector = ConditionalValues.CustomVRInputVector;//this->CustomVRInputVector;
BaseCharMove->VRReplicatedMovementMode = this->VRReplicatedMovementMode;
}
if (!ConditionalValues.RequestedVelocity.IsZero())
{
BaseCharMove->RequestedVelocity = ConditionalValues.RequestedVelocity;
BaseCharMove->SetHasRequestedVelocity(true);
}
else
{
BaseCharMove->SetHasRequestedVelocity(false);
}
FSavedMove_Character::PrepMoveFor(Character);
}
FVRCharacterScopedMovementUpdate::FVRCharacterScopedMovementUpdate(USceneComponent* Component, EScopedUpdate::Type ScopeBehavior, bool bRequireOverlapsEventFlagToQueueOverlaps)
: FScopedMovementUpdate(Component, ScopeBehavior, bRequireOverlapsEventFlagToQueueOverlaps)
{
UVRRootComponent* RootComponent = Cast<UVRRootComponent>(Owner);
if (RootComponent)
{
InitialVRTransform = RootComponent->OffsetComponentToWorld;
}
}
void FVRCharacterScopedMovementUpdate::RevertMove()
{
bool bTransformIsDirty = IsTransformDirty();
FScopedMovementUpdate::RevertMove();
UVRRootComponent* RootComponent = Cast<UVRRootComponent>(Owner);
if (RootComponent)
{
// If the base class was going to miss bad overlaps, ie: the offsetcomponent to world is different but the transform isn't
if (!bTransformIsDirty && !IsDeferringUpdates() && !InitialVRTransform.Equals(RootComponent->OffsetComponentToWorld))
{
RootComponent->UpdateOverlaps();
}
// Fix offset
RootComponent->GenerateOffsetToWorld();
}
}
FVRCharacterNetworkMoveData::FVRCharacterNetworkMoveData() : FCharacterNetworkMoveData()
{
VRCapsuleLocation = FVector::ZeroVector;
LFDiff = FVector::ZeroVector;
CapsuleHeight = 0.f;
VRCapsuleRotation = 0.f;
ReplicatedMovementMode = EVRConjoinedMovementModes::C_MOVE_MAX;
}
FVRCharacterNetworkMoveData::~FVRCharacterNetworkMoveData()
{
}
void FVRCharacterNetworkMoveData::ClientFillNetworkMoveData(const FSavedMove_Character& ClientMove, ENetworkMoveType MoveType)
{
// Handles the movement base itself now
FCharacterNetworkMoveData::ClientFillNetworkMoveData(ClientMove, MoveType);
// I know that we overloaded this, so it should be our base type
if (const FSavedMove_VRBaseCharacter* SavedMove = (const FSavedMove_VRBaseCharacter*)(&ClientMove))
{
ReplicatedMovementMode = SavedMove->VRReplicatedMovementMode;
ConditionalMoveReps = SavedMove->ConditionalValues;
// #TODO: Roll these into the conditionals
VRCapsuleLocation = SavedMove->VRCapsuleLocation;
LFDiff = SavedMove->LFDiff;
CapsuleHeight = SavedMove->CapsuleHeight;
VRCapsuleRotation = FRotator::CompressAxisToShort(SavedMove->VRCapsuleRotation.Yaw);
}
}
bool FVRCharacterNetworkMoveData::Serialize(UCharacterMovementComponent& CharacterMovement, FArchive& Ar, UPackageMap* PackageMap, ENetworkMoveType MoveType)
{
NetworkMoveType = MoveType;
bool bLocalSuccess = true;
const bool bIsSaving = Ar.IsSaving();
Ar << TimeStamp;
// Handle switching the acceleration rep
// Can't use SerializeOptionalValue here as I don't want to bitwise compare floats
bool bRepAccel = bIsSaving ? !Acceleration.IsNearlyZero() : false;
Ar.SerializeBits(&bRepAccel, 1);
if (bRepAccel)
{
Acceleration.NetSerialize(Ar, PackageMap, bLocalSuccess);
}
else
{
if (!bIsSaving)
{
Acceleration = FVector::ZeroVector;
}
}
//Location.NetSerialize(Ar, PackageMap, bLocalSuccess);
uint16 Yaw = bIsSaving ? FRotator::CompressAxisToShort(ControlRotation.Yaw) : 0;
uint16 Pitch = bIsSaving ? FRotator::CompressAxisToShort(ControlRotation.Pitch) : 0;
uint16 Roll = bIsSaving ? FRotator::CompressAxisToShort(ControlRotation.Roll) : 0;
bool bRepYaw = Yaw != 0;
ACharacter* CharacterOwner = CharacterMovement.GetCharacterOwner();
bool bCanRepRollAndPitch = (CharacterOwner && (CharacterOwner->bUseControllerRotationRoll || CharacterOwner->bUseControllerRotationPitch));
bool bRepRollAndPitch = bCanRepRollAndPitch && (Roll != 0 || Pitch != 0);
Ar.SerializeBits(&bRepRollAndPitch, 1);
if (bRepRollAndPitch)
{
// Reversed the order of these
uint32 Rotation32 = 0;
uint32 Yaw32 = bIsSaving ? Yaw : 0;
if (bIsSaving)
{
Rotation32 = (((uint32)Roll) << 16) | ((uint32)Pitch);
Ar.SerializeIntPacked(Rotation32);
}
else
{
Ar.SerializeIntPacked(Rotation32);
// Reversed the order of these so it costs less to replicate
Pitch = (Rotation32 & 65535);
Roll = (Rotation32 >> 16);
}
}
uint32 Yaw32 = bIsSaving ? Yaw : 0;
Ar.SerializeBits(&bRepYaw, 1);
if (bRepYaw)
{
Ar.SerializeIntPacked(Yaw32);
Yaw = (uint16)Yaw32;
}
if (!bIsSaving)
{
ControlRotation.Yaw = bRepYaw ? FRotator::DecompressAxisFromShort(Yaw) : 0;
ControlRotation.Pitch = bRepRollAndPitch ? FRotator::DecompressAxisFromShort(Pitch) : 0;
ControlRotation.Roll = bRepRollAndPitch ? FRotator::DecompressAxisFromShort(Roll) : 0;
}
// ControlRotation : FRotator handles each component zero/non-zero test; it uses a single signal bit for zero/non-zero, and uses 16 bits per component if non-zero.
//ControlRotation.NetSerialize(Ar, PackageMap, bLocalSuccess);
SerializeOptionalValue<uint8>(bIsSaving, Ar, CompressedMoveFlags, 0);
SerializeOptionalValue<uint8>(bIsSaving, Ar, MovementMode, MOVE_Walking);
VRCapsuleLocation.NetSerialize(Ar, PackageMap, bLocalSuccess);
Ar << VRCapsuleRotation;
// Location is only used for error checking, so only save for the final move.
//if (MoveType == ENetworkMoveType::NewMove)
//{
Location.NetSerialize(Ar, PackageMap, bLocalSuccess);
//}
// Movement base needs to always send now since they allow for relative based velocity
SerializeOptionalValue<UPrimitiveComponent*>(bIsSaving, Ar, MovementBase, nullptr);
SerializeOptionalValue<FName>(bIsSaving, Ar, MovementBaseBoneName, NAME_None);
bool bHasReplicatedMovementMode = ReplicatedMovementMode != EVRConjoinedMovementModes::C_MOVE_MAX;
Ar.SerializeBits(&bHasReplicatedMovementMode, 1);
if (bHasReplicatedMovementMode)
{
// Increased to 6 bits for 64 total elements instead of 16
Ar.SerializeBits(&ReplicatedMovementMode, 6);
}
else if(!bIsSaving)
{
ReplicatedMovementMode = EVRConjoinedMovementModes::C_MOVE_MAX;
}
// Rep out our custom move settings
ConditionalMoveReps.NetSerialize(Ar, PackageMap, bLocalSuccess);
//VRCapsuleLocation.NetSerialize(Ar, PackageMap, bLocalSuccess);
if (AVRBaseCharacter* VRChar = Cast<AVRBaseCharacter>(CharacterOwner))
{
if (!VRChar->bRetainRoomscale)
{
SerializePackedVector<10000, 32>(LFDiff, Ar);
}
else
{
SerializePackedVector<100, 30>(LFDiff, Ar);
}
}
else
{
SerializePackedVector<100, 30>(LFDiff, Ar);
}
bool bHasCapsuleHeight = CapsuleHeight > 0.f;
Ar.SerializeBits(&bHasCapsuleHeight, 1);
if (bHasCapsuleHeight)
{
// This is 0.0 - 512.0, using compression to get it smaller, 8 bits = max 256 + 1 bit for sign and 7 bits precision for 128 / full 2 digit precision
if (Ar.IsSaving())
{
WriteFixedCompressedFloat<1024, 18>(CapsuleHeight, Ar);
}
else
{
ReadFixedCompressedFloat<1024, 18>(CapsuleHeight, Ar);
}
}
//LFDiff.NetSerialize(Ar, PackageMap, bLocalSuccess);
//Ar << VRCapsuleRotation;
return !Ar.IsError();
}
void FVRCharacterMoveResponseDataContainer::ServerFillResponseData(const UCharacterMovementComponent& CharacterMovement, const FClientAdjustment& PendingAdjustment)
{
FCharacterMoveResponseDataContainer::ServerFillResponseData(CharacterMovement, PendingAdjustment);
if (const UVRBaseCharacterMovementComponent* BaseMovecomp = Cast<const UVRBaseCharacterMovementComponent>(&CharacterMovement))
{
// #TODO: This is set in the pending adjustment now in 5.1
//bHasRotation = CharacterMovement.ShouldCorrectRotation();
bHasRotation = !BaseMovecomp->bUseClientControlRotation;
}
}
FScopedMeshBoneUpdateOverrideVR::FScopedMeshBoneUpdateOverrideVR(USkeletalMeshComponent* Mesh, EKinematicBonesUpdateToPhysics::Type OverrideSetting)
: MeshRef(Mesh)
{
if (MeshRef)
{
// Save current state.
SavedUpdateSetting = MeshRef->KinematicBonesUpdateType;
// Override bone update setting.
MeshRef->KinematicBonesUpdateType = OverrideSetting;
}
}
FScopedMeshBoneUpdateOverrideVR::~FScopedMeshBoneUpdateOverrideVR()
{
if (MeshRef)
{
// Restore bone update flag.
MeshRef->KinematicBonesUpdateType = SavedUpdateSetting;
}
}

View file

@ -0,0 +1,233 @@
// Fill out your copyright notice in the Description page of Project Settings.
#include "GripScripts/GS_Default.h"
#include UE_INLINE_GENERATED_CPP_BY_NAME(GS_Default)
#include "VRGripInterface.h"
#include "Components/PrimitiveComponent.h"
#include "GameFramework/Actor.h"
#include "GameFramework/WorldSettings.h"
#include "GripMotionControllerComponent.h"
UGS_Default::UGS_Default(const FObjectInitializer& ObjectInitializer) :
Super(ObjectInitializer)
{
bIsActive = true;
WorldTransformOverrideType = EGSTransformOverrideType::OverridesWorldTransform;
}
void UGS_Default::GetAnyScaling(FVector& Scaler, FBPActorGripInformation& Grip, FVector& frontLoc, FVector& frontLocOrig, ESecondaryGripType SecondaryType, FTransform& SecondaryTransform)
{
if (Grip.SecondaryGripInfo.GripLerpState != EGripLerpState::EndLerp)
{
//float Scaler = 1.0f;
if (SecondaryType == ESecondaryGripType::SG_FreeWithScaling_Retain || SecondaryType == ESecondaryGripType::SG_SlotOnlyWithScaling_Retain || SecondaryType == ESecondaryGripType::SG_ScalingOnly)
{
/*Grip.SecondaryScaler*/ Scaler = FVector(frontLoc.Size() / frontLocOrig.Size());
//bRescalePhysicsGrips = true; // This is for the physics grips
}
}
}
void UGS_Default::ApplySmoothingAndLerp(FBPActorGripInformation& Grip, FVector& frontLoc, FVector& frontLocOrig, float DeltaTime)
{
if (Grip.SecondaryGripInfo.GripLerpState == EGripLerpState::StartLerp) // Lerp into the new grip to smooth the transition
{
/*if (Grip.AdvancedGripSettings.SecondaryGripSettings.SecondaryGripScaler_DEPRECATED < 1.0f)
{
FVector SmoothedValue = Grip.AdvancedGripSettings.SecondaryGripSettings.SecondarySmoothing.RunFilterSmoothing(frontLoc, DeltaTime);
frontLoc = FMath::Lerp(SmoothedValue, frontLoc, Grip.AdvancedGripSettings.SecondaryGripSettings.SecondaryGripScaler_DEPRECATED);
}*/
frontLocOrig = FMath::Lerp(frontLocOrig, frontLoc, FMath::Clamp(Grip.SecondaryGripInfo.curLerp / Grip.SecondaryGripInfo.LerpToRate, 0.0f, 1.0f));
}
/*else if (Grip.SecondaryGripInfo.GripLerpState == EGripLerpState::ConstantLerp_DEPRECATED) // If there is a frame by frame lerp
{
FVector SmoothedValue = Grip.AdvancedGripSettings.SecondaryGripSettings.SecondarySmoothing.RunFilterSmoothing(frontLoc, DeltaTime);
frontLoc = FMath::Lerp(SmoothedValue, frontLoc, Grip.AdvancedGripSettings.SecondaryGripSettings.SecondaryGripScaler_DEPRECATED);
}*/
}
bool UGS_Default::GetWorldTransform_Implementation
(
UGripMotionControllerComponent* GrippingController,
float DeltaTime, FTransform& WorldTransform,
const FTransform& ParentTransform,
FBPActorGripInformation& Grip,
AActor* actor,
UPrimitiveComponent* root,
bool bRootHasInterface,
bool bActorHasInterface,
bool bIsForTeleport
)
{
if (!GrippingController)
return false;
// Just simple transform setting
WorldTransform = Grip.RelativeTransform * Grip.AdditionTransform * ParentTransform;
// Check the grip lerp state, this it ouside of the secondary attach check below because it can change the result of it
if ((Grip.SecondaryGripInfo.bHasSecondaryAttachment && Grip.SecondaryGripInfo.SecondaryAttachment) || Grip.SecondaryGripInfo.GripLerpState == EGripLerpState::EndLerp)
{
switch (Grip.SecondaryGripInfo.GripLerpState)
{
case EGripLerpState::StartLerp:
case EGripLerpState::EndLerp:
{
if (Grip.SecondaryGripInfo.curLerp > 0.01f)
Grip.SecondaryGripInfo.curLerp -= DeltaTime;
else
{
/*if (Grip.SecondaryGripInfo.bHasSecondaryAttachment &&
Grip.AdvancedGripSettings.SecondaryGripSettings.bUseSecondaryGripSettings &&
Grip.AdvancedGripSettings.SecondaryGripSettings.SecondaryGripScaler_DEPRECATED < 1.0f)
{
Grip.SecondaryGripInfo.GripLerpState = EGripLerpState::ConstantLerp_DEPRECATED;
}
else*/
Grip.SecondaryGripInfo.GripLerpState = EGripLerpState::NotLerping;
}
}break;
//case EGripLerpState::ConstantLerp_DEPRECATED:
case EGripLerpState::NotLerping:
default:break;
}
}
// Handle the interp and multi grip situations, re-checking the grip situation here as it may have changed in the switch above.
if ((Grip.SecondaryGripInfo.bHasSecondaryAttachment && Grip.SecondaryGripInfo.SecondaryAttachment) || Grip.SecondaryGripInfo.GripLerpState == EGripLerpState::EndLerp)
{
FTransform SecondaryTransform = Grip.RelativeTransform * ParentTransform;
// Checking secondary grip type for the scaling setting
ESecondaryGripType SecondaryType = ESecondaryGripType::SG_None;
if (bRootHasInterface)
SecondaryType = IVRGripInterface::Execute_SecondaryGripType(root);
else if (bActorHasInterface)
SecondaryType = IVRGripInterface::Execute_SecondaryGripType(actor);
// If the grip is a custom one, skip all of this logic we won't be changing anything
if (SecondaryType != ESecondaryGripType::SG_Custom)
{
// Variables needed for multi grip transform
FVector BasePoint = ParentTransform.GetLocation(); // Get our pivot point
const FTransform PivotToWorld = FTransform(FQuat::Identity, BasePoint);
const FTransform WorldToPivot = FTransform(FQuat::Identity, -BasePoint);
FVector frontLocOrig;
FVector frontLoc;
// Ending lerp out of a multi grip
if (Grip.SecondaryGripInfo.GripLerpState == EGripLerpState::EndLerp)
{
frontLocOrig = (/*WorldTransform*/SecondaryTransform.TransformPosition(Grip.SecondaryGripInfo.SecondaryRelativeTransform.GetLocation())) - BasePoint;
frontLoc = Grip.SecondaryGripInfo.LastRelativeLocation;
frontLocOrig = FMath::Lerp(frontLoc, frontLocOrig, FMath::Clamp(Grip.SecondaryGripInfo.curLerp / Grip.SecondaryGripInfo.LerpToRate, 0.0f, 1.0f));
}
else // Is in a multi grip, might be lerping into it as well.
{
//FVector curLocation; // Current location of the secondary grip
// Calculates the correct secondary attachment location and sets frontLoc to it
CalculateSecondaryLocation(frontLoc, BasePoint, Grip, GrippingController);
frontLocOrig = (/*WorldTransform*/SecondaryTransform.TransformPosition(Grip.SecondaryGripInfo.SecondaryRelativeTransform.GetLocation())) - BasePoint;
// Apply any smoothing settings and lerping in / constant lerping
ApplySmoothingAndLerp(Grip, frontLoc, frontLocOrig, DeltaTime);
Grip.SecondaryGripInfo.LastRelativeLocation = frontLoc;
}
// Get any scaling addition from a scaling secondary grip type
FVector Scaler = FVector(1.0f);
if (SecondaryType == ESecondaryGripType::SG_FreeWithScaling_Retain || SecondaryType == ESecondaryGripType::SG_SlotOnlyWithScaling_Retain || SecondaryType == ESecondaryGripType::SG_ScalingOnly)
{
GetAnyScaling(Scaler, Grip, frontLoc, frontLocOrig, SecondaryType, SecondaryTransform);
}
Grip.SecondaryGripInfo.SecondaryGripDistance = FVector::Dist(frontLocOrig, frontLoc);
/*if (Grip.AdvancedGripSettings.SecondaryGripSettings.bUseSecondaryGripSettings && Grip.AdvancedGripSettings.SecondaryGripSettings.bUseSecondaryGripDistanceInfluence_DEPRECATED)
{
float rotScaler = 1.0f - FMath::Clamp((Grip.SecondaryGripInfo.SecondaryGripDistance - Grip.AdvancedGripSettings.SecondaryGripSettings.GripInfluenceDeadZone_DEPRECATED) / FMath::Max(Grip.AdvancedGripSettings.SecondaryGripSettings.GripInfluenceDistanceToZero_DEPRECATED, 1.0f), 0.0f, 1.0f);
frontLoc = FMath::Lerp(frontLocOrig, frontLoc, rotScaler);
}*/
// Skip rot val for scaling only
if (SecondaryType != ESecondaryGripType::SG_ScalingOnly)
{
// Get the rotation difference from the initial second grip
FQuat rotVal = FQuat::FindBetweenVectors(frontLocOrig, frontLoc);
// Rebase the world transform to the pivot point, add the rotation, remove the pivot point rebase
WorldTransform = WorldTransform * WorldToPivot * FTransform(rotVal, FVector::ZeroVector, Scaler) * PivotToWorld;
}
else
{
// Rebase the world transform to the pivot point, add the scaler, remove the pivot point rebase
WorldTransform = WorldTransform * WorldToPivot * FTransform(FQuat::Identity, FVector::ZeroVector, Scaler) * PivotToWorld;
}
}
}
return true;
}
void UGS_Default::CalculateSecondaryLocation(FVector& frontLoc, const FVector& BasePoint, FBPActorGripInformation& Grip, UGripMotionControllerComponent* GrippingController)
{
bool bPulledControllerLoc = false;
if (UGripMotionControllerComponent* OtherController = Cast<UGripMotionControllerComponent>(Grip.SecondaryGripInfo.SecondaryAttachment))
{
bool bPulledCurrentTransform = false;
if (IsValid(OtherController->CustomPivotComponent))
{
FTransform SecondaryTrans = FTransform::Identity;
SecondaryTrans = OtherController->GetPivotTransform();
bPulledControllerLoc = true;
frontLoc = SecondaryTrans.GetLocation() - BasePoint;
}
}
if (!bPulledControllerLoc)
{
frontLoc = Grip.SecondaryGripInfo.SecondaryAttachment->GetComponentLocation() - BasePoint;
}
}
void UGS_ExtendedDefault::GetAnyScaling(FVector& Scaler, FBPActorGripInformation& Grip, FVector& frontLoc, FVector& frontLocOrig, ESecondaryGripType SecondaryType, FTransform& SecondaryTransform)
{
if (Grip.SecondaryGripInfo.GripLerpState != EGripLerpState::EndLerp)
{
//float Scaler = 1.0f;
if (SecondaryType == ESecondaryGripType::SG_FreeWithScaling_Retain || SecondaryType == ESecondaryGripType::SG_SlotOnlyWithScaling_Retain || SecondaryType == ESecondaryGripType::SG_ScalingOnly)
{
/*Grip.SecondaryScaler*/ Scaler = FVector(frontLoc.Size() / frontLocOrig.Size());
//bRescalePhysicsGrips = true; // This is for the physics grips
if (bLimitGripScaling)
{
// Get the total scale after modification
// #TODO: convert back to singular float version? Can get Min() & Max() to convert the float to a range...think about it
FVector WorldScale = /*WorldTransform*/SecondaryTransform.GetScale3D();
FVector CombinedScale = WorldScale * Scaler;
// Clamp to the minimum and maximum values
CombinedScale.X = FMath::Clamp(CombinedScale.X, MinimumGripScaling.X, MaximumGripScaling.X);
CombinedScale.Y = FMath::Clamp(CombinedScale.Y, MinimumGripScaling.Y, MaximumGripScaling.Y);
CombinedScale.Z = FMath::Clamp(CombinedScale.Z, MinimumGripScaling.Z, MaximumGripScaling.Z);
// Recreate in scaler form so that the transform chain below works as normal
Scaler = CombinedScale / WorldScale;
}
//Scaler = Grip.SecondaryScaler;
}
}
}

View file

@ -0,0 +1,601 @@
// Fill out your copyright notice in the Description page of Project Settings.
#include "GripScripts/GS_GunTools.h"
#include UE_INLINE_GENERATED_CPP_BY_NAME(GS_GunTools)
#include "VRGripInterface.h"
#include "GripMotionControllerComponent.h"
#include "VRExpansionFunctionLibrary.h"
#include "IXRTrackingSystem.h"
#include "VRGlobalSettings.h"
#include "VRBaseCharacter.h"
#include "VRCharacter.h"
#include "VRRootComponent.h"
#include "Components/PrimitiveComponent.h"
#include "GameFramework/Actor.h"
//#include "Camera/CameraComponent.h"
#include "ReplicatedVRCameraComponent.h"
#include "DrawDebugHelpers.h"
UGS_GunTools::UGS_GunTools(const FObjectInitializer& ObjectInitializer) :
Super(ObjectInitializer)
{
bIsActive = true;
WorldTransformOverrideType = EGSTransformOverrideType::OverridesWorldTransform;
PivotOffset = FVector::ZeroVector;
VirtualStockComponent = nullptr;
MountWorldTransform = FTransform::Identity;
//StockSnapOffset = FVector(0.f, 0.f, 0.f);
bIsMounted = false;
bHasRecoil = false;
bApplyRecoilAsPhysicalForce = false;
MaxRecoilTranslation = FVector::ZeroVector;
MaxRecoilRotation = FVector::ZeroVector;
MaxRecoilScale = FVector(1.f);
bHasActiveRecoil = false;
DecayRate = 20.f;
LerpRate = 30.f;
BackEndRecoilStorage = FTransform::Identity;
bUseGlobalVirtualStockSettings = true;
bUseHighQualityRemoteSimulation = false;
bInjectPrePhysicsHandle = true;
//bInjectPostPhysicsHandle = true;
WeaponRootOrientationComponent = NAME_None;
OrientationComponentRelativeFacing = FTransform::Identity;
StoredRootOffset = FQuat::Identity;
}
void UGS_GunTools::OnBeginPlay_Implementation(UObject* CallingOwner)
{
// Grip base has no super of this
if (WeaponRootOrientationComponent.IsValid())
{
if (AActor * Owner = GetOwner())
{
FName CurrentCompName = NAME_None;
for (UActorComponent* ChildComp : Owner->GetComponents())
{
CurrentCompName = ChildComp->GetFName();
if (CurrentCompName == NAME_None)
continue;
if (CurrentCompName == WeaponRootOrientationComponent)
{
if (USceneComponent * SceneComp = Cast<USceneComponent>(ChildComp))
{
OrientationComponentRelativeFacing = SceneComp->GetRelativeTransform();
}
break;
}
}
}
}
}
void UGS_GunTools::HandlePrePhysicsHandle(UGripMotionControllerComponent* GrippingController, const FBPActorGripInformation &GripInfo, FBPActorPhysicsHandleInformation* HandleInfo, FTransform& KinPose)
{
if (!bIsActive)
return;
if (WeaponRootOrientationComponent != NAME_None)
{
StoredRootOffset = HandleInfo->RootBoneRotation.GetRotation().Inverse() * OrientationComponentRelativeFacing.GetRotation();
// Alter to rotate to x+ if we have an orientation component
FQuat DeltaQuat = OrientationComponentRelativeFacing.GetRotation();
KinPose.SetRotation(KinPose.GetRotation() * StoredRootOffset);
HandleInfo->COMPosition.SetRotation(HandleInfo->COMPosition.GetRotation() * StoredRootOffset);
}
else
{
StoredRootOffset = FQuat::Identity;
}
if (GripInfo.bIsSlotGrip && !PivotOffset.IsZero())
{
KinPose.SetLocation(KinPose.TransformPosition(PivotOffset));
HandleInfo->COMPosition.SetLocation(HandleInfo->COMPosition.TransformPosition(PivotOffset));
}
}
/*void UGS_GunTools::HandlePostPhysicsHandle(UGripMotionControllerComponent* GrippingController, FBPActorPhysicsHandleInformation* HandleInfo)
{
}*/
bool UGS_GunTools::GetWorldTransform_Implementation
(
UGripMotionControllerComponent* GrippingController,
float DeltaTime, FTransform & WorldTransform,
const FTransform &ParentTransform,
FBPActorGripInformation &Grip,
AActor * actor,
UPrimitiveComponent * root,
bool bRootHasInterface,
bool bActorHasInterface,
bool bIsForTeleport
)
{
if (!GrippingController)
return false;
bool bSkipHighQualityOperations = !bUseHighQualityRemoteSimulation && !GrippingController->HasAuthority();
/*if (!GunViewExtension.IsValid() && GEngine)
{
GunViewExtension = FSceneViewExtensions::NewExtension<FGunViewExtension>(GrippingController);
}*/
// Just simple transform setting
if (bHasRecoil && bHasActiveRecoil)
{
BackEndRecoilStorage.Blend(BackEndRecoilStorage, BackEndRecoilTarget, FMath::Clamp(LerpRate * DeltaTime, 0.f, 1.f));
BackEndRecoilTarget.Blend(BackEndRecoilTarget, FTransform::Identity, FMath::Clamp(DecayRate * DeltaTime, 0.f, 1.f));
bHasActiveRecoil = !BackEndRecoilTarget.Equals(FTransform::Identity);
if (!bHasActiveRecoil)
{
BackEndRecoilStorage.SetIdentity();
BackEndRecoilTarget.SetIdentity();
}
}
if (bHasActiveRecoil)
{
// Using a matrix to avoid FTransform inverse math issues
FTransform relTransform(Grip.RelativeTransform.ToInverseMatrixWithScale());
// Eventually may want to adjust the pivot of the recoil rotation by the PivotOffset vector...
FVector Pivot = relTransform.GetLocation() + PivotOffset;
const FTransform PivotToWorld = FTransform(FQuat::Identity, Pivot);
const FTransform WorldToPivot = FTransform(FQuat::Identity, -Pivot);
WorldTransform = WorldToPivot * BackEndRecoilStorage * PivotToWorld * Grip.RelativeTransform * Grip.AdditionTransform * ParentTransform;
}
else
WorldTransform = Grip.RelativeTransform * Grip.AdditionTransform * ParentTransform;
// Check the grip lerp state, this it ouside of the secondary attach check below because it can change the result of it
if (Grip.SecondaryGripInfo.bHasSecondaryAttachment && Grip.SecondaryGripInfo.SecondaryAttachment)
{
if (!bSkipHighQualityOperations && bUseVirtualStock)
{
if (IsValid(VirtualStockComponent))
{
FRotator PureYaw = UVRExpansionFunctionLibrary::GetHMDPureYaw_I(VirtualStockComponent->GetComponentRotation());
MountWorldTransform = FTransform(PureYaw.Quaternion(), VirtualStockComponent->GetComponentLocation() + PureYaw.RotateVector(VirtualStockSettings.StockSnapOffset));
}
/*else if (GrippingController->bHasAuthority && GEngine->XRSystem.IsValid() && GEngine->XRSystem->IsHeadTrackingAllowedForWorld(*GetWorld()))
{
FQuat curRot = FQuat::Identity;
FVector curLoc = FVector::ZeroVector;
if (GEngine->XRSystem->GetCurrentPose(IXRTrackingSystem::HMDDeviceId, curRot, curLoc))
{
// Translate hmd offset by the gripping controllers parent component, this should be in the same space
FRotator PureYaw = UVRExpansionFunctionLibrary::GetHMDPureYaw_I(curRot.Rotator());
if (AVRCharacter* OwningCharacter = Cast<AVRCharacter>(GrippingController->GetOwner()))
{
if (!OwningCharacter->bRetainRoomscale)
{
curLoc.X = 0.0f;
curLoc.Y = 0.0f;
curLoc += PureYaw.RotateVector(FVector(-OwningCharacter->VRRootReference->VRCapsuleOffset.X, -OwningCharacter->VRRootReference->VRCapsuleOffset.Y, -OwningCharacter->VRRootReference->GetScaledCapsuleHalfHeight()));
}
}
MountWorldTransform = FTransform(PureYaw.Quaternion(), curLoc + PureYaw.RotateVector(VirtualStockSettings.StockSnapOffset)) * GrippingController->GetAttachParent()->GetComponentTransform();
}
}*/
else if(IsValid(CameraComponent))
{
FRotator PureYaw = UVRExpansionFunctionLibrary::GetHMDPureYaw_I(CameraComponent->GetComponentRotation());
MountWorldTransform = FTransform(PureYaw.Quaternion(), CameraComponent->GetComponentLocation() + PureYaw.RotateVector(VirtualStockSettings.StockSnapOffset));
}
float StockSnapDistance = FMath::Square(VirtualStockSettings.StockSnapDistance);
float DistSquared = FVector::DistSquared(ParentTransform.GetTranslation(), MountWorldTransform.GetTranslation());
if (!VirtualStockSettings.bUseDistanceBasedStockSnapping || (DistSquared <= StockSnapDistance))
{
float StockSnapLerpThresh = FMath::Square(VirtualStockSettings.StockSnapLerpThreshold);
if (StockSnapLerpThresh > 0.0f)
VirtualStockSettings.StockLerpValue = 1.0f - FMath::Clamp((DistSquared - (StockSnapDistance - StockSnapLerpThresh)) / StockSnapLerpThresh, 0.0f, 1.0f);
else
VirtualStockSettings.StockLerpValue = 1.0f; // Just skip lerping logic
if (!bIsMounted)
{
VirtualStockSettings.StockHandSmoothing.ResetSmoothingFilter();
// Mount up
bIsMounted = true;
OnVirtualStockModeChanged.Broadcast(bIsMounted);
}
// Adjust the mount location to follow the Z of the primary hand
if (VirtualStockSettings.bAdjustZOfStockToPrimaryHand)
{
FVector WorldTransVec = MountWorldTransform.GetTranslation();
if (WorldTransVec.Z >= ParentTransform.GetTranslation().Z)
{
WorldTransVec.Z = ParentTransform.GetTranslation().Z;
MountWorldTransform.SetLocation(WorldTransVec);
}
}
#if !(UE_BUILD_SHIPPING || UE_BUILD_TEST)
if (VirtualStockSettings.bDebugDrawVirtualStock)
{
DrawDebugLine(GetWorld(), ParentTransform.GetTranslation(), MountWorldTransform.GetTranslation(), FColor::Red);
DrawDebugLine(GetWorld(), Grip.SecondaryGripInfo.SecondaryAttachment->GetComponentLocation(), MountWorldTransform.GetTranslation(), FColor::Green);
DrawDebugSphere(GetWorld(), MountWorldTransform.GetTranslation(), 10.f, 32, FColor::White);
}
#endif
}
else
{
if (bIsMounted)
{
bIsMounted = false;
VirtualStockSettings.StockLerpValue = 0.0f;
OnVirtualStockModeChanged.Broadcast(bIsMounted);
}
}
}
}
else
{
if (bIsMounted)
{
bIsMounted = false;
VirtualStockSettings.StockLerpValue = 0.0f;
OnVirtualStockModeChanged.Broadcast(bIsMounted);
}
}
// Check the grip lerp state, this it ouside of the secondary attach check below because it can change the result of it
if ((Grip.SecondaryGripInfo.bHasSecondaryAttachment && Grip.SecondaryGripInfo.SecondaryAttachment) || Grip.SecondaryGripInfo.GripLerpState == EGripLerpState::EndLerp)
{
switch (Grip.SecondaryGripInfo.GripLerpState)
{
case EGripLerpState::StartLerp:
case EGripLerpState::EndLerp:
{
if (Grip.SecondaryGripInfo.curLerp > 0.01f)
Grip.SecondaryGripInfo.curLerp -= DeltaTime;
else
{
Grip.SecondaryGripInfo.GripLerpState = EGripLerpState::NotLerping;
}
}break;
//case EGripLerpState::ConstantLerp_DEPRECATED:
case EGripLerpState::NotLerping:
default:break;
}
}
// Handle the interp and multi grip situations, re-checking the grip situation here as it may have changed in the switch above.
if ((Grip.SecondaryGripInfo.bHasSecondaryAttachment && Grip.SecondaryGripInfo.SecondaryAttachment) || Grip.SecondaryGripInfo.GripLerpState == EGripLerpState::EndLerp)
{
FTransform NewWorldTransform = WorldTransform;
FTransform SecondaryTransform = Grip.RelativeTransform * ParentTransform;
// Checking secondary grip type for the scaling setting
ESecondaryGripType SecondaryType = ESecondaryGripType::SG_None;
if (bRootHasInterface)
SecondaryType = IVRGripInterface::Execute_SecondaryGripType(root);
else if (bActorHasInterface)
SecondaryType = IVRGripInterface::Execute_SecondaryGripType(actor);
// If the grip is a custom one, skip all of this logic we won't be changing anything
if (SecondaryType != ESecondaryGripType::SG_Custom)
{
// Variables needed for multi grip transform
FVector BasePoint = ParentTransform.GetLocation();
FVector Pivot = ParentTransform.GetLocation();
if (Grip.bIsSlotGrip)
{
if (FBPActorPhysicsHandleInformation * PhysHandle = GrippingController->GetPhysicsGrip(Grip))
{
Pivot = SecondaryTransform.TransformPositionNoScale(SecondaryTransform.InverseTransformPositionNoScale(Pivot) + (StoredRootOffset * PhysHandle->RootBoneRotation.GetRotation()).RotateVector(PivotOffset));
}
else
{
Pivot = SecondaryTransform.TransformPositionNoScale(SecondaryTransform.InverseTransformPositionNoScale(Pivot) + OrientationComponentRelativeFacing.GetRotation().RotateVector(PivotOffset));
}
}
// Debug draw for COM movement with physics grips
#if !(UE_BUILD_SHIPPING || UE_BUILD_TEST)
static const auto CVarDrawCOMDebugSpheresAccess = IConsoleManager::Get().FindConsoleVariable(TEXT("vr.DrawDebugCenterOfMassForGrips"));
if (CVarDrawCOMDebugSpheresAccess->GetInt() > 0)
{
DrawDebugSphere(GetWorld(), Pivot, 5, 32, FColor::Orange, false);
}
#endif
const FTransform PivotToWorld = FTransform(FQuat::Identity, Pivot);//BasePoint);
const FTransform WorldToPivot = FTransform(FQuat::Identity, -Pivot);//-BasePoint);
FVector frontLocOrig;
FVector frontLoc;
// Ending lerp out of a multi grip
if (Grip.SecondaryGripInfo.GripLerpState == EGripLerpState::EndLerp)
{
WorldTransform.Blend(WorldTransform, RelativeTransOnSecondaryRelease* GrippingController->GetPivotTransform(), FMath::Clamp(Grip.SecondaryGripInfo.curLerp / Grip.SecondaryGripInfo.LerpToRate, 0.0f, 1.0f));
return true;
}
else // Is in a multi grip, might be lerping into it as well.
{
//FVector curLocation; // Current location of the secondary grip
// Calculates the correct secondary attachment location and sets frontLoc to it
CalculateSecondaryLocation(frontLoc, BasePoint, Grip, GrippingController);
frontLocOrig = (/*WorldTransform*/SecondaryTransform.TransformPosition(Grip.SecondaryGripInfo.SecondaryRelativeTransform.GetLocation())) - BasePoint;
// Apply any smoothing settings and lerping in / constant lerping
GunTools_ApplySmoothingAndLerp(Grip, frontLoc, frontLocOrig, DeltaTime, bSkipHighQualityOperations);
Grip.SecondaryGripInfo.LastRelativeLocation = frontLoc;
}
// Get any scaling addition from a scaling secondary grip type
FVector Scaler = FVector(1.0f);
GetAnyScaling(Scaler, Grip, frontLoc, frontLocOrig, SecondaryType, SecondaryTransform);
Grip.SecondaryGripInfo.SecondaryGripDistance = FVector::Dist(frontLocOrig, frontLoc);
if (!bSkipHighQualityOperations && AdvSecondarySettings.bUseAdvancedSecondarySettings && AdvSecondarySettings.bUseSecondaryGripDistanceInfluence)
{
float rotScaler = 1.0f - FMath::Clamp((Grip.SecondaryGripInfo.SecondaryGripDistance - AdvSecondarySettings.GripInfluenceDeadZone) / FMath::Max(AdvSecondarySettings.GripInfluenceDistanceToZero, 1.0f), 0.0f, 1.0f);
frontLoc = FMath::Lerp(frontLocOrig, frontLoc, rotScaler);
}
// Skip rot val for scaling only
if (SecondaryType != ESecondaryGripType::SG_ScalingOnly)
{
// Get shoulder mount addition rotation
if (!bSkipHighQualityOperations && bUseVirtualStock && bIsMounted)
{
// Get the rotation difference from the initial second grip
FQuat rotVal = FQuat::FindBetweenVectors(GrippingController->GetPivotLocation() - MountWorldTransform.GetTranslation(), (frontLoc + BasePoint) - MountWorldTransform.GetTranslation());
FQuat MountAdditionRotation = FQuat::FindBetweenVectors(frontLocOrig, GrippingController->GetPivotLocation() - MountWorldTransform.GetTranslation());
if (VirtualStockSettings.StockLerpValue < 1.0f)
{
// Rebase the world transform to the pivot point, add the rotation, remove the pivot point rebase
FTransform NA = FTransform(rotVal * MountAdditionRotation, FVector::ZeroVector, Scaler);
FTransform NB = FTransform(FQuat::FindBetweenVectors(frontLocOrig, frontLoc), FVector::ZeroVector, Scaler);
NA.NormalizeRotation();
NB.NormalizeRotation();
// Quaternion interpolation
NA.Blend(NB, NA, VirtualStockSettings.StockLerpValue);
NewWorldTransform = WorldTransform * WorldToPivot * NA * PivotToWorld;
}
else
{
// Rebase the world transform to the pivot point, add the rotation, remove the pivot point rebase
NewWorldTransform = WorldTransform * WorldToPivot * MountAdditionRotation * FTransform(rotVal, FVector::ZeroVector, Scaler) * PivotToWorld;
}
}
else
{
// Get the rotation difference from the initial second grip
FQuat rotVal = FQuat::FindBetweenVectors(frontLocOrig, frontLoc);
// Rebase the world transform to the pivot point, add the rotation, remove the pivot point rebase
NewWorldTransform = WorldTransform * WorldToPivot * FTransform(rotVal, FVector::ZeroVector, Scaler) * PivotToWorld;
}
}
else
{
// Get shoulder mount addition rotation
if (!bSkipHighQualityOperations && bUseVirtualStock && bIsMounted)
{
FQuat MountAdditionRotation = FQuat::FindBetweenVectors(frontLocOrig, GrippingController->GetPivotLocation() - MountWorldTransform.GetTranslation());
// If it is exactly 1.0f then lets skip all of the extra logic and just set it
if (VirtualStockSettings.StockLerpValue < 1.0f)
{
FTransform NA = FTransform(MountAdditionRotation, FVector::ZeroVector, Scaler);
FTransform NB = FTransform(FQuat::Identity, FVector::ZeroVector, Scaler);
NA.NormalizeRotation();
NB.NormalizeRotation();
// Quaternion interpolation
NA.Blend(NB, NA, VirtualStockSettings.StockLerpValue);
NewWorldTransform = WorldTransform * WorldToPivot * NA * PivotToWorld;
}
else
{
// Rebase the world transform to the pivot point, add the scaler, remove the pivot point rebase
NewWorldTransform = WorldTransform * WorldToPivot * MountAdditionRotation * FTransform(FQuat::Identity, FVector::ZeroVector, Scaler) * PivotToWorld;
}
}
else
{
// Rebase the world transform to the pivot point, add the scaler, remove the pivot point rebase
NewWorldTransform = WorldTransform * WorldToPivot * FTransform(FQuat::Identity, FVector::ZeroVector, Scaler) * PivotToWorld;
}
}
}
if (Grip.SecondaryGripInfo.GripLerpState == EGripLerpState::StartLerp)
{
WorldTransform.Blend(NewWorldTransform, WorldTransform, FMath::Clamp(Grip.SecondaryGripInfo.curLerp / Grip.SecondaryGripInfo.LerpToRate, 0.0f, 1.0f));
}
else
{
WorldTransform = NewWorldTransform;
}
if (bIsMounted && VirtualStockSettings.bSmoothStockHand)
{
if (GrippingController->GetAttachParent())
{
FTransform ParentTrans = GrippingController->GetAttachParent()->GetComponentTransform();
FTransform ParentRel = WorldTransform * ParentTrans.Inverse();
ParentRel.Blend(ParentRel, VirtualStockSettings.StockHandSmoothing.RunFilterSmoothing(ParentRel, DeltaTime), VirtualStockSettings.SmoothingValueForStock);
WorldTransform = ParentRel * ParentTrans;
}
}
if (Grip.SecondaryGripInfo.bHasSecondaryAttachment)
{
RelativeTransOnSecondaryRelease = WorldTransform.GetRelativeTransform(GrippingController->GetPivotTransform());
}
}
return true;
}
void UGS_GunTools::OnGrip_Implementation(UGripMotionControllerComponent * GrippingController, const FBPActorGripInformation & GripInformation)
{
if (bUseGlobalVirtualStockSettings)
{
if (GrippingController->IsLocallyControlled())
{
FBPVirtualStockSettings VirtualSettings;
UVRGlobalSettings::GetVirtualStockGlobalSettings(VirtualSettings);
VirtualStockSettings.CopyFrom(VirtualSettings);
}
}
// Super doesn't do anything on grip
// Reset smoothing filters
if (AdvSecondarySettings.bUseConstantGripScaler)
{
if (AdvSecondarySettings.bUseGlobalSmoothingSettings)
{
const UVRGlobalSettings& VRSettings = *GetDefault<UVRGlobalSettings>();
AdvSecondarySettings.SecondarySmoothing.CutoffSlope = VRSettings.OneEuroCutoffSlope;
AdvSecondarySettings.SecondarySmoothing.DeltaCutoff = VRSettings.OneEuroDeltaCutoff;
AdvSecondarySettings.SecondarySmoothing.MinCutoff = VRSettings.OneEuroMinCutoff;
}
AdvSecondarySettings.SecondarySmoothing.ResetSmoothingFilter();
}
if (bUseVirtualStock)
{
ResetStockVariables();
}
GetVirtualStockTarget(GrippingController);
}
void UGS_GunTools::GetVirtualStockTarget(UGripMotionControllerComponent * GrippingController)
{
if (GrippingController && (GrippingController->HasAuthority() || bUseHighQualityRemoteSimulation))
{
if (AVRBaseCharacter * vrOwner = Cast<AVRBaseCharacter>(GrippingController->GetOwner()))
{
CameraComponent = vrOwner->VRReplicatedCamera;
return;
}
else
{
TArray<USceneComponent*> children = GrippingController->GetOwner()->GetRootComponent()->GetAttachChildren();
for (int i = 0; i < children.Num(); i++)
{
if (children[i]->IsA(UCameraComponent::StaticClass()))
{
CameraComponent = children[i];
return;
}
}
}
CameraComponent = nullptr;
}
}
void UGS_GunTools::OnSecondaryGrip_Implementation(UGripMotionControllerComponent * Controller, USceneComponent * SecondaryGripComponent, const FBPActorGripInformation & GripInformation)
{
// Super doesn't do anything on Secondary grip
// Reset smoothing filters
if (AdvSecondarySettings.bUseConstantGripScaler)
{
if (AdvSecondarySettings.bUseGlobalSmoothingSettings)
{
const UVRGlobalSettings& VRSettings = *GetDefault<UVRGlobalSettings>();
AdvSecondarySettings.SecondarySmoothing.CutoffSlope = VRSettings.OneEuroCutoffSlope;
AdvSecondarySettings.SecondarySmoothing.DeltaCutoff = VRSettings.OneEuroDeltaCutoff;
AdvSecondarySettings.SecondarySmoothing.MinCutoff = VRSettings.OneEuroMinCutoff;
}
AdvSecondarySettings.SecondarySmoothing.ResetSmoothingFilter();
}
if (bUseVirtualStock)
ResetStockVariables();
}
void UGS_GunTools::ResetRecoil()
{
BackEndRecoilStorage = FTransform::Identity;
BackEndRecoilTarget = FTransform::Identity;
}
void UGS_GunTools::AddRecoilInstance(const FTransform & RecoilAddition, FVector Optional_Location)
{
if (!bHasRecoil)
return;
if (bApplyRecoilAsPhysicalForce)
{
if (FBodyInstance * BodyInst = GetParentBodyInstance())
{
BodyInst->AddImpulseAtPosition(RecoilAddition.GetLocation(), Optional_Location);
}
}
else
{
BackEndRecoilTarget += RecoilAddition;
FVector CurVec = BackEndRecoilTarget.GetTranslation();
CurVec.X = FMath::Clamp(CurVec.X, -FMath::Abs(MaxRecoilTranslation.X), FMath::Abs(MaxRecoilTranslation.X));
CurVec.Y = FMath::Clamp(CurVec.Y, -FMath::Abs(MaxRecoilTranslation.Y), FMath::Abs(MaxRecoilTranslation.Y));
CurVec.Z = FMath::Clamp(CurVec.Z, -FMath::Abs(MaxRecoilTranslation.Z), FMath::Abs(MaxRecoilTranslation.Z));
BackEndRecoilTarget.SetTranslation(CurVec);
FVector CurScale = BackEndRecoilTarget.GetScale3D();
CurScale.X = FMath::Clamp(CurScale.X, -FMath::Abs(MaxRecoilScale.X), FMath::Abs(MaxRecoilScale.X));
CurScale.Y = FMath::Clamp(CurScale.Y, -FMath::Abs(MaxRecoilScale.Y), FMath::Abs(MaxRecoilScale.Y));
CurScale.Z = FMath::Clamp(CurScale.Z, -FMath::Abs(MaxRecoilScale.Z), FMath::Abs(MaxRecoilScale.Z));
BackEndRecoilTarget.SetScale3D(CurScale);
FRotator curRot = BackEndRecoilTarget.Rotator();
curRot.Pitch = FMath::Clamp(curRot.Pitch, -FMath::Abs(MaxRecoilRotation.Y), FMath::Abs(MaxRecoilRotation.Y));
curRot.Yaw = FMath::Clamp(curRot.Yaw, -FMath::Abs(MaxRecoilRotation.Z), FMath::Abs(MaxRecoilRotation.Z));
curRot.Roll = FMath::Clamp(curRot.Roll, -FMath::Abs(MaxRecoilRotation.X), FMath::Abs(MaxRecoilRotation.X));
BackEndRecoilTarget.SetRotation(curRot.Quaternion());
bHasActiveRecoil = !BackEndRecoilTarget.Equals(FTransform::Identity);
}
}

View file

@ -0,0 +1,136 @@
// Fill out your copyright notice in the Description page of Project Settings.
#include "GripScripts/GS_InteractibleSettings.h"
#include UE_INLINE_GENERATED_CPP_BY_NAME(GS_InteractibleSettings)
#include "Components/PrimitiveComponent.h"
#include "GripMotionControllerComponent.h"
#include "GameFramework/Actor.h"
UGS_InteractibleSettings::UGS_InteractibleSettings(const FObjectInitializer& ObjectInitializer) :
Super(ObjectInitializer)
{
bIsActive = true;
WorldTransformOverrideType = EGSTransformOverrideType::OverridesWorldTransform;
}
void UGS_InteractibleSettings::OnBeginPlay_Implementation(UObject * CallingOwner)
{
if (InteractionSettings.bGetInitialPositionsOnBeginPlay)
{
FTransform parentTrans = GetParentTransform(!InteractionSettings.bLimitsInLocalSpace);
InteractionSettings.InitialAngularTranslation = parentTrans.Rotator();
InteractionSettings.InitialLinearTranslation = parentTrans.GetTranslation();
}
}
void UGS_InteractibleSettings::OnGrip_Implementation(UGripMotionControllerComponent * GrippingController, const FBPActorGripInformation & GripInformation)
{
if (InteractionSettings.bIgnoreHandRotation && !InteractionSettings.bHasValidBaseTransform)
{
RemoveRelativeRotation(GrippingController, GripInformation);
}
}
void UGS_InteractibleSettings::OnGripRelease_Implementation(UGripMotionControllerComponent * ReleasingController, const FBPActorGripInformation & GripInformation, bool bWasSocketed)
{
InteractionSettings.bHasValidBaseTransform = false;
}
void UGS_InteractibleSettings::RemoveRelativeRotation(UGripMotionControllerComponent* GrippingController, const FBPActorGripInformation& GripInformation)
{
InteractionSettings.BaseTransform = GripInformation.RelativeTransform;
// Reconstitute the controller transform relative to the object, then remove the rotation and set it back to relative to controller
// This could likely be done easier by just removing rotation that the object doesn't possess but for now this will do.
FTransform compTrans = this->GetParentTransform(true, GripInformation.GrippedBoneName);
InteractionSettings.BaseTransform = FTransform(InteractionSettings.BaseTransform.ToInverseMatrixWithScale()) * compTrans; // Reconstitute transform
InteractionSettings.BaseTransform.SetScale3D(GrippingController->GetPivotTransform().GetScale3D());
InteractionSettings.BaseTransform.SetRotation(FQuat::Identity); // Remove rotation
InteractionSettings.BaseTransform = compTrans.GetRelativeTransform(InteractionSettings.BaseTransform); // Set back to relative
InteractionSettings.bHasValidBaseTransform = true;
}
bool UGS_InteractibleSettings::GetWorldTransform_Implementation
(
UGripMotionControllerComponent* GrippingController,
float DeltaTime, FTransform & WorldTransform,
const FTransform &ParentTransform,
FBPActorGripInformation &Grip,
AActor * actor,
UPrimitiveComponent * root,
bool bRootHasInterface,
bool bActorHasInterface,
bool bIsForTeleport
)
{
if (!root)
return false;
FTransform LocalTransform;
if (InteractionSettings.bIgnoreHandRotation)
{
if (!InteractionSettings.bHasValidBaseTransform)
{
// Removes the rotation portion of the relative grip transform
RemoveRelativeRotation(GrippingController, Grip);
}
FTransform RotationalessTransform = ParentTransform;
RotationalessTransform.SetRotation(FQuat::Identity);
WorldTransform = InteractionSettings.BaseTransform * Grip.AdditionTransform * RotationalessTransform;
}
else
WorldTransform = Grip.RelativeTransform * Grip.AdditionTransform * ParentTransform;
if (InteractionSettings.bLimitsInLocalSpace)
{
if (USceneComponent * parent = root->GetAttachParent())
LocalTransform = parent->GetComponentTransform();
else
LocalTransform = FTransform::Identity;
WorldTransform = WorldTransform.GetRelativeTransform(LocalTransform);
}
FVector componentLoc = WorldTransform.GetLocation();
// Translation settings
if (InteractionSettings.bLimitX)
componentLoc.X = FMath::Clamp(componentLoc.X, InteractionSettings.InitialLinearTranslation.X + InteractionSettings.MinLinearTranslation.X, InteractionSettings.InitialLinearTranslation.X + InteractionSettings.MaxLinearTranslation.X);
if (InteractionSettings.bLimitY)
componentLoc.Y = FMath::Clamp(componentLoc.Y, InteractionSettings.InitialLinearTranslation.Y + InteractionSettings.MinLinearTranslation.Y, InteractionSettings.InitialLinearTranslation.Y + InteractionSettings.MaxLinearTranslation.Y);
if (InteractionSettings.bLimitZ)
componentLoc.Z = FMath::Clamp(componentLoc.Z, InteractionSettings.InitialLinearTranslation.Z + InteractionSettings.MinLinearTranslation.Z, InteractionSettings.InitialLinearTranslation.Z + InteractionSettings.MaxLinearTranslation.Z);
WorldTransform.SetLocation(componentLoc);
FRotator componentRot = WorldTransform.GetRotation().Rotator();
// Rotation Settings
if (InteractionSettings.bLimitPitch)
componentRot.Pitch = FMath::Clamp(componentRot.Pitch, InteractionSettings.InitialAngularTranslation.Pitch + InteractionSettings.MinAngularTranslation.Pitch, InteractionSettings.InitialAngularTranslation.Pitch + InteractionSettings.MaxAngularTranslation.Pitch);
if (InteractionSettings.bLimitYaw)
componentRot.Yaw = FMath::Clamp(componentRot.Yaw, InteractionSettings.InitialAngularTranslation.Yaw + InteractionSettings.MinAngularTranslation.Yaw, InteractionSettings.InitialAngularTranslation.Yaw + InteractionSettings.MaxAngularTranslation.Yaw);
if (InteractionSettings.bLimitRoll)
componentRot.Roll = FMath::Clamp(componentRot.Roll, InteractionSettings.InitialAngularTranslation.Roll + InteractionSettings.MinAngularTranslation.Roll, InteractionSettings.InitialAngularTranslation.Roll + InteractionSettings.MaxAngularTranslation.Roll);
WorldTransform.SetRotation(componentRot.Quaternion());
if (InteractionSettings.bLimitsInLocalSpace)
{
WorldTransform = WorldTransform * LocalTransform;
}
return true;
}

View file

@ -0,0 +1,193 @@
// Fill out your copyright notice in the Description page of Project Settings.
#include "GripScripts/GS_LerpToHand.h"
#include UE_INLINE_GENERATED_CPP_BY_NAME(GS_LerpToHand)
#include "GripMotionControllerComponent.h"
#include "VRGlobalSettings.h"
#include "Components/PrimitiveComponent.h"
#include "GameFramework/Actor.h"
#include "Math/DualQuat.h"
UGS_LerpToHand::UGS_LerpToHand(const FObjectInitializer& ObjectInitializer) :
Super(ObjectInitializer)
{
bIsActive = false;
bDenyAutoDrop = true; // Always deny auto dropping while this script is active
WorldTransformOverrideType = EGSTransformOverrideType::ModifiesWorldTransform;
LerpInterpolationMode = EVRLerpInterpolationMode::QuatInterp;
LerpDuration = 1.f;
LerpSpeed = 0.0f;
CurrentLerpTime = 0.0f;
OnGripTransform = FTransform::Identity;
bUseCurve = false;
MinDistanceForLerp = 0.0f;
MinSpeedForLerp = 0.f;
MaxSpeedForLerp = 0.f;
TargetGrip = INVALID_VRGRIP_ID;
}
//void UGS_InteractibleSettings::BeginPlay_Implementation() {}
void UGS_LerpToHand::OnGrip_Implementation(UGripMotionControllerComponent * GrippingController, const FBPActorGripInformation & GripInformation)
{
const UVRGlobalSettings& VRSettings = *GetDefault<UVRGlobalSettings>();
// Removed this, let per object scripts overide
// Dont run if the global lerping is enabled
/*if (VRSettings.bUseGlobalLerpToHand)
{
bIsActive = false;
return;
}*/
TargetGrip = GripInformation.GripID;
OnGripTransform = GetParentTransform(true, GripInformation.GrippedBoneName);
UObject* ParentObj = this->GetParent();
FTransform TargetTransform = GripInformation.RelativeTransform * GrippingController->GetPivotTransform();
float Distance = FVector::Dist(OnGripTransform.GetLocation(), TargetTransform.GetLocation());
if (MinDistanceForLerp > 0.0f && Distance < MinDistanceForLerp)
{
// Don't init
OnLerpToHandFinished.Broadcast();
return;
}
else
{
float LerpScaler = 1.0f;
float DistanceToSpeed = Distance / LerpDuration;
if (DistanceToSpeed < MinSpeedForLerp)
{
LerpScaler = MinSpeedForLerp / DistanceToSpeed;
}
else if (MaxSpeedForLerp > 0.f && DistanceToSpeed > MaxSpeedForLerp)
{
LerpScaler = MaxSpeedForLerp / DistanceToSpeed;
}
else
{
LerpScaler = 1.0f;
}
// Get the modified lerp speed
LerpSpeed = ((1.f / LerpDuration) * LerpScaler);
OnLerpToHandBegin.Broadcast();
if (FBPActorGripInformation* GripInfo = GrippingController->GetGripPtrByID(GripInformation.GripID))
{
GripInfo->bIsLerping = true;
}
}
bIsActive = true;
CurrentLerpTime = 0.0f;
}
void UGS_LerpToHand::OnGripRelease_Implementation(UGripMotionControllerComponent * ReleasingController, const FBPActorGripInformation & GripInformation, bool bWasSocketed)
{
if(GripInformation.GripID == TargetGrip)
{
TargetGrip = INVALID_VRGRIP_ID;
bIsActive = false;
}
}
bool UGS_LerpToHand::GetWorldTransform_Implementation
(
UGripMotionControllerComponent* GrippingController,
float DeltaTime, FTransform & WorldTransform,
const FTransform &ParentTransform,
FBPActorGripInformation &Grip,
AActor * actor,
UPrimitiveComponent * root,
bool bRootHasInterface,
bool bActorHasInterface,
bool bIsForTeleport
)
{
if (!root)
return false;
if (LerpDuration <= 0.f || !Grip.bIsLerping)
{
Grip.bIsLerping = false;
GrippingController->OnLerpToHandFinished.Broadcast(Grip);
bIsActive = false;
}
FTransform NA = OnGripTransform;//root->GetComponentTransform();
float Alpha = 0.0f;
CurrentLerpTime += DeltaTime * LerpSpeed;
float OrigAlpha = FMath::Clamp(CurrentLerpTime, 0.f, 1.0f);
Alpha = OrigAlpha;
if (bUseCurve)
{
if (FRichCurve * richCurve = OptionalCurveToFollow.GetRichCurve())
{
/*if (CurrentLerpTime > richCurve->GetLastKey().Time)
{
// Stop lerping
OnLerpToHandFinished.Broadcast();
CurrentLerpTime = 0.0f;
bIsActive = false;
return true;
}
else*/
{
Alpha = FMath::Clamp(richCurve->Eval(Alpha), 0.f, 1.f);
//CurrentLerpTime += DeltaTime;
}
}
}
FTransform NB = WorldTransform;
NA.NormalizeRotation();
NB.NormalizeRotation();
// Quaternion interpolation
if (LerpInterpolationMode == EVRLerpInterpolationMode::QuatInterp)
{
WorldTransform.Blend(NA, NB, Alpha);
}
// Euler Angle interpolation
else if (LerpInterpolationMode == EVRLerpInterpolationMode::EulerInterp)
{
WorldTransform.SetTranslation(FMath::Lerp(NA.GetTranslation(), NB.GetTranslation(), Alpha));
WorldTransform.SetScale3D(FMath::Lerp(NA.GetScale3D(), NB.GetScale3D(), Alpha));
FRotator A = NA.Rotator();
FRotator B = NB.Rotator();
WorldTransform.SetRotation(FQuat(A + (Alpha * (B - A))));
}
// Dual quaternion interpolation
else
{
if ((NB.GetRotation() | NA.GetRotation()) < 0.0f)
{
NB.SetRotation(NB.GetRotation()*-1.0f);
}
WorldTransform = (FDualQuat(NA)*(1 - Alpha) + FDualQuat(NB)*Alpha).Normalized().AsFTransform(FMath::Lerp(NA.GetScale3D(), NB.GetScale3D(), Alpha));
}
// Turn it off if we need to
if (OrigAlpha == 1.0f)
{
OnLerpToHandFinished.Broadcast();
Grip.bIsLerping = false;
GrippingController->OnLerpToHandFinished.Broadcast(Grip);
CurrentLerpTime = 0.0f;
bIsActive = false;
}
return true;
}

View file

@ -0,0 +1,972 @@
// Fill out your copyright notice in the Description page of Project Settings.
#include "GripScripts/GS_Melee.h"
#include UE_INLINE_GENERATED_CPP_BY_NAME(GS_Melee)
#include "VRGripInterface.h"
#include "GameFramework/WorldSettings.h"
#include "PhysicalMaterials/PhysicalMaterial.h"
#include "PhysicsEngine/PhysicsConstraintActor.h"
#include "PhysicsEngine/PhysicsConstraintComponent.h"
#include "VRExpansionFunctionLibrary.h"
#include "GripMotionControllerComponent.h"
#include "VRGlobalSettings.h"
#include "DrawDebugHelpers.h"
#include "Components/PrimitiveComponent.h"
#include "GameFramework/Actor.h"
#include "GripMotionControllerComponent.h"
UGS_Melee::UGS_Melee(const FObjectInitializer& ObjectInitializer) :
Super(ObjectInitializer)
{
bIsActive = true;
WorldTransformOverrideType = EGSTransformOverrideType::ModifiesWorldTransform;
bDenyLateUpdates = true;
bInjectPrePhysicsHandle = true;
bInjectPostPhysicsHandle = true;
WeaponRootOrientationComponent = NAME_None;
OrientationComponentRelativeFacing = FTransform::Identity;
bAutoSetPrimaryAndSecondaryHands = true;
PrimaryHandSelectionType = EVRMeleePrimaryHandType::VRPHAND_Rear;
bHasValidPrimaryHand = false;
//RollingVelocityAverage = FVector::ZeroVector;
bIsLodged = false;
//bCanEverTick = true;
bCheckLodge = false;
bIsHeld = false;
bCanEverTick = false;
bAlwaysTickPenetration = false;
bUsePrimaryHandSettingsWithOneHand = false;
COMType = EVRMeleeComType::VRPMELEECOM_BetweenHands;
bOnlyPenetrateWithTwoHands = false;
}
void UGS_Melee::UpdateDualHandInfo()
{
TArray<FBPGripPair> HoldingControllers;
bool bIsHeldOther;
IVRGripInterface::Execute_IsHeld(GetParent(), HoldingControllers, bIsHeldOther);
float PHand = 0.0f;
float SHand = 0.0f;
bHasValidPrimaryHand = false;
FBPActorGripInformation* FrontHandGrip = nullptr;
FBPActorGripInformation* RearHandGrip = nullptr;
SecondaryHand = FBPGripPair();
PrimaryHand = FBPGripPair();
int NumControllers = HoldingControllers.Num();
for (FBPGripPair& Grip : HoldingControllers)
{
if (NumControllers > 1)
{
FBPActorGripInformation* GripInfo = Grip.HoldingController->GetGripPtrByID(Grip.GripID);
if (GripInfo)
{
float GripDistanceOnPrimaryAxis = 0.f;
FTransform relTransform(GripInfo->RelativeTransform.ToInverseMatrixWithScale());
relTransform = relTransform.GetRelativeTransform(OrientationComponentRelativeFacing);
// This is the Forward vector projected transform
// The most negative one of these is the rearmost hand
FVector localLoc = relTransform.GetTranslation();
switch (PrimaryHandSelectionType)
{
case EVRMeleePrimaryHandType::VRPHAND_Slotted:
{
if (GripInfo->bIsSlotGrip)
{
PrimaryHand = Grip;
bHasValidPrimaryHand = true;
}
else
{
if (!PrimaryHand.IsValid())
{
PrimaryHand = Grip;
}
SecondaryHand = Grip;
}
}break;
case EVRMeleePrimaryHandType::VRPHAND_Front:
case EVRMeleePrimaryHandType::VRPHAND_Rear:
{
if (((PrimaryHandSelectionType == EVRMeleePrimaryHandType::VRPHAND_Rear) ? localLoc.X < PHand : localLoc.X > PHand) || !PrimaryHand.HoldingController)
{
PrimaryHand = Grip;
PHand = localLoc.X;
bHasValidPrimaryHand = true;
}
if ((((PrimaryHandSelectionType == EVRMeleePrimaryHandType::VRPHAND_Rear) ? localLoc.X > SHand : localLoc.X < SHand) || !SecondaryHand.HoldingController || SecondaryHand.HoldingController == PrimaryHand.HoldingController))
{
SecondaryHand = Grip;
SHand = localLoc.X;
}
}break;
default:break;
}
}
}
else
{
PrimaryHand = Grip;
SecondaryHand = FBPGripPair();
}
}
if (PrimaryHand.IsValid() && (COMType == EVRMeleeComType::VRPMELEECOM_BetweenHands || COMType == EVRMeleeComType::VRPMELEECOM_PrimaryHand))
{
FBPActorGripInformation* GripInfo = PrimaryHand.HoldingController->GetGripPtrByID(PrimaryHand.GripID);
if (SecondaryHand.IsValid())
{
FBPActorGripInformation* GripInfoS = SecondaryHand.HoldingController->GetGripPtrByID(SecondaryHand.GripID);
if (GripInfo && GripInfoS)
{
FVector Primary = GripInfo->RelativeTransform.InverseTransformPositionNoScale(FVector::ZeroVector);
FVector Secondary = GripInfoS->RelativeTransform.InverseTransformPositionNoScale(FVector::ZeroVector);
FVector Final = (COMType == EVRMeleeComType::VRPMELEECOM_PrimaryHand) ? Primary : ((Primary + Secondary) / 2.f);
ObjectRelativeGripCenter.SetLocation(Final);
}
}
else
{
if (GripInfo)
{
if (GripInfo->SecondaryGripInfo.bHasSecondaryAttachment)
{
FVector gripLoc = GripInfo->RelativeTransform.InverseTransformPositionNoScale(FVector::ZeroVector);
FVector secGripLoc = GripInfo->SecondaryGripInfo.SecondaryRelativeTransform.GetLocation();
FVector finalloc = (COMType == EVRMeleeComType::VRPMELEECOM_PrimaryHand) ? gripLoc : (gripLoc + secGripLoc) / 2.f;
FVector finalScaled = finalloc * GripInfo->RelativeTransform.GetScale3D();
FTransform ownerTrans = GetOwner()->GetActorTransform();
//DrawDebugSphere(GetWorld(), ownerTrans.TransformPosition(finalScaled), 4.0f, 32, FColor::Orange, true);
ObjectRelativeGripCenter.SetLocation(finalScaled);
PrimaryHand.HoldingController->ReCreateGrip(*GripInfo);
}
else
{
ObjectRelativeGripCenter = FTransform::Identity;
}
}
}
}
}
void UGS_Melee::UpdateHandPositionAndRotation(FBPGripPair HandPair, FTransform HandWorldTransform, FVector& LocDifference, float& RotDifference, bool bUpdateLocation, bool bUpdateRotation)
{
LocDifference = FVector::ZeroVector;
if (HandPair.IsValid())
{
FBPActorGripInformation* GripInfo = HandPair.HoldingController->GetGripPtrByID(HandPair.GripID);
if (GripInfo)
{
// Make hand relative to object transform
FTransform RelativeTrans = GripInfo->RelativeTransform.Inverse();
FVector OriginalLoc = RelativeTrans.GetLocation();
FQuat OriginalRot = RelativeTrans.GetRotation();
// Get our current parent transform
FTransform ParentTransform = GetParentTransform();
FQuat orientationRot = OrientationComponentRelativeFacing.GetRotation();
if (bUpdateLocation)
{
FVector currentRelVec = orientationRot.RotateVector(ParentTransform.InverseTransformPosition(HandWorldTransform.GetLocation()));
FVector currentLoc = orientationRot.RotateVector(RelativeTrans.GetLocation());
currentLoc.X = currentRelVec.X;
RelativeTrans.SetLocation(orientationRot.UnrotateVector(currentLoc));
}
if (bUpdateRotation)
{
FRotator currentRelRot = (orientationRot * (ParentTransform.GetRotation().Inverse() * HandWorldTransform.GetRotation())).Rotator();
FRotator currentRot = (orientationRot * RelativeTrans.GetRotation()).Rotator();
currentRot.Roll = currentRelRot.Roll;
RelativeTrans.SetRotation(orientationRot.Inverse() * currentRot.Quaternion());
}
GripInfo->RelativeTransform = RelativeTrans.Inverse();
HandPair.HoldingController->UpdatePhysicsHandle(*GripInfo, true);
HandPair.HoldingController->NotifyGripTransformChanged(*GripInfo);
LocDifference = RelativeTrans.GetLocation() - OriginalLoc;
RotDifference = RelativeTrans.GetRotation().Rotator().Roll - OriginalRot.Rotator().Roll;
// Instead of recreating, can directly set local pose here
FBPGripPair SecHand = SecondaryHand;
UpdateDualHandInfo();
if (SecondaryHand.IsValid() && !(SecHand == SecondaryHand))
{
GripInfo = SecondaryHand.HoldingController->GetGripPtrByID(SecondaryHand.GripID);
GripInfo->AdvancedGripSettings.PhysicsSettings.PhysicsGripLocationSettings = EPhysicsGripCOMType::COM_GripAtControllerLoc;
FBPActorPhysicsHandleInformation* HandleInfo = SecondaryHand.HoldingController->GetPhysicsGrip(SecondaryHand.GripID);
if (HandleInfo)
{
SecondaryHandPhysicsSettings.FillTo(HandleInfo);
SecondaryHand.HoldingController->UpdatePhysicsHandle(SecondaryHand.GripID, true);
}
GripInfo = PrimaryHand.HoldingController->GetGripPtrByID(PrimaryHand.GripID);
switch (COMType)
{
case EVRMeleeComType::VRPMELEECOM_Normal:
case EVRMeleeComType::VRPMELEECOM_BetweenHands:
{
GripInfo->AdvancedGripSettings.PhysicsSettings.PhysicsGripLocationSettings = EPhysicsGripCOMType::COM_GripAtControllerLoc;
}break;
case EVRMeleeComType::VRPMELEECOM_PrimaryHand:
{
GripInfo->AdvancedGripSettings.PhysicsSettings.PhysicsGripLocationSettings = EPhysicsGripCOMType::COM_SetAndGripAt;
}
}
HandleInfo = PrimaryHand.HoldingController->GetPhysicsGrip(PrimaryHand.GripID);
if (HandleInfo)
{
if (bHasValidPrimaryHand)
{
PrimaryHandPhysicsSettings.FillTo(HandleInfo);
}
else
{
SecondaryHandPhysicsSettings.FillTo(HandleInfo);
}
PrimaryHand.HoldingController->UpdatePhysicsHandle(PrimaryHand.GripID, true);
}
}
if (COMType != EVRMeleeComType::VRPMELEECOM_Normal)
SetComBetweenHands(HandPair.HoldingController, HandPair.HoldingController->GetPhysicsGrip(HandPair.GripID));
}
}
}
void UGS_Melee::UpdateHandPosition(FBPGripPair HandPair, FVector HandWorldPosition, FVector& LocDifference)
{
LocDifference = FVector::ZeroVector;
if (HandPair.IsValid())
{
FBPActorGripInformation* GripInfo = HandPair.HoldingController->GetGripPtrByID(HandPair.GripID);
if (GripInfo)
{
// Make hand relative to object transform
FTransform RelativeTrans = GripInfo->RelativeTransform.Inverse();
FVector OriginalLoc = RelativeTrans.GetLocation();
// Get our current parent transform
FTransform ParentTransform = GetParentTransform();
FQuat orientationRot = OrientationComponentRelativeFacing.GetRotation();
FVector currentRelVec = orientationRot.RotateVector(ParentTransform.InverseTransformPosition(HandWorldPosition));
//currentRelVec = OrientationComponentRelativeFacing.GetRotation().UnrotateVector(currentRelVec);
FVector currentLoc = orientationRot.RotateVector(RelativeTrans.GetLocation());
currentLoc.X = currentRelVec.X;
RelativeTrans.SetLocation(orientationRot.UnrotateVector(currentLoc));
GripInfo->RelativeTransform = RelativeTrans.Inverse();
HandPair.HoldingController->UpdatePhysicsHandle(*GripInfo, true);
HandPair.HoldingController->NotifyGripTransformChanged(*GripInfo);
LocDifference = RelativeTrans.GetLocation() - OriginalLoc;
// Instead of recreating, can directly set local pose here
FBPGripPair SecHand = SecondaryHand;
UpdateDualHandInfo();
if (SecondaryHand.IsValid() && !(SecHand == SecondaryHand))
{
GripInfo = SecondaryHand.HoldingController->GetGripPtrByID(SecondaryHand.GripID);
GripInfo->AdvancedGripSettings.PhysicsSettings.PhysicsGripLocationSettings = EPhysicsGripCOMType::COM_GripAtControllerLoc;
FBPActorPhysicsHandleInformation* HandleInfo = SecondaryHand.HoldingController->GetPhysicsGrip(SecondaryHand.GripID);
if (HandleInfo)
{
SecondaryHandPhysicsSettings.FillTo(HandleInfo);
SecondaryHand.HoldingController->UpdatePhysicsHandle(SecondaryHand.GripID, true);
}
GripInfo = PrimaryHand.HoldingController->GetGripPtrByID(PrimaryHand.GripID);
switch (COMType)
{
case EVRMeleeComType::VRPMELEECOM_Normal:
case EVRMeleeComType::VRPMELEECOM_BetweenHands:
{
GripInfo->AdvancedGripSettings.PhysicsSettings.PhysicsGripLocationSettings = EPhysicsGripCOMType::COM_GripAtControllerLoc;
}break;
case EVRMeleeComType::VRPMELEECOM_PrimaryHand:
{
GripInfo->AdvancedGripSettings.PhysicsSettings.PhysicsGripLocationSettings = EPhysicsGripCOMType::COM_SetAndGripAt;
}
}
HandleInfo = PrimaryHand.HoldingController->GetPhysicsGrip(PrimaryHand.GripID);
if (HandleInfo)
{
if (bHasValidPrimaryHand)
{
PrimaryHandPhysicsSettings.FillTo(HandleInfo);
}
else
{
SecondaryHandPhysicsSettings.FillTo(HandleInfo);
}
PrimaryHand.HoldingController->UpdatePhysicsHandle(PrimaryHand.GripID, true);
}
}
if (COMType != EVRMeleeComType::VRPMELEECOM_Normal)
SetComBetweenHands(HandPair.HoldingController, HandPair.HoldingController->GetPhysicsGrip(HandPair.GripID));
}
}
}
void UGS_Melee::SetPrimaryAndSecondaryHands(FBPGripPair& PrimaryGrip, FBPGripPair& SecondaryGrip)
{
PrimaryHand = PrimaryGrip;
SecondaryHand = SecondaryGrip;
}
void UGS_Melee::OnSecondaryGrip_Implementation(UGripMotionControllerComponent* Controller, USceneComponent* SecondaryGripComponent, const FBPActorGripInformation& GripInformation)
{
if (!bIsActive)
return;
UpdateDualHandInfo();
}
void UGS_Melee::OnGrip_Implementation(UGripMotionControllerComponent* GrippingController, const FBPActorGripInformation& GripInformation)
{
if (!bIsActive)
return;
// Not storing an id, we should only be doing this once
// GetOwner()->OnActorHit.AddDynamic(this, &UGS_Melee::OnActorHit);
// This lets us change the grip settings prior to actually starting the grip off
//SetTickEnabled(true);
//bCheckLodge = true;
bIsHeld = true;
//if (GrippingController->HasGripAuthority(GripInformation))
{
UpdateDualHandInfo();
// If we have multiple hands then alter the grip settings here for what we have already, the other will wait until post event
if (SecondaryHand.IsValid())
{
FBPActorGripInformation * GripInfo = SecondaryHand.HoldingController->GetGripPtrByID(SecondaryHand.GripID);
GripInfo->AdvancedGripSettings.PhysicsSettings.PhysicsGripLocationSettings = EPhysicsGripCOMType::COM_GripAtControllerLoc;
FBPActorPhysicsHandleInformation* HandleInfo = SecondaryHand.HoldingController->GetPhysicsGrip(SecondaryHand.GripID);
if (HandleInfo)
{
SecondaryHandPhysicsSettings.FillTo(HandleInfo);
SecondaryHand.HoldingController->UpdatePhysicsHandle(SecondaryHand.GripID, true);
}
GripInfo = PrimaryHand.HoldingController->GetGripPtrByID(PrimaryHand.GripID);
switch (COMType)
{
case EVRMeleeComType::VRPMELEECOM_Normal:
case EVRMeleeComType::VRPMELEECOM_BetweenHands:
{
GripInfo->AdvancedGripSettings.PhysicsSettings.PhysicsGripLocationSettings = EPhysicsGripCOMType::COM_GripAtControllerLoc;
}break;
case EVRMeleeComType::VRPMELEECOM_PrimaryHand:
{
GripInfo->AdvancedGripSettings.PhysicsSettings.PhysicsGripLocationSettings = EPhysicsGripCOMType::COM_SetAndGripAt;
}
}
HandleInfo = PrimaryHand.HoldingController->GetPhysicsGrip(PrimaryHand.GripID);
if (HandleInfo)
{
if (bHasValidPrimaryHand)
{
PrimaryHandPhysicsSettings.FillTo(HandleInfo);
}
else
{
SecondaryHandPhysicsSettings.FillTo(HandleInfo);
}
PrimaryHand.HoldingController->UpdatePhysicsHandle(PrimaryHand.GripID, true);
}
}
}
}
void UGS_Melee::OnGripRelease_Implementation(UGripMotionControllerComponent* ReleasingController, const FBPActorGripInformation& GripInformation, bool bWasSocketed)
{
if (!bIsActive)
return;
// Refresh our IsHeld var
TArray<FBPGripPair> HoldingControllers;
IVRGripInterface::Execute_IsHeld(GetParent(), HoldingControllers, bIsHeld);
//if(!bAlwaysTickPenetration)
//SetTickEnabled(false);
//if (!bAlwaysTickPenetration)
// bCheckLodge = false;
if (SecondaryHand.IsValid() && SecondaryHand.HoldingController == ReleasingController && SecondaryHand.GripID == GripInformation.GripID)
{
SecondaryHand = FBPGripPair();
}
else if (PrimaryHand.IsValid() && PrimaryHand.HoldingController == ReleasingController && PrimaryHand.GripID == GripInformation.GripID)
{
if (SecondaryHand.IsValid())
{
PrimaryHand = SecondaryHand;
SecondaryHand = FBPGripPair();
}
else
{
PrimaryHand = FBPGripPair();
}
}
if (PrimaryHand.IsValid())
{
FBPActorPhysicsHandleInformation* HandleInfo = PrimaryHand.HoldingController->GetPhysicsGrip(PrimaryHand.GripID);
if (HandleInfo)
{
FBPActorGripInformation * GripInfo = PrimaryHand.HoldingController->GetGripPtrByID(PrimaryHand.GripID);
if (GripInfo)
{
//Reset defaults here still!!!
HandleInfo->LinConstraint.XDrive.bEnablePositionDrive = true;
HandleInfo->LinConstraint.XDrive.bEnableVelocityDrive = true;
HandleInfo->LinConstraint.XDrive.Stiffness = GripInfo->Stiffness;
HandleInfo->LinConstraint.XDrive.Damping = GripInfo->Damping;
HandleInfo->LinConstraint.YDrive = HandleInfo->LinConstraint.XDrive;
HandleInfo->LinConstraint.ZDrive = HandleInfo->LinConstraint.XDrive;
HandleInfo->AngConstraint.SwingDrive.bEnablePositionDrive = false;
HandleInfo->AngConstraint.SwingDrive.bEnableVelocityDrive = false;
HandleInfo->AngConstraint.TwistDrive.bEnablePositionDrive = false;
HandleInfo->AngConstraint.TwistDrive.bEnableVelocityDrive = false;
HandleInfo->AngConstraint.AngularDriveMode = EAngularDriveMode::SLERP;
HandleInfo->AngConstraint.SlerpDrive.bEnablePositionDrive = true;
HandleInfo->AngConstraint.SlerpDrive.bEnableVelocityDrive = true;
if (GripInfo->AdvancedGripSettings.PhysicsSettings.bUsePhysicsSettings && GripInfo->AdvancedGripSettings.PhysicsSettings.bUseCustomAngularValues)
{
HandleInfo->AngConstraint.SlerpDrive.Damping = GripInfo->AdvancedGripSettings.PhysicsSettings.AngularDamping;
HandleInfo->AngConstraint.SlerpDrive.Stiffness = GripInfo->AdvancedGripSettings.PhysicsSettings.AngularStiffness;
}
else
{
HandleInfo->AngConstraint.SlerpDrive.Damping = GripInfo->Damping * 1.4f;
HandleInfo->AngConstraint.SlerpDrive.Stiffness = GripInfo->Stiffness * 1.5f;
}
FBPAdvGripSettings AdvSettings = IVRGripInterface::Execute_AdvancedGripSettings(GripInfo->GrippedObject);
GripInfo->AdvancedGripSettings.PhysicsSettings.PhysicsGripLocationSettings = AdvSettings.PhysicsSettings.PhysicsGripLocationSettings;
PrimaryHand.HoldingController->UpdatePhysicsHandle(PrimaryHand.GripID, true);
}
}
}
}
void UGS_Melee::OnBeginPlay_Implementation(UObject * CallingOwner)
{
// Grip base has no super of this
if (AActor * Owner = GetOwner())
{
FName CurrentCompName = NAME_None;
bool bSearchRootComp = WeaponRootOrientationComponent.IsValid();
int RemainingCount = PenetrationNotifierComponents.Num();
for (UActorComponent* ChildComp : Owner->GetComponents())
{
CurrentCompName = ChildComp->GetFName();
if (CurrentCompName == NAME_None)
continue;
if (bSearchRootComp && CurrentCompName == WeaponRootOrientationComponent)
{
bSearchRootComp = false;
if (USceneComponent * SceneComp = Cast<USceneComponent>(ChildComp))
{
OrientationComponentRelativeFacing = SceneComp->GetRelativeTransform();
}
}
if (FBPLodgeComponentInfo * Found = PenetrationNotifierComponents.FindByKey(CurrentCompName))
{
if (UPrimitiveComponent * PrimComp = Cast<UPrimitiveComponent>(ChildComp))
{
Found->TargetComponent = TObjectPtr<UPrimitiveComponent>(PrimComp);
//PrimComp->OnComponentHit.AddDynamic(this, &UGS_Melee::OnLodgeHitCallback);
}
// Decrement even if it failed the cast, they just had it wrong.
RemainingCount--;
}
if (!bSearchRootComp && RemainingCount < 1)
{
break;
}
}
// If we found at least one penetration object
if (RemainingCount < PenetrationNotifierComponents.Num())
{
Owner->OnActorHit.AddDynamic(this, &UGS_Melee::OnLodgeHitCallback);
bCheckLodge = true;
}
}
}
void UGS_Melee::OnEndPlay_Implementation(const EEndPlayReason::Type EndPlayReason)
{
if (AActor * Owner = GetOwner())
{
Owner->OnActorHit.RemoveDynamic(this, &UGS_Melee::OnLodgeHitCallback);
}
}
void UGS_Melee::OnLodgeHitCallback(AActor* SelfActor, AActor* OtherActor, FVector NormalImpulse, const FHitResult& Hit)
{
if (!Hit.GetComponent())
return;
if (!bCheckLodge || !bIsActive || bIsLodged || OtherActor == SelfActor)
{
if (bAlwaysTickPenetration || bIsHeld)
{
OnMeleeInvalidHit.Broadcast(OtherActor, Hit.GetComponent(), NormalImpulse, Hit);
}
return;
}
// Escape out if we are not held and are not set to always tick penetration
if (!bAlwaysTickPenetration && !bIsHeld)
return;
TArray<FBPHitSurfaceProperties> AllowedPenetrationSurfaceTypes;
if (OverrideMeleeSurfaceSettings.Num() > 0)
{
// Use our local copy
AllowedPenetrationSurfaceTypes = OverrideMeleeSurfaceSettings;
}
else
{
// Use the global settings
UVRGlobalSettings::GetMeleeSurfaceGlobalSettings(AllowedPenetrationSurfaceTypes);
}
FBPHitSurfaceProperties HitSurfaceProperties;
if (Hit.PhysMaterial.IsValid())
{
HitSurfaceProperties.SurfaceType = Hit.PhysMaterial->SurfaceType;
}
if (AllowedPenetrationSurfaceTypes.Num())
{
// Reject bad surface types
if (!Hit.PhysMaterial.IsValid())
{
OnMeleeInvalidHit.Broadcast(OtherActor, Hit.GetComponent(), NormalImpulse, Hit);
return;
}
EPhysicalSurface PhysSurfaceType = Hit.PhysMaterial->SurfaceType;
int32 IndexOfSurface = AllowedPenetrationSurfaceTypes.IndexOfByPredicate([&PhysSurfaceType](const FBPHitSurfaceProperties& Entry) { return Entry.SurfaceType == PhysSurfaceType; });
if (IndexOfSurface != INDEX_NONE)
{
HitSurfaceProperties = AllowedPenetrationSurfaceTypes[IndexOfSurface];
}
else
{
// Surface is not part of our default list, don't allow penetration
HitSurfaceProperties.bSurfaceAllowsPenetration = false;
// Not a valid surface type to throw an event
//return;
}
}
/*if (UPrimitiveComponent * root = Cast<UPrimitiveComponent>(SelfActor->GetRootComponent()))
{
if (FBodyInstance * rBodyInstance = root->GetBodyInstance())
{
//float mass =rBodyInstance->GetBodyMass();
//ImpulseVelocity = (NormalImpulse / rBodyInstance->GetBodyMass()).SizeSquared();
RollingVelocityAverage += FVector::CrossProduct(RollingAngVelocityAverage, Hit.ImpactPoint - (rBodyInstance->GetCOMPosition()));
}
}*/
// FVector FrameToFrameVelocity = RollingVelocityAverage;
// If we hit, then regardless of penetration, end the velocity history and reset
// RollingVelocityAverage = FVector::ZeroVector;
// RollingAngVelocityAverage = FVector::ZeroVector;
bool bHadFirstHit = false;
FBPLodgeComponentInfo FirstHitComp;
float HitNormalImpulse = NormalImpulse.SizeSquared();
for(FBPLodgeComponentInfo &LodgeData : PenetrationNotifierComponents)
{
if (!IsValid(LodgeData.TargetComponent))
continue;
FBox LodgeLocalBox = LodgeData.TargetComponent->CalcLocalBounds().GetBox();
FVector LocalHit = LodgeData.TargetComponent->GetComponentTransform().InverseTransformPosition(Hit.ImpactPoint);
//FBox LodgeBox = LodgeData.TargetComponent->Bounds.GetBox();
if (IsValid(LodgeData.TargetComponent) && LodgeLocalBox.IsInsideOrOn(LocalHit))//LodgeBox.IsInsideOrOn(Hit.ImpactPoint))
{
FVector ForwardVec = LodgeData.TargetComponent->GetForwardVector();
// Using swept objects hit normal as we are looking for a facing from ourselves
float DotValue = FMath::Abs(FVector::DotProduct(Hit.Normal, ForwardVec));
float Velocity = NormalImpulse.ProjectOnToNormal(ForwardVec).SizeSquared();//FrameToFrameVelocity.ProjectOnToNormal(ForwardVec);
// Check if the velocity was strong enough along our axis to count as a lodge event
// Also that our facing was in the relatively correct direction
if (HitSurfaceProperties.bSurfaceAllowsPenetration && (!bOnlyPenetrateWithTwoHands || SecondaryHand.IsValid()))
{
if (LodgeData.ZoneType != EVRMeleeZoneType::VRPMELLE_ZONETYPE_Hit && DotValue >= (1.0f - LodgeData.AcceptableForwardProductRange) && (Velocity * HitSurfaceProperties.StabVelocityScaler) >= FMath::Square(LodgeData.PenetrationVelocity))
{
OnShouldLodgeInObject.Broadcast(LodgeData, OtherActor, Hit.GetComponent(), Hit.GetComponent()->GetCollisionObjectType(), HitSurfaceProperties, NormalImpulse, Hit);
return;
//break;
}
}
float HitImpulse = LodgeData.bIgnoreForwardVectorForHitImpulse ? HitNormalImpulse : Velocity;
if (!bHadFirstHit && LodgeData.ZoneType > EVRMeleeZoneType::VRPMELLE_ZONETYPE_Stab && DotValue >= (1.0f - LodgeData.AcceptableForwardProductRangeForHits) && HitImpulse >= FMath::Square(LodgeData.MinimumHitVelocity))
{
bHadFirstHit = true;
FirstHitComp = LodgeData;
}
}
}
if (bHadFirstHit)
{
OnMeleeHit.Broadcast(FirstHitComp, OtherActor, Hit.GetComponent(), Hit.GetComponent()->GetCollisionObjectType(), HitSurfaceProperties, NormalImpulse, Hit);
}
else
{
OnMeleeInvalidHit.Broadcast(OtherActor, Hit.GetComponent(), NormalImpulse, Hit);
}
}
void UGS_Melee::HandlePrePhysicsHandle(UGripMotionControllerComponent* GrippingController, const FBPActorGripInformation &GripInfo, FBPActorPhysicsHandleInformation * HandleInfo, FTransform & KinPose)
{
if (!bIsActive)
return;
if (WeaponRootOrientationComponent != NAME_None)
{
// Alter to rotate to x+ if we have an orientation component
FQuat DeltaQuat = OrientationComponentRelativeFacing.GetRotation();
// This moves the kinematic actor to face its X+ in the direction designated
KinPose.SetRotation(KinPose.GetRotation() * (HandleInfo->RootBoneRotation.GetRotation().Inverse() * DeltaQuat));
HandleInfo->COMPosition.SetRotation(HandleInfo->COMPosition.GetRotation() * (HandleInfo->RootBoneRotation.GetRotation().Inverse() * DeltaQuat));
}
}
void UGS_Melee::HandlePostPhysicsHandle(UGripMotionControllerComponent* GrippingController, FBPActorPhysicsHandleInformation * HandleInfo)
{
if (!bIsActive)
return;
if (SecondaryHand.IsValid() )// && GrippingController == PrimaryHand.HoldingController)
{
if (GrippingController == SecondaryHand.HoldingController && HandleInfo->GripID == SecondaryHand.GripID)
{
SecondaryHandPhysicsSettings.FillTo(HandleInfo);
}
else if (GrippingController == PrimaryHand.HoldingController && HandleInfo->GripID == PrimaryHand.GripID)
{
if (bHasValidPrimaryHand)
{
PrimaryHandPhysicsSettings.FillTo(HandleInfo);
}
else
{
SecondaryHandPhysicsSettings.FillTo(HandleInfo);
}
}
if (COMType != EVRMeleeComType::VRPMELEECOM_Normal)
SetComBetweenHands(GrippingController, HandleInfo);
}
else
{
if (bUsePrimaryHandSettingsWithOneHand)
{
PrimaryHandPhysicsSettings.FillTo(HandleInfo);
}
//HandleInfo->bSetCOM = false; // Should i remove this?
HandleInfo->bSkipResettingCom = false;
}
}
void UGS_Melee::SetComBetweenHands(UGripMotionControllerComponent* GrippingController, FBPActorPhysicsHandleInformation * HandleInfo)
{
if (!GrippingController || !HandleInfo)
return;
if (COMType != EVRMeleeComType::VRPMELEECOM_Normal && SecondaryHand.IsValid())
{
//if (PrimaryHand.HoldingController == GrippingController)
{
if (UPrimitiveComponent * PrimComp = Cast<UPrimitiveComponent>(GetParentSceneComp()))
{
if (FBodyInstance * rBodyInstance = PrimComp->GetBodyInstance())
{
FPhysicsCommand::ExecuteWrite(rBodyInstance->ActorHandle, [&](const FPhysicsActorHandle& Actor)
{
FTransform localCom = FPhysicsInterface::GetComTransformLocal_AssumesLocked(Actor);
localCom.SetLocation((HandleInfo->RootBoneRotation * ObjectRelativeGripCenter).GetLocation());
FPhysicsInterface::SetComLocalPose_AssumesLocked(Actor, localCom);
});
}
}
}
HandleInfo->bSetCOM = true; // Should i remove this?
HandleInfo->bSkipResettingCom = true;
}
}
/*void UGS_Melee::Tick(float DeltaTime)
{
AActor* myOwner = GetOwner();
if (UPrimitiveComponent * root = Cast<UPrimitiveComponent>(myOwner->GetRootComponent()))
{
FBodyInstance* rBodyInstance = root->GetBodyInstance();
if (rBodyInstance && rBodyInstance->IsValidBodyInstance())
{
RollingVelocityAverage = rBodyInstance->GetUnrealWorldVelocity();
RollingAngVelocityAverage = rBodyInstance->GetUnrealWorldAngularVelocityInRadians();
}
}
}*/
bool UGS_Melee::Wants_DenyTeleport_Implementation(UGripMotionControllerComponent* Controller)
{
/*if (PrimaryHand.IsValid() && Controller != PrimaryHand.HoldingController)
{
return true;
}*/
return false;
}
bool UGS_Melee::GetWorldTransform_Implementation
(
UGripMotionControllerComponent* GrippingController,
float DeltaTime, FTransform & WorldTransform,
const FTransform &ParentTransform,
FBPActorGripInformation &Grip,
AActor * actor,
UPrimitiveComponent * root,
bool bRootHasInterface,
bool bActorHasInterface,
bool bIsForTeleport
)
{
if (!GrippingController)
return false;
// Just simple transform setting
WorldTransform = Grip.RelativeTransform * Grip.AdditionTransform * ParentTransform;
if (Grip.SecondaryGripInfo.bHasSecondaryAttachment)
{
WorldTransform.SetLocation((GrippingController->GetPivotLocation() + Grip.SecondaryGripInfo.SecondaryAttachment->GetComponentLocation()) / 2.f);
}
// Check the grip lerp state, this it ouside of the secondary attach check below because it can change the result of it
if ((Grip.SecondaryGripInfo.bHasSecondaryAttachment && Grip.SecondaryGripInfo.SecondaryAttachment) || Grip.SecondaryGripInfo.GripLerpState == EGripLerpState::EndLerp)
{
switch (Grip.SecondaryGripInfo.GripLerpState)
{
case EGripLerpState::StartLerp:
case EGripLerpState::EndLerp:
{
if (Grip.SecondaryGripInfo.curLerp > 0.01f)
Grip.SecondaryGripInfo.curLerp -= DeltaTime;
else
{
/*if (Grip.SecondaryGripInfo.bHasSecondaryAttachment &&
Grip.AdvancedGripSettings.SecondaryGripSettings.bUseSecondaryGripSettings &&
Grip.AdvancedGripSettings.SecondaryGripSettings.SecondaryGripScaler_DEPRECATED < 1.0f)
{
Grip.SecondaryGripInfo.GripLerpState = EGripLerpState::ConstantLerp_DEPRECATED;
}
else*/
Grip.SecondaryGripInfo.GripLerpState = EGripLerpState::NotLerping;
}
}break;
//case EGripLerpState::ConstantLerp_DEPRECATED:
case EGripLerpState::NotLerping:
default:break;
}
}
// Handle the interp and multi grip situations, re-checking the grip situation here as it may have changed in the switch above.
if ((Grip.SecondaryGripInfo.bHasSecondaryAttachment && Grip.SecondaryGripInfo.SecondaryAttachment) || Grip.SecondaryGripInfo.GripLerpState == EGripLerpState::EndLerp)
{
FTransform SecondaryTransform = Grip.RelativeTransform * ParentTransform;
// Checking secondary grip type for the scaling setting
ESecondaryGripType SecondaryType = ESecondaryGripType::SG_None;
if (bRootHasInterface)
SecondaryType = IVRGripInterface::Execute_SecondaryGripType(root);
else if (bActorHasInterface)
SecondaryType = IVRGripInterface::Execute_SecondaryGripType(actor);
// If the grip is a custom one, skip all of this logic we won't be changing anything
if (SecondaryType != ESecondaryGripType::SG_Custom)
{
// Variables needed for multi grip transform
FVector BasePoint = ParentTransform.GetLocation(); // Get our pivot point
const FTransform PivotToWorld = FTransform(FQuat::Identity, BasePoint);
const FTransform WorldToPivot = FTransform(FQuat::Identity, -BasePoint);
FVector frontLocOrig;
FVector frontLoc;
// Ending lerp out of a multi grip
if (Grip.SecondaryGripInfo.GripLerpState == EGripLerpState::EndLerp)
{
frontLocOrig = (/*WorldTransform*/SecondaryTransform.TransformPosition(Grip.SecondaryGripInfo.SecondaryRelativeTransform.GetLocation())) - BasePoint;
frontLoc = Grip.SecondaryGripInfo.LastRelativeLocation;
frontLocOrig = FMath::Lerp(frontLoc, frontLocOrig, FMath::Clamp(Grip.SecondaryGripInfo.curLerp / Grip.SecondaryGripInfo.LerpToRate, 0.0f, 1.0f));
}
else // Is in a multi grip, might be lerping into it as well.
{
//FVector curLocation; // Current location of the secondary grip
// Calculates the correct secondary attachment location and sets frontLoc to it
CalculateSecondaryLocation(frontLoc, BasePoint, Grip, GrippingController);
frontLocOrig = (/*WorldTransform*/SecondaryTransform.TransformPosition(Grip.SecondaryGripInfo.SecondaryRelativeTransform.GetLocation())) - BasePoint;
// Apply any smoothing settings and lerping in / constant lerping
ApplySmoothingAndLerp(Grip, frontLoc, frontLocOrig, DeltaTime);
Grip.SecondaryGripInfo.LastRelativeLocation = frontLoc;
}
// Get any scaling addition from a scaling secondary grip type
FVector Scaler = FVector(1.0f);
if (SecondaryType == ESecondaryGripType::SG_FreeWithScaling_Retain || SecondaryType == ESecondaryGripType::SG_SlotOnlyWithScaling_Retain || SecondaryType == ESecondaryGripType::SG_ScalingOnly)
{
GetAnyScaling(Scaler, Grip, frontLoc, frontLocOrig, SecondaryType, SecondaryTransform);
}
Grip.SecondaryGripInfo.SecondaryGripDistance = FVector::Dist(frontLocOrig, frontLoc);
/*if (Grip.AdvancedGripSettings.SecondaryGripSettings.bUseSecondaryGripSettings && Grip.AdvancedGripSettings.SecondaryGripSettings.bUseSecondaryGripDistanceInfluence_DEPRECATED)
{
float rotScaler = 1.0f - FMath::Clamp((Grip.SecondaryGripInfo.SecondaryGripDistance - Grip.AdvancedGripSettings.SecondaryGripSettings.GripInfluenceDeadZone_DEPRECATED) / FMath::Max(Grip.AdvancedGripSettings.SecondaryGripSettings.GripInfluenceDistanceToZero_DEPRECATED, 1.0f), 0.0f, 1.0f);
frontLoc = FMath::Lerp(frontLocOrig, frontLoc, rotScaler);
}*/
// Skip rot val for scaling only
if (SecondaryType != ESecondaryGripType::SG_ScalingOnly)
{
// Get the rotation difference from the initial second grip
FQuat rotVal = FQuat::FindBetweenVectors(frontLocOrig, frontLoc);
// Rebase the world transform to the pivot point, add the rotation, remove the pivot point rebase
WorldTransform = WorldTransform * WorldToPivot * FTransform(rotVal, FVector::ZeroVector, Scaler) * PivotToWorld;
}
else
{
// Rebase the world transform to the pivot point, add the scaler, remove the pivot point rebase
WorldTransform = WorldTransform * WorldToPivot * FTransform(FQuat::Identity, FVector::ZeroVector, Scaler) * PivotToWorld;
}
}
// Fixup X rotation to keep it aligned with the primary hand.
//WorldTransform = Grip.RelativeTransform * Grip.AdditionTransform * ParentTransform;
// Get primary hand relative position
FTransform InverseTrans(Grip.RelativeTransform.ToInverseMatrixWithScale());
// Get the original location of the hand
FVector origLocation = InverseTrans.GetLocation();
FVector orientedvector = FVector::VectorPlaneProject(origLocation, -OrientationComponentRelativeFacing.GetRotation().GetForwardVector());
FVector newLocation = FVector::VectorPlaneProject(WorldTransform.InverseTransformPosition(GrippingController->GetPivotLocation()), OrientationComponentRelativeFacing.GetRotation().GetForwardVector());
FQuat DeltaQuat = FQuat::FindBetweenVectors(orientedvector, newLocation);
WorldTransform.SetRotation(DeltaQuat * WorldTransform.GetRotation());
}
return true;
}

View file

@ -0,0 +1,111 @@
// Fill out your copyright notice in the Description page of Project Settings.
#include "GripScripts/GS_Physics.h"
#include UE_INLINE_GENERATED_CPP_BY_NAME(GS_Physics)
#include "VRGripInterface.h"
#include "Components/PrimitiveComponent.h"
#include "GameFramework/Actor.h"
#include "GripMotionControllerComponent.h"
UGS_Physics::UGS_Physics(const FObjectInitializer& ObjectInitializer) :
Super(ObjectInitializer)
{
bIsActive = true;
WorldTransformOverrideType = EGSTransformOverrideType::None;
bDenyLateUpdates = true;
bInjectPrePhysicsHandle = false;
bInjectPostPhysicsHandle = true;
bCanEverTick = false;
SingleHandPhysicsSettings.TwistSettings.MaxForceCoefficient = 1.f;
SingleHandPhysicsSettings.SwingSettings = SingleHandPhysicsSettings.TwistSettings;
SingleHandPhysicsSettings.SlerpSettings = SingleHandPhysicsSettings.TwistSettings;
SingleHandPhysicsSettings.XAxisSettings.MaxForceCoefficient = 1.f;
SingleHandPhysicsSettings.YAxisSettings = SingleHandPhysicsSettings.XAxisSettings;
SingleHandPhysicsSettings.ZAxisSettings = SingleHandPhysicsSettings.XAxisSettings;
}
void UGS_Physics::UpdateDualHandInfo(UGripMotionControllerComponent* GrippingController, bool bReCreate)
{
TArray<FBPGripPair> HoldingControllers;
bool bIsHeld;
IVRGripInterface::Execute_IsHeld(GetParent(), HoldingControllers, bIsHeld);
int NumControllers = HoldingControllers.Num();
for (FBPGripPair& Grip : HoldingControllers)
{
if (NumControllers > 1)
{
FBPActorPhysicsHandleInformation* HandleInfo = Grip.HoldingController->GetPhysicsGrip(Grip.GripID);
if (HandleInfo)
{
MultiHandPhysicsSettings.FillTo(HandleInfo);
if(bReCreate && Grip.HoldingController != GrippingController)
Grip.HoldingController->UpdatePhysicsHandle(Grip.GripID, true);
}
}
else
{
FBPActorPhysicsHandleInformation* HandleInfo = Grip.HoldingController->GetPhysicsGrip(Grip.GripID);
if (HandleInfo)
{
SingleHandPhysicsSettings.FillTo(HandleInfo);
if(bReCreate)
Grip.HoldingController->UpdatePhysicsHandle(Grip.GripID, true);
}
}
}
}
void UGS_Physics::OnGrip_Implementation(UGripMotionControllerComponent* GrippingController, const FBPActorGripInformation& GripInformation)
{
if (!bIsActive)
return;
UpdateDualHandInfo(GrippingController, true);
}
void UGS_Physics::OnGripRelease_Implementation(UGripMotionControllerComponent* ReleasingController, const FBPActorGripInformation& GripInformation, bool bWasSocketed)
{
if (!bIsActive)
return;
UpdateDualHandInfo(nullptr, true);
}
// Should I be orienting it to the controller for these types of grips?
/*void UGS_Physics::HandlePrePhysicsHandle(UGripMotionControllerComponent* GrippingController, const FBPActorGripInformation &GripInfo, FBPActorPhysicsHandleInformation * HandleInfo, FTransform & KinPose)
{
if (!bIsActive)
return;
if (WeaponRootOrientationComponent != NAME_None)
{
// Alter to rotate to x+ if we have an orientation component
FQuat DeltaQuat = OrientationComponentRelativeFacing.GetRotation();
// This moves the kinematic actor to face its X+ in the direction designated
KinPose.SetRotation(KinPose.GetRotation() * (HandleInfo->RootBoneRotation.GetRotation().Inverse() * DeltaQuat));
HandleInfo->COMPosition.SetRotation(HandleInfo->COMPosition.GetRotation() * (HandleInfo->RootBoneRotation.GetRotation().Inverse() * DeltaQuat));
}
}*/
void UGS_Physics::HandlePostPhysicsHandle(UGripMotionControllerComponent* GrippingController, FBPActorPhysicsHandleInformation * HandleInfo)
{
if (!bIsActive)
return;
UpdateDualHandInfo(GrippingController, false);
}

View file

@ -0,0 +1,357 @@
// Fill out your copyright notice in the Description page of Project Settings.
#include "GripScripts/VRGripScriptBase.h"
#include UE_INLINE_GENERATED_CPP_BY_NAME(VRGripScriptBase)
#include "GripMotionControllerComponent.h"
#include "VRGripInterface.h"
#include "Engine/BlueprintGeneratedClass.h"
#include "Components/PrimitiveComponent.h"
#include "GameFramework/Actor.h"
#include "Net/UnrealNetwork.h"
#include "Engine/NetDriver.h"
UVRGripScriptBase::UVRGripScriptBase(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{
// PrimaryComponentTick.bCanEverTick = false;
// PrimaryComponentTick.bStartWithTickEnabled = false;
// PrimaryComponentTick.TickGroup = ETickingGroup::TG_PrePhysics;
WorldTransformOverrideType = EGSTransformOverrideType::None;
bDenyAutoDrop = false;
bDenyLateUpdates = false;
bForceDrop = false;
bIsActive = false;
bCanEverTick = false;
bAllowTicking = false;
}
void UVRGripScriptBase::OnEndPlay_Implementation(const EEndPlayReason::Type EndPlayReason) {};
void UVRGripScriptBase::OnBeginPlay_Implementation(UObject * CallingOwner) {};
bool UVRGripScriptBase::GetWorldTransform_Implementation(UGripMotionControllerComponent* GrippingController, float DeltaTime, FTransform & WorldTransform, const FTransform &ParentTransform, FBPActorGripInformation &Grip, AActor * actor, UPrimitiveComponent * root, bool bRootHasInterface, bool bActorHasInterface, bool bIsForTeleport) { return true; }
void UVRGripScriptBase::OnGrip_Implementation(UGripMotionControllerComponent * GrippingController, const FBPActorGripInformation & GripInformation) {}
void UVRGripScriptBase::OnGripRelease_Implementation(UGripMotionControllerComponent * ReleasingController, const FBPActorGripInformation & GripInformation, bool bWasSocketed) {}
void UVRGripScriptBase::OnSecondaryGrip_Implementation(UGripMotionControllerComponent * Controller, USceneComponent * SecondaryGripComponent, const FBPActorGripInformation & GripInformation) {}
void UVRGripScriptBase::OnSecondaryGripRelease_Implementation(UGripMotionControllerComponent * Controller, USceneComponent * ReleasingSecondaryGripComponent, const FBPActorGripInformation & GripInformation) {}
EGSTransformOverrideType UVRGripScriptBase::GetWorldTransformOverrideType() { return WorldTransformOverrideType; }
bool UVRGripScriptBase::IsScriptActive() { return bIsActive; }
//bool UVRGripScriptBase::Wants_DenyAutoDrop() { return bDenyAutoDrop; }
//bool UVRGripScriptBase::Wants_DenyLateUpdates() { return bDenyLateUpdates; }
//bool UVRGripScriptBase::Wants_ToForceDrop() { return bForceDrop; }
bool UVRGripScriptBase::Wants_DenyTeleport_Implementation(UGripMotionControllerComponent * Controller) { return false; }
void UVRGripScriptBase::HandlePrePhysicsHandle(UGripMotionControllerComponent* GrippingController, const FBPActorGripInformation &GripInfo, FBPActorPhysicsHandleInformation * HandleInfo, FTransform & KinPose) {}
void UVRGripScriptBase::HandlePostPhysicsHandle(UGripMotionControllerComponent* GrippingController, FBPActorPhysicsHandleInformation * HandleInfo) {}
UVRGripScriptBase* UVRGripScriptBase::GetGripScriptByClass(UObject* WorldContextObject, TSubclassOf<UVRGripScriptBase> GripScriptClass, EBPVRResultSwitch& Result)
{
if (WorldContextObject->GetClass()->ImplementsInterface(UVRGripInterface::StaticClass()))
{
TArray<UVRGripScriptBase*> GripScripts;
if (IVRGripInterface::Execute_GetGripScripts(WorldContextObject, GripScripts))
{
for (UVRGripScriptBase* Script : GripScripts)
{
if (Script && Script->IsA(GripScriptClass))
{
Result = EBPVRResultSwitch::OnSucceeded;
return Script;
}
}
}
}
Result = EBPVRResultSwitch::OnFailed;
return nullptr;
}
void UVRGripScriptBase::GetLifetimeReplicatedProps(TArray< class FLifetimeProperty > & OutLifetimeProps) const
{
// Uobject has no replicated props
//Super::GetLifetimeReplicatedProps(OutLifetimeProps);
// Replicate here if required
UBlueprintGeneratedClass* BPClass = Cast<UBlueprintGeneratedClass>(GetClass());
if (BPClass != NULL)
{
BPClass->GetLifetimeBlueprintReplicationList(OutLifetimeProps);
}
}
void UVRGripScriptBase::Tick(float DeltaTime)
{
// Do nothing by default
}
bool UVRGripScriptBase::IsTickable() const
{
return bAllowTicking;
}
UWorld* UVRGripScriptBase::GetTickableGameObjectWorld() const
{
return GetWorld();
}
bool UVRGripScriptBase::IsTickableInEditor() const
{
return false;
}
bool UVRGripScriptBase::IsTickableWhenPaused() const
{
return false;
}
ETickableTickType UVRGripScriptBase::GetTickableTickType() const
{
if(IsTemplate(RF_ClassDefaultObject))
return ETickableTickType::Never;
return bCanEverTick ? ETickableTickType::Conditional : ETickableTickType::Never;
}
TStatId UVRGripScriptBase::GetStatId() const
{
RETURN_QUICK_DECLARE_CYCLE_STAT(UVRGripScriptBase, STATGROUP_Tickables);
}
void UVRGripScriptBase::SetTickEnabled(bool bTickEnabled)
{
bAllowTicking = bTickEnabled;
}
// Not currently compiling in editor builds....not entirely sure why...
/*
void UVRGripScriptBase::PreReplication(IRepChangedPropertyTracker & ChangedPropertyTracker)
{
// In the grippables pre replication to pass it on
#ifndef WITH_EDITOR
// Run pre-replication for any grip scripts
if (GripLogicScripts.Num())
{
if (UNetDriver* NetDriver = GetNetDriver())
{
for (UVRGripScriptBase* Script : GripLogicScripts)
{
if (Script && IsValid(Script))
{
Script->PreReplication(*((IRepChangedPropertyTracker *)NetDriver->FindOrCreateRepChangedPropertyTracker(Script).Get()));
}
}
}
}
#endif
UBlueprintGeneratedClass* BPClass = Cast<UBlueprintGeneratedClass>(GetClass());
if (BPClass != NULL)
{
BPClass->InstancePreReplication(this, ChangedPropertyTracker);
}
}*/
bool UVRGripScriptBase::CallRemoteFunction(UFunction * Function, void * Parms, FOutParmRec * OutParms, FFrame * Stack)
{
bool bProcessed = false;
if (AActor* MyOwner = GetOwner())
{
FWorldContext* const Context = GEngine->GetWorldContextFromWorld(GetWorld());
if (Context != nullptr)
{
for (FNamedNetDriver& Driver : Context->ActiveNetDrivers)
{
if (Driver.NetDriver != nullptr && Driver.NetDriver->ShouldReplicateFunction(MyOwner, Function))
{
Driver.NetDriver->ProcessRemoteFunction(MyOwner, Function, Parms, OutParms, Stack, this);
bProcessed = true;
}
}
}
}
return bProcessed;
}
int32 UVRGripScriptBase::GetFunctionCallspace(UFunction * Function, FFrame * Stack)
{
AActor* Owner = GetOwner();
if (HasAnyFlags(RF_ClassDefaultObject) || !IsSupportedForNetworking() || !Owner)
{
// This handles absorbing authority/cosmetic
return GEngine->GetGlobalFunctionCallspace(Function, this, Stack);
}
// Owner is certified valid now
return Owner->GetFunctionCallspace(Function, Stack);
}
FTransform UVRGripScriptBase::GetGripTransform(const FBPActorGripInformation &Grip, const FTransform & ParentTransform)
{
return Grip.RelativeTransform * Grip.AdditionTransform * ParentTransform;
}
USceneComponent * UVRGripScriptBase::GetParentSceneComp()
{
UObject* ParentObj = this->GetParent();
if (USceneComponent * PrimParent = Cast<USceneComponent>(ParentObj))
{
return PrimParent;
}
else if (AActor * ParentActor = Cast<AActor>(ParentObj))
{
return ParentActor->GetRootComponent();
}
return nullptr;
}
FTransform UVRGripScriptBase::GetParentTransform(bool bGetWorldTransform, FName BoneName)
{
UObject* ParentObj = this->GetParent();
if (USceneComponent* PrimParent = Cast<USceneComponent>(ParentObj))
{
if (BoneName != NAME_None)
{
return PrimParent->GetSocketTransform(BoneName);
}
else
{
return PrimParent->GetComponentTransform();
}
}
else if (AActor* ParentActor = Cast<AActor>(ParentObj))
{
return ParentActor->GetActorTransform();
}
return FTransform::Identity;
}
FBodyInstance * UVRGripScriptBase::GetParentBodyInstance(FName OptionalBoneName)
{
UObject * ParentObj = this->GetParent();
if (UPrimitiveComponent * PrimParent = Cast<UPrimitiveComponent>(ParentObj))
{
return PrimParent->GetBodyInstance(OptionalBoneName);
}
else if (AActor * ParentActor = Cast<AActor>(ParentObj))
{
if (UPrimitiveComponent * Prim = Cast<UPrimitiveComponent>(ParentActor->GetRootComponent()))
{
return Prim->GetBodyInstance(OptionalBoneName);
}
}
return nullptr;
}
UObject * UVRGripScriptBase::GetParent()
{
return this->GetOuter();
}
AActor * UVRGripScriptBase::GetOwner()
{
UObject * myOuter = this->GetOuter();
if (!myOuter)
return nullptr;
if (AActor * ActorOwner = Cast<AActor>(myOuter))
{
return ActorOwner;
}
else if (UActorComponent * ComponentOwner = Cast<UActorComponent>(myOuter))
{
return ComponentOwner->GetOwner();
}
return nullptr;
}
bool UVRGripScriptBase::HasAuthority()
{
if (AActor * MyOwner = GetOwner())
{
return MyOwner->GetLocalRole() == ROLE_Authority;
}
return false;
}
bool UVRGripScriptBase::IsServer()
{
if (AActor * MyOwner = GetOwner())
{
return MyOwner->GetNetMode() < ENetMode::NM_Client;
}
return false;
}
UWorld* UVRGripScriptBase::GetWorld() const
{
if (IsTemplate())
return nullptr;
if (GIsEditor && !GIsPlayInEditorWorld)
{
return nullptr;
}
else if (UObject * Outer = GetOuter())
{
return Outer->GetWorld();
}
return nullptr;
}
void UVRGripScriptBase::EndPlay(const EEndPlayReason::Type EndPlayReason)
{
OnEndPlay(EndPlayReason);
}
void UVRGripScriptBase::BeginPlay(UObject * CallingOwner)
{
if (bAlreadyNotifiedPlay)
return;
bAlreadyNotifiedPlay = true;
// Notify the subscripts about begin play
OnBeginPlay(CallingOwner);
}
void UVRGripScriptBase::PostInitProperties()
{
Super::PostInitProperties();
//Called in game, when World exist . BeginPlay will not be called in editor
if (GetWorld())
{
if (AActor* Owner = GetOwner())
{
if (Owner->IsActorInitialized())
{
BeginPlay(GetOwner());
}
}
}
}
void UVRGripScriptBaseBP::Tick(float DeltaTime)
{
ReceiveTick(DeltaTime);
}

View file

@ -0,0 +1,825 @@
// Copyright 1998-2016 Epic Games, Inc. All Rights Reserved.
#include "Grippables/GrippableActor.h"
#include UE_INLINE_GENERATED_CPP_BY_NAME(GrippableActor)
#include "TimerManager.h"
#include "Net/UnrealNetwork.h"
#include "PhysicsReplication.h"
#include "GameFramework/PlayerController.h"
#include "GameFramework/PlayerState.h"
#include "GripMotionControllerComponent.h"
#include "VRExpansionFunctionLibrary.h"
#include "Misc/BucketUpdateSubsystem.h"
#include "GripScripts/VRGripScriptBase.h"
#include "DrawDebugHelpers.h"
#include "Physics/Experimental/PhysScene_Chaos.h"
#if WITH_PUSH_MODEL
#include "Net/Core/PushModel/PushModel.h"
#endif
//=============================================================================
AGrippableActor::AGrippableActor(const FObjectInitializer& ObjectInitializer)
: Super()
{
VRGripInterfaceSettings.bDenyGripping = false;
VRGripInterfaceSettings.OnTeleportBehavior = EGripInterfaceTeleportBehavior::TeleportAllComponents;
VRGripInterfaceSettings.bSimulateOnDrop = true;
VRGripInterfaceSettings.SlotDefaultGripType = EGripCollisionType::InteractiveCollisionWithPhysics;
VRGripInterfaceSettings.FreeDefaultGripType = EGripCollisionType::InteractiveCollisionWithPhysics;
VRGripInterfaceSettings.SecondaryGripType = ESecondaryGripType::SG_None;
VRGripInterfaceSettings.MovementReplicationType = EGripMovementReplicationSettings::ForceClientSideMovement;
VRGripInterfaceSettings.LateUpdateSetting = EGripLateUpdateSettings::NotWhenCollidingOrDoubleGripping;
VRGripInterfaceSettings.ConstraintStiffness = 1500.0f;
VRGripInterfaceSettings.ConstraintDamping = 200.0f;
VRGripInterfaceSettings.ConstraintBreakDistance = 100.0f;
VRGripInterfaceSettings.SecondarySlotRange = 20.0f;
VRGripInterfaceSettings.PrimarySlotRange = 20.0f;
VRGripInterfaceSettings.bIsHeld = false;
// Default replication on for multiplayer
//this->bNetLoadOnClient = false;
SetReplicatingMovement(true);
bReplicates = true;
bRepGripSettingsAndGameplayTags = true;
bAllowIgnoringAttachOnOwner = true;
bReplicateGripScripts = false;
// #TODO we can register them maybe in the future
// Don't use the replicated list, use our custom replication instead
bReplicateUsingRegisteredSubObjectList = false;
// 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
MinNetUpdateFrequency = 30.0f;
}
void AGrippableActor::GetLifetimeReplicatedProps(TArray< class FLifetimeProperty > & OutLifetimeProps) const
{
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
DOREPLIFETIME_CONDITION(AGrippableActor, GripLogicScripts, COND_Custom);
DOREPLIFETIME(AGrippableActor, bReplicateGripScripts);
DOREPLIFETIME(AGrippableActor, bRepGripSettingsAndGameplayTags);
DOREPLIFETIME(AGrippableActor, bAllowIgnoringAttachOnOwner);
DOREPLIFETIME(AGrippableActor, ClientAuthReplicationData);
DOREPLIFETIME_CONDITION(AGrippableActor, VRGripInterfaceSettings, COND_Custom);
DOREPLIFETIME_CONDITION(AGrippableActor, GameplayTags, COND_Custom);
DISABLE_REPLICATED_PRIVATE_PROPERTY(AActor, AttachmentReplication);
FDoRepLifetimeParams AttachmentReplicationParams{ COND_Custom, REPNOTIFY_Always, /*bIsPushBased=*/true };
DOREPLIFETIME_WITH_PARAMS_FAST(AGrippableActor, AttachmentWeldReplication, AttachmentReplicationParams);
}
void AGrippableActor::PreReplication(IRepChangedPropertyTracker & ChangedPropertyTracker)
{
// Don't replicate if set to not do it
DOREPLIFETIME_ACTIVE_OVERRIDE_FAST(AGrippableActor, VRGripInterfaceSettings, bRepGripSettingsAndGameplayTags);
DOREPLIFETIME_ACTIVE_OVERRIDE_FAST(AGrippableActor, GameplayTags, bRepGripSettingsAndGameplayTags);
DOREPLIFETIME_ACTIVE_OVERRIDE_FAST(AGrippableActor, GripLogicScripts, bReplicateGripScripts);
//Super::PreReplication(ChangedPropertyTracker);
#if WITH_PUSH_MODEL
const AActor* const OldAttachParent = AttachmentWeldReplication.AttachParent;
const UActorComponent* const OldAttachComponent = AttachmentWeldReplication.AttachComponent;
#endif
// Attachment replication gets filled in by GatherCurrentMovement(), but in the case of a detached root we need to trigger remote detachment.
AttachmentWeldReplication.AttachParent = nullptr;
AttachmentWeldReplication.AttachComponent = nullptr;
GatherCurrentMovement();
DOREPLIFETIME_ACTIVE_OVERRIDE_FAST(AActor, ReplicatedMovement, IsReplicatingMovement());
// Don't need to replicate AttachmentReplication if the root component replicates, because it already handles it.
DOREPLIFETIME_ACTIVE_OVERRIDE_FAST(AGrippableActor, AttachmentWeldReplication, RootComponent && !RootComponent->GetIsReplicated());
// Don't need to replicate AttachmentReplication if the root component replicates, because it already handles it.
DOREPLIFETIME_ACTIVE_OVERRIDE_FAST(AActor, AttachmentReplication, false);// RootComponent && !RootComponent->GetIsReplicated());
#if WITH_PUSH_MODEL
if (UNLIKELY(OldAttachParent != AttachmentWeldReplication.AttachParent || OldAttachComponent != AttachmentWeldReplication.AttachComponent))
{
MARK_PROPERTY_DIRTY_FROM_NAME(AGrippableActor, AttachmentWeldReplication, this);
}
#endif
/*PRAGMA_DISABLE_DEPRECATION_WARNINGS
UBlueprintGeneratedClass* BPClass = Cast<UBlueprintGeneratedClass>(GetClass());
if (BPClass != nullptr)
{
BPClass->InstancePreReplication(this, ChangedPropertyTracker);
}
PRAGMA_ENABLE_DEPRECATION_WARNINGS*/
}
void AGrippableActor::GatherCurrentMovement()
{
if (IsReplicatingMovement() || (RootComponent && RootComponent->GetAttachParent()))
{
bool bWasAttachmentModified = false;
bool bWasRepMovementModified = false;
AActor* OldAttachParent = AttachmentWeldReplication.AttachParent;
USceneComponent* OldAttachComponent = AttachmentWeldReplication.AttachComponent;
AttachmentWeldReplication.AttachParent = nullptr;
AttachmentWeldReplication.AttachComponent = nullptr;
FRepMovement& RepMovement = GetReplicatedMovement_Mutable();
UPrimitiveComponent* RootPrimComp = Cast<UPrimitiveComponent>(GetRootComponent());
if (RootPrimComp && RootPrimComp->IsSimulatingPhysics())
{
#if UE_WITH_IRIS
const bool bPrevRepPhysics = GetReplicatedMovement_Mutable().bRepPhysics;
#endif // UE_WITH_IRIS
bool bFoundInCache = false;
UWorld* World = GetWorld();
int ServerFrame = 0;
if (FPhysScene_Chaos* Scene = static_cast<FPhysScene_Chaos*>(World->GetPhysicsScene()))
{
if (const FRigidBodyState* FoundState = Scene->GetStateFromReplicationCache(RootPrimComp, ServerFrame))
{
RepMovement.FillFrom(*FoundState, this, Scene->ReplicationCache.ServerFrame);
bFoundInCache = true;
}
}
if (!bFoundInCache)
{
// fallback to GT data
FRigidBodyState RBState;
RootPrimComp->GetRigidBodyState(RBState);
RepMovement.FillFrom(RBState, this, 0);
}
// Don't replicate movement if we're welded to another parent actor.
// Their replication will affect our position indirectly since we are attached.
RepMovement.bRepPhysics = !RootPrimComp->IsWelded();
if (!RepMovement.bRepPhysics)
{
if (RootComponent->GetAttachParent() != nullptr)
{
// Networking for attachments assumes the RootComponent of the AttachParent actor.
// If that's not the case, we can't update this, as the client wouldn't be able to resolve the Component and would detach as a result.
AttachmentWeldReplication.AttachParent = RootComponent->GetAttachParent()->GetAttachmentRootActor();
if (AttachmentWeldReplication.AttachParent != nullptr)
{
AttachmentWeldReplication.LocationOffset = RootComponent->GetRelativeLocation();
AttachmentWeldReplication.RotationOffset = RootComponent->GetRelativeRotation();
AttachmentWeldReplication.RelativeScale3D = RootComponent->GetRelativeScale3D();
AttachmentWeldReplication.AttachComponent = RootComponent->GetAttachParent();
AttachmentWeldReplication.AttachSocket = RootComponent->GetAttachSocketName();
AttachmentWeldReplication.bIsWelded = RootPrimComp ? RootPrimComp->IsWelded() : false;
// Technically, the values might have stayed the same, but we'll just assume they've changed.
bWasAttachmentModified = true;
}
}
}
// Technically, the values might have stayed the same, but we'll just assume they've changed.
bWasRepMovementModified = true;
#if UE_WITH_IRIS
// If RepPhysics has changed value then notify the ReplicationSystem
if (bPrevRepPhysics != GetReplicatedMovement_Mutable().bRepPhysics)
{
UpdateReplicatePhysicsCondition();
}
#endif // UE_WITH_IRIS
}
else if (RootComponent != nullptr)
{
// If we are attached, don't replicate absolute position, use AttachmentReplication instead.
if (RootComponent->GetAttachParent() != nullptr)
{
// Networking for attachments assumes the RootComponent of the AttachParent actor.
// If that's not the case, we can't update this, as the client wouldn't be able to resolve the Component and would detach as a result.
AttachmentWeldReplication.AttachParent = RootComponent->GetAttachParentActor();
if (AttachmentWeldReplication.AttachParent != nullptr)
{
AttachmentWeldReplication.LocationOffset = RootComponent->GetRelativeLocation();
AttachmentWeldReplication.RotationOffset = RootComponent->GetRelativeRotation();
AttachmentWeldReplication.RelativeScale3D = RootComponent->GetRelativeScale3D();
AttachmentWeldReplication.AttachComponent = RootComponent->GetAttachParent();
AttachmentWeldReplication.AttachSocket = RootComponent->GetAttachSocketName();
AttachmentWeldReplication.bIsWelded = RootPrimComp ? RootPrimComp->IsWelded() : false;
// Technically, the values might have stayed the same, but we'll just assume they've changed.
bWasAttachmentModified = true;
}
}
else
{
RepMovement.Location = FRepMovement::RebaseOntoZeroOrigin(RootComponent->GetComponentLocation(), this);
RepMovement.Rotation = RootComponent->GetComponentRotation();
RepMovement.LinearVelocity = GetVelocity();
RepMovement.AngularVelocity = FVector::ZeroVector;
// Technically, the values might have stayed the same, but we'll just assume they've changed.
bWasRepMovementModified = true;
}
bWasRepMovementModified = (bWasRepMovementModified || RepMovement.bRepPhysics);
RepMovement.bRepPhysics = false;
}
#if WITH_PUSH_MODEL
if (bWasRepMovementModified)
{
MARK_PROPERTY_DIRTY_FROM_NAME(AActor, ReplicatedMovement, this);
}
if (bWasAttachmentModified ||
OldAttachParent != AttachmentWeldReplication.AttachParent ||
OldAttachComponent != AttachmentWeldReplication.AttachComponent)
{
MARK_PROPERTY_DIRTY_FROM_NAME(AGrippableActor, AttachmentWeldReplication, this);
}
#endif
}
}
void AGrippableActor::OnRep_AttachmentReplication()
{
if (bAllowIgnoringAttachOnOwner && (ClientAuthReplicationData.bIsCurrentlyClientAuth || ShouldWeSkipAttachmentReplication()))
//if (bAllowIgnoringAttachOnOwner && ShouldWeSkipAttachmentReplication())
{
return;
}
if (AttachmentWeldReplication.AttachParent)
{
if (RootComponent)
{
USceneComponent* AttachParentComponent = (AttachmentWeldReplication.AttachComponent ? ToRawPtr(AttachmentWeldReplication.AttachComponent) : AttachmentWeldReplication.AttachParent->GetRootComponent());
if (AttachParentComponent)
{
RootComponent->SetRelativeLocation_Direct(AttachmentWeldReplication.LocationOffset);
RootComponent->SetRelativeRotation_Direct(AttachmentWeldReplication.RotationOffset);
RootComponent->SetRelativeScale3D_Direct(AttachmentWeldReplication.RelativeScale3D);
// If we're already attached to the correct Parent and Socket, then the update must be position only.
// AttachToComponent would early out in this case.
// Note, we ignore the special case for simulated bodies in AttachToComponent as AttachmentReplication shouldn't get updated
// if the body is simulated (see AActor::GatherMovement).
const bool bAlreadyAttached = (AttachParentComponent == RootComponent->GetAttachParent() && AttachmentWeldReplication.AttachSocket == RootComponent->GetAttachSocketName() && AttachParentComponent->GetAttachChildren().Contains(RootComponent));
if (bAlreadyAttached)
{
// Note, this doesn't match AttachToComponent, but we're assuming it's safe to skip physics (see comment above).
RootComponent->UpdateComponentToWorld(EUpdateTransformFlags::SkipPhysicsUpdate, ETeleportType::None);
}
else
{
FAttachmentTransformRules attachRules = FAttachmentTransformRules::KeepRelativeTransform;
attachRules.bWeldSimulatedBodies = AttachmentWeldReplication.bIsWelded;
RootComponent->AttachToComponent(AttachParentComponent, attachRules, AttachmentWeldReplication.AttachSocket);
}
}
}
}
else
{
DetachFromActor(FDetachmentTransformRules::KeepWorldTransform);
// Handle the case where an object was both detached and moved on the server in the same frame.
// Calling this extraneously does not hurt but will properly fire events if the movement state changed while attached.
// This is needed because client side movement is ignored when attached
if (IsReplicatingMovement())
{
OnRep_ReplicatedMovement();
}
}
}
bool AGrippableActor::ReplicateSubobjects(UActorChannel* Channel, class FOutBunch *Bunch, FReplicationFlags *RepFlags)
{
bool WroteSomething = Super::ReplicateSubobjects(Channel, Bunch, RepFlags);
if (bReplicateGripScripts)
{
for (UVRGripScriptBase* Script : GripLogicScripts)
{
if (Script && IsValid(Script))
{
WroteSomething |= Channel->ReplicateSubobject(Script, *Bunch, *RepFlags);
}
}
}
return WroteSomething;
}
//=============================================================================
AGrippableActor::~AGrippableActor()
{
}
void AGrippableActor::BeginPlay()
{
// Call the base class
Super::BeginPlay();
// Call all grip scripts begin play events so they can perform any needed logic
for (UVRGripScriptBase* Script : GripLogicScripts)
{
if (Script)
{
Script->BeginPlay(this);
}
}
}
void AGrippableActor::SetDenyGripping(bool bDenyGripping)
{
VRGripInterfaceSettings.bDenyGripping = bDenyGripping;
}
void AGrippableActor::SetGripPriority(int NewGripPriority)
{
VRGripInterfaceSettings.AdvancedGripSettings.GripPriority = NewGripPriority;
}
void AGrippableActor::TickGrip_Implementation(UGripMotionControllerComponent * GrippingController, const FBPActorGripInformation & GripInformation, float DeltaTime) {}
void AGrippableActor::OnGrip_Implementation(UGripMotionControllerComponent* GrippingController, const FBPActorGripInformation& GripInformation) {}
void AGrippableActor::OnGripRelease_Implementation(UGripMotionControllerComponent* ReleasingController, const FBPActorGripInformation& GripInformation, bool bWasSocketed) { }
void AGrippableActor::OnChildGrip_Implementation(UGripMotionControllerComponent * GrippingController, const FBPActorGripInformation & GripInformation) {}
void AGrippableActor::OnChildGripRelease_Implementation(UGripMotionControllerComponent * ReleasingController, const FBPActorGripInformation & GripInformation, bool bWasSocketed) {}
void AGrippableActor::OnSecondaryGrip_Implementation(UGripMotionControllerComponent* GripOwningController, USceneComponent* SecondaryGripComponent, const FBPActorGripInformation& GripInformation) { OnSecondaryGripAdded.Broadcast(GripOwningController, GripInformation); }
void AGrippableActor::OnSecondaryGripRelease_Implementation(UGripMotionControllerComponent* GripOwningController, USceneComponent* ReleasingSecondaryGripComponent, const FBPActorGripInformation& GripInformation) { OnSecondaryGripRemoved.Broadcast(GripOwningController, GripInformation); }
void AGrippableActor::OnUsed_Implementation() {}
void AGrippableActor::OnEndUsed_Implementation() {}
void AGrippableActor::OnSecondaryUsed_Implementation() {}
void AGrippableActor::OnEndSecondaryUsed_Implementation() {}
void AGrippableActor::OnInput_Implementation(FKey Key, EInputEvent KeyEvent) {}
bool AGrippableActor::RequestsSocketing_Implementation(USceneComponent *& ParentToSocketTo, FName & OptionalSocketName, FTransform_NetQuantize & RelativeTransform) { return false; }
bool AGrippableActor::DenyGripping_Implementation(UGripMotionControllerComponent * GripInitiator)
{
return VRGripInterfaceSettings.bDenyGripping;
}
EGripInterfaceTeleportBehavior AGrippableActor::TeleportBehavior_Implementation()
{
return VRGripInterfaceSettings.OnTeleportBehavior;
}
bool AGrippableActor::SimulateOnDrop_Implementation()
{
return VRGripInterfaceSettings.bSimulateOnDrop;
}
EGripCollisionType AGrippableActor::GetPrimaryGripType_Implementation(bool bIsSlot)
{
return bIsSlot ? VRGripInterfaceSettings.SlotDefaultGripType : VRGripInterfaceSettings.FreeDefaultGripType;
}
ESecondaryGripType AGrippableActor::SecondaryGripType_Implementation()
{
return VRGripInterfaceSettings.SecondaryGripType;
}
EGripMovementReplicationSettings AGrippableActor::GripMovementReplicationType_Implementation()
{
return VRGripInterfaceSettings.MovementReplicationType;
}
EGripLateUpdateSettings AGrippableActor::GripLateUpdateSetting_Implementation()
{
return VRGripInterfaceSettings.LateUpdateSetting;
}
void AGrippableActor::GetGripStiffnessAndDamping_Implementation(float &GripStiffnessOut, float &GripDampingOut)
{
GripStiffnessOut = VRGripInterfaceSettings.ConstraintStiffness;
GripDampingOut = VRGripInterfaceSettings.ConstraintDamping;
}
FBPAdvGripSettings AGrippableActor::AdvancedGripSettings_Implementation()
{
return VRGripInterfaceSettings.AdvancedGripSettings;
}
float AGrippableActor::GripBreakDistance_Implementation()
{
return VRGripInterfaceSettings.ConstraintBreakDistance;
}
void AGrippableActor::ClosestGripSlotInRange_Implementation(FVector WorldLocation, bool bSecondarySlot, bool & bHadSlotInRange, FTransform & SlotWorldTransform, FName & SlotName, UGripMotionControllerComponent * CallingController, FName OverridePrefix)
{
if (OverridePrefix.IsNone())
bSecondarySlot ? OverridePrefix = "VRGripS" : OverridePrefix = "VRGripP";
UVRExpansionFunctionLibrary::GetGripSlotInRangeByTypeName(OverridePrefix, this, WorldLocation, bSecondarySlot ? VRGripInterfaceSettings.SecondarySlotRange : VRGripInterfaceSettings.PrimarySlotRange, bHadSlotInRange, SlotWorldTransform, SlotName, CallingController);
}
bool AGrippableActor::AllowsMultipleGrips_Implementation()
{
return VRGripInterfaceSettings.bAllowMultipleGrips;
}
void AGrippableActor::IsHeld_Implementation(TArray<FBPGripPair> & HoldingControllers, bool & bIsHeld)
{
HoldingControllers = VRGripInterfaceSettings.HoldingControllers;
bIsHeld = VRGripInterfaceSettings.bIsHeld;
}
bool AGrippableActor::AddToClientReplicationBucket()
{
if (ShouldWeSkipAttachmentReplication(false))
{
// The subsystem automatically removes entries with the same function signature so its safe to just always add here
GetWorld()->GetSubsystem<UBucketUpdateSubsystem>()->AddObjectToBucket(ClientAuthReplicationData.UpdateRate, this, FName(TEXT("PollReplicationEvent")));
ClientAuthReplicationData.bIsCurrentlyClientAuth = true;
if (UWorld * World = GetWorld())
ClientAuthReplicationData.TimeAtInitialThrow = World->GetTimeSeconds();
return true;
}
return false;
}
bool AGrippableActor::RemoveFromClientReplicationBucket()
{
if (ClientAuthReplicationData.bIsCurrentlyClientAuth)
{
GetWorld()->GetSubsystem<UBucketUpdateSubsystem>()->RemoveObjectFromBucketByFunctionName(this, FName(TEXT("PollReplicationEvent")));
CeaseReplicationBlocking();
return true;
}
return false;
}
void AGrippableActor::Native_NotifyThrowGripDelegates(UGripMotionControllerComponent* Controller, bool bGripped, const FBPActorGripInformation& GripInformation, bool bWasSocketed)
{
if (bGripped)
{
OnGripped.Broadcast(Controller, GripInformation);
}
else
{
OnDropped.Broadcast(Controller, GripInformation, bWasSocketed);
}
}
void AGrippableActor::SetHeld_Implementation(UGripMotionControllerComponent * HoldingController, uint8 GripID, bool bIsHeld)
{
if (bIsHeld)
{
VRGripInterfaceSettings.HoldingControllers.AddUnique(FBPGripPair(HoldingController, GripID));
RemoveFromClientReplicationBucket();
VRGripInterfaceSettings.bWasHeld = true;
VRGripInterfaceSettings.bIsHeld = VRGripInterfaceSettings.HoldingControllers.Num() > 0;
}
else
{
VRGripInterfaceSettings.HoldingControllers.Remove(FBPGripPair(HoldingController, GripID));
VRGripInterfaceSettings.bIsHeld = VRGripInterfaceSettings.HoldingControllers.Num() > 0;
if (ClientAuthReplicationData.bUseClientAuthThrowing && !VRGripInterfaceSettings.bIsHeld)
{
bool bWasLocallyOwned = HoldingController ? HoldingController->IsLocallyControlled() : false;
if (bWasLocallyOwned && ShouldWeSkipAttachmentReplication(false))
{
if (UPrimitiveComponent* PrimComp = Cast<UPrimitiveComponent>(GetRootComponent()))
{
if (PrimComp->IsSimulatingPhysics())
{
AddToClientReplicationBucket();
}
}
}
}
}
}
bool AGrippableActor::GetGripScripts_Implementation(TArray<UVRGripScriptBase*> & ArrayReference)
{
ArrayReference = GripLogicScripts;
return GripLogicScripts.Num() > 0;
}
/*FBPInteractionSettings AGrippableActor::GetInteractionSettings_Implementation()
{
return VRGripInterfaceSettings.InteractionSettings;
}*/
bool AGrippableActor::PollReplicationEvent()
{
if (!ClientAuthReplicationData.bIsCurrentlyClientAuth || !this->HasLocalNetOwner() || VRGripInterfaceSettings.bIsHeld)
return false; // Tell the bucket subsystem to remove us from consideration
UWorld* OurWorld = GetWorld();
if (!OurWorld)
return false; // Tell the bucket subsystem to remove us from consideration
bool bRemoveBlocking = false;
if ((OurWorld->GetTimeSeconds() - ClientAuthReplicationData.TimeAtInitialThrow) > 10.0f)
{
// Lets time out sending, its been 10 seconds since we threw the object and its likely that it is conflicting with some server
// Authed movement that is forcing it to keep momentum.
//return false; // Tell the bucket subsystem to remove us from consideration
bRemoveBlocking = true;
}
// Store current transform for resting check
FTransform CurTransform = this->GetActorTransform();
if (!bRemoveBlocking)
{
if (!CurTransform.GetRotation().Equals(ClientAuthReplicationData.LastActorTransform.GetRotation()) || !CurTransform.GetLocation().Equals(ClientAuthReplicationData.LastActorTransform.GetLocation()))
{
ClientAuthReplicationData.LastActorTransform = CurTransform;
if (UPrimitiveComponent * PrimComp = Cast<UPrimitiveComponent>(RootComponent))
{
// Need to clamp to a max time since start, to handle cases with conflicting collisions
if (PrimComp->IsSimulatingPhysics() && ShouldWeSkipAttachmentReplication(false))
{
FRepMovementVR ClientAuthMovementRep;
if (ClientAuthMovementRep.GatherActorsMovement(this))
{
Server_GetClientAuthReplication(ClientAuthMovementRep);
if (PrimComp->RigidBodyIsAwake())
{
return true;
}
}
}
}
else
{
bRemoveBlocking = true;
//return false; // Tell the bucket subsystem to remove us from consideration
}
}
//else
// {
// Difference is too small, lets end sending location
//ClientAuthReplicationData.LastActorTransform = FTransform::Identity;
// }
}
bool TimedBlockingRelease = false;
AActor* TopOwner = GetOwner();
if (TopOwner != nullptr)
{
AActor* tempOwner = TopOwner->GetOwner();
// I have an owner so search that for the top owner
while (tempOwner)
{
TopOwner = tempOwner;
tempOwner = TopOwner->GetOwner();
}
if (APlayerController * PlayerController = Cast<APlayerController>(TopOwner))
{
if (APlayerState * PlayerState = PlayerController->PlayerState)
{
if (ClientAuthReplicationData.ResetReplicationHandle.IsValid())
{
OurWorld->GetTimerManager().ClearTimer(ClientAuthReplicationData.ResetReplicationHandle);
}
// Lets clamp the ping to a min / max value just in case
float clampedPing = FMath::Clamp(PlayerState->ExactPing * 0.001f, 0.0f, 1000.0f);
OurWorld->GetTimerManager().SetTimer(ClientAuthReplicationData.ResetReplicationHandle, this, &AGrippableActor::CeaseReplicationBlocking, clampedPing, false);
TimedBlockingRelease = true;
}
}
}
if (!TimedBlockingRelease)
{
CeaseReplicationBlocking();
}
// Tell server to kill us
Server_EndClientAuthReplication();
return false; // Tell the bucket subsystem to remove us from consideration
}
void AGrippableActor::CeaseReplicationBlocking()
{
if (ClientAuthReplicationData.bIsCurrentlyClientAuth)
ClientAuthReplicationData.bIsCurrentlyClientAuth = false;
ClientAuthReplicationData.LastActorTransform = FTransform::Identity;
if (ClientAuthReplicationData.ResetReplicationHandle.IsValid())
{
if (UWorld * OurWorld = GetWorld())
{
OurWorld->GetTimerManager().ClearTimer(ClientAuthReplicationData.ResetReplicationHandle);
}
}
}
void AGrippableActor::EndPlay(const EEndPlayReason::Type EndPlayReason)
{
RemoveFromClientReplicationBucket();
// Call all grip scripts begin play events so they can perform any needed logic
for (UVRGripScriptBase* Script : GripLogicScripts)
{
if (Script)
{
Script->EndPlay(EndPlayReason);
}
}
Super::EndPlay(EndPlayReason);
}
bool AGrippableActor::Server_EndClientAuthReplication_Validate()
{
return true;
}
void AGrippableActor::Server_EndClientAuthReplication_Implementation()
{
if (UWorld* World = GetWorld())
{
if (FPhysScene* PhysScene = World->GetPhysicsScene())
{
if (IPhysicsReplication* PhysicsReplication = PhysScene->GetPhysicsReplication())
{
if (UPrimitiveComponent* RootPrim = Cast<UPrimitiveComponent>(GetRootComponent()))
{
PhysicsReplication->RemoveReplicatedTarget(RootPrim);
}
}
}
}
}
bool AGrippableActor::Server_GetClientAuthReplication_Validate(const FRepMovementVR & newMovement)
{
return true;
}
void AGrippableActor::Server_GetClientAuthReplication_Implementation(const FRepMovementVR & newMovement)
{
if (!VRGripInterfaceSettings.bIsHeld)
{
if (!newMovement.Location.ContainsNaN() && !newMovement.Rotation.ContainsNaN())
{
FRepMovement& MovementRep = GetReplicatedMovement_Mutable();
newMovement.CopyTo(MovementRep);
OnRep_ReplicatedMovement();
}
}
}
void AGrippableActor::OnRep_ReplicateMovement()
{
if (bAllowIgnoringAttachOnOwner && (ClientAuthReplicationData.bIsCurrentlyClientAuth || ShouldWeSkipAttachmentReplication()))
//if (bAllowIgnoringAttachOnOwner && (ClientAuthReplicationData.bIsCurrentlyClientAuth || ShouldWeSkipAttachmentReplication()))
{
return;
}
if (RootComponent)
{
const FRepAttachment ReplicationAttachment = GetAttachmentReplication();
if (!ReplicationAttachment.AttachParent)
{
const FRepMovement& RepMove = GetReplicatedMovement();
// This "fix" corrects the simulation state not replicating over correctly
// If you turn off movement replication, simulate an object, turn movement replication back on and un-simulate, it never knows the difference
// This change ensures that it is checking against the current state
if (RootComponent->IsSimulatingPhysics() != RepMove.bRepPhysics)//SavedbRepPhysics != ReplicatedMovement.bRepPhysics)
{
// Turn on/off physics sim to match server.
SyncReplicatedPhysicsSimulation();
// It doesn't really hurt to run it here, the super can call it again but it will fail out as they already match
}
}
}
Super::OnRep_ReplicateMovement();
}
void AGrippableActor::OnRep_ReplicatedMovement()
{
if (bAllowIgnoringAttachOnOwner && (ClientAuthReplicationData.bIsCurrentlyClientAuth || ShouldWeSkipAttachmentReplication()))
//if (ClientAuthReplicationData.bIsCurrentlyClientAuth && ShouldWeSkipAttachmentReplication(false))
{
return;
}
Super::OnRep_ReplicatedMovement();
}
void AGrippableActor::PostNetReceivePhysicState()
{
if (bAllowIgnoringAttachOnOwner && (ClientAuthReplicationData.bIsCurrentlyClientAuth || ShouldWeSkipAttachmentReplication()))
//if ((ClientAuthReplicationData.bIsCurrentlyClientAuth || VRGripInterfaceSettings.bIsHeld) && bAllowIgnoringAttachOnOwner && ShouldWeSkipAttachmentReplication(false))
{
return;
}
Super::PostNetReceivePhysicState();
}
// This isn't called very many places but it does come up
void AGrippableActor::MarkComponentsAsGarbage(bool bModify)
{
Super::MarkComponentsAsGarbage(bModify);
for (int32 i = 0; i < GripLogicScripts.Num(); ++i)
{
if (UObject *SubObject = GripLogicScripts[i])
{
SubObject->MarkAsGarbage();
}
}
GripLogicScripts.Empty();
}
/** Called right before being marked for destruction due to network replication */
// Clean up our objects so that they aren't sitting around for GC
void AGrippableActor::PreDestroyFromReplication()
{
Super::PreDestroyFromReplication();
// Destroy any sub-objects we created
for (int32 i = 0; i < GripLogicScripts.Num(); ++i)
{
if (UObject *SubObject = GripLogicScripts[i])
{
OnSubobjectDestroyFromReplication(SubObject); //-V595
SubObject->PreDestroyFromReplication();
SubObject->MarkAsGarbage();
}
}
for (UActorComponent * ActorComp : GetComponents())
{
// Pending kill components should have already had this called as they were network spawned and are being killed
if (ActorComp && IsValid(ActorComp) && ActorComp->GetClass()->ImplementsInterface(UVRGripInterface::StaticClass()))
ActorComp->PreDestroyFromReplication();
}
GripLogicScripts.Empty();
}
// On Destroy clean up our objects
void AGrippableActor::BeginDestroy()
{
Super::BeginDestroy();
for (int32 i = 0; i < GripLogicScripts.Num(); i++)
{
if (UObject *SubObject = GripLogicScripts[i])
{
SubObject->MarkAsGarbage();
}
}
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);
}
}
}
}

View file

@ -0,0 +1,323 @@
// Copyright 1998-2016 Epic Games, Inc. All Rights Reserved.
#include "Grippables/GrippableBoxComponent.h"
#include UE_INLINE_GENERATED_CPP_BY_NAME(GrippableBoxComponent)
#include "GripMotionControllerComponent.h"
#include "VRExpansionFunctionLibrary.h"
#include "GripScripts/VRGripScriptBase.h"
#include "Net/UnrealNetwork.h"
//=============================================================================
UGrippableBoxComponent::~UGrippableBoxComponent()
{
}
//=============================================================================
UGrippableBoxComponent::UGrippableBoxComponent(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{
VRGripInterfaceSettings.bDenyGripping = false;
VRGripInterfaceSettings.OnTeleportBehavior = EGripInterfaceTeleportBehavior::DropOnTeleport;
VRGripInterfaceSettings.bSimulateOnDrop = true;
VRGripInterfaceSettings.SlotDefaultGripType = EGripCollisionType::ManipulationGrip;
VRGripInterfaceSettings.FreeDefaultGripType = EGripCollisionType::ManipulationGrip;
VRGripInterfaceSettings.SecondaryGripType = ESecondaryGripType::SG_None;
VRGripInterfaceSettings.MovementReplicationType = EGripMovementReplicationSettings::ForceClientSideMovement;
VRGripInterfaceSettings.LateUpdateSetting = EGripLateUpdateSettings::LateUpdatesAlwaysOff;
VRGripInterfaceSettings.ConstraintStiffness = 1500.0f;
VRGripInterfaceSettings.ConstraintDamping = 200.0f;
VRGripInterfaceSettings.ConstraintBreakDistance = 100.0f;
VRGripInterfaceSettings.SecondarySlotRange = 20.0f;
VRGripInterfaceSettings.PrimarySlotRange = 20.0f;
VRGripInterfaceSettings.bIsHeld = false;
bReplicateMovement = false;
//this->bReplicates = true;
bRepGripSettingsAndGameplayTags = true;
bReplicateGripScripts = false;
// #TODO we can register them maybe in the future
// Don't use the replicated list, use our custom replication instead
bReplicateUsingRegisteredSubObjectList = false;
}
void UGrippableBoxComponent::GetLifetimeReplicatedProps(TArray< class FLifetimeProperty > & OutLifetimeProps) const
{
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
DOREPLIFETIME_CONDITION(UGrippableBoxComponent, GripLogicScripts, COND_Custom);
DOREPLIFETIME(UGrippableBoxComponent, bReplicateGripScripts);
DOREPLIFETIME(UGrippableBoxComponent, bRepGripSettingsAndGameplayTags);
DOREPLIFETIME(UGrippableBoxComponent, bReplicateMovement);
DOREPLIFETIME_CONDITION(UGrippableBoxComponent, VRGripInterfaceSettings, COND_Custom);
DOREPLIFETIME_CONDITION(UGrippableBoxComponent, GameplayTags, COND_Custom);
}
void UGrippableBoxComponent::PreReplication(IRepChangedPropertyTracker & ChangedPropertyTracker)
{
Super::PreReplication(ChangedPropertyTracker);
// Don't replicate if set to not do it
DOREPLIFETIME_ACTIVE_OVERRIDE_FAST(UGrippableBoxComponent, VRGripInterfaceSettings, bRepGripSettingsAndGameplayTags);
DOREPLIFETIME_ACTIVE_OVERRIDE_FAST(UGrippableBoxComponent, GameplayTags, bRepGripSettingsAndGameplayTags);
DOREPLIFETIME_ACTIVE_OVERRIDE_FAST(UGrippableBoxComponent, GripLogicScripts, bReplicateGripScripts);
DOREPLIFETIME_ACTIVE_OVERRIDE_FAST(USceneComponent, RelativeLocation, bReplicateMovement);
DOREPLIFETIME_ACTIVE_OVERRIDE_FAST(USceneComponent, RelativeRotation, bReplicateMovement);
DOREPLIFETIME_ACTIVE_OVERRIDE_FAST(USceneComponent, RelativeScale3D, bReplicateMovement);
}
bool UGrippableBoxComponent::ReplicateSubobjects(UActorChannel* Channel, class FOutBunch *Bunch, FReplicationFlags *RepFlags)
{
bool WroteSomething = Super::ReplicateSubobjects(Channel, Bunch, RepFlags);
if (bReplicateGripScripts)
{
for (UVRGripScriptBase* Script : GripLogicScripts)
{
if (Script && IsValid(Script))
{
WroteSomething |= Channel->ReplicateSubobject(Script, *Bunch, *RepFlags);
}
}
}
return WroteSomething;
}
void UGrippableBoxComponent::BeginPlay()
{
// Call the base class
Super::BeginPlay();
// Call all grip scripts begin play events so they can perform any needed logic
for (UVRGripScriptBase* Script : GripLogicScripts)
{
if (Script)
{
Script->BeginPlay(this);
}
}
bOriginalReplicatesMovement = bReplicateMovement;
}
void UGrippableBoxComponent::EndPlay(const EEndPlayReason::Type EndPlayReason)
{
// Call the base class
Super::EndPlay(EndPlayReason);
// Call all grip scripts begin play events so they can perform any needed logic
for (UVRGripScriptBase* Script : GripLogicScripts)
{
if (Script)
{
Script->EndPlay(EndPlayReason);
}
}
}
void UGrippableBoxComponent::SetDenyGripping(bool bDenyGripping)
{
VRGripInterfaceSettings.bDenyGripping = bDenyGripping;
}
void UGrippableBoxComponent::SetGripPriority(int NewGripPriority)
{
VRGripInterfaceSettings.AdvancedGripSettings.GripPriority = NewGripPriority;
}
void UGrippableBoxComponent::TickGrip_Implementation(UGripMotionControllerComponent * GrippingController, const FBPActorGripInformation & GripInformation, float DeltaTime) {}
void UGrippableBoxComponent::OnGrip_Implementation(UGripMotionControllerComponent* GrippingController, const FBPActorGripInformation& GripInformation) { }
void UGrippableBoxComponent::OnGripRelease_Implementation(UGripMotionControllerComponent* ReleasingController, const FBPActorGripInformation& GripInformation, bool bWasSocketed) { }
void UGrippableBoxComponent::OnChildGrip_Implementation(UGripMotionControllerComponent* GrippingController, const FBPActorGripInformation& GripInformation) {}
void UGrippableBoxComponent::OnChildGripRelease_Implementation(UGripMotionControllerComponent* ReleasingController, const FBPActorGripInformation& GripInformation, bool bWasSocketed) {}
void UGrippableBoxComponent::OnSecondaryGrip_Implementation(UGripMotionControllerComponent* GripOwningController, USceneComponent* SecondaryGripComponent, const FBPActorGripInformation& GripInformation) { OnSecondaryGripAdded.Broadcast(GripOwningController, GripInformation); }
void UGrippableBoxComponent::OnSecondaryGripRelease_Implementation(UGripMotionControllerComponent* GripOwningController, USceneComponent* ReleasingSecondaryGripComponent, const FBPActorGripInformation& GripInformation) { OnSecondaryGripRemoved.Broadcast(GripOwningController, GripInformation); }
void UGrippableBoxComponent::OnUsed_Implementation() {}
void UGrippableBoxComponent::OnEndUsed_Implementation() {}
void UGrippableBoxComponent::OnSecondaryUsed_Implementation() {}
void UGrippableBoxComponent::OnEndSecondaryUsed_Implementation() {}
void UGrippableBoxComponent::OnInput_Implementation(FKey Key, EInputEvent KeyEvent) {}
bool UGrippableBoxComponent::RequestsSocketing_Implementation(USceneComponent *& ParentToSocketTo, FName & OptionalSocketName, FTransform_NetQuantize & RelativeTransform) { return false; }
bool UGrippableBoxComponent::DenyGripping_Implementation(UGripMotionControllerComponent * GripInitiator)
{
return VRGripInterfaceSettings.bDenyGripping;
}
EGripInterfaceTeleportBehavior UGrippableBoxComponent::TeleportBehavior_Implementation()
{
return VRGripInterfaceSettings.OnTeleportBehavior;
}
bool UGrippableBoxComponent::SimulateOnDrop_Implementation()
{
return VRGripInterfaceSettings.bSimulateOnDrop;
}
EGripCollisionType UGrippableBoxComponent::GetPrimaryGripType_Implementation(bool bIsSlot)
{
return bIsSlot ? VRGripInterfaceSettings.SlotDefaultGripType : VRGripInterfaceSettings.FreeDefaultGripType;
}
ESecondaryGripType UGrippableBoxComponent::SecondaryGripType_Implementation()
{
return VRGripInterfaceSettings.SecondaryGripType;
}
EGripMovementReplicationSettings UGrippableBoxComponent::GripMovementReplicationType_Implementation()
{
return VRGripInterfaceSettings.MovementReplicationType;
}
EGripLateUpdateSettings UGrippableBoxComponent::GripLateUpdateSetting_Implementation()
{
return VRGripInterfaceSettings.LateUpdateSetting;
}
void UGrippableBoxComponent::GetGripStiffnessAndDamping_Implementation(float &GripStiffnessOut, float &GripDampingOut)
{
GripStiffnessOut = VRGripInterfaceSettings.ConstraintStiffness;
GripDampingOut = VRGripInterfaceSettings.ConstraintDamping;
}
FBPAdvGripSettings UGrippableBoxComponent::AdvancedGripSettings_Implementation()
{
return VRGripInterfaceSettings.AdvancedGripSettings;
}
float UGrippableBoxComponent::GripBreakDistance_Implementation()
{
return VRGripInterfaceSettings.ConstraintBreakDistance;
}
void UGrippableBoxComponent::ClosestGripSlotInRange_Implementation(FVector WorldLocation, bool bSecondarySlot, bool & bHadSlotInRange, FTransform & SlotWorldTransform, FName & SlotName, UGripMotionControllerComponent * CallingController, FName OverridePrefix)
{
if (OverridePrefix.IsNone())
bSecondarySlot ? OverridePrefix = "VRGripS" : OverridePrefix = "VRGripP";
UVRExpansionFunctionLibrary::GetGripSlotInRangeByTypeName_Component(OverridePrefix, this, WorldLocation, bSecondarySlot ? VRGripInterfaceSettings.SecondarySlotRange : VRGripInterfaceSettings.PrimarySlotRange, bHadSlotInRange, SlotWorldTransform, SlotName, CallingController);
}
bool UGrippableBoxComponent::AllowsMultipleGrips_Implementation()
{
return VRGripInterfaceSettings.bAllowMultipleGrips;
}
void UGrippableBoxComponent::IsHeld_Implementation(TArray<FBPGripPair> & HoldingControllers, bool & bIsHeld)
{
HoldingControllers = VRGripInterfaceSettings.HoldingControllers;
bIsHeld = VRGripInterfaceSettings.bIsHeld;
}
void UGrippableBoxComponent::Native_NotifyThrowGripDelegates(UGripMotionControllerComponent* Controller, bool bGripped, const FBPActorGripInformation& GripInformation, bool bWasSocketed)
{
if (bGripped)
{
OnGripped.Broadcast(Controller, GripInformation);
}
else
{
OnDropped.Broadcast(Controller, GripInformation, bWasSocketed);
}
}
void UGrippableBoxComponent::SetHeld_Implementation(UGripMotionControllerComponent * HoldingController, uint8 GripID, bool bIsHeld)
{
if (bIsHeld)
{
if (VRGripInterfaceSettings.MovementReplicationType != EGripMovementReplicationSettings::ForceServerSideMovement)
{
if (!VRGripInterfaceSettings.bIsHeld)
bOriginalReplicatesMovement = bReplicateMovement;
bReplicateMovement = false;
}
VRGripInterfaceSettings.bWasHeld = true;
VRGripInterfaceSettings.HoldingControllers.AddUnique(FBPGripPair(HoldingController, GripID));
}
else
{
if (VRGripInterfaceSettings.MovementReplicationType != EGripMovementReplicationSettings::ForceServerSideMovement)
{
bReplicateMovement = bOriginalReplicatesMovement;
}
VRGripInterfaceSettings.HoldingControllers.Remove(FBPGripPair(HoldingController, GripID));
}
VRGripInterfaceSettings.bIsHeld = VRGripInterfaceSettings.HoldingControllers.Num() > 0;
}
/*FBPInteractionSettings UGrippableBoxComponent::GetInteractionSettings_Implementation()
{
return VRGripInterfaceSettings.InteractionSettings;
}*/
bool UGrippableBoxComponent::GetGripScripts_Implementation(TArray<UVRGripScriptBase*> & ArrayReference)
{
ArrayReference = GripLogicScripts;
return GripLogicScripts.Num() > 0;
}
void UGrippableBoxComponent::PreDestroyFromReplication()
{
Super::PreDestroyFromReplication();
// Destroy any sub-objects we created
for (int32 i = 0; i < GripLogicScripts.Num(); ++i)
{
if (UObject *SubObject = GripLogicScripts[i])
{
SubObject->PreDestroyFromReplication();
SubObject->MarkAsGarbage();
}
}
GripLogicScripts.Empty();
}
void UGrippableBoxComponent::GetSubobjectsWithStableNamesForNetworking(TArray<UObject*> &ObjList)
{
if (bReplicateGripScripts)
{
for (int32 i = 0; i < GripLogicScripts.Num(); ++i)
{
if (UObject* SubObject = GripLogicScripts[i])
{
ObjList.Add(SubObject);
}
}
}
}
// This one is for components to clean up
void UGrippableBoxComponent::OnComponentDestroyed(bool bDestroyingHierarchy)
{
// Call the super at the end, after we've done what we needed to do
Super::OnComponentDestroyed(bDestroyingHierarchy);
// Don't set these in editor preview window and the like, it causes saving issues
if (UWorld * World = GetWorld())
{
EWorldType::Type WorldType = World->WorldType;
if (WorldType == EWorldType::Editor || WorldType == EWorldType::EditorPreview)
{
return;
}
}
for (int32 i = 0; i < GripLogicScripts.Num(); i++)
{
if (UObject *SubObject = GripLogicScripts[i])
{
SubObject->MarkAsGarbage();
}
}
GripLogicScripts.Empty();
}

View file

@ -0,0 +1,323 @@
// Copyright 1998-2016 Epic Games, Inc. All Rights Reserved.
#include "Grippables/GrippableCapsuleComponent.h"
#include UE_INLINE_GENERATED_CPP_BY_NAME(GrippableCapsuleComponent)
#include "GripMotionControllerComponent.h"
#include "VRExpansionFunctionLibrary.h"
#include "GripScripts/VRGripScriptBase.h"
#include "Net/UnrealNetwork.h"
//=============================================================================
UGrippableCapsuleComponent::UGrippableCapsuleComponent(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{
VRGripInterfaceSettings.bDenyGripping = false;
VRGripInterfaceSettings.OnTeleportBehavior = EGripInterfaceTeleportBehavior::DropOnTeleport;
VRGripInterfaceSettings.bSimulateOnDrop = true;
VRGripInterfaceSettings.SlotDefaultGripType = EGripCollisionType::ManipulationGrip;
VRGripInterfaceSettings.FreeDefaultGripType = EGripCollisionType::ManipulationGrip;
VRGripInterfaceSettings.SecondaryGripType = ESecondaryGripType::SG_None;
VRGripInterfaceSettings.MovementReplicationType = EGripMovementReplicationSettings::ForceClientSideMovement;
VRGripInterfaceSettings.LateUpdateSetting = EGripLateUpdateSettings::LateUpdatesAlwaysOff;
VRGripInterfaceSettings.ConstraintStiffness = 1500.0f;
VRGripInterfaceSettings.ConstraintDamping = 200.0f;
VRGripInterfaceSettings.ConstraintBreakDistance = 100.0f;
VRGripInterfaceSettings.SecondarySlotRange = 20.0f;
VRGripInterfaceSettings.PrimarySlotRange = 20.0f;
VRGripInterfaceSettings.bIsHeld = false;
bReplicateMovement = false;
//this->bReplicates = true;
bRepGripSettingsAndGameplayTags = true;
bReplicateGripScripts = false;
// #TODO we can register them maybe in the future
// Don't use the replicated list, use our custom replication instead
bReplicateUsingRegisteredSubObjectList = false;
}
void UGrippableCapsuleComponent::GetLifetimeReplicatedProps(TArray< class FLifetimeProperty > & OutLifetimeProps) const
{
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
DOREPLIFETIME_CONDITION(UGrippableCapsuleComponent, GripLogicScripts, COND_Custom);
DOREPLIFETIME(UGrippableCapsuleComponent, bReplicateGripScripts);
DOREPLIFETIME(UGrippableCapsuleComponent, bRepGripSettingsAndGameplayTags);
DOREPLIFETIME(UGrippableCapsuleComponent, bReplicateMovement);
DOREPLIFETIME_CONDITION(UGrippableCapsuleComponent, VRGripInterfaceSettings, COND_Custom);
DOREPLIFETIME_CONDITION(UGrippableCapsuleComponent, GameplayTags, COND_Custom);
}
void UGrippableCapsuleComponent::PreReplication(IRepChangedPropertyTracker & ChangedPropertyTracker)
{
Super::PreReplication(ChangedPropertyTracker);
// Don't replicate if set to not do it
DOREPLIFETIME_ACTIVE_OVERRIDE_FAST(UGrippableCapsuleComponent, VRGripInterfaceSettings, bRepGripSettingsAndGameplayTags);
DOREPLIFETIME_ACTIVE_OVERRIDE_FAST(UGrippableCapsuleComponent, GameplayTags, bRepGripSettingsAndGameplayTags);
DOREPLIFETIME_ACTIVE_OVERRIDE_FAST(UGrippableCapsuleComponent, GripLogicScripts, bReplicateGripScripts);
DOREPLIFETIME_ACTIVE_OVERRIDE_FAST(USceneComponent, RelativeLocation, bReplicateMovement);
DOREPLIFETIME_ACTIVE_OVERRIDE_FAST(USceneComponent, RelativeRotation, bReplicateMovement);
DOREPLIFETIME_ACTIVE_OVERRIDE_FAST(USceneComponent, RelativeScale3D, bReplicateMovement);
}
bool UGrippableCapsuleComponent::ReplicateSubobjects(UActorChannel* Channel, class FOutBunch *Bunch, FReplicationFlags *RepFlags)
{
bool WroteSomething = Super::ReplicateSubobjects(Channel, Bunch, RepFlags);
if (bReplicateGripScripts)
{
for (UVRGripScriptBase* Script : GripLogicScripts)
{
if (Script && IsValid(Script))
{
WroteSomething |= Channel->ReplicateSubobject(Script, *Bunch, *RepFlags);
}
}
}
return WroteSomething;
}
//=============================================================================
UGrippableCapsuleComponent::~UGrippableCapsuleComponent()
{
}
void UGrippableCapsuleComponent::BeginPlay()
{
// Call the base class
Super::BeginPlay();
// Call all grip scripts begin play events so they can perform any needed logic
for (UVRGripScriptBase* Script : GripLogicScripts)
{
if (Script)
{
Script->BeginPlay(this);
}
}
bOriginalReplicatesMovement = bReplicateMovement;
}
void UGrippableCapsuleComponent::EndPlay(const EEndPlayReason::Type EndPlayReason)
{
// Call the base class
Super::EndPlay(EndPlayReason);
// Call all grip scripts begin play events so they can perform any needed logic
for (UVRGripScriptBase* Script : GripLogicScripts)
{
if (Script)
{
Script->EndPlay(EndPlayReason);
}
}
}
void UGrippableCapsuleComponent::SetDenyGripping(bool bDenyGripping)
{
VRGripInterfaceSettings.bDenyGripping = bDenyGripping;
}
void UGrippableCapsuleComponent::SetGripPriority(int NewGripPriority)
{
VRGripInterfaceSettings.AdvancedGripSettings.GripPriority = NewGripPriority;
}
void UGrippableCapsuleComponent::TickGrip_Implementation(UGripMotionControllerComponent * GrippingController, const FBPActorGripInformation & GripInformation, float DeltaTime) {}
void UGrippableCapsuleComponent::OnGrip_Implementation(UGripMotionControllerComponent* GrippingController, const FBPActorGripInformation& GripInformation) {}
void UGrippableCapsuleComponent::OnGripRelease_Implementation(UGripMotionControllerComponent* ReleasingController, const FBPActorGripInformation& GripInformation, bool bWasSocketed) { }
void UGrippableCapsuleComponent::OnChildGrip_Implementation(UGripMotionControllerComponent* GrippingController, const FBPActorGripInformation& GripInformation) {}
void UGrippableCapsuleComponent::OnChildGripRelease_Implementation(UGripMotionControllerComponent* ReleasingController, const FBPActorGripInformation& GripInformation, bool bWasSocketed) {}
void UGrippableCapsuleComponent::OnSecondaryGrip_Implementation(UGripMotionControllerComponent* GripOwningController, USceneComponent* SecondaryGripComponent, const FBPActorGripInformation& GripInformation) { OnSecondaryGripAdded.Broadcast(GripOwningController, GripInformation); }
void UGrippableCapsuleComponent::OnSecondaryGripRelease_Implementation(UGripMotionControllerComponent* GripOwningController, USceneComponent* ReleasingSecondaryGripComponent, const FBPActorGripInformation& GripInformation) { OnSecondaryGripRemoved.Broadcast(GripOwningController, GripInformation); }
void UGrippableCapsuleComponent::OnUsed_Implementation() {}
void UGrippableCapsuleComponent::OnEndUsed_Implementation() {}
void UGrippableCapsuleComponent::OnSecondaryUsed_Implementation() {}
void UGrippableCapsuleComponent::OnEndSecondaryUsed_Implementation() {}
void UGrippableCapsuleComponent::OnInput_Implementation(FKey Key, EInputEvent KeyEvent) {}
bool UGrippableCapsuleComponent::RequestsSocketing_Implementation(USceneComponent *& ParentToSocketTo, FName & OptionalSocketName, FTransform_NetQuantize & RelativeTransform) { return false; }
bool UGrippableCapsuleComponent::DenyGripping_Implementation(UGripMotionControllerComponent * GripInitiator)
{
return VRGripInterfaceSettings.bDenyGripping;
}
EGripInterfaceTeleportBehavior UGrippableCapsuleComponent::TeleportBehavior_Implementation()
{
return VRGripInterfaceSettings.OnTeleportBehavior;
}
bool UGrippableCapsuleComponent::SimulateOnDrop_Implementation()
{
return VRGripInterfaceSettings.bSimulateOnDrop;
}
EGripCollisionType UGrippableCapsuleComponent::GetPrimaryGripType_Implementation(bool bIsSlot)
{
return bIsSlot ? VRGripInterfaceSettings.SlotDefaultGripType : VRGripInterfaceSettings.FreeDefaultGripType;
}
ESecondaryGripType UGrippableCapsuleComponent::SecondaryGripType_Implementation()
{
return VRGripInterfaceSettings.SecondaryGripType;
}
EGripMovementReplicationSettings UGrippableCapsuleComponent::GripMovementReplicationType_Implementation()
{
return VRGripInterfaceSettings.MovementReplicationType;
}
EGripLateUpdateSettings UGrippableCapsuleComponent::GripLateUpdateSetting_Implementation()
{
return VRGripInterfaceSettings.LateUpdateSetting;
}
void UGrippableCapsuleComponent::GetGripStiffnessAndDamping_Implementation(float &GripStiffnessOut, float &GripDampingOut)
{
GripStiffnessOut = VRGripInterfaceSettings.ConstraintStiffness;
GripDampingOut = VRGripInterfaceSettings.ConstraintDamping;
}
FBPAdvGripSettings UGrippableCapsuleComponent::AdvancedGripSettings_Implementation()
{
return VRGripInterfaceSettings.AdvancedGripSettings;
}
float UGrippableCapsuleComponent::GripBreakDistance_Implementation()
{
return VRGripInterfaceSettings.ConstraintBreakDistance;
}
void UGrippableCapsuleComponent::ClosestGripSlotInRange_Implementation(FVector WorldLocation, bool bSecondarySlot, bool & bHadSlotInRange, FTransform & SlotWorldTransform, FName & SlotName, UGripMotionControllerComponent * CallingController, FName OverridePrefix)
{
if (OverridePrefix.IsNone())
bSecondarySlot ? OverridePrefix = "VRGripS" : OverridePrefix = "VRGripP";
UVRExpansionFunctionLibrary::GetGripSlotInRangeByTypeName_Component(OverridePrefix, this, WorldLocation, bSecondarySlot ? VRGripInterfaceSettings.SecondarySlotRange : VRGripInterfaceSettings.PrimarySlotRange, bHadSlotInRange, SlotWorldTransform, SlotName, CallingController);
}
bool UGrippableCapsuleComponent::AllowsMultipleGrips_Implementation()
{
return VRGripInterfaceSettings.bAllowMultipleGrips;
}
void UGrippableCapsuleComponent::IsHeld_Implementation(TArray<FBPGripPair> & HoldingControllers, bool & bIsHeld)
{
HoldingControllers = VRGripInterfaceSettings.HoldingControllers;
bIsHeld = VRGripInterfaceSettings.bIsHeld;
}
void UGrippableCapsuleComponent::Native_NotifyThrowGripDelegates(UGripMotionControllerComponent* Controller, bool bGripped, const FBPActorGripInformation& GripInformation, bool bWasSocketed)
{
if (bGripped)
{
OnGripped.Broadcast(Controller, GripInformation);
}
else
{
OnDropped.Broadcast(Controller, GripInformation, bWasSocketed);
}
}
void UGrippableCapsuleComponent::SetHeld_Implementation(UGripMotionControllerComponent * HoldingController, uint8 GripID, bool bIsHeld)
{
if (bIsHeld)
{
if (VRGripInterfaceSettings.MovementReplicationType != EGripMovementReplicationSettings::ForceServerSideMovement)
{
if (!VRGripInterfaceSettings.bIsHeld)
bOriginalReplicatesMovement = bReplicateMovement;
bReplicateMovement = false;
}
VRGripInterfaceSettings.bWasHeld = true;
VRGripInterfaceSettings.HoldingControllers.AddUnique(FBPGripPair(HoldingController, GripID));
}
else
{
if (VRGripInterfaceSettings.MovementReplicationType != EGripMovementReplicationSettings::ForceServerSideMovement)
{
bReplicateMovement = bOriginalReplicatesMovement;
}
VRGripInterfaceSettings.HoldingControllers.Remove(FBPGripPair(HoldingController, GripID));
}
VRGripInterfaceSettings.bIsHeld = VRGripInterfaceSettings.HoldingControllers.Num() > 0;
}
/*FBPInteractionSettings UGrippableCapsuleComponent::GetInteractionSettings_Implementation()
{
return VRGripInterfaceSettings.InteractionSettings;
}*/
bool UGrippableCapsuleComponent::GetGripScripts_Implementation(TArray<UVRGripScriptBase*> & ArrayReference)
{
ArrayReference = GripLogicScripts;
return GripLogicScripts.Num() > 0;
}
void UGrippableCapsuleComponent::PreDestroyFromReplication()
{
Super::PreDestroyFromReplication();
// Destroy any sub-objects we created
for (int32 i = 0; i < GripLogicScripts.Num(); ++i)
{
if (UObject *SubObject = GripLogicScripts[i])
{
SubObject->PreDestroyFromReplication();
SubObject->MarkAsGarbage();
}
}
GripLogicScripts.Empty();
}
void UGrippableCapsuleComponent::GetSubobjectsWithStableNamesForNetworking(TArray<UObject*> &ObjList)
{
if (bReplicateGripScripts)
{
for (int32 i = 0; i < GripLogicScripts.Num(); ++i)
{
if (UObject* SubObject = GripLogicScripts[i])
{
ObjList.Add(SubObject);
}
}
}
}
void UGrippableCapsuleComponent::OnComponentDestroyed(bool bDestroyingHierarchy)
{
// Call the super at the end, after we've done what we needed to do
Super::OnComponentDestroyed(bDestroyingHierarchy);
// Don't set these in editor preview window and the like, it causes saving issues
if (UWorld * World = GetWorld())
{
EWorldType::Type WorldType = World->WorldType;
if (WorldType == EWorldType::Editor || WorldType == EWorldType::EditorPreview)
{
return;
}
}
for (int32 i = 0; i < GripLogicScripts.Num(); i++)
{
if (UObject *SubObject = GripLogicScripts[i])
{
SubObject->MarkAsGarbage();
}
}
GripLogicScripts.Empty();
}

View file

@ -0,0 +1,31 @@
// Copyright 1998-2016 Epic Games, Inc. All Rights Reserved.
#include "Grippables/GrippableCharacter.h"
#include UE_INLINE_GENERATED_CPP_BY_NAME(GrippableCharacter)
#include "VRGlobalSettings.h"
#include "Grippables/GrippableSkeletalMeshComponent.h"
AGrippableCharacter::AGrippableCharacter(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer.SetDefaultSubobjectClass(ACharacter::MeshComponentName, UVRGlobalSettings::GetDefaultGrippableCharacterMeshComponentClass()))
{
ViewOriginationSocket = NAME_None;
GrippableMeshReference = Cast<UGrippableSkeletalMeshComponent>(GetMesh());
}
void AGrippableCharacter::GetActorEyesViewPoint(FVector& Location, FRotator& Rotation) const
{
if (ViewOriginationSocket != NAME_None)
{
if (USkeletalMeshComponent* MyMesh = GetMesh())
{
FTransform SocketTransform = MyMesh->GetSocketTransform(ViewOriginationSocket, RTS_World);
Location = SocketTransform.GetLocation();
Rotation = SocketTransform.GetRotation().Rotator();
return;
}
}
Super::GetActorEyesViewPoint(Location, Rotation);
}

View file

@ -0,0 +1,25 @@
// Copyright 1998-2016 Epic Games, Inc. All Rights Reserved.
#include "Grippables/GrippableDataTypes.h"
#include UE_INLINE_GENERATED_CPP_BY_NAME(GrippableDataTypes)
#include "GameFramework/Actor.h"
bool FRepAttachmentWithWeld::NetSerialize(FArchive& Ar, class UPackageMap* Map, bool& bOutSuccess)
{
// Our additional weld bit is here
Ar.SerializeBits(&bIsWelded, 1);
Ar << AttachParent;
LocationOffset.NetSerialize(Ar, Map, bOutSuccess);
RelativeScale3D.NetSerialize(Ar, Map, bOutSuccess);
RotationOffset.SerializeCompressedShort(Ar);
Ar << AttachSocket;
Ar << AttachComponent;
return true;
}
FRepAttachmentWithWeld::FRepAttachmentWithWeld()
{
bIsWelded = false;
}

View file

@ -0,0 +1,910 @@
// Copyright 1998-2016 Epic Games, Inc. All Rights Reserved.
#include "Grippables/GrippableSkeletalMeshActor.h"
#include UE_INLINE_GENERATED_CPP_BY_NAME(GrippableSkeletalMeshActor)
#include "TimerManager.h"
#include "GameFramework/PlayerController.h"
#include "GameFramework/PlayerState.h"
#include "GripMotionControllerComponent.h"
#include "VRExpansionFunctionLibrary.h"
#include "Misc/BucketUpdateSubsystem.h"
#include "Net/UnrealNetwork.h"
#include "PhysicsReplication.h"
#include "Physics/Experimental/PhysScene_Chaos.h"
#include "PhysicsEngine/PhysicsAsset.h" // Tmp until epic bug fixes skeletal welding
#if WITH_PUSH_MODEL
#include "Net/Core/PushModel/PushModel.h"
#endif
UOptionalRepSkeletalMeshComponent::UOptionalRepSkeletalMeshComponent(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{
bReplicateMovement = true;
}
void UOptionalRepSkeletalMeshComponent::PreReplication(IRepChangedPropertyTracker& ChangedPropertyTracker)
{
Super::PreReplication(ChangedPropertyTracker);
// Don't replicate if set to not do it
DOREPLIFETIME_ACTIVE_OVERRIDE_FAST(USceneComponent, RelativeLocation, bReplicateMovement);
DOREPLIFETIME_ACTIVE_OVERRIDE_FAST(USceneComponent, RelativeRotation, bReplicateMovement);
DOREPLIFETIME_ACTIVE_OVERRIDE_FAST(USceneComponent, RelativeScale3D, bReplicateMovement);
}
void UOptionalRepSkeletalMeshComponent::GetLifetimeReplicatedProps(TArray< class FLifetimeProperty >& OutLifetimeProps) const
{
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
DOREPLIFETIME(UOptionalRepSkeletalMeshComponent, bReplicateMovement);
}
void UOptionalRepSkeletalMeshComponent::GetWeldedBodies(TArray<FBodyInstance*>& OutWeldedBodies, TArray<FName>& OutLabels, bool bIncludingAutoWeld)
{
UPhysicsAsset* PhysicsAsset = GetPhysicsAsset();
for (int32 BodyIdx = 0; BodyIdx < Bodies.Num(); ++BodyIdx)
{
FBodyInstance* BI = Bodies[BodyIdx];
if (BI && (BI->WeldParent != nullptr || (bIncludingAutoWeld && BI->bAutoWeld)))
{
OutWeldedBodies.Add(BI);
if (PhysicsAsset)
{
if (UBodySetup* PhysicsAssetBodySetup = PhysicsAsset->SkeletalBodySetups[BodyIdx])
{
OutLabels.Add(PhysicsAssetBodySetup->BoneName);
}
else
{
OutLabels.Add(NAME_None);
}
}
else
{
OutLabels.Add(NAME_None);
}
}
}
for (USceneComponent* Child : GetAttachChildren())
{
if (UPrimitiveComponent* PrimChild = Cast<UPrimitiveComponent>(Child))
{
PrimChild->GetWeldedBodies(OutWeldedBodies, OutLabels, bIncludingAutoWeld);
}
}
}
FBodyInstance* UOptionalRepSkeletalMeshComponent::GetBodyInstance(FName BoneName, bool bGetWelded, int32) const
{
UPhysicsAsset* const PhysicsAsset = GetPhysicsAsset();
FBodyInstance* BodyInst = NULL;
if (PhysicsAsset != NULL)
{
// A name of NAME_None indicates 'root body'
if (BoneName == NAME_None)
{
if (Bodies.IsValidIndex(RootBodyData.BodyIndex))
{
BodyInst = Bodies[RootBodyData.BodyIndex];
}
}
// otherwise, look for the body
else
{
int32 BodyIndex = PhysicsAsset->FindBodyIndex(BoneName);
if (Bodies.IsValidIndex(BodyIndex))
{
BodyInst = Bodies[BodyIndex];
}
}
BodyInst = (bGetWelded && BodyInstance.WeldParent) ? BodyInstance.WeldParent : BodyInst;
}
return BodyInst;
}
//=============================================================================
AGrippableSkeletalMeshActor::AGrippableSkeletalMeshActor(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer.SetDefaultSubobjectClass<UOptionalRepSkeletalMeshComponent>(TEXT("SkeletalMeshComponent0")))
{
VRGripInterfaceSettings.bDenyGripping = false;
VRGripInterfaceSettings.OnTeleportBehavior = EGripInterfaceTeleportBehavior::TeleportAllComponents;
VRGripInterfaceSettings.bSimulateOnDrop = true;
VRGripInterfaceSettings.SlotDefaultGripType = EGripCollisionType::InteractiveCollisionWithPhysics;
VRGripInterfaceSettings.FreeDefaultGripType = EGripCollisionType::InteractiveCollisionWithPhysics;
VRGripInterfaceSettings.SecondaryGripType = ESecondaryGripType::SG_None;
VRGripInterfaceSettings.MovementReplicationType = EGripMovementReplicationSettings::ForceClientSideMovement;
VRGripInterfaceSettings.LateUpdateSetting = EGripLateUpdateSettings::NotWhenCollidingOrDoubleGripping;
VRGripInterfaceSettings.ConstraintStiffness = 1500.0f;
VRGripInterfaceSettings.ConstraintDamping = 200.0f;
VRGripInterfaceSettings.ConstraintBreakDistance = 100.0f;
VRGripInterfaceSettings.SecondarySlotRange = 20.0f;
VRGripInterfaceSettings.PrimarySlotRange = 20.0f;
VRGripInterfaceSettings.bIsHeld = false;
// Default replication on for multiplayer
//this->bNetLoadOnClient = false;
SetReplicatingMovement(true);
bReplicates = true;
bRepGripSettingsAndGameplayTags = true;
bReplicateGripScripts = false;
// #TODO we can register them maybe in the future
// Don't use the replicated list, use our custom replication instead
bReplicateUsingRegisteredSubObjectList = false;
bAllowIgnoringAttachOnOwner = 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
MinNetUpdateFrequency = 30.0f;
}
void AGrippableSkeletalMeshActor::GetLifetimeReplicatedProps(TArray< class FLifetimeProperty >& OutLifetimeProps) const
{
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
DOREPLIFETIME_CONDITION(AGrippableSkeletalMeshActor, GripLogicScripts, COND_Custom);
DOREPLIFETIME(AGrippableSkeletalMeshActor, bReplicateGripScripts);
DOREPLIFETIME(AGrippableSkeletalMeshActor, bRepGripSettingsAndGameplayTags);
DOREPLIFETIME(AGrippableSkeletalMeshActor, bAllowIgnoringAttachOnOwner);
DOREPLIFETIME(AGrippableSkeletalMeshActor, ClientAuthReplicationData);
DOREPLIFETIME_CONDITION(AGrippableSkeletalMeshActor, VRGripInterfaceSettings, COND_Custom);
DOREPLIFETIME_CONDITION(AGrippableSkeletalMeshActor, GameplayTags, COND_Custom);
DISABLE_REPLICATED_PRIVATE_PROPERTY(AActor, AttachmentReplication);
FDoRepLifetimeParams AttachmentReplicationParams{ COND_Custom, REPNOTIFY_Always, /*bIsPushBased=*/true };
DOREPLIFETIME_WITH_PARAMS_FAST(AGrippableSkeletalMeshActor, AttachmentWeldReplication, AttachmentReplicationParams);
}
void AGrippableSkeletalMeshActor::PreReplication(IRepChangedPropertyTracker& ChangedPropertyTracker)
{
// Don't replicate if set to not do it
DOREPLIFETIME_ACTIVE_OVERRIDE_FAST(AGrippableSkeletalMeshActor, VRGripInterfaceSettings, bRepGripSettingsAndGameplayTags);
DOREPLIFETIME_ACTIVE_OVERRIDE_FAST(AGrippableSkeletalMeshActor, GameplayTags, bRepGripSettingsAndGameplayTags);
DOREPLIFETIME_ACTIVE_OVERRIDE_FAST(AGrippableSkeletalMeshActor, GripLogicScripts, bReplicateGripScripts);
//Super::PreReplication(ChangedPropertyTracker);
#if WITH_PUSH_MODEL
const AActor* const OldAttachParent = AttachmentWeldReplication.AttachParent;
const UActorComponent* const OldAttachComponent = AttachmentWeldReplication.AttachComponent;
#endif
// Attachment replication gets filled in by GatherCurrentMovement(), but in the case of a detached root we need to trigger remote detachment.
AttachmentWeldReplication.AttachParent = nullptr;
AttachmentWeldReplication.AttachComponent = nullptr;
GatherCurrentMovement();
DOREPLIFETIME_ACTIVE_OVERRIDE_FAST(AActor, ReplicatedMovement, IsReplicatingMovement());
// Don't need to replicate AttachmentReplication if the root component replicates, because it already handles it.
DOREPLIFETIME_ACTIVE_OVERRIDE(AGrippableSkeletalMeshActor, AttachmentWeldReplication, RootComponent && !RootComponent->GetIsReplicated());
// Don't need to replicate AttachmentReplication if the root component replicates, because it already handles it.
DOREPLIFETIME_ACTIVE_OVERRIDE_FAST(AActor, AttachmentReplication, false);// RootComponent && !RootComponent->GetIsReplicated());
#if WITH_PUSH_MODEL
if (UNLIKELY(OldAttachParent != AttachmentWeldReplication.AttachParent || OldAttachComponent != AttachmentWeldReplication.AttachComponent))
{
MARK_PROPERTY_DIRTY_FROM_NAME(AGrippableSkeletalMeshActor, AttachmentWeldReplication, this);
}
#endif
/*PRAGMA_DISABLE_DEPRECATION_WARNINGS
UBlueprintGeneratedClass* BPClass = Cast<UBlueprintGeneratedClass>(GetClass());
if (BPClass != nullptr)
{
BPClass->InstancePreReplication(this, ChangedPropertyTracker);
}
PRAGMA_ENABLE_DEPRECATION_WARNINGS*/
}
void AGrippableSkeletalMeshActor::GatherCurrentMovement()
{
if (IsReplicatingMovement() || (RootComponent && RootComponent->GetAttachParent()))
{
bool bWasAttachmentModified = false;
bool bWasRepMovementModified = false;
AActor* OldAttachParent = AttachmentWeldReplication.AttachParent;
USceneComponent* OldAttachComponent = AttachmentWeldReplication.AttachComponent;
AttachmentWeldReplication.AttachParent = nullptr;
AttachmentWeldReplication.AttachComponent = nullptr;
FRepMovement& RepMovement = GetReplicatedMovement_Mutable();
UPrimitiveComponent* RootPrimComp = Cast<UPrimitiveComponent>(GetRootComponent());
if (RootPrimComp && RootPrimComp->IsSimulatingPhysics())
{
#if UE_WITH_IRIS
const bool bPrevRepPhysics = GetReplicatedMovement_Mutable().bRepPhysics;
#endif // UE_WITH_IRIS
bool bFoundInCache = false;
UWorld* World = GetWorld();
int ServerFrame = 0;
if (FPhysScene_Chaos* Scene = static_cast<FPhysScene_Chaos*>(World->GetPhysicsScene()))
{
if (const FRigidBodyState* FoundState = Scene->GetStateFromReplicationCache(RootPrimComp, ServerFrame))
{
RepMovement.FillFrom(*FoundState, this, Scene->ReplicationCache.ServerFrame);
bFoundInCache = true;
}
}
if (!bFoundInCache)
{
// fallback to GT data
FRigidBodyState RBState;
RootPrimComp->GetRigidBodyState(RBState);
RepMovement.FillFrom(RBState, this, 0);
}
// Don't replicate movement if we're welded to another parent actor.
// Their replication will affect our position indirectly since we are attached.
RepMovement.bRepPhysics = !RootPrimComp->IsWelded();
if (!RepMovement.bRepPhysics)
{
if (RootComponent->GetAttachParent() != nullptr)
{
// Networking for attachments assumes the RootComponent of the AttachParent actor.
// If that's not the case, we can't update this, as the client wouldn't be able to resolve the Component and would detach as a result.
AttachmentWeldReplication.AttachParent = RootComponent->GetAttachParent()->GetAttachmentRootActor();
if (AttachmentWeldReplication.AttachParent != nullptr)
{
AttachmentWeldReplication.LocationOffset = RootComponent->GetRelativeLocation();
AttachmentWeldReplication.RotationOffset = RootComponent->GetRelativeRotation();
AttachmentWeldReplication.RelativeScale3D = RootComponent->GetRelativeScale3D();
AttachmentWeldReplication.AttachComponent = RootComponent->GetAttachParent();
AttachmentWeldReplication.AttachSocket = RootComponent->GetAttachSocketName();
AttachmentWeldReplication.bIsWelded = RootPrimComp ? RootPrimComp->IsWelded() : false;
// Technically, the values might have stayed the same, but we'll just assume they've changed.
bWasAttachmentModified = true;
#if UE_WITH_IRIS
// If RepPhysics has changed value then notify the ReplicationSystem
if (bPrevRepPhysics != GetReplicatedMovement_Mutable().bRepPhysics)
{
UpdateReplicatePhysicsCondition();
}
#endif // UE_WITH_IRIS
}
}
}
// Technically, the values might have stayed the same, but we'll just assume they've changed.
bWasRepMovementModified = true;
}
else if (RootComponent != nullptr)
{
// If we are attached, don't replicate absolute position, use AttachmentReplication instead.
if (RootComponent->GetAttachParent() != nullptr)
{
// Networking for attachments assumes the RootComponent of the AttachParent actor.
// If that's not the case, we can't update this, as the client wouldn't be able to resolve the Component and would detach as a result.
AttachmentWeldReplication.AttachParent = RootComponent->GetAttachParentActor();
if (AttachmentWeldReplication.AttachParent != nullptr)
{
AttachmentWeldReplication.LocationOffset = RootComponent->GetRelativeLocation();
AttachmentWeldReplication.RotationOffset = RootComponent->GetRelativeRotation();
AttachmentWeldReplication.RelativeScale3D = RootComponent->GetRelativeScale3D();
AttachmentWeldReplication.AttachComponent = RootComponent->GetAttachParent();
AttachmentWeldReplication.AttachSocket = RootComponent->GetAttachSocketName();
AttachmentWeldReplication.bIsWelded = RootPrimComp ? RootPrimComp->IsWelded() : false;
// Technically, the values might have stayed the same, but we'll just assume they've changed.
bWasAttachmentModified = true;
}
}
else
{
RepMovement.Location = FRepMovement::RebaseOntoZeroOrigin(RootComponent->GetComponentLocation(), this);
RepMovement.Rotation = RootComponent->GetComponentRotation();
RepMovement.LinearVelocity = GetVelocity();
RepMovement.AngularVelocity = FVector::ZeroVector;
// Technically, the values might have stayed the same, but we'll just assume they've changed.
bWasRepMovementModified = true;
}
bWasRepMovementModified = (bWasRepMovementModified || RepMovement.bRepPhysics);
RepMovement.bRepPhysics = false;
}
#if WITH_PUSH_MODEL
if (bWasRepMovementModified)
{
MARK_PROPERTY_DIRTY_FROM_NAME(AActor, ReplicatedMovement, this);
}
if (bWasAttachmentModified ||
OldAttachParent != AttachmentWeldReplication.AttachParent ||
OldAttachComponent != AttachmentWeldReplication.AttachComponent)
{
MARK_PROPERTY_DIRTY_FROM_NAME(AGrippableSkeletalMeshActor, AttachmentWeldReplication, this);
}
#endif
}
}
bool AGrippableSkeletalMeshActor::ReplicateSubobjects(UActorChannel* Channel, class FOutBunch* Bunch, FReplicationFlags* RepFlags)
{
bool WroteSomething = Super::ReplicateSubobjects(Channel, Bunch, RepFlags);
if (bReplicateGripScripts)
{
for (UVRGripScriptBase* Script : GripLogicScripts)
{
if (Script && IsValid(Script))
{
WroteSomething |= Channel->ReplicateSubobject(Script, *Bunch, *RepFlags);
}
}
}
return WroteSomething;
}
/*void AGrippableSkeletalMeshActor::GetLifetimeReplicatedProps(TArray< class FLifetimeProperty > & OutLifetimeProps) const
{
DOREPLIFETIME(AGrippableSkeletalMeshActor, VRGripInterfaceSettings);
}*/
//=============================================================================
AGrippableSkeletalMeshActor::~AGrippableSkeletalMeshActor()
{
}
void AGrippableSkeletalMeshActor::BeginPlay()
{
// Call the base class
Super::BeginPlay();
// Call all grip scripts begin play events so they can perform any needed logic
for (UVRGripScriptBase* Script : GripLogicScripts)
{
if (Script)
{
Script->BeginPlay(this);
}
}
}
void AGrippableSkeletalMeshActor::SetDenyGripping(bool bDenyGripping)
{
VRGripInterfaceSettings.bDenyGripping = bDenyGripping;
}
void AGrippableSkeletalMeshActor::SetGripPriority(int NewGripPriority)
{
VRGripInterfaceSettings.AdvancedGripSettings.GripPriority = NewGripPriority;
}
void AGrippableSkeletalMeshActor::TickGrip_Implementation(UGripMotionControllerComponent* GrippingController, const FBPActorGripInformation& GripInformation, float DeltaTime) {}
void AGrippableSkeletalMeshActor::OnGrip_Implementation(UGripMotionControllerComponent* GrippingController, const FBPActorGripInformation& GripInformation) { }
void AGrippableSkeletalMeshActor::OnGripRelease_Implementation(UGripMotionControllerComponent* ReleasingController, const FBPActorGripInformation& GripInformation, bool bWasSocketed) { }
void AGrippableSkeletalMeshActor::OnChildGrip_Implementation(UGripMotionControllerComponent* GrippingController, const FBPActorGripInformation& GripInformation) {}
void AGrippableSkeletalMeshActor::OnChildGripRelease_Implementation(UGripMotionControllerComponent* ReleasingController, const FBPActorGripInformation& GripInformation, bool bWasSocketed) {}
void AGrippableSkeletalMeshActor::OnSecondaryGrip_Implementation(UGripMotionControllerComponent* GripOwningController, USceneComponent* SecondaryGripComponent, const FBPActorGripInformation& GripInformation) { OnSecondaryGripAdded.Broadcast(GripOwningController, GripInformation); }
void AGrippableSkeletalMeshActor::OnSecondaryGripRelease_Implementation(UGripMotionControllerComponent* GripOwningController, USceneComponent* ReleasingSecondaryGripComponent, const FBPActorGripInformation& GripInformation) { OnSecondaryGripRemoved.Broadcast(GripOwningController, GripInformation); }
void AGrippableSkeletalMeshActor::OnUsed_Implementation() {}
void AGrippableSkeletalMeshActor::OnEndUsed_Implementation() {}
void AGrippableSkeletalMeshActor::OnSecondaryUsed_Implementation() {}
void AGrippableSkeletalMeshActor::OnEndSecondaryUsed_Implementation() {}
void AGrippableSkeletalMeshActor::OnInput_Implementation(FKey Key, EInputEvent KeyEvent) {}
bool AGrippableSkeletalMeshActor::RequestsSocketing_Implementation(USceneComponent*& ParentToSocketTo, FName& OptionalSocketName, FTransform_NetQuantize& RelativeTransform) { return false; }
bool AGrippableSkeletalMeshActor::DenyGripping_Implementation(UGripMotionControllerComponent * GripInitiator)
{
return VRGripInterfaceSettings.bDenyGripping;
}
EGripInterfaceTeleportBehavior AGrippableSkeletalMeshActor::TeleportBehavior_Implementation()
{
return VRGripInterfaceSettings.OnTeleportBehavior;
}
bool AGrippableSkeletalMeshActor::SimulateOnDrop_Implementation()
{
return VRGripInterfaceSettings.bSimulateOnDrop;
}
EGripCollisionType AGrippableSkeletalMeshActor::GetPrimaryGripType_Implementation(bool bIsSlot)
{
return bIsSlot ? VRGripInterfaceSettings.SlotDefaultGripType : VRGripInterfaceSettings.FreeDefaultGripType;
}
ESecondaryGripType AGrippableSkeletalMeshActor::SecondaryGripType_Implementation()
{
return VRGripInterfaceSettings.SecondaryGripType;
}
EGripMovementReplicationSettings AGrippableSkeletalMeshActor::GripMovementReplicationType_Implementation()
{
return VRGripInterfaceSettings.MovementReplicationType;
}
EGripLateUpdateSettings AGrippableSkeletalMeshActor::GripLateUpdateSetting_Implementation()
{
return VRGripInterfaceSettings.LateUpdateSetting;
}
void AGrippableSkeletalMeshActor::GetGripStiffnessAndDamping_Implementation(float& GripStiffnessOut, float& GripDampingOut)
{
GripStiffnessOut = VRGripInterfaceSettings.ConstraintStiffness;
GripDampingOut = VRGripInterfaceSettings.ConstraintDamping;
}
FBPAdvGripSettings AGrippableSkeletalMeshActor::AdvancedGripSettings_Implementation()
{
return VRGripInterfaceSettings.AdvancedGripSettings;
}
float AGrippableSkeletalMeshActor::GripBreakDistance_Implementation()
{
return VRGripInterfaceSettings.ConstraintBreakDistance;
}
void AGrippableSkeletalMeshActor::ClosestGripSlotInRange_Implementation(FVector WorldLocation, bool bSecondarySlot, bool& bHadSlotInRange, FTransform& SlotWorldTransform, FName& SlotName, UGripMotionControllerComponent* CallingController, FName OverridePrefix)
{
if (OverridePrefix.IsNone())
bSecondarySlot ? OverridePrefix = "VRGripS" : OverridePrefix = "VRGripP";
UVRExpansionFunctionLibrary::GetGripSlotInRangeByTypeName(OverridePrefix, this, WorldLocation, bSecondarySlot ? VRGripInterfaceSettings.SecondarySlotRange : VRGripInterfaceSettings.PrimarySlotRange, bHadSlotInRange, SlotWorldTransform, SlotName, CallingController);
}
bool AGrippableSkeletalMeshActor::AllowsMultipleGrips_Implementation()
{
return VRGripInterfaceSettings.bAllowMultipleGrips;
}
void AGrippableSkeletalMeshActor::IsHeld_Implementation(TArray<FBPGripPair>& HoldingControllers, bool& bIsHeld)
{
HoldingControllers = VRGripInterfaceSettings.HoldingControllers;
bIsHeld = VRGripInterfaceSettings.bIsHeld;
}
bool AGrippableSkeletalMeshActor::AddToClientReplicationBucket()
{
if (ShouldWeSkipAttachmentReplication(false))
{
// The subsystem automatically removes entries with the same function signature so its safe to just always add here
GetWorld()->GetSubsystem<UBucketUpdateSubsystem>()->AddObjectToBucket(ClientAuthReplicationData.UpdateRate, this, FName(TEXT("PollReplicationEvent")));
ClientAuthReplicationData.bIsCurrentlyClientAuth = true;
if (UWorld* World = GetWorld())
ClientAuthReplicationData.TimeAtInitialThrow = World->GetTimeSeconds();
return true;
}
return false;
}
bool AGrippableSkeletalMeshActor::RemoveFromClientReplicationBucket()
{
if (ClientAuthReplicationData.bIsCurrentlyClientAuth)
{
GetWorld()->GetSubsystem<UBucketUpdateSubsystem>()->RemoveObjectFromBucketByFunctionName(this, FName(TEXT("PollReplicationEvent")));
CeaseReplicationBlocking();
return true;
}
return false;
}
void AGrippableSkeletalMeshActor::Native_NotifyThrowGripDelegates(UGripMotionControllerComponent* Controller, bool bGripped, const FBPActorGripInformation& GripInformation, bool bWasSocketed)
{
if (bGripped)
{
OnGripped.Broadcast(Controller, GripInformation);
}
else
{
OnDropped.Broadcast(Controller, GripInformation, bWasSocketed);
}
}
void AGrippableSkeletalMeshActor::SetHeld_Implementation(UGripMotionControllerComponent* HoldingController, uint8 GripID, bool bIsHeld)
{
if (bIsHeld)
{
VRGripInterfaceSettings.HoldingControllers.AddUnique(FBPGripPair(HoldingController, GripID));
RemoveFromClientReplicationBucket();
VRGripInterfaceSettings.bWasHeld = true;
VRGripInterfaceSettings.bIsHeld = VRGripInterfaceSettings.HoldingControllers.Num() > 0;
}
else
{
VRGripInterfaceSettings.HoldingControllers.Remove(FBPGripPair(HoldingController, GripID));
VRGripInterfaceSettings.bIsHeld = VRGripInterfaceSettings.HoldingControllers.Num() > 0;
if (ClientAuthReplicationData.bUseClientAuthThrowing && !VRGripInterfaceSettings.bIsHeld)
{
bool bWasLocallyOwned = HoldingController ? HoldingController->IsLocallyControlled() : false;
if (bWasLocallyOwned && ShouldWeSkipAttachmentReplication(false))
{
if (UPrimitiveComponent* PrimComp = Cast<UPrimitiveComponent>(GetRootComponent()))
{
if (PrimComp->IsSimulatingPhysics())
{
AddToClientReplicationBucket();
}
}
}
}
}
}
/*FBPInteractionSettings AGrippableSkeletalMeshActor::GetInteractionSettings_Implementation()
{
return VRGripInterfaceSettings.InteractionSettings;
}*/
bool AGrippableSkeletalMeshActor::GetGripScripts_Implementation(TArray<UVRGripScriptBase*>& ArrayReference)
{
ArrayReference = GripLogicScripts;
return GripLogicScripts.Num() > 0;
}
bool AGrippableSkeletalMeshActor::PollReplicationEvent()
{
if (!ClientAuthReplicationData.bIsCurrentlyClientAuth || !this->HasLocalNetOwner() || VRGripInterfaceSettings.bIsHeld)
return false; // Tell the bucket subsystem to remove us from consideration
UWorld* OurWorld = GetWorld();
if (!OurWorld)
return false; // Tell the bucket subsystem to remove us from consideration
bool bRemoveBlocking = false;
if ((OurWorld->GetTimeSeconds() - ClientAuthReplicationData.TimeAtInitialThrow) > 10.0f)
{
// Lets time out sending, its been 10 seconds since we threw the object and its likely that it is conflicting with some server
// Authed movement that is forcing it to keep momentum.
//return false; // Tell the bucket subsystem to remove us from consideration
bRemoveBlocking = true;
}
// Store current transform for resting check
FTransform CurTransform = this->GetActorTransform();
if (!bRemoveBlocking)
{
if (!CurTransform.GetRotation().Equals(ClientAuthReplicationData.LastActorTransform.GetRotation()) || !CurTransform.GetLocation().Equals(ClientAuthReplicationData.LastActorTransform.GetLocation()))
{
ClientAuthReplicationData.LastActorTransform = CurTransform;
if (UPrimitiveComponent* PrimComp = Cast<UPrimitiveComponent>(RootComponent))
{
// Need to clamp to a max time since start, to handle cases with conflicting collisions
if (PrimComp->IsSimulatingPhysics() && ShouldWeSkipAttachmentReplication(false))
{
FRepMovementVR ClientAuthMovementRep;
if (ClientAuthMovementRep.GatherActorsMovement(this))
{
Server_GetClientAuthReplication(ClientAuthMovementRep);
if (PrimComp->RigidBodyIsAwake())
{
return true;
}
}
}
}
else
{
bRemoveBlocking = true;
//return false; // Tell the bucket subsystem to remove us from consideration
}
}
//else
// {
// Difference is too small, lets end sending location
//ClientAuthReplicationData.LastActorTransform = FTransform::Identity;
// }
}
bool TimedBlockingRelease = false;
AActor* TopOwner = GetOwner();
if (TopOwner != nullptr)
{
AActor* tempOwner = TopOwner->GetOwner();
// I have an owner so search that for the top owner
while (tempOwner)
{
TopOwner = tempOwner;
tempOwner = TopOwner->GetOwner();
}
if (APlayerController* PlayerController = Cast<APlayerController>(TopOwner))
{
if (APlayerState* PlayerState = PlayerController->PlayerState)
{
if (ClientAuthReplicationData.ResetReplicationHandle.IsValid())
{
OurWorld->GetTimerManager().ClearTimer(ClientAuthReplicationData.ResetReplicationHandle);
}
// Lets clamp the ping to a min / max value just in case
float clampedPing = FMath::Clamp(PlayerState->ExactPing * 0.001f, 0.0f, 1000.0f);
OurWorld->GetTimerManager().SetTimer(ClientAuthReplicationData.ResetReplicationHandle, this, &AGrippableSkeletalMeshActor::CeaseReplicationBlocking, clampedPing, false);
TimedBlockingRelease = true;
}
}
}
if (!TimedBlockingRelease)
{
CeaseReplicationBlocking();
}
// Tell server to kill us
Server_EndClientAuthReplication();
return false; // Tell the bucket subsystem to remove us from consideration
}
void AGrippableSkeletalMeshActor::CeaseReplicationBlocking()
{
if (ClientAuthReplicationData.bIsCurrentlyClientAuth)
ClientAuthReplicationData.bIsCurrentlyClientAuth = false;
ClientAuthReplicationData.LastActorTransform = FTransform::Identity;
if (ClientAuthReplicationData.ResetReplicationHandle.IsValid())
{
if (UWorld* OurWorld = GetWorld())
{
OurWorld->GetTimerManager().ClearTimer(ClientAuthReplicationData.ResetReplicationHandle);
}
}
}
void AGrippableSkeletalMeshActor::EndPlay(const EEndPlayReason::Type EndPlayReason)
{
RemoveFromClientReplicationBucket();
// Call all grip scripts begin play events so they can perform any needed logic
for (UVRGripScriptBase* Script : GripLogicScripts)
{
if (Script)
{
Script->EndPlay(EndPlayReason);
}
}
Super::EndPlay(EndPlayReason);
}
bool AGrippableSkeletalMeshActor::Server_EndClientAuthReplication_Validate()
{
return true;
}
void AGrippableSkeletalMeshActor::Server_EndClientAuthReplication_Implementation()
{
if (UWorld* World = GetWorld())
{
if (FPhysScene* PhysScene = World->GetPhysicsScene())
{
if (IPhysicsReplication* PhysicsReplication = PhysScene->GetPhysicsReplication())
{
PhysicsReplication->RemoveReplicatedTarget(this->GetSkeletalMeshComponent());
}
}
}
}
bool AGrippableSkeletalMeshActor::Server_GetClientAuthReplication_Validate(const FRepMovementVR& newMovement)
{
return true;
}
void AGrippableSkeletalMeshActor::Server_GetClientAuthReplication_Implementation(const FRepMovementVR& newMovement)
{
if (!VRGripInterfaceSettings.bIsHeld)
{
if (!newMovement.Location.ContainsNaN() && !newMovement.Rotation.ContainsNaN())
{
FRepMovement& MovementRep = GetReplicatedMovement_Mutable();
newMovement.CopyTo(MovementRep);
OnRep_ReplicatedMovement();
}
}
}
void AGrippableSkeletalMeshActor::OnRep_AttachmentReplication()
{
if (bAllowIgnoringAttachOnOwner && (ClientAuthReplicationData.bIsCurrentlyClientAuth || ShouldWeSkipAttachmentReplication()))
//if (bAllowIgnoringAttachOnOwner && ShouldWeSkipAttachmentReplication())
{
return;
}
if (AttachmentWeldReplication.AttachParent)
{
if (RootComponent)
{
USceneComponent* AttachParentComponent = (AttachmentWeldReplication.AttachComponent ? ToRawPtr(AttachmentWeldReplication.AttachComponent) : AttachmentWeldReplication.AttachParent->GetRootComponent());
if (AttachParentComponent)
{
RootComponent->SetRelativeLocation_Direct(AttachmentWeldReplication.LocationOffset);
RootComponent->SetRelativeRotation_Direct(AttachmentWeldReplication.RotationOffset);
RootComponent->SetRelativeScale3D_Direct(AttachmentWeldReplication.RelativeScale3D);
// If we're already attached to the correct Parent and Socket, then the update must be position only.
// AttachToComponent would early out in this case.
// Note, we ignore the special case for simulated bodies in AttachToComponent as AttachmentReplication shouldn't get updated
// if the body is simulated (see AActor::GatherMovement).
const bool bAlreadyAttached = (AttachParentComponent == RootComponent->GetAttachParent() && AttachmentWeldReplication.AttachSocket == RootComponent->GetAttachSocketName() && AttachParentComponent->GetAttachChildren().Contains(RootComponent));
if (bAlreadyAttached)
{
// Note, this doesn't match AttachToComponent, but we're assuming it's safe to skip physics (see comment above).
RootComponent->UpdateComponentToWorld(EUpdateTransformFlags::SkipPhysicsUpdate, ETeleportType::None);
}
else
{
FAttachmentTransformRules attachRules = FAttachmentTransformRules::KeepRelativeTransform;
attachRules.bWeldSimulatedBodies = AttachmentWeldReplication.bIsWelded;
RootComponent->AttachToComponent(AttachParentComponent, attachRules, AttachmentWeldReplication.AttachSocket);
}
}
}
}
else
{
DetachFromActor(FDetachmentTransformRules::KeepWorldTransform);
// Handle the case where an object was both detached and moved on the server in the same frame.
// Calling this extraneously does not hurt but will properly fire events if the movement state changed while attached.
// This is needed because client side movement is ignored when attached
if (IsReplicatingMovement())
{
OnRep_ReplicatedMovement();
}
}
}
void AGrippableSkeletalMeshActor::OnRep_ReplicateMovement()
{
if (bAllowIgnoringAttachOnOwner && (ClientAuthReplicationData.bIsCurrentlyClientAuth || ShouldWeSkipAttachmentReplication()))
//if (bAllowIgnoringAttachOnOwner && (ClientAuthReplicationData.bIsCurrentlyClientAuth || ShouldWeSkipAttachmentReplication()))
{
return;
}
if (RootComponent)
{
const FRepAttachment ReplicationAttachment = GetAttachmentReplication();
if (!ReplicationAttachment.AttachParent)
{
const FRepMovement& RepMove = GetReplicatedMovement();
// This "fix" corrects the simulation state not replicating over correctly
// If you turn off movement replication, simulate an object, turn movement replication back on and un-simulate, it never knows the difference
// This change ensures that it is checking against the current state
if (RootComponent->IsSimulatingPhysics() != RepMove.bRepPhysics)//SavedbRepPhysics != ReplicatedMovement.bRepPhysics)
{
// Turn on/off physics sim to match server.
SyncReplicatedPhysicsSimulation();
// It doesn't really hurt to run it here, the super can call it again but it will fail out as they already match
}
}
}
Super::OnRep_ReplicateMovement();
}
void AGrippableSkeletalMeshActor::OnRep_ReplicatedMovement()
{
if (bAllowIgnoringAttachOnOwner && (ClientAuthReplicationData.bIsCurrentlyClientAuth || ShouldWeSkipAttachmentReplication()))
//if (ClientAuthReplicationData.bIsCurrentlyClientAuth && ShouldWeSkipAttachmentReplication(false))
{
return;
}
Super::OnRep_ReplicatedMovement();
}
void AGrippableSkeletalMeshActor::PostNetReceivePhysicState()
{
if (bAllowIgnoringAttachOnOwner && (ClientAuthReplicationData.bIsCurrentlyClientAuth || ShouldWeSkipAttachmentReplication()))
//if ((ClientAuthReplicationData.bIsCurrentlyClientAuth || VRGripInterfaceSettings.bIsHeld) && bAllowIgnoringAttachOnOwner && ShouldWeSkipAttachmentReplication(false))
{
return;
}
Super::PostNetReceivePhysicState();
}
void AGrippableSkeletalMeshActor::MarkComponentsAsGarbage(bool bModify)
{
Super::MarkComponentsAsGarbage(bModify);
for (int32 i = 0; i < GripLogicScripts.Num(); ++i)
{
if (UObject* SubObject = GripLogicScripts[i])
{
SubObject->MarkAsGarbage();
}
}
GripLogicScripts.Empty();
}
void AGrippableSkeletalMeshActor::PreDestroyFromReplication()
{
Super::PreDestroyFromReplication();
// Destroy any sub-objects we created
for (int32 i = 0; i < GripLogicScripts.Num(); ++i)
{
if (UObject* SubObject = GripLogicScripts[i])
{
OnSubobjectDestroyFromReplication(SubObject); //-V595
SubObject->PreDestroyFromReplication();
SubObject->MarkAsGarbage();
}
}
for (UActorComponent* ActorComp : GetComponents())
{
// Pending kill components should have already had this called as they were network spawned and are being killed
if (ActorComp && IsValid(ActorComp) && ActorComp->GetClass()->ImplementsInterface(UVRGripInterface::StaticClass()))
ActorComp->PreDestroyFromReplication();
}
GripLogicScripts.Empty();
}
void AGrippableSkeletalMeshActor::BeginDestroy()
{
Super::BeginDestroy();
for (int32 i = 0; i < GripLogicScripts.Num(); i++)
{
if (UObject* SubObject = GripLogicScripts[i])
{
SubObject->MarkAsGarbage();
}
}
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);
}
}
}
}

View file

@ -0,0 +1,392 @@
// Copyright 1998-2016 Epic Games, Inc. All Rights Reserved.
#include "Grippables/GrippableSkeletalMeshComponent.h"
#include UE_INLINE_GENERATED_CPP_BY_NAME(GrippableSkeletalMeshComponent)
#include "GripMotionControllerComponent.h"
#include "VRExpansionFunctionLibrary.h"
#include "GripScripts/VRGripScriptBase.h"
#include "PhysicsEngine/PhysicsAsset.h" // Tmp until epic bug fixes skeletal welding
#include "Net/UnrealNetwork.h"
//=============================================================================
UGrippableSkeletalMeshComponent::UGrippableSkeletalMeshComponent(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{
VRGripInterfaceSettings.bDenyGripping = false;
VRGripInterfaceSettings.OnTeleportBehavior = EGripInterfaceTeleportBehavior::DropOnTeleport;
VRGripInterfaceSettings.bSimulateOnDrop = true;
VRGripInterfaceSettings.SlotDefaultGripType = EGripCollisionType::ManipulationGrip;
VRGripInterfaceSettings.FreeDefaultGripType = EGripCollisionType::ManipulationGrip;
VRGripInterfaceSettings.SecondaryGripType = ESecondaryGripType::SG_None;
VRGripInterfaceSettings.MovementReplicationType = EGripMovementReplicationSettings::ForceClientSideMovement;
VRGripInterfaceSettings.LateUpdateSetting = EGripLateUpdateSettings::LateUpdatesAlwaysOff;
VRGripInterfaceSettings.ConstraintStiffness = 1500.0f;
VRGripInterfaceSettings.ConstraintDamping = 200.0f;
VRGripInterfaceSettings.ConstraintBreakDistance = 100.0f;
VRGripInterfaceSettings.SecondarySlotRange = 20.0f;
VRGripInterfaceSettings.PrimarySlotRange = 20.0f;
VRGripInterfaceSettings.bIsHeld = false;
bReplicateMovement = false;
//this->bReplicates = true;
bRepGripSettingsAndGameplayTags = true;
bReplicateGripScripts = false;
// #TODO we can register them maybe in the future
// Don't use the replicated list, use our custom replication instead
bReplicateUsingRegisteredSubObjectList = false;
}
void UGrippableSkeletalMeshComponent::GetLifetimeReplicatedProps(TArray< class FLifetimeProperty > & OutLifetimeProps) const
{
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
DOREPLIFETIME_CONDITION(UGrippableSkeletalMeshComponent, GripLogicScripts, COND_Custom);
DOREPLIFETIME(UGrippableSkeletalMeshComponent, bReplicateGripScripts);
DOREPLIFETIME(UGrippableSkeletalMeshComponent, bRepGripSettingsAndGameplayTags);
DOREPLIFETIME(UGrippableSkeletalMeshComponent, bReplicateMovement);
DOREPLIFETIME_CONDITION(UGrippableSkeletalMeshComponent, VRGripInterfaceSettings, COND_Custom);
DOREPLIFETIME_CONDITION(UGrippableSkeletalMeshComponent, GameplayTags, COND_Custom);
}
void UGrippableSkeletalMeshComponent::PreReplication(IRepChangedPropertyTracker & ChangedPropertyTracker)
{
Super::PreReplication(ChangedPropertyTracker);
// Don't replicate if set to not do it
DOREPLIFETIME_ACTIVE_OVERRIDE_FAST(UGrippableSkeletalMeshComponent, VRGripInterfaceSettings, bRepGripSettingsAndGameplayTags);
DOREPLIFETIME_ACTIVE_OVERRIDE_FAST(UGrippableSkeletalMeshComponent, GameplayTags, bRepGripSettingsAndGameplayTags);
DOREPLIFETIME_ACTIVE_OVERRIDE_FAST(UGrippableSkeletalMeshComponent, GripLogicScripts, bReplicateGripScripts);
DOREPLIFETIME_ACTIVE_OVERRIDE_FAST(USceneComponent, RelativeLocation, bReplicateMovement);
DOREPLIFETIME_ACTIVE_OVERRIDE_FAST(USceneComponent, RelativeRotation, bReplicateMovement);
DOREPLIFETIME_ACTIVE_OVERRIDE_FAST(USceneComponent, RelativeScale3D, bReplicateMovement);
}
bool UGrippableSkeletalMeshComponent::ReplicateSubobjects(UActorChannel* Channel, class FOutBunch *Bunch, FReplicationFlags *RepFlags)
{
bool WroteSomething = Super::ReplicateSubobjects(Channel, Bunch, RepFlags);
if (bReplicateGripScripts)
{
for (UVRGripScriptBase* Script : GripLogicScripts)
{
if (Script && IsValid(Script))
{
WroteSomething |= Channel->ReplicateSubobject(Script, *Bunch, *RepFlags);
}
}
}
return WroteSomething;
}
//=============================================================================
UGrippableSkeletalMeshComponent::~UGrippableSkeletalMeshComponent()
{
}
void UGrippableSkeletalMeshComponent::BeginPlay()
{
// Call the base class
Super::BeginPlay();
// Call all grip scripts begin play events so they can perform any needed logic
for (UVRGripScriptBase* Script : GripLogicScripts)
{
if (Script)
{
Script->BeginPlay(this);
}
}
bOriginalReplicatesMovement = bReplicateMovement;
}
void UGrippableSkeletalMeshComponent::EndPlay(const EEndPlayReason::Type EndPlayReason)
{
// Call the base class
Super::EndPlay(EndPlayReason);
// Call all grip scripts begin play events so they can perform any needed logic
for (UVRGripScriptBase* Script : GripLogicScripts)
{
if (Script)
{
Script->EndPlay(EndPlayReason);
}
}
}
void UGrippableSkeletalMeshComponent::SetDenyGripping(bool bDenyGripping)
{
VRGripInterfaceSettings.bDenyGripping = bDenyGripping;
}
void UGrippableSkeletalMeshComponent::SetGripPriority(int NewGripPriority)
{
VRGripInterfaceSettings.AdvancedGripSettings.GripPriority = NewGripPriority;
}
void UGrippableSkeletalMeshComponent::TickGrip_Implementation(UGripMotionControllerComponent * GrippingController, const FBPActorGripInformation & GripInformation, float DeltaTime) {}
void UGrippableSkeletalMeshComponent::OnGrip_Implementation(UGripMotionControllerComponent* GrippingController, const FBPActorGripInformation& GripInformation) { }
void UGrippableSkeletalMeshComponent::OnGripRelease_Implementation(UGripMotionControllerComponent* ReleasingController, const FBPActorGripInformation& GripInformation, bool bWasSocketed) { }
void UGrippableSkeletalMeshComponent::OnChildGrip_Implementation(UGripMotionControllerComponent* GrippingController, const FBPActorGripInformation& GripInformation) {}
void UGrippableSkeletalMeshComponent::OnChildGripRelease_Implementation(UGripMotionControllerComponent* ReleasingController, const FBPActorGripInformation& GripInformation, bool bWasSocketed) {}
void UGrippableSkeletalMeshComponent::OnSecondaryGrip_Implementation(UGripMotionControllerComponent* GripOwningController, USceneComponent* SecondaryGripComponent, const FBPActorGripInformation& GripInformation) { OnSecondaryGripAdded.Broadcast(GripOwningController, GripInformation); }
void UGrippableSkeletalMeshComponent::OnSecondaryGripRelease_Implementation(UGripMotionControllerComponent* GripOwningController, USceneComponent* ReleasingSecondaryGripComponent, const FBPActorGripInformation& GripInformation) { OnSecondaryGripRemoved.Broadcast(GripOwningController, GripInformation); }
void UGrippableSkeletalMeshComponent::OnUsed_Implementation() {}
void UGrippableSkeletalMeshComponent::OnEndUsed_Implementation() {}
void UGrippableSkeletalMeshComponent::OnSecondaryUsed_Implementation() {}
void UGrippableSkeletalMeshComponent::OnEndSecondaryUsed_Implementation() {}
void UGrippableSkeletalMeshComponent::OnInput_Implementation(FKey Key, EInputEvent KeyEvent) {}
bool UGrippableSkeletalMeshComponent::RequestsSocketing_Implementation(USceneComponent *& ParentToSocketTo, FName & OptionalSocketName, FTransform_NetQuantize & RelativeTransform) { return false; }
bool UGrippableSkeletalMeshComponent::DenyGripping_Implementation(UGripMotionControllerComponent * GripInitiator)
{
return VRGripInterfaceSettings.bDenyGripping;
}
EGripInterfaceTeleportBehavior UGrippableSkeletalMeshComponent::TeleportBehavior_Implementation()
{
return VRGripInterfaceSettings.OnTeleportBehavior;
}
bool UGrippableSkeletalMeshComponent::SimulateOnDrop_Implementation()
{
return VRGripInterfaceSettings.bSimulateOnDrop;
}
EGripCollisionType UGrippableSkeletalMeshComponent::GetPrimaryGripType_Implementation(bool bIsSlot)
{
return bIsSlot ? VRGripInterfaceSettings.SlotDefaultGripType : VRGripInterfaceSettings.FreeDefaultGripType;
}
ESecondaryGripType UGrippableSkeletalMeshComponent::SecondaryGripType_Implementation()
{
return VRGripInterfaceSettings.SecondaryGripType;
}
EGripMovementReplicationSettings UGrippableSkeletalMeshComponent::GripMovementReplicationType_Implementation()
{
return VRGripInterfaceSettings.MovementReplicationType;
}
EGripLateUpdateSettings UGrippableSkeletalMeshComponent::GripLateUpdateSetting_Implementation()
{
return VRGripInterfaceSettings.LateUpdateSetting;
}
void UGrippableSkeletalMeshComponent::GetGripStiffnessAndDamping_Implementation(float &GripStiffnessOut, float &GripDampingOut)
{
GripStiffnessOut = VRGripInterfaceSettings.ConstraintStiffness;
GripDampingOut = VRGripInterfaceSettings.ConstraintDamping;
}
FBPAdvGripSettings UGrippableSkeletalMeshComponent::AdvancedGripSettings_Implementation()
{
return VRGripInterfaceSettings.AdvancedGripSettings;
}
float UGrippableSkeletalMeshComponent::GripBreakDistance_Implementation()
{
return VRGripInterfaceSettings.ConstraintBreakDistance;
}
void UGrippableSkeletalMeshComponent::ClosestGripSlotInRange_Implementation(FVector WorldLocation, bool bSecondarySlot, bool & bHadSlotInRange, FTransform & SlotWorldTransform, FName & SlotName, UGripMotionControllerComponent * CallingController, FName OverridePrefix)
{
if (OverridePrefix.IsNone())
bSecondarySlot ? OverridePrefix = "VRGripS" : OverridePrefix = "VRGripP";
UVRExpansionFunctionLibrary::GetGripSlotInRangeByTypeName_Component(OverridePrefix, this, WorldLocation, bSecondarySlot ? VRGripInterfaceSettings.SecondarySlotRange : VRGripInterfaceSettings.PrimarySlotRange, bHadSlotInRange, SlotWorldTransform, SlotName, CallingController);
}
bool UGrippableSkeletalMeshComponent::AllowsMultipleGrips_Implementation()
{
return VRGripInterfaceSettings.bAllowMultipleGrips;
}
void UGrippableSkeletalMeshComponent::IsHeld_Implementation(TArray<FBPGripPair> & HoldingControllers, bool & bIsHeld)
{
HoldingControllers = VRGripInterfaceSettings.HoldingControllers;
bIsHeld = VRGripInterfaceSettings.bIsHeld;
}
void UGrippableSkeletalMeshComponent::Native_NotifyThrowGripDelegates(UGripMotionControllerComponent* Controller, bool bGripped, const FBPActorGripInformation& GripInformation, bool bWasSocketed)
{
if (bGripped)
{
OnGripped.Broadcast(Controller, GripInformation);
}
else
{
OnDropped.Broadcast(Controller, GripInformation, bWasSocketed);
}
}
void UGrippableSkeletalMeshComponent::SetHeld_Implementation(UGripMotionControllerComponent * HoldingController, uint8 GripID, bool bIsHeld)
{
if (bIsHeld)
{
if (VRGripInterfaceSettings.MovementReplicationType != EGripMovementReplicationSettings::ForceServerSideMovement)
{
if (!VRGripInterfaceSettings.bIsHeld)
bOriginalReplicatesMovement = bReplicateMovement;
bReplicateMovement = false;
}
VRGripInterfaceSettings.bWasHeld = true;
VRGripInterfaceSettings.HoldingControllers.AddUnique(FBPGripPair(HoldingController, GripID));
}
else
{
if (VRGripInterfaceSettings.MovementReplicationType != EGripMovementReplicationSettings::ForceServerSideMovement)
{
bReplicateMovement = bOriginalReplicatesMovement;
}
VRGripInterfaceSettings.HoldingControllers.Remove(FBPGripPair(HoldingController, GripID));
}
VRGripInterfaceSettings.bIsHeld = VRGripInterfaceSettings.HoldingControllers.Num() > 0;
}
/*FBPInteractionSettings UGrippableSkeletalMeshComponent::GetInteractionSettings_Implementation()
{
return VRGripInterfaceSettings.InteractionSettings;
}*/
bool UGrippableSkeletalMeshComponent::GetGripScripts_Implementation(TArray<UVRGripScriptBase*> & ArrayReference)
{
ArrayReference = GripLogicScripts;
return GripLogicScripts.Num() > 0;
}
void UGrippableSkeletalMeshComponent::PreDestroyFromReplication()
{
Super::PreDestroyFromReplication();
// Destroy any sub-objects we created
for (int32 i = 0; i < GripLogicScripts.Num(); ++i)
{
if (UObject *SubObject = GripLogicScripts[i])
{
SubObject->PreDestroyFromReplication();
SubObject->MarkAsGarbage();
}
}
GripLogicScripts.Empty();
}
void UGrippableSkeletalMeshComponent::GetSubobjectsWithStableNamesForNetworking(TArray<UObject*> &ObjList)
{
if (bReplicateGripScripts)
{
for (int32 i = 0; i < GripLogicScripts.Num(); ++i)
{
if (UObject* SubObject = GripLogicScripts[i])
{
ObjList.Add(SubObject);
}
}
}
}
void UGrippableSkeletalMeshComponent::OnComponentDestroyed(bool bDestroyingHierarchy)
{
// Call the super at the end, after we've done what we needed to do
Super::OnComponentDestroyed(bDestroyingHierarchy);
// Don't set these in editor preview window and the like, it causes saving issues
if (UWorld * World = GetWorld())
{
EWorldType::Type WorldType = World->WorldType;
if (WorldType == EWorldType::Editor || WorldType == EWorldType::EditorPreview)
{
return;
}
}
for (int32 i = 0; i < GripLogicScripts.Num(); i++)
{
if (UObject *SubObject = GripLogicScripts[i])
{
SubObject->MarkAsGarbage();
}
}
GripLogicScripts.Empty();
}
void UGrippableSkeletalMeshComponent::GetWeldedBodies(TArray<FBodyInstance*>& OutWeldedBodies, TArray<FName>& OutLabels, bool bIncludingAutoWeld)
{
UPhysicsAsset* PhysicsAsset = GetPhysicsAsset();
for (int32 BodyIdx = 0; BodyIdx < Bodies.Num(); ++BodyIdx)
{
FBodyInstance* BI = Bodies[BodyIdx];
if (BI && (BI->WeldParent != nullptr || (bIncludingAutoWeld && BI->bAutoWeld)))
{
OutWeldedBodies.Add(BI);
if (PhysicsAsset)
{
if (UBodySetup* PhysicsAssetBodySetup = PhysicsAsset->SkeletalBodySetups[BodyIdx])
{
OutLabels.Add(PhysicsAssetBodySetup->BoneName);
}
else
{
OutLabels.Add(NAME_None);
}
}
else
{
OutLabels.Add(NAME_None);
}
}
}
for (USceneComponent* Child : GetAttachChildren())
{
if (UPrimitiveComponent* PrimChild = Cast<UPrimitiveComponent>(Child))
{
PrimChild->GetWeldedBodies(OutWeldedBodies, OutLabels, bIncludingAutoWeld);
}
}
}
FBodyInstance* UGrippableSkeletalMeshComponent::GetBodyInstance(FName BoneName, bool bGetWelded, int32) const
{
UPhysicsAsset* const PhysicsAsset = GetPhysicsAsset();
FBodyInstance* BodyInst = NULL;
if (PhysicsAsset != NULL)
{
// A name of NAME_None indicates 'root body'
if (BoneName == NAME_None)
{
if (Bodies.IsValidIndex(RootBodyData.BodyIndex))
{
BodyInst = Bodies[RootBodyData.BodyIndex];
}
}
// otherwise, look for the body
else
{
int32 BodyIndex = PhysicsAsset->FindBodyIndex(BoneName);
if (Bodies.IsValidIndex(BodyIndex))
{
BodyInst = Bodies[BodyIndex];
}
}
BodyInst = (bGetWelded && BodyInstance.WeldParent) ? BodyInstance.WeldParent : BodyInst;
}
return BodyInst;
}

View file

@ -0,0 +1,322 @@
// Copyright 1998-2016 Epic Games, Inc. All Rights Reserved.
#include "Grippables/GrippableSphereComponent.h"
#include UE_INLINE_GENERATED_CPP_BY_NAME(GrippableSphereComponent)
#include "GripMotionControllerComponent.h"
#include "VRExpansionFunctionLibrary.h"
#include "GripScripts/VRGripScriptBase.h"
#include "Net/UnrealNetwork.h"
//=============================================================================
UGrippableSphereComponent::UGrippableSphereComponent(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{
VRGripInterfaceSettings.bDenyGripping = false;
VRGripInterfaceSettings.OnTeleportBehavior = EGripInterfaceTeleportBehavior::DropOnTeleport;
VRGripInterfaceSettings.bSimulateOnDrop = true;
VRGripInterfaceSettings.SlotDefaultGripType = EGripCollisionType::ManipulationGrip;
VRGripInterfaceSettings.FreeDefaultGripType = EGripCollisionType::ManipulationGrip;
VRGripInterfaceSettings.SecondaryGripType = ESecondaryGripType::SG_None;
VRGripInterfaceSettings.MovementReplicationType = EGripMovementReplicationSettings::ForceClientSideMovement;
VRGripInterfaceSettings.LateUpdateSetting = EGripLateUpdateSettings::LateUpdatesAlwaysOff;
VRGripInterfaceSettings.ConstraintStiffness = 1500.0f;
VRGripInterfaceSettings.ConstraintDamping = 200.0f;
VRGripInterfaceSettings.ConstraintBreakDistance = 100.0f;
VRGripInterfaceSettings.SecondarySlotRange = 20.0f;
VRGripInterfaceSettings.PrimarySlotRange = 20.0f;
bReplicateMovement = false;
//this->bReplicates = true;
VRGripInterfaceSettings.bIsHeld = false;
bRepGripSettingsAndGameplayTags = true;
bReplicateGripScripts = false;
// #TODO we can register them maybe in the future
// Don't use the replicated list, use our custom replication instead
bReplicateUsingRegisteredSubObjectList = false;
}
void UGrippableSphereComponent::GetLifetimeReplicatedProps(TArray< class FLifetimeProperty > & OutLifetimeProps) const
{
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
DOREPLIFETIME_CONDITION(UGrippableSphereComponent, GripLogicScripts, COND_Custom);
DOREPLIFETIME(UGrippableSphereComponent, bReplicateGripScripts)
DOREPLIFETIME(UGrippableSphereComponent, bRepGripSettingsAndGameplayTags);
DOREPLIFETIME(UGrippableSphereComponent, bReplicateMovement);
DOREPLIFETIME_CONDITION(UGrippableSphereComponent, VRGripInterfaceSettings, COND_Custom);
DOREPLIFETIME_CONDITION(UGrippableSphereComponent, GameplayTags, COND_Custom);
}
void UGrippableSphereComponent::PreReplication(IRepChangedPropertyTracker & ChangedPropertyTracker)
{
Super::PreReplication(ChangedPropertyTracker);
// Don't replicate if set to not do it
DOREPLIFETIME_ACTIVE_OVERRIDE_FAST(UGrippableSphereComponent, VRGripInterfaceSettings, bRepGripSettingsAndGameplayTags);
DOREPLIFETIME_ACTIVE_OVERRIDE_FAST(UGrippableSphereComponent, GameplayTags, bRepGripSettingsAndGameplayTags);
DOREPLIFETIME_ACTIVE_OVERRIDE_FAST(UGrippableSphereComponent, GripLogicScripts, bReplicateGripScripts);
DOREPLIFETIME_ACTIVE_OVERRIDE_FAST(USceneComponent, RelativeLocation, bReplicateMovement);
DOREPLIFETIME_ACTIVE_OVERRIDE_FAST(USceneComponent, RelativeRotation, bReplicateMovement);
DOREPLIFETIME_ACTIVE_OVERRIDE_FAST(USceneComponent, RelativeScale3D, bReplicateMovement);
}
bool UGrippableSphereComponent::ReplicateSubobjects(UActorChannel* Channel, class FOutBunch *Bunch, FReplicationFlags *RepFlags)
{
bool WroteSomething = Super::ReplicateSubobjects(Channel, Bunch, RepFlags);
if (bReplicateGripScripts)
{
for (UVRGripScriptBase* Script : GripLogicScripts)
{
if (Script && IsValid(Script))
{
WroteSomething |= Channel->ReplicateSubobject(Script, *Bunch, *RepFlags);
}
}
}
return WroteSomething;
}
//=============================================================================
UGrippableSphereComponent::~UGrippableSphereComponent()
{
}
void UGrippableSphereComponent::BeginPlay()
{
// Call the base class
Super::BeginPlay();
// Call all grip scripts begin play events so they can perform any needed logic
for (UVRGripScriptBase* Script : GripLogicScripts)
{
if (Script)
{
Script->BeginPlay(this);
}
}
bOriginalReplicatesMovement = bReplicateMovement;
}
void UGrippableSphereComponent::EndPlay(const EEndPlayReason::Type EndPlayReason)
{
// Call the base class
Super::EndPlay(EndPlayReason);
// Call all grip scripts begin play events so they can perform any needed logic
for (UVRGripScriptBase* Script : GripLogicScripts)
{
if (Script)
{
Script->EndPlay(EndPlayReason);
}
}
}
void UGrippableSphereComponent::SetDenyGripping(bool bDenyGripping)
{
VRGripInterfaceSettings.bDenyGripping = bDenyGripping;
}
void UGrippableSphereComponent::SetGripPriority(int NewGripPriority)
{
VRGripInterfaceSettings.AdvancedGripSettings.GripPriority = NewGripPriority;
}
void UGrippableSphereComponent::TickGrip_Implementation(UGripMotionControllerComponent * GrippingController, const FBPActorGripInformation & GripInformation, float DeltaTime) {}
void UGrippableSphereComponent::OnGrip_Implementation(UGripMotionControllerComponent* GrippingController, const FBPActorGripInformation& GripInformation) {}
void UGrippableSphereComponent::OnGripRelease_Implementation(UGripMotionControllerComponent* ReleasingController, const FBPActorGripInformation& GripInformation, bool bWasSocketed) { }
void UGrippableSphereComponent::OnChildGrip_Implementation(UGripMotionControllerComponent* GrippingController, const FBPActorGripInformation& GripInformation) {}
void UGrippableSphereComponent::OnChildGripRelease_Implementation(UGripMotionControllerComponent* ReleasingController, const FBPActorGripInformation& GripInformation, bool bWasSocketed) {}
void UGrippableSphereComponent::OnSecondaryGrip_Implementation(UGripMotionControllerComponent* GripOwningController, USceneComponent* SecondaryGripComponent, const FBPActorGripInformation& GripInformation) { OnSecondaryGripAdded.Broadcast(GripOwningController, GripInformation); }
void UGrippableSphereComponent::OnSecondaryGripRelease_Implementation(UGripMotionControllerComponent* GripOwningController, USceneComponent* ReleasingSecondaryGripComponent, const FBPActorGripInformation& GripInformation) { OnSecondaryGripRemoved.Broadcast(GripOwningController, GripInformation); }
void UGrippableSphereComponent::OnUsed_Implementation() {}
void UGrippableSphereComponent::OnEndUsed_Implementation() {}
void UGrippableSphereComponent::OnSecondaryUsed_Implementation() {}
void UGrippableSphereComponent::OnEndSecondaryUsed_Implementation() {}
void UGrippableSphereComponent::OnInput_Implementation(FKey Key, EInputEvent KeyEvent) {}
bool UGrippableSphereComponent::RequestsSocketing_Implementation(USceneComponent *& ParentToSocketTo, FName & OptionalSocketName, FTransform_NetQuantize & RelativeTransform) { return false; }
bool UGrippableSphereComponent::DenyGripping_Implementation(UGripMotionControllerComponent * GripInitiator)
{
return VRGripInterfaceSettings.bDenyGripping;
}
EGripInterfaceTeleportBehavior UGrippableSphereComponent::TeleportBehavior_Implementation()
{
return VRGripInterfaceSettings.OnTeleportBehavior;
}
bool UGrippableSphereComponent::SimulateOnDrop_Implementation()
{
return VRGripInterfaceSettings.bSimulateOnDrop;
}
EGripCollisionType UGrippableSphereComponent::GetPrimaryGripType_Implementation(bool bIsSlot)
{
return bIsSlot ? VRGripInterfaceSettings.SlotDefaultGripType : VRGripInterfaceSettings.FreeDefaultGripType;
}
ESecondaryGripType UGrippableSphereComponent::SecondaryGripType_Implementation()
{
return VRGripInterfaceSettings.SecondaryGripType;
}
EGripMovementReplicationSettings UGrippableSphereComponent::GripMovementReplicationType_Implementation()
{
return VRGripInterfaceSettings.MovementReplicationType;
}
EGripLateUpdateSettings UGrippableSphereComponent::GripLateUpdateSetting_Implementation()
{
return VRGripInterfaceSettings.LateUpdateSetting;
}
void UGrippableSphereComponent::GetGripStiffnessAndDamping_Implementation(float &GripStiffnessOut, float &GripDampingOut)
{
GripStiffnessOut = VRGripInterfaceSettings.ConstraintStiffness;
GripDampingOut = VRGripInterfaceSettings.ConstraintDamping;
}
FBPAdvGripSettings UGrippableSphereComponent::AdvancedGripSettings_Implementation()
{
return VRGripInterfaceSettings.AdvancedGripSettings;
}
float UGrippableSphereComponent::GripBreakDistance_Implementation()
{
return VRGripInterfaceSettings.ConstraintBreakDistance;
}
void UGrippableSphereComponent::ClosestGripSlotInRange_Implementation(FVector WorldLocation, bool bSecondarySlot, bool & bHadSlotInRange, FTransform & SlotWorldTransform, FName & SlotName, UGripMotionControllerComponent * CallingController, FName OverridePrefix)
{
if (OverridePrefix.IsNone())
bSecondarySlot ? OverridePrefix = "VRGripS" : OverridePrefix = "VRGripP";
UVRExpansionFunctionLibrary::GetGripSlotInRangeByTypeName_Component(OverridePrefix, this, WorldLocation, bSecondarySlot ? VRGripInterfaceSettings.SecondarySlotRange : VRGripInterfaceSettings.PrimarySlotRange, bHadSlotInRange, SlotWorldTransform, SlotName, CallingController);
}
bool UGrippableSphereComponent::AllowsMultipleGrips_Implementation()
{
return VRGripInterfaceSettings.bAllowMultipleGrips;
}
void UGrippableSphereComponent::IsHeld_Implementation(TArray<FBPGripPair> & HoldingControllers, bool & bIsHeld)
{
HoldingControllers = VRGripInterfaceSettings.HoldingControllers;
bIsHeld = VRGripInterfaceSettings.bIsHeld;
}
void UGrippableSphereComponent::Native_NotifyThrowGripDelegates(UGripMotionControllerComponent* Controller, bool bGripped, const FBPActorGripInformation& GripInformation, bool bWasSocketed)
{
if (bGripped)
{
OnGripped.Broadcast(Controller, GripInformation);
}
else
{
OnDropped.Broadcast(Controller, GripInformation, bWasSocketed);
}
}
void UGrippableSphereComponent::SetHeld_Implementation(UGripMotionControllerComponent * HoldingController, uint8 GripID, bool bIsHeld)
{
if (bIsHeld)
{
if (VRGripInterfaceSettings.MovementReplicationType != EGripMovementReplicationSettings::ForceServerSideMovement)
{
if (!VRGripInterfaceSettings.bIsHeld)
bOriginalReplicatesMovement = bReplicateMovement;
bReplicateMovement = false;
}
VRGripInterfaceSettings.bWasHeld = true;
VRGripInterfaceSettings.HoldingControllers.AddUnique(FBPGripPair(HoldingController, GripID));
}
else
{
if (VRGripInterfaceSettings.MovementReplicationType != EGripMovementReplicationSettings::ForceServerSideMovement)
{
bReplicateMovement = bOriginalReplicatesMovement;
}
VRGripInterfaceSettings.HoldingControllers.Remove(FBPGripPair(HoldingController, GripID));
}
VRGripInterfaceSettings.bIsHeld = VRGripInterfaceSettings.HoldingControllers.Num() > 0;
}
/*FBPInteractionSettings UGrippableSphereComponent::GetInteractionSettings_Implementation()
{
return VRGripInterfaceSettings.InteractionSettings;
}*/
bool UGrippableSphereComponent::GetGripScripts_Implementation(TArray<UVRGripScriptBase*> & ArrayReference)
{
ArrayReference = GripLogicScripts;
return GripLogicScripts.Num() > 0;
}
void UGrippableSphereComponent::PreDestroyFromReplication()
{
Super::PreDestroyFromReplication();
// Destroy any sub-objects we created
for (int32 i = 0; i < GripLogicScripts.Num(); ++i)
{
if (UObject *SubObject = GripLogicScripts[i])
{
SubObject->PreDestroyFromReplication();
SubObject->MarkAsGarbage();
}
}
GripLogicScripts.Empty();
}
void UGrippableSphereComponent::GetSubobjectsWithStableNamesForNetworking(TArray<UObject*> &ObjList)
{
if (bReplicateGripScripts)
{
for (int32 i = 0; i < GripLogicScripts.Num(); ++i)
{
if (UObject* SubObject = GripLogicScripts[i])
{
ObjList.Add(SubObject);
}
}
}
}
void UGrippableSphereComponent::OnComponentDestroyed(bool bDestroyingHierarchy)
{
// Call the super at the end, after we've done what we needed to do
Super::OnComponentDestroyed(bDestroyingHierarchy);
// Don't set these in editor preview window and the like, it causes saving issues
if (UWorld * World = GetWorld())
{
EWorldType::Type WorldType = World->WorldType;
if (WorldType == EWorldType::Editor || WorldType == EWorldType::EditorPreview)
{
return;
}
}
for (int32 i = 0; i < GripLogicScripts.Num(); i++)
{
if (UObject *SubObject = GripLogicScripts[i])
{
SubObject->MarkAsGarbage();
}
}
GripLogicScripts.Empty();
}

View file

@ -0,0 +1,861 @@
// Copyright 1998-2016 Epic Games, Inc. All Rights Reserved.
#include "Grippables/GrippableStaticMeshActor.h"
#include UE_INLINE_GENERATED_CPP_BY_NAME(GrippableStaticMeshActor)
#include "TimerManager.h"
#include "GameFramework/PlayerController.h"
#include "GameFramework/PlayerState.h"
#include "GripMotionControllerComponent.h"
#include "VRExpansionFunctionLibrary.h"
#include "Misc/BucketUpdateSubsystem.h"
#include "Net/UnrealNetwork.h"
#include "PhysicsReplication.h"
#include "GripScripts/VRGripScriptBase.h"
#include "DrawDebugHelpers.h"
#include "Physics/Experimental/PhysScene_Chaos.h"
#if WITH_PUSH_MODEL
#include "Net/Core/PushModel/PushModel.h"
#endif
// #TODO: Pull request this? This macro could be very useful
/*#define DOREPLIFETIME_CHANGE_NOTIFY(c,v,rncond) \
{ \
static UProperty* sp##v = GetReplicatedProperty(StaticClass(), c::StaticClass(),GET_MEMBER_NAME_CHECKED(c,v)); \
bool bFound = false; \
for ( int32 i = 0; i < OutLifetimeProps.Num(); i++ ) \
{ \
if ( OutLifetimeProps[i].RepIndex == sp##v->RepIndex ) \
{ \
for ( int32 j = 0; j < sp##v->ArrayDim; j++ ) \
{ \
OutLifetimeProps[i + j].RepNotifyCondition = rncond; \
} \
bFound = true; \
break; \
} \
} \
check( bFound ); \
}*/
UOptionalRepStaticMeshComponent::UOptionalRepStaticMeshComponent(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{
bReplicateMovement = true;
}
void UOptionalRepStaticMeshComponent::PreReplication(IRepChangedPropertyTracker & ChangedPropertyTracker)
{
Super::PreReplication(ChangedPropertyTracker);
// Don't replicate if set to not do it
DOREPLIFETIME_ACTIVE_OVERRIDE_FAST(USceneComponent, RelativeLocation, bReplicateMovement);
DOREPLIFETIME_ACTIVE_OVERRIDE_FAST(USceneComponent, RelativeRotation, bReplicateMovement);
DOREPLIFETIME_ACTIVE_OVERRIDE_FAST(USceneComponent, RelativeScale3D, bReplicateMovement);
}
void UOptionalRepStaticMeshComponent::GetLifetimeReplicatedProps(TArray< class FLifetimeProperty > & OutLifetimeProps) const
{
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
DOREPLIFETIME(UOptionalRepStaticMeshComponent, bReplicateMovement);
}
//=============================================================================
AGrippableStaticMeshActor::AGrippableStaticMeshActor(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer.SetDefaultSubobjectClass<UOptionalRepStaticMeshComponent>(TEXT("StaticMeshComponent0")))
{
VRGripInterfaceSettings.bDenyGripping = false;
VRGripInterfaceSettings.OnTeleportBehavior = EGripInterfaceTeleportBehavior::TeleportAllComponents;
VRGripInterfaceSettings.bSimulateOnDrop = true;
VRGripInterfaceSettings.SlotDefaultGripType = EGripCollisionType::InteractiveCollisionWithPhysics;
VRGripInterfaceSettings.FreeDefaultGripType = EGripCollisionType::InteractiveCollisionWithPhysics;
VRGripInterfaceSettings.SecondaryGripType = ESecondaryGripType::SG_None;
VRGripInterfaceSettings.MovementReplicationType = EGripMovementReplicationSettings::ForceClientSideMovement;
VRGripInterfaceSettings.LateUpdateSetting = EGripLateUpdateSettings::NotWhenCollidingOrDoubleGripping;
VRGripInterfaceSettings.ConstraintStiffness = 1500.0f;
VRGripInterfaceSettings.ConstraintDamping = 200.0f;
VRGripInterfaceSettings.ConstraintBreakDistance = 100.0f;
VRGripInterfaceSettings.SecondarySlotRange = 20.0f;
VRGripInterfaceSettings.PrimarySlotRange = 20.0f;
VRGripInterfaceSettings.bIsHeld = false;
this->SetMobility(EComponentMobility::Movable);
// Default replication on for multiplayer
//this->bNetLoadOnClient = false;
SetReplicatingMovement(true);
this->bReplicates = true;
bRepGripSettingsAndGameplayTags = true;
bReplicateGripScripts = false;
// #TODO we can register them maybe in the future
// Don't use the replicated list, use our custom replication instead
bReplicateUsingRegisteredSubObjectList = false;
bAllowIgnoringAttachOnOwner = 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
MinNetUpdateFrequency = 30.0f;
}
void AGrippableStaticMeshActor::GetLifetimeReplicatedProps(TArray< class FLifetimeProperty > & OutLifetimeProps) const
{
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
DOREPLIFETIME_CONDITION(AGrippableStaticMeshActor, GripLogicScripts, COND_Custom);
DOREPLIFETIME(AGrippableStaticMeshActor, bReplicateGripScripts);
DOREPLIFETIME(AGrippableStaticMeshActor, bRepGripSettingsAndGameplayTags);
DOREPLIFETIME(AGrippableStaticMeshActor, bAllowIgnoringAttachOnOwner);
DOREPLIFETIME(AGrippableStaticMeshActor, ClientAuthReplicationData);
DOREPLIFETIME_CONDITION(AGrippableStaticMeshActor, VRGripInterfaceSettings, COND_Custom);
DOREPLIFETIME_CONDITION(AGrippableStaticMeshActor, GameplayTags, COND_Custom);
DISABLE_REPLICATED_PRIVATE_PROPERTY(AActor, AttachmentReplication);
FDoRepLifetimeParams AttachmentReplicationParams{ COND_Custom, REPNOTIFY_Always, /*bIsPushBased=*/true };
DOREPLIFETIME_WITH_PARAMS_FAST(AGrippableStaticMeshActor, AttachmentWeldReplication, AttachmentReplicationParams);
}
void AGrippableStaticMeshActor::PreReplication(IRepChangedPropertyTracker & ChangedPropertyTracker)
{
//Super::PreReplication(ChangedPropertyTracker);
DOREPLIFETIME_ACTIVE_OVERRIDE_FAST(AGrippableStaticMeshActor, VRGripInterfaceSettings, bRepGripSettingsAndGameplayTags);
DOREPLIFETIME_ACTIVE_OVERRIDE_FAST(AGrippableStaticMeshActor, GameplayTags, bRepGripSettingsAndGameplayTags);
DOREPLIFETIME_ACTIVE_OVERRIDE_FAST(AGrippableStaticMeshActor, GripLogicScripts, bReplicateGripScripts);
//Super::PreReplication(ChangedPropertyTracker);
#if WITH_PUSH_MODEL
const AActor* const OldAttachParent = AttachmentWeldReplication.AttachParent;
const UActorComponent* const OldAttachComponent = AttachmentWeldReplication.AttachComponent;
#endif
// Attachment replication gets filled in by GatherCurrentMovement(), but in the case of a detached root we need to trigger remote detachment.
AttachmentWeldReplication.AttachParent = nullptr;
AttachmentWeldReplication.AttachComponent = nullptr;
GatherCurrentMovement();
DOREPLIFETIME_ACTIVE_OVERRIDE_FAST(AActor, ReplicatedMovement, IsReplicatingMovement());
// Don't need to replicate AttachmentReplication if the root component replicates, because it already handles it.
DOREPLIFETIME_ACTIVE_OVERRIDE_FAST(AGrippableStaticMeshActor, AttachmentWeldReplication, RootComponent && !RootComponent->GetIsReplicated());
// Don't need to replicate AttachmentReplication if the root component replicates, because it already handles it.
DOREPLIFETIME_ACTIVE_OVERRIDE_FAST(AActor, AttachmentReplication, false);// RootComponent && !RootComponent->GetIsReplicated());
#if WITH_PUSH_MODEL
if (UNLIKELY(OldAttachParent != AttachmentWeldReplication.AttachParent || OldAttachComponent != AttachmentWeldReplication.AttachComponent))
{
MARK_PROPERTY_DIRTY_FROM_NAME(AGrippableStaticMeshActor, AttachmentWeldReplication, this);
}
#endif
/*PRAGMA_DISABLE_DEPRECATION_WARNINGS
UBlueprintGeneratedClass* BPClass = Cast<UBlueprintGeneratedClass>(GetClass());
if (BPClass != nullptr)
{
BPClass->InstancePreReplication(this, ChangedPropertyTracker);
}
PRAGMA_ENABLE_DEPRECATION_WARNINGS*/
}
void AGrippableStaticMeshActor::GatherCurrentMovement()
{
if (IsReplicatingMovement() || (RootComponent && RootComponent->GetAttachParent()))
{
bool bWasAttachmentModified = false;
bool bWasRepMovementModified = false;
AActor* OldAttachParent = AttachmentWeldReplication.AttachParent;
USceneComponent* OldAttachComponent = AttachmentWeldReplication.AttachComponent;
AttachmentWeldReplication.AttachParent = nullptr;
AttachmentWeldReplication.AttachComponent = nullptr;
FRepMovement& RepMovement = GetReplicatedMovement_Mutable();
UPrimitiveComponent* RootPrimComp = Cast<UPrimitiveComponent>(GetRootComponent());
if (RootPrimComp && RootPrimComp->IsSimulatingPhysics())
{
#if UE_WITH_IRIS
const bool bPrevRepPhysics = GetReplicatedMovement_Mutable().bRepPhysics;
#endif // UE_WITH_IRIS
bool bFoundInCache = false;
UWorld* World = GetWorld();
int ServerFrame = 0;
if (FPhysScene_Chaos* Scene = static_cast<FPhysScene_Chaos*>(World->GetPhysicsScene()))
{
if (const FRigidBodyState* FoundState = Scene->GetStateFromReplicationCache(RootPrimComp, ServerFrame))
{
RepMovement.FillFrom(*FoundState, this, Scene->ReplicationCache.ServerFrame);
bFoundInCache = true;
}
}
if (!bFoundInCache)
{
// fallback to GT data
FRigidBodyState RBState;
RootPrimComp->GetRigidBodyState(RBState);
RepMovement.FillFrom(RBState, this, 0);
}
// Don't replicate movement if we're welded to another parent actor.
// Their replication will affect our position indirectly since we are attached.
RepMovement.bRepPhysics = !RootPrimComp->IsWelded();
if (!RepMovement.bRepPhysics)
{
if (RootComponent->GetAttachParent() != nullptr)
{
// Networking for attachments assumes the RootComponent of the AttachParent actor.
// If that's not the case, we can't update this, as the client wouldn't be able to resolve the Component and would detach as a result.
AttachmentWeldReplication.AttachParent = RootComponent->GetAttachParent()->GetAttachmentRootActor();
if (AttachmentWeldReplication.AttachParent != nullptr)
{
AttachmentWeldReplication.LocationOffset = RootComponent->GetRelativeLocation();
AttachmentWeldReplication.RotationOffset = RootComponent->GetRelativeRotation();
AttachmentWeldReplication.RelativeScale3D = RootComponent->GetRelativeScale3D();
AttachmentWeldReplication.AttachComponent = RootComponent->GetAttachParent();
AttachmentWeldReplication.AttachSocket = RootComponent->GetAttachSocketName();
AttachmentWeldReplication.bIsWelded = RootPrimComp ? RootPrimComp->IsWelded() : false;
// Technically, the values might have stayed the same, but we'll just assume they've changed.
bWasAttachmentModified = true;
}
}
}
// Technically, the values might have stayed the same, but we'll just assume they've changed.
bWasRepMovementModified = true;
#if UE_WITH_IRIS
// If RepPhysics has changed value then notify the ReplicationSystem
if (bPrevRepPhysics != GetReplicatedMovement_Mutable().bRepPhysics)
{
UpdateReplicatePhysicsCondition();
}
#endif // UE_WITH_IRIS
}
else if (RootComponent != nullptr)
{
// If we are attached, don't replicate absolute position, use AttachmentReplication instead.
if (RootComponent->GetAttachParent() != nullptr)
{
// Networking for attachments assumes the RootComponent of the AttachParent actor.
// If that's not the case, we can't update this, as the client wouldn't be able to resolve the Component and would detach as a result.
AttachmentWeldReplication.AttachParent = RootComponent->GetAttachParentActor();
if (AttachmentWeldReplication.AttachParent != nullptr)
{
AttachmentWeldReplication.LocationOffset = RootComponent->GetRelativeLocation();
AttachmentWeldReplication.RotationOffset = RootComponent->GetRelativeRotation();
AttachmentWeldReplication.RelativeScale3D = RootComponent->GetRelativeScale3D();
AttachmentWeldReplication.AttachComponent = RootComponent->GetAttachParent();
AttachmentWeldReplication.AttachSocket = RootComponent->GetAttachSocketName();
AttachmentWeldReplication.bIsWelded = RootPrimComp ? RootPrimComp->IsWelded() : false;
// Technically, the values might have stayed the same, but we'll just assume they've changed.
bWasAttachmentModified = true;
}
}
else
{
RepMovement.Location = FRepMovement::RebaseOntoZeroOrigin(RootComponent->GetComponentLocation(), this);
RepMovement.Rotation = RootComponent->GetComponentRotation();
RepMovement.LinearVelocity = GetVelocity();
RepMovement.AngularVelocity = FVector::ZeroVector;
// Technically, the values might have stayed the same, but we'll just assume they've changed.
bWasRepMovementModified = true;
}
bWasRepMovementModified = (bWasRepMovementModified || RepMovement.bRepPhysics);
RepMovement.bRepPhysics = false;
}
#if WITH_PUSH_MODEL
if (bWasRepMovementModified)
{
MARK_PROPERTY_DIRTY_FROM_NAME(AActor, ReplicatedMovement, this);
}
if (bWasAttachmentModified ||
OldAttachParent != AttachmentWeldReplication.AttachParent ||
OldAttachComponent != AttachmentWeldReplication.AttachComponent)
{
MARK_PROPERTY_DIRTY_FROM_NAME(AGrippableStaticMeshActor, AttachmentWeldReplication, this);
}
#endif
}
}
bool AGrippableStaticMeshActor::ReplicateSubobjects(UActorChannel* Channel, class FOutBunch *Bunch, FReplicationFlags *RepFlags)
{
bool WroteSomething = Super::ReplicateSubobjects(Channel, Bunch, RepFlags);
if (bReplicateGripScripts)
{
for (UVRGripScriptBase* Script : GripLogicScripts)
{
if (Script && IsValid(Script))
{
WroteSomething |= Channel->ReplicateSubobject(Script, *Bunch, *RepFlags);
}
}
}
return WroteSomething;
}
//=============================================================================
AGrippableStaticMeshActor::~AGrippableStaticMeshActor()
{
}
void AGrippableStaticMeshActor::BeginPlay()
{
// Call the base class
Super::BeginPlay();
// Call all grip scripts begin play events so they can perform any needed logic
for (UVRGripScriptBase* Script : GripLogicScripts)
{
if (Script)
{
Script->BeginPlay(this);
}
}
}
void AGrippableStaticMeshActor::SetDenyGripping(bool bDenyGripping)
{
VRGripInterfaceSettings.bDenyGripping = bDenyGripping;
}
void AGrippableStaticMeshActor::SetGripPriority(int NewGripPriority)
{
VRGripInterfaceSettings.AdvancedGripSettings.GripPriority = NewGripPriority;
}
void AGrippableStaticMeshActor::TickGrip_Implementation(UGripMotionControllerComponent * GrippingController, const FBPActorGripInformation & GripInformation, float DeltaTime) {}
void AGrippableStaticMeshActor::OnGrip_Implementation(UGripMotionControllerComponent* GrippingController, const FBPActorGripInformation& GripInformation) { }
void AGrippableStaticMeshActor::OnGripRelease_Implementation(UGripMotionControllerComponent* ReleasingController, const FBPActorGripInformation& GripInformation, bool bWasSocketed) { }
void AGrippableStaticMeshActor::OnChildGrip_Implementation(UGripMotionControllerComponent* GrippingController, const FBPActorGripInformation& GripInformation) {}
void AGrippableStaticMeshActor::OnChildGripRelease_Implementation(UGripMotionControllerComponent* ReleasingController, const FBPActorGripInformation& GripInformation, bool bWasSocketed) {}
void AGrippableStaticMeshActor::OnSecondaryGrip_Implementation(UGripMotionControllerComponent* GripOwningController, USceneComponent* SecondaryGripComponent, const FBPActorGripInformation& GripInformation) { OnSecondaryGripAdded.Broadcast(GripOwningController, GripInformation); }
void AGrippableStaticMeshActor::OnSecondaryGripRelease_Implementation(UGripMotionControllerComponent* GripOwningController, USceneComponent* ReleasingSecondaryGripComponent, const FBPActorGripInformation& GripInformation) { OnSecondaryGripRemoved.Broadcast(GripOwningController, GripInformation); }
void AGrippableStaticMeshActor::OnUsed_Implementation() {}
void AGrippableStaticMeshActor::OnEndUsed_Implementation() {}
void AGrippableStaticMeshActor::OnSecondaryUsed_Implementation() {}
void AGrippableStaticMeshActor::OnEndSecondaryUsed_Implementation() {}
void AGrippableStaticMeshActor::OnInput_Implementation(FKey Key, EInputEvent KeyEvent) {}
bool AGrippableStaticMeshActor::RequestsSocketing_Implementation(USceneComponent *& ParentToSocketTo, FName & OptionalSocketName, FTransform_NetQuantize & RelativeTransform) { return false; }
bool AGrippableStaticMeshActor::DenyGripping_Implementation(UGripMotionControllerComponent * GripInitiator)
{
return VRGripInterfaceSettings.bDenyGripping;
}
EGripInterfaceTeleportBehavior AGrippableStaticMeshActor::TeleportBehavior_Implementation()
{
return VRGripInterfaceSettings.OnTeleportBehavior;
}
bool AGrippableStaticMeshActor::SimulateOnDrop_Implementation()
{
return VRGripInterfaceSettings.bSimulateOnDrop;
}
EGripCollisionType AGrippableStaticMeshActor::GetPrimaryGripType_Implementation(bool bIsSlot)
{
return bIsSlot ? VRGripInterfaceSettings.SlotDefaultGripType : VRGripInterfaceSettings.FreeDefaultGripType;
}
ESecondaryGripType AGrippableStaticMeshActor::SecondaryGripType_Implementation()
{
return VRGripInterfaceSettings.SecondaryGripType;
}
EGripMovementReplicationSettings AGrippableStaticMeshActor::GripMovementReplicationType_Implementation()
{
return VRGripInterfaceSettings.MovementReplicationType;
}
EGripLateUpdateSettings AGrippableStaticMeshActor::GripLateUpdateSetting_Implementation()
{
return VRGripInterfaceSettings.LateUpdateSetting;
}
void AGrippableStaticMeshActor::GetGripStiffnessAndDamping_Implementation(float &GripStiffnessOut, float &GripDampingOut)
{
GripStiffnessOut = VRGripInterfaceSettings.ConstraintStiffness;
GripDampingOut = VRGripInterfaceSettings.ConstraintDamping;
}
FBPAdvGripSettings AGrippableStaticMeshActor::AdvancedGripSettings_Implementation()
{
return VRGripInterfaceSettings.AdvancedGripSettings;
}
float AGrippableStaticMeshActor::GripBreakDistance_Implementation()
{
return VRGripInterfaceSettings.ConstraintBreakDistance;
}
void AGrippableStaticMeshActor::ClosestGripSlotInRange_Implementation(FVector WorldLocation, bool bSecondarySlot, bool & bHadSlotInRange, FTransform & SlotWorldTransform, FName & SlotName, UGripMotionControllerComponent * CallingController, FName OverridePrefix)
{
if (OverridePrefix.IsNone())
bSecondarySlot ? OverridePrefix = "VRGripS" : OverridePrefix = "VRGripP";
UVRExpansionFunctionLibrary::GetGripSlotInRangeByTypeName(OverridePrefix, this, WorldLocation, bSecondarySlot ? VRGripInterfaceSettings.SecondarySlotRange : VRGripInterfaceSettings.PrimarySlotRange, bHadSlotInRange, SlotWorldTransform, SlotName, CallingController);
}
bool AGrippableStaticMeshActor::AllowsMultipleGrips_Implementation()
{
return VRGripInterfaceSettings.bAllowMultipleGrips;
}
void AGrippableStaticMeshActor::IsHeld_Implementation(TArray<FBPGripPair> & HoldingControllers, bool & bIsHeld)
{
HoldingControllers = VRGripInterfaceSettings.HoldingControllers;
bIsHeld = VRGripInterfaceSettings.bIsHeld;
}
bool AGrippableStaticMeshActor::AddToClientReplicationBucket()
{
if (ShouldWeSkipAttachmentReplication(false))
{
// The subsystem automatically removes entries with the same function signature so its safe to just always add here
GetWorld()->GetSubsystem<UBucketUpdateSubsystem>()->AddObjectToBucket(ClientAuthReplicationData.UpdateRate, this, FName(TEXT("PollReplicationEvent")));
ClientAuthReplicationData.bIsCurrentlyClientAuth = true;
if (UWorld * World = GetWorld())
ClientAuthReplicationData.TimeAtInitialThrow = World->GetTimeSeconds();
return true;
}
return false;
}
bool AGrippableStaticMeshActor::RemoveFromClientReplicationBucket()
{
if (ClientAuthReplicationData.bIsCurrentlyClientAuth)
{
GetWorld()->GetSubsystem<UBucketUpdateSubsystem>()->RemoveObjectFromBucketByFunctionName(this, FName(TEXT("PollReplicationEvent")));
CeaseReplicationBlocking();
return true;
}
return false;
}
void AGrippableStaticMeshActor::Native_NotifyThrowGripDelegates(UGripMotionControllerComponent* Controller, bool bGripped, const FBPActorGripInformation& GripInformation, bool bWasSocketed)
{
if (bGripped)
{
OnGripped.Broadcast(Controller, GripInformation);
}
else
{
OnDropped.Broadcast(Controller, GripInformation, bWasSocketed);
}
}
void AGrippableStaticMeshActor::SetHeld_Implementation(UGripMotionControllerComponent* HoldingController, uint8 GripID, bool bIsHeld)
{
if (bIsHeld)
{
VRGripInterfaceSettings.HoldingControllers.AddUnique(FBPGripPair(HoldingController, GripID));
RemoveFromClientReplicationBucket();
VRGripInterfaceSettings.bWasHeld = true;
VRGripInterfaceSettings.bIsHeld = VRGripInterfaceSettings.HoldingControllers.Num() > 0;
}
else
{
VRGripInterfaceSettings.HoldingControllers.Remove(FBPGripPair(HoldingController, GripID));
VRGripInterfaceSettings.bIsHeld = VRGripInterfaceSettings.HoldingControllers.Num() > 0;
if (ClientAuthReplicationData.bUseClientAuthThrowing && !VRGripInterfaceSettings.bIsHeld)
{
bool bWasLocallyOwned = HoldingController ? HoldingController->IsLocallyControlled() : false;
if (bWasLocallyOwned && ShouldWeSkipAttachmentReplication(false))
{
if (UPrimitiveComponent* PrimComp = Cast<UPrimitiveComponent>(GetRootComponent()))
{
if (PrimComp->IsSimulatingPhysics())
{
AddToClientReplicationBucket();
}
}
}
}
}
}
bool AGrippableStaticMeshActor::GetGripScripts_Implementation(TArray<UVRGripScriptBase*> & ArrayReference)
{
ArrayReference = GripLogicScripts;
return GripLogicScripts.Num() > 0;
}
bool AGrippableStaticMeshActor::PollReplicationEvent()
{
if (!ClientAuthReplicationData.bIsCurrentlyClientAuth || !this->HasLocalNetOwner() || VRGripInterfaceSettings.bIsHeld)
return false; // Tell the bucket subsystem to remove us from consideration
UWorld *OurWorld = GetWorld();
if (!OurWorld)
return false; // Tell the bucket subsystem to remove us from consideration
bool bRemoveBlocking = false;
if ((OurWorld->GetTimeSeconds() - ClientAuthReplicationData.TimeAtInitialThrow) > 10.0f)
{
// Lets time out sending, its been 10 seconds since we threw the object and its likely that it is conflicting with some server
// Authed movement that is forcing it to keep momentum.
//return false; // Tell the bucket subsystem to remove us from consideration
bRemoveBlocking = true;
}
// Store current transform for resting check
FTransform CurTransform = this->GetActorTransform();
if (!bRemoveBlocking)
{
if (!CurTransform.GetRotation().Equals(ClientAuthReplicationData.LastActorTransform.GetRotation()) || !CurTransform.GetLocation().Equals(ClientAuthReplicationData.LastActorTransform.GetLocation()))
{
ClientAuthReplicationData.LastActorTransform = CurTransform;
if (UPrimitiveComponent * PrimComp = Cast<UPrimitiveComponent>(RootComponent))
{
// Need to clamp to a max time since start, to handle cases with conflicting collisions
if (PrimComp->IsSimulatingPhysics() && ShouldWeSkipAttachmentReplication(false))
{
FRepMovementVR ClientAuthMovementRep;
if (ClientAuthMovementRep.GatherActorsMovement(this))
{
Server_GetClientAuthReplication(ClientAuthMovementRep);
if (PrimComp->RigidBodyIsAwake())
{
return true;
}
}
}
}
else
{
bRemoveBlocking = true;
//return false; // Tell the bucket subsystem to remove us from consideration
}
}
//else
// {
// Difference is too small, lets end sending location
//ClientAuthReplicationData.LastActorTransform = FTransform::Identity;
// }
}
bool TimedBlockingRelease = false;
AActor* TopOwner = GetOwner();
if (TopOwner != nullptr)
{
AActor * tempOwner = TopOwner->GetOwner();
// I have an owner so search that for the top owner
while (tempOwner)
{
TopOwner = tempOwner;
tempOwner = TopOwner->GetOwner();
}
if (APlayerController* PlayerController = Cast<APlayerController>(TopOwner))
{
if (APlayerState* PlayerState = PlayerController->PlayerState)
{
if (ClientAuthReplicationData.ResetReplicationHandle.IsValid())
{
OurWorld->GetTimerManager().ClearTimer(ClientAuthReplicationData.ResetReplicationHandle);
}
// Lets clamp the ping to a min / max value just in case
float clampedPing = FMath::Clamp(PlayerState->ExactPing * 0.001f, 0.0f, 1000.0f);
OurWorld->GetTimerManager().SetTimer(ClientAuthReplicationData.ResetReplicationHandle, this, &AGrippableStaticMeshActor::CeaseReplicationBlocking, clampedPing, false);
TimedBlockingRelease = true;
}
}
}
if (!TimedBlockingRelease)
{
CeaseReplicationBlocking();
}
// Tell server to kill us
Server_EndClientAuthReplication();
return false; // Tell the bucket subsystem to remove us from consideration
}
void AGrippableStaticMeshActor::CeaseReplicationBlocking()
{
if(ClientAuthReplicationData.bIsCurrentlyClientAuth)
ClientAuthReplicationData.bIsCurrentlyClientAuth = false;
ClientAuthReplicationData.LastActorTransform = FTransform::Identity;
if (ClientAuthReplicationData.ResetReplicationHandle.IsValid())
{
if (UWorld * OurWorld = GetWorld())
{
OurWorld->GetTimerManager().ClearTimer(ClientAuthReplicationData.ResetReplicationHandle);
}
}
}
void AGrippableStaticMeshActor::EndPlay(const EEndPlayReason::Type EndPlayReason)
{
RemoveFromClientReplicationBucket();
// Call all grip scripts begin play events so they can perform any needed logic
for (UVRGripScriptBase* Script : GripLogicScripts)
{
if (Script)
{
Script->EndPlay(EndPlayReason);
}
}
Super::EndPlay(EndPlayReason);
}
bool AGrippableStaticMeshActor::Server_EndClientAuthReplication_Validate()
{
return true;
}
void AGrippableStaticMeshActor::Server_EndClientAuthReplication_Implementation()
{
if (UWorld* World = GetWorld())
{
if (FPhysScene* PhysScene = World->GetPhysicsScene())
{
if (IPhysicsReplication* PhysicsReplication = PhysScene->GetPhysicsReplication())
{
PhysicsReplication->RemoveReplicatedTarget(this->GetStaticMeshComponent());
}
}
}
}
bool AGrippableStaticMeshActor::Server_GetClientAuthReplication_Validate(const FRepMovementVR & newMovement)
{
return true;
}
void AGrippableStaticMeshActor::Server_GetClientAuthReplication_Implementation(const FRepMovementVR & newMovement)
{
if (!VRGripInterfaceSettings.bIsHeld)
{
if (!newMovement.Location.ContainsNaN() && !newMovement.Rotation.ContainsNaN())
{
FRepMovement& MovementRep = GetReplicatedMovement_Mutable();
newMovement.CopyTo(MovementRep);
OnRep_ReplicatedMovement();
}
}
}
void AGrippableStaticMeshActor::OnRep_AttachmentReplication()
{
if (bAllowIgnoringAttachOnOwner && (ClientAuthReplicationData.bIsCurrentlyClientAuth || ShouldWeSkipAttachmentReplication()))
//if (bAllowIgnoringAttachOnOwner && ShouldWeSkipAttachmentReplication())
{
return;
}
if (AttachmentWeldReplication.AttachParent)
{
if (RootComponent)
{
USceneComponent* AttachParentComponent = (AttachmentWeldReplication.AttachComponent ? ToRawPtr(AttachmentWeldReplication.AttachComponent) : AttachmentWeldReplication.AttachParent->GetRootComponent());
if (AttachParentComponent)
{
RootComponent->SetRelativeLocation_Direct(AttachmentWeldReplication.LocationOffset);
RootComponent->SetRelativeRotation_Direct(AttachmentWeldReplication.RotationOffset);
RootComponent->SetRelativeScale3D_Direct(AttachmentWeldReplication.RelativeScale3D);
// If we're already attached to the correct Parent and Socket, then the update must be position only.
// AttachToComponent would early out in this case.
// Note, we ignore the special case for simulated bodies in AttachToComponent as AttachmentReplication shouldn't get updated
// if the body is simulated (see AActor::GatherMovement).
const bool bAlreadyAttached = (AttachParentComponent == RootComponent->GetAttachParent() && AttachmentWeldReplication.AttachSocket == RootComponent->GetAttachSocketName() && AttachParentComponent->GetAttachChildren().Contains(RootComponent));
if (bAlreadyAttached)
{
// Note, this doesn't match AttachToComponent, but we're assuming it's safe to skip physics (see comment above).
RootComponent->UpdateComponentToWorld(EUpdateTransformFlags::SkipPhysicsUpdate, ETeleportType::None);
}
else
{
FAttachmentTransformRules attachRules = FAttachmentTransformRules::KeepRelativeTransform;
attachRules.bWeldSimulatedBodies = AttachmentWeldReplication.bIsWelded;
RootComponent->AttachToComponent(AttachParentComponent, attachRules, AttachmentWeldReplication.AttachSocket);
}
}
}
}
else
{
DetachFromActor(FDetachmentTransformRules::KeepWorldTransform);
// Handle the case where an object was both detached and moved on the server in the same frame.
// Calling this extraneously does not hurt but will properly fire events if the movement state changed while attached.
// This is needed because client side movement is ignored when attached
if (IsReplicatingMovement())
{
OnRep_ReplicatedMovement();
}
}
}
void AGrippableStaticMeshActor::OnRep_ReplicateMovement()
{
if (bAllowIgnoringAttachOnOwner && (ClientAuthReplicationData.bIsCurrentlyClientAuth || ShouldWeSkipAttachmentReplication()))
//if (bAllowIgnoringAttachOnOwner && (ClientAuthReplicationData.bIsCurrentlyClientAuth || ShouldWeSkipAttachmentReplication()))
{
return;
}
if (RootComponent)
{
const FRepAttachment ReplicationAttachment = GetAttachmentReplication();
if (!ReplicationAttachment.AttachParent)
{
const FRepMovement& RepMove = GetReplicatedMovement();
// This "fix" corrects the simulation state not replicating over correctly
// If you turn off movement replication, simulate an object, turn movement replication back on and un-simulate, it never knows the difference
// This change ensures that it is checking against the current state
if (RootComponent->IsSimulatingPhysics() != RepMove.bRepPhysics)//SavedbRepPhysics != ReplicatedMovement.bRepPhysics)
{
// Turn on/off physics sim to match server.
SyncReplicatedPhysicsSimulation();
// It doesn't really hurt to run it here, the super can call it again but it will fail out as they already match
}
}
}
Super::OnRep_ReplicateMovement();
}
void AGrippableStaticMeshActor::OnRep_ReplicatedMovement()
{
if (bAllowIgnoringAttachOnOwner && (ClientAuthReplicationData.bIsCurrentlyClientAuth || ShouldWeSkipAttachmentReplication()))
//if (ClientAuthReplicationData.bIsCurrentlyClientAuth && ShouldWeSkipAttachmentReplication(false))
{
return;
}
Super::OnRep_ReplicatedMovement();
}
void AGrippableStaticMeshActor::PostNetReceivePhysicState()
{
if (bAllowIgnoringAttachOnOwner && (ClientAuthReplicationData.bIsCurrentlyClientAuth || ShouldWeSkipAttachmentReplication()))
//if ((ClientAuthReplicationData.bIsCurrentlyClientAuth || VRGripInterfaceSettings.bIsHeld) && bAllowIgnoringAttachOnOwner && ShouldWeSkipAttachmentReplication(false))
{
return;
}
Super::PostNetReceivePhysicState();
}
void AGrippableStaticMeshActor::MarkComponentsAsGarbage(bool bModify)
{
Super::MarkComponentsAsGarbage(bModify);
for (int32 i = 0; i < GripLogicScripts.Num(); ++i)
{
if (UObject *SubObject = GripLogicScripts[i])
{
SubObject->MarkAsGarbage();
}
}
GripLogicScripts.Empty();
}
void AGrippableStaticMeshActor::PreDestroyFromReplication()
{
Super::PreDestroyFromReplication();
// Destroy any sub-objects we created
for (int32 i = 0; i < GripLogicScripts.Num(); ++i)
{
if (UObject *SubObject = GripLogicScripts[i])
{
OnSubobjectDestroyFromReplication(SubObject); //-V595
SubObject->PreDestroyFromReplication();
SubObject->MarkAsGarbage();
}
}
for (UActorComponent * ActorComp : GetComponents())
{
// Pending kill components should have already had this called as they were network spawned and are being killed
// We only call this on our interfaced components since they are the only ones that should implement grip scripts
if (ActorComp && IsValid(ActorComp) && ActorComp->GetClass()->ImplementsInterface(UVRGripInterface::StaticClass()))
ActorComp->PreDestroyFromReplication();
}
GripLogicScripts.Empty();
}
void AGrippableStaticMeshActor::BeginDestroy()
{
Super::BeginDestroy();
for (int32 i = 0; i < GripLogicScripts.Num(); i++)
{
if (UObject *SubObject = GripLogicScripts[i])
{
SubObject->MarkAsGarbage();
}
}
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);
}
}
}
}

View file

@ -0,0 +1,317 @@
// Copyright 1998-2016 Epic Games, Inc. All Rights Reserved.
#include "Grippables/GrippableStaticMeshComponent.h"
#include UE_INLINE_GENERATED_CPP_BY_NAME(GrippableStaticMeshComponent)
#include "GripMotionControllerComponent.h"
#include "VRExpansionFunctionLibrary.h"
#include "GripScripts/VRGripScriptBase.h"
#include "Net/UnrealNetwork.h"
//=============================================================================
UGrippableStaticMeshComponent::UGrippableStaticMeshComponent(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{
VRGripInterfaceSettings.bDenyGripping = false;
VRGripInterfaceSettings.OnTeleportBehavior = EGripInterfaceTeleportBehavior::DropOnTeleport;
VRGripInterfaceSettings.bSimulateOnDrop = true;
VRGripInterfaceSettings.SlotDefaultGripType = EGripCollisionType::ManipulationGrip;
VRGripInterfaceSettings.FreeDefaultGripType = EGripCollisionType::ManipulationGrip;
VRGripInterfaceSettings.SecondaryGripType = ESecondaryGripType::SG_None;
VRGripInterfaceSettings.MovementReplicationType = EGripMovementReplicationSettings::ForceClientSideMovement;
VRGripInterfaceSettings.LateUpdateSetting = EGripLateUpdateSettings::LateUpdatesAlwaysOff;
VRGripInterfaceSettings.ConstraintStiffness = 1500.0f;
VRGripInterfaceSettings.ConstraintDamping = 200.0f;
VRGripInterfaceSettings.ConstraintBreakDistance = 100.0f;
VRGripInterfaceSettings.SecondarySlotRange = 20.0f;
VRGripInterfaceSettings.PrimarySlotRange = 20.0f;
VRGripInterfaceSettings.bIsHeld = false;
bReplicateMovement = false;
//this->bReplicates = true;
bRepGripSettingsAndGameplayTags = true;
bReplicateGripScripts = false;
// #TODO we can register them maybe in the future
// Don't use the replicated list, use our custom replication instead
bReplicateUsingRegisteredSubObjectList = false;
}
void UGrippableStaticMeshComponent::GetLifetimeReplicatedProps(TArray< class FLifetimeProperty > & OutLifetimeProps) const
{
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
DOREPLIFETIME_CONDITION(UGrippableStaticMeshComponent, GripLogicScripts, COND_Custom);
DOREPLIFETIME(UGrippableStaticMeshComponent, bReplicateGripScripts);
DOREPLIFETIME(UGrippableStaticMeshComponent, bRepGripSettingsAndGameplayTags);
DOREPLIFETIME(UGrippableStaticMeshComponent, bReplicateMovement);
DOREPLIFETIME_CONDITION(UGrippableStaticMeshComponent, VRGripInterfaceSettings, COND_Custom);
DOREPLIFETIME_CONDITION(UGrippableStaticMeshComponent, GameplayTags, COND_Custom);
}
void UGrippableStaticMeshComponent::PreReplication(IRepChangedPropertyTracker & ChangedPropertyTracker)
{
Super::PreReplication(ChangedPropertyTracker);
// Don't replicate if set to not do it
DOREPLIFETIME_ACTIVE_OVERRIDE_FAST(UGrippableStaticMeshComponent, VRGripInterfaceSettings, bRepGripSettingsAndGameplayTags);
DOREPLIFETIME_ACTIVE_OVERRIDE_FAST(UGrippableStaticMeshComponent, GameplayTags, bRepGripSettingsAndGameplayTags);
DOREPLIFETIME_ACTIVE_OVERRIDE_FAST(UGrippableStaticMeshComponent, GripLogicScripts, bReplicateGripScripts);
DOREPLIFETIME_ACTIVE_OVERRIDE_FAST(USceneComponent, RelativeLocation, bReplicateMovement);
DOREPLIFETIME_ACTIVE_OVERRIDE_FAST(USceneComponent, RelativeRotation, bReplicateMovement);
DOREPLIFETIME_ACTIVE_OVERRIDE_FAST(USceneComponent, RelativeScale3D, bReplicateMovement);
}
bool UGrippableStaticMeshComponent::ReplicateSubobjects(UActorChannel* Channel, class FOutBunch *Bunch, FReplicationFlags *RepFlags)
{
bool WroteSomething = Super::ReplicateSubobjects(Channel, Bunch, RepFlags);
if (bReplicateGripScripts)
{
for (UVRGripScriptBase* Script : GripLogicScripts)
{
if (Script && IsValid(Script))
{
WroteSomething |= Channel->ReplicateSubobject(Script, *Bunch, *RepFlags);
}
}
}
return WroteSomething;
}
//=============================================================================
UGrippableStaticMeshComponent::~UGrippableStaticMeshComponent()
{
}
void UGrippableStaticMeshComponent::BeginPlay()
{
// Call the base class
Super::BeginPlay();
// Call all grip scripts begin play events so they can perform any needed logic
for (UVRGripScriptBase* Script : GripLogicScripts)
{
if (Script)
{
Script->BeginPlay(this);
}
}
bOriginalReplicatesMovement = bReplicateMovement;
}
void UGrippableStaticMeshComponent::EndPlay(const EEndPlayReason::Type EndPlayReason)
{
// Call the base class
Super::EndPlay(EndPlayReason);
// Call all grip scripts begin play events so they can perform any needed logic
for (UVRGripScriptBase* Script : GripLogicScripts)
{
if (Script)
{
Script->EndPlay(EndPlayReason);
}
}
}
void UGrippableStaticMeshComponent::SetDenyGripping(bool bDenyGripping)
{
VRGripInterfaceSettings.bDenyGripping = bDenyGripping;
}
void UGrippableStaticMeshComponent::SetGripPriority(int NewGripPriority)
{
VRGripInterfaceSettings.AdvancedGripSettings.GripPriority = NewGripPriority;
}
void UGrippableStaticMeshComponent::TickGrip_Implementation(UGripMotionControllerComponent * GrippingController, const FBPActorGripInformation & GripInformation, float DeltaTime) {}
void UGrippableStaticMeshComponent::OnGrip_Implementation(UGripMotionControllerComponent* GrippingController, const FBPActorGripInformation& GripInformation) { }
void UGrippableStaticMeshComponent::OnGripRelease_Implementation(UGripMotionControllerComponent* ReleasingController, const FBPActorGripInformation& GripInformation, bool bWasSocketed) { }
void UGrippableStaticMeshComponent::OnChildGrip_Implementation(UGripMotionControllerComponent* GrippingController, const FBPActorGripInformation& GripInformation) {}
void UGrippableStaticMeshComponent::OnChildGripRelease_Implementation(UGripMotionControllerComponent* ReleasingController, const FBPActorGripInformation& GripInformation, bool bWasSocketed) {}
void UGrippableStaticMeshComponent::OnSecondaryGrip_Implementation(UGripMotionControllerComponent* GripOwningController, USceneComponent* SecondaryGripComponent, const FBPActorGripInformation& GripInformation) { OnSecondaryGripAdded.Broadcast(GripOwningController, GripInformation); }
void UGrippableStaticMeshComponent::OnSecondaryGripRelease_Implementation(UGripMotionControllerComponent* GripOwningController, USceneComponent* ReleasingSecondaryGripComponent, const FBPActorGripInformation& GripInformation) { OnSecondaryGripRemoved.Broadcast(GripOwningController, GripInformation); }
void UGrippableStaticMeshComponent::OnUsed_Implementation() {}
void UGrippableStaticMeshComponent::OnEndUsed_Implementation() {}
void UGrippableStaticMeshComponent::OnSecondaryUsed_Implementation() {}
void UGrippableStaticMeshComponent::OnEndSecondaryUsed_Implementation() {}
void UGrippableStaticMeshComponent::OnInput_Implementation(FKey Key, EInputEvent KeyEvent) {}
bool UGrippableStaticMeshComponent::RequestsSocketing_Implementation(USceneComponent *& ParentToSocketTo, FName & OptionalSocketName, FTransform_NetQuantize & RelativeTransform) { return false; }
bool UGrippableStaticMeshComponent::DenyGripping_Implementation(UGripMotionControllerComponent * GripInitiator)
{
return VRGripInterfaceSettings.bDenyGripping;
}
EGripInterfaceTeleportBehavior UGrippableStaticMeshComponent::TeleportBehavior_Implementation()
{
return VRGripInterfaceSettings.OnTeleportBehavior;
}
bool UGrippableStaticMeshComponent::SimulateOnDrop_Implementation()
{
return VRGripInterfaceSettings.bSimulateOnDrop;
}
EGripCollisionType UGrippableStaticMeshComponent::GetPrimaryGripType_Implementation(bool bIsSlot)
{
return bIsSlot ? VRGripInterfaceSettings.SlotDefaultGripType : VRGripInterfaceSettings.FreeDefaultGripType;
}
ESecondaryGripType UGrippableStaticMeshComponent::SecondaryGripType_Implementation()
{
return VRGripInterfaceSettings.SecondaryGripType;
}
EGripMovementReplicationSettings UGrippableStaticMeshComponent::GripMovementReplicationType_Implementation()
{
return VRGripInterfaceSettings.MovementReplicationType;
}
EGripLateUpdateSettings UGrippableStaticMeshComponent::GripLateUpdateSetting_Implementation()
{
return VRGripInterfaceSettings.LateUpdateSetting;
}
void UGrippableStaticMeshComponent::GetGripStiffnessAndDamping_Implementation(float &GripStiffnessOut, float &GripDampingOut)
{
GripStiffnessOut = VRGripInterfaceSettings.ConstraintStiffness;
GripDampingOut = VRGripInterfaceSettings.ConstraintDamping;
}
FBPAdvGripSettings UGrippableStaticMeshComponent::AdvancedGripSettings_Implementation()
{
return VRGripInterfaceSettings.AdvancedGripSettings;
}
float UGrippableStaticMeshComponent::GripBreakDistance_Implementation()
{
return VRGripInterfaceSettings.ConstraintBreakDistance;
}
void UGrippableStaticMeshComponent::ClosestGripSlotInRange_Implementation(FVector WorldLocation, bool bSecondarySlot, bool & bHadSlotInRange, FTransform & SlotWorldTransform, FName & SlotName, UGripMotionControllerComponent * CallingController, FName OverridePrefix)
{
if (OverridePrefix.IsNone())
bSecondarySlot ? OverridePrefix = "VRGripS" : OverridePrefix = "VRGripP";
UVRExpansionFunctionLibrary::GetGripSlotInRangeByTypeName_Component(OverridePrefix, this, WorldLocation, bSecondarySlot ? VRGripInterfaceSettings.SecondarySlotRange : VRGripInterfaceSettings.PrimarySlotRange, bHadSlotInRange, SlotWorldTransform, SlotName, CallingController);
}
bool UGrippableStaticMeshComponent::AllowsMultipleGrips_Implementation()
{
return VRGripInterfaceSettings.bAllowMultipleGrips;
}
void UGrippableStaticMeshComponent::IsHeld_Implementation(TArray<FBPGripPair> & HoldingControllers, bool & bIsHeld)
{
HoldingControllers = VRGripInterfaceSettings.HoldingControllers;
bIsHeld = VRGripInterfaceSettings.bIsHeld;
}
void UGrippableStaticMeshComponent::Native_NotifyThrowGripDelegates(UGripMotionControllerComponent* Controller, bool bGripped, const FBPActorGripInformation& GripInformation, bool bWasSocketed)
{
if (bGripped)
{
OnGripped.Broadcast(Controller, GripInformation);
}
else
{
OnDropped.Broadcast(Controller, GripInformation, bWasSocketed);
}
}
void UGrippableStaticMeshComponent::SetHeld_Implementation(UGripMotionControllerComponent * HoldingController, uint8 GripID, bool bIsHeld)
{
if (bIsHeld)
{
if (VRGripInterfaceSettings.MovementReplicationType != EGripMovementReplicationSettings::ForceServerSideMovement)
{
if (!VRGripInterfaceSettings.bIsHeld)
bOriginalReplicatesMovement = bReplicateMovement;
bReplicateMovement = false;
}
VRGripInterfaceSettings.bWasHeld = true;
VRGripInterfaceSettings.HoldingControllers.AddUnique(FBPGripPair(HoldingController, GripID));
}
else
{
if (VRGripInterfaceSettings.MovementReplicationType != EGripMovementReplicationSettings::ForceServerSideMovement)
{
bReplicateMovement = bOriginalReplicatesMovement;
}
VRGripInterfaceSettings.HoldingControllers.Remove(FBPGripPair(HoldingController, GripID));
}
VRGripInterfaceSettings.bIsHeld = VRGripInterfaceSettings.HoldingControllers.Num() > 0;
}
bool UGrippableStaticMeshComponent::GetGripScripts_Implementation(TArray<UVRGripScriptBase*> & ArrayReference)
{
ArrayReference = GripLogicScripts;
return GripLogicScripts.Num() > 0;
}
void UGrippableStaticMeshComponent::PreDestroyFromReplication()
{
Super::PreDestroyFromReplication();
// Destroy any sub-objects we created
for (int32 i = 0; i < GripLogicScripts.Num(); ++i)
{
if (UObject *SubObject = GripLogicScripts[i])
{
SubObject->PreDestroyFromReplication();
SubObject->MarkAsGarbage();
}
}
GripLogicScripts.Empty();
}
void UGrippableStaticMeshComponent::GetSubobjectsWithStableNamesForNetworking(TArray<UObject*> &ObjList)
{
if (bReplicateGripScripts)
{
for (int32 i = 0; i < GripLogicScripts.Num(); ++i)
{
if (UObject* SubObject = GripLogicScripts[i])
{
ObjList.Add(SubObject);
}
}
}
}
void UGrippableStaticMeshComponent::OnComponentDestroyed(bool bDestroyingHierarchy)
{
// Call the super at the end, after we've done what we needed to do
Super::OnComponentDestroyed(bDestroyingHierarchy);
// Don't set these in editor preview window and the like, it causes saving issues
if (UWorld * World = GetWorld())
{
EWorldType::Type WorldType = World->WorldType;
if (WorldType == EWorldType::Editor || WorldType == EWorldType::EditorPreview)
{
return;
}
}
for (int32 i = 0; i < GripLogicScripts.Num(); i++)
{
if (UObject *SubObject = GripLogicScripts[i])
{
SubObject->MarkAsGarbage();
}
}
GripLogicScripts.Empty();
}

View file

@ -0,0 +1,857 @@
// Copyright 1998-2016 Epic Games, Inc. All Rights Reserved.
#include "Grippables/HandSocketComponent.h"
#include UE_INLINE_GENERATED_CPP_BY_NAME(HandSocketComponent)
#include "Engine/CollisionProfile.h"
#include "Animation/AnimSequence.h"
#include "Animation/AnimInstanceProxy.h"
#include "Animation/PoseSnapshot.h"
#include "Animation/AnimData/AnimDataModel.h"
//#include "VRExpansionFunctionLibrary.h"
#include "Components/SkeletalMeshComponent.h"
#include "Components/PoseableMeshComponent.h"
#include "GripMotionControllerComponent.h"
//#include "VRGripInterface.h"
//#include "VRBPDatatypes.h"
#include "Net/UnrealNetwork.h"
#include "Serialization/CustomVersion.h"
DEFINE_LOG_CATEGORY(LogVRHandSocketComponent);
const FGuid FVRHandSocketCustomVersion::GUID(0x5A018B7F, 0x48A7AFDE, 0xAFBEB580, 0xAD575412);
// Register the custom version with core
FCustomVersionRegistration GRegisterHandSocketCustomVersion(FVRHandSocketCustomVersion::GUID, FVRHandSocketCustomVersion::LatestVersion, TEXT("HandSocketVer"));
void UHandSocketComponent::Serialize(FArchive& Ar)
{
Super::Serialize(Ar);
Ar.UsingCustomVersion(FVRHandSocketCustomVersion::GUID);
#if WITH_EDITORONLY_DATA
const int32 CustomHandSocketVersion = Ar.CustomVer(FVRHandSocketCustomVersion::GUID);
if (CustomHandSocketVersion < FVRHandSocketCustomVersion::HandSocketStoringSetState)
{
bDecoupled = bDecoupleMeshPlacement;
}
#endif
}
//=============================================================================
UHandSocketComponent::UHandSocketComponent(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{
bReplicateMovement = false;
PrimaryComponentTick.bCanEverTick = false;
PrimaryComponentTick.bStartWithTickEnabled = false;
// Setting absolute scale so we don't have to care about our parents scale
this->SetUsingAbsoluteScale(true);
//this->bReplicates = true;
bRepGameplayTags = true;
#if WITH_EDITORONLY_DATA
bTickedPose = false;
bDecoupled = false;
bShowVisualizationMesh = true;
bMirrorVisualizationMesh = false;
bShowRangeVisualization = false;
bFilterBonesByPostfix = false;
FilterPostfix = FString(TEXT("_r"));
#endif
HandRelativePlacement = FTransform::Identity;
bAlwaysInRange = false;
bDisabled = false;
bMatchRotation = false;
OverrideDistance = 0.0f;
SlotPrefix = FName("VRGripP");
bUseCustomPoseDeltas = false;
HandTargetAnimation = nullptr;
MirroredScale = FVector(1.f, 1.f, -1.f);
bOnlySnapMesh = false;
bOnlyUseHandPose = false;
bIgnoreAttachBone = false;
bFlipForLeftHand = false;
bLeftHandDominant = false;
bOnlyFlipRotation = false;
MirrorAxis = EVRAxis::X;
FlipAxis = EVRAxis::Y;
}
UAnimSequence* UHandSocketComponent::GetTargetAnimation()
{
return HandTargetAnimation;
}
bool UHandSocketComponent::GetAnimationSequenceAsPoseSnapShot(UAnimSequence* InAnimationSequence, FPoseSnapshot& OutPoseSnapShot, USkeletalMeshComponent* TargetMesh, bool bSkipRootBone, bool bFlipHand)
{
if (InAnimationSequence)
{
OutPoseSnapShot.SkeletalMeshName = /*TargetMesh ? TargetMesh->SkeletalMesh->GetFName(): */InAnimationSequence->GetSkeleton()->GetFName();
OutPoseSnapShot.SnapshotName = InAnimationSequence->GetFName();
OutPoseSnapShot.BoneNames.Empty();
OutPoseSnapShot.LocalTransforms.Empty();
TArray<FName> AnimSeqNames;
if (USkeleton* AnimationSkele = InAnimationSequence->GetSkeleton())
{
// pre-size the array to avoid unnecessary reallocation
OutPoseSnapShot.BoneNames.AddUninitialized(AnimationSkele->GetReferenceSkeleton().GetNum());
for (int32 i = 0; i < AnimationSkele->GetReferenceSkeleton().GetNum(); i++)
{
OutPoseSnapShot.BoneNames[i] = AnimationSkele->GetReferenceSkeleton().GetBoneName(i);
if (bFlipHand)
{
FString bName = OutPoseSnapShot.BoneNames[i].ToString();
if (bName.Contains("_r"))
{
bName = bName.Replace(TEXT("_r"), TEXT("_l"));
}
else
{
bName = bName.Replace(TEXT("_l"), TEXT("_r"));
}
OutPoseSnapShot.BoneNames[i] = FName(bName);
}
}
}
else
{
return false;
}
const FReferenceSkeleton& RefSkeleton = (TargetMesh) ? TargetMesh->GetSkinnedAsset()->GetRefSkeleton() : InAnimationSequence->GetSkeleton()->GetReferenceSkeleton();
FTransform LocalTransform;
const TArray<FTrackToSkeletonMap>& TrackMap = InAnimationSequence->GetCompressedTrackToSkeletonMapTable();
int32 TrackIndex = INDEX_NONE;
OutPoseSnapShot.LocalTransforms.Reserve(OutPoseSnapShot.BoneNames.Num());
for (int32 BoneNameIndex = 0; BoneNameIndex < OutPoseSnapShot.BoneNames.Num(); ++BoneNameIndex)
{
const FName& BoneName = OutPoseSnapShot.BoneNames[BoneNameIndex];
TrackIndex = INDEX_NONE;
if (BoneNameIndex != INDEX_NONE && BoneNameIndex < TrackMap.Num() && TrackMap[BoneNameIndex].BoneTreeIndex == BoneNameIndex)
{
TrackIndex = BoneNameIndex;
}
else
{
// This shouldn't happen but I need a fallback
// Don't currently want to reconstruct the map inversely
for (int i = 0; i < TrackMap.Num(); ++i)
{
if (TrackMap[i].BoneTreeIndex == BoneNameIndex)
{
TrackIndex = i;
break;
}
}
}
if (TrackIndex != INDEX_NONE && (!bSkipRootBone || TrackIndex != 0))
{
double TrackLocation = 0.0f;
InAnimationSequence->GetBoneTransform(LocalTransform, FSkeletonPoseBoneIndex(TrackMap[TrackIndex].BoneTreeIndex), TrackLocation, false);
}
else
{
// otherwise, get ref pose if exists
const int32 BoneIDX = RefSkeleton.FindBoneIndex(BoneName);
if (BoneIDX != INDEX_NONE)
{
LocalTransform = RefSkeleton.GetRefBonePose()[BoneIDX];
}
else
{
LocalTransform = FTransform::Identity;
}
}
if (bFlipHand && (!bSkipRootBone || TrackIndex != 0))
{
FMatrix M = LocalTransform.ToMatrixWithScale();
M.Mirror(EAxis::X, EAxis::X);
M.Mirror(EAxis::Y, EAxis::Y);
M.Mirror(EAxis::Z, EAxis::Z);
LocalTransform.SetFromMatrix(M);
}
OutPoseSnapShot.LocalTransforms.Add(LocalTransform);
}
OutPoseSnapShot.bIsValid = true;
return true;
}
return false;
}
bool UHandSocketComponent::GetBlendedPoseSnapShot(FPoseSnapshot& PoseSnapShot, USkeletalMeshComponent* TargetMesh, bool bSkipRootBone, bool bFlipHand)
{
if (HandTargetAnimation)// && bUseCustomPoseDeltas && CustomPoseDeltas.Num() > 0)
{
PoseSnapShot.SkeletalMeshName = HandTargetAnimation->GetSkeleton()->GetFName();
PoseSnapShot.SnapshotName = HandTargetAnimation->GetFName();
PoseSnapShot.BoneNames.Empty();
PoseSnapShot.LocalTransforms.Empty();
TArray<FName> OrigBoneNames;
if(USkeleton * AnimationSkele = HandTargetAnimation->GetSkeleton())
{
// pre-size the array to avoid unnecessary reallocation
PoseSnapShot.BoneNames.AddUninitialized(AnimationSkele->GetReferenceSkeleton().GetNum());
OrigBoneNames.AddUninitialized(AnimationSkele->GetReferenceSkeleton().GetNum());
for (int32 i = 0; i < AnimationSkele->GetReferenceSkeleton().GetNum(); i++)
{
PoseSnapShot.BoneNames[i] = AnimationSkele->GetReferenceSkeleton().GetBoneName(i);
OrigBoneNames[i] = PoseSnapShot.BoneNames[i];
if (bFlipHand)
{
FString bName = PoseSnapShot.BoneNames[i].ToString();
if (bName.Contains("_r"))
{
bName = bName.Replace(TEXT("_r"), TEXT("_l"));
}
else
{
bName = bName.Replace(TEXT("_l"), TEXT("_r"));
}
PoseSnapShot.BoneNames[i] = FName(bName);
}
}
}
else
{
return false;
}
const FReferenceSkeleton& RefSkeleton = (TargetMesh) ? TargetMesh->GetSkinnedAsset()->GetRefSkeleton() : HandTargetAnimation->GetSkeleton()->GetReferenceSkeleton();
FTransform LocalTransform;
const TArray<FTrackToSkeletonMap>& TrackMap = HandTargetAnimation->GetCompressedTrackToSkeletonMapTable();
int32 TrackIndex = INDEX_NONE;
for (int32 BoneNameIndex = 0; BoneNameIndex < PoseSnapShot.BoneNames.Num(); ++BoneNameIndex)
{
TrackIndex = INDEX_NONE;
if (BoneNameIndex < TrackMap.Num() && TrackMap[BoneNameIndex].BoneTreeIndex == BoneNameIndex)
{
TrackIndex = BoneNameIndex;
}
else
{
// This shouldn't happen but I need a fallback
// Don't currently want to reconstruct the map inversely
for (int i = 0; i < TrackMap.Num(); ++i)
{
if (TrackMap[i].BoneTreeIndex == BoneNameIndex)
{
TrackIndex = i;
break;
}
}
}
const FName& BoneName = PoseSnapShot.BoneNames[BoneNameIndex];
if (TrackIndex != INDEX_NONE && (!bSkipRootBone || TrackIndex != 0))
{
double TrackLocation = 0.0f;
HandTargetAnimation->GetBoneTransform(LocalTransform, FSkeletonPoseBoneIndex(TrackMap[TrackIndex].BoneTreeIndex), TrackLocation, false);
}
else
{
// otherwise, get ref pose if exists
const int32 BoneIDX = RefSkeleton.FindBoneIndex(BoneName);
if (BoneIDX != INDEX_NONE)
{
LocalTransform = RefSkeleton.GetRefBonePose()[BoneIDX];
}
else
{
LocalTransform = FTransform::Identity;
}
}
if (bUseCustomPoseDeltas)
{
FQuat DeltaQuat = FQuat::Identity;
if (FBPVRHandPoseBonePair* HandPair = CustomPoseDeltas.FindByKey(OrigBoneNames[BoneNameIndex]))
{
DeltaQuat = HandPair->DeltaPose;
}
LocalTransform.ConcatenateRotation(DeltaQuat);
LocalTransform.NormalizeRotation();
}
if (bFlipHand && (!bSkipRootBone || TrackIndex != 0))
{
FMatrix M = LocalTransform.ToMatrixWithScale();
M.Mirror(EAxis::X, EAxis::X);
M.Mirror(EAxis::Y, EAxis::Y);
M.Mirror(EAxis::Z, EAxis::Z);
LocalTransform.SetFromMatrix(M);
}
PoseSnapShot.LocalTransforms.Add(LocalTransform);
}
PoseSnapShot.bIsValid = true;
return true;
}
else if (bUseCustomPoseDeltas && CustomPoseDeltas.Num() && TargetMesh)
{
PoseSnapShot.SkeletalMeshName = TargetMesh->GetSkinnedAsset()->GetSkeleton()->GetFName();
PoseSnapShot.SnapshotName = FName(TEXT("RawDeltaPose"));
PoseSnapShot.BoneNames.Empty();
PoseSnapShot.LocalTransforms.Empty();
TargetMesh->GetBoneNames(PoseSnapShot.BoneNames);
PoseSnapShot.LocalTransforms = TargetMesh->GetSkinnedAsset()->GetSkeleton()->GetRefLocalPoses();
FQuat DeltaQuat = FQuat::Identity;
FName TargetBoneName = NAME_None;
for (FBPVRHandPoseBonePair& HandPair : CustomPoseDeltas)
{
if (bFlipHand)
{
FString bName = HandPair.BoneName.ToString();
if (bName.Contains("_r"))
{
bName = bName.Replace(TEXT("_r"), TEXT("_l"));
}
else
{
bName = bName.Replace(TEXT("_l"), TEXT("_r"));
}
TargetBoneName = FName(bName);
}
else
{
TargetBoneName = HandPair.BoneName;
}
int32 BoneIdx = TargetMesh->GetBoneIndex(TargetBoneName);
if (BoneIdx != INDEX_NONE)
{
DeltaQuat = HandPair.DeltaPose;
if (bFlipHand)
{
FTransform DeltaTrans(DeltaQuat);
FMatrix M = DeltaTrans.ToMatrixWithScale();
M.Mirror(EAxis::X, EAxis::X);
M.Mirror(EAxis::Y, EAxis::Y);
M.Mirror(EAxis::Z, EAxis::Z);
DeltaTrans.SetFromMatrix(M);
DeltaQuat = DeltaTrans.GetRotation();
}
PoseSnapShot.LocalTransforms[BoneIdx].ConcatenateRotation(DeltaQuat);
PoseSnapShot.LocalTransforms[BoneIdx].NormalizeRotation();
}
}
PoseSnapShot.bIsValid = true;
return true;
}
return false;
}
FTransform UHandSocketComponent::GetHandRelativePlacement()
{
// Optionally mirror for left hand
if (bDecoupleMeshPlacement)
{
if (USceneComponent* ParentComp = GetAttachParent())
{
return HandRelativePlacement.GetRelativeTransform(this->GetRelativeTransform());
//FTransform curTrans = HandRelativePlacement * ParentComp->GetComponentTransform();
//return curTrans.GetRelativeTransform(this->GetComponentTransform());
}
}
return HandRelativePlacement;
}
FTransform UHandSocketComponent::GetHandSocketTransform(UGripMotionControllerComponent* QueryController, bool bIgnoreOnlySnapMesh)
{
// Optionally mirror for left hand
if (!bIgnoreOnlySnapMesh && bOnlySnapMesh)
{
if (!QueryController)
{
// No controller input
UE_LOG(LogVRMotionController, Warning, TEXT("HandSocketComponent::GetHandSocketTransform was missing required motion controller for bOnlySnapMesh! Check that you are passing a controller into GetClosestSocketInRange!"));
}
else
{
return QueryController->GetPivotTransform();
}
}
if (bFlipForLeftHand)
{
if (!QueryController)
{
// No controller input
UE_LOG(LogVRMotionController, Warning, TEXT("HandSocketComponent::GetHandSocketTransform was missing required motion controller for bFlipForLeftand! Check that you are passing a controller into GetClosestSocketInRange!"));
}
else
{
EControllerHand HandType;
QueryController->GetHandType(HandType);
bool bIsRightHand = HandType == EControllerHand::Right;
if (bLeftHandDominant == bIsRightHand)
{
FTransform ReturnTrans = this->GetRelativeTransform();
if (USceneComponent* AttParent = this->GetAttachParent())
{
ReturnTrans.Mirror(GetAsEAxis(MirrorAxis), GetAsEAxis(FlipAxis));
if (bOnlyFlipRotation)
{
ReturnTrans.SetTranslation(this->GetRelativeLocation());
}
if (this->GetAttachSocketName() != NAME_None)
{
ReturnTrans = ReturnTrans * AttParent->GetSocketTransform(GetAttachSocketName(), RTS_Component);
}
ReturnTrans = ReturnTrans * AttParent->GetComponentTransform();
}
return ReturnTrans;
}
}
}
return this->GetComponentTransform();
}
FTransform UHandSocketComponent::GetMeshRelativeTransform(bool bIsRightHand, bool bUseParentScale, bool bUseMirrorScale)
{
// Optionally mirror for left hand
// Failsafe
if (!this->GetAttachParent())
return FTransform::Identity;
FTransform relTrans = this->GetRelativeTransform();
FTransform HandTrans = GetHandRelativePlacement();
FTransform ReturnTrans = FTransform::Identity;
// Fix the scale
if (!bUseParentScale && this->IsUsingAbsoluteScale() /*&& !bDecoupleMeshPlacement*/)
{
FVector ParentScale = this->GetAttachParent()->GetComponentScale();
// Take parent scale out of our relative transform early
relTrans.ScaleTranslation(ParentScale);
ReturnTrans = HandTrans * relTrans;
// We add in the inverse of the parent scale to adjust the hand mesh
ReturnTrans.ScaleTranslation((FVector(1.0f) / ParentScale));
ReturnTrans.SetScale3D(FVector(1.0f));
}
else
{
ReturnTrans = HandTrans * relTrans;
}
// If we should mirror the transform, do it now that it is in our parent relative space
if ((bFlipForLeftHand && (bLeftHandDominant == bIsRightHand)))
{
//FTransform relTrans = this->GetRelativeTransform();
MirrorHandTransform(ReturnTrans, relTrans);
if (bUseMirrorScale)
{
ReturnTrans.SetScale3D(ReturnTrans.GetScale3D() * MirroredScale.GetSignVector());
}
}
if (bIgnoreAttachBone && this->GetAttachSocketName() != NAME_None)
{
ReturnTrans = ReturnTrans * GetAttachParent()->GetSocketTransform(GetAttachSocketName(), RTS_Component);
}
return ReturnTrans;
}
#if WITH_EDITORONLY_DATA
FTransform UHandSocketComponent::GetBoneTransformAtTime(UAnimSequence* MyAnimSequence, /*float AnimTime,*/ int BoneIdx, FName BoneName, bool bUseRawDataOnly)
{
double tracklen = MyAnimSequence->GetPlayLength();
FTransform BoneTransform = FTransform::Identity;
IAnimationDataController& AnimController = MyAnimSequence->GetController();
if (const IAnimationDataModel* AnimModel = AnimController.GetModel())
{
const TArray<FTrackToSkeletonMap>& TrackMap = MyAnimSequence->GetCompressedTrackToSkeletonMapTable();
int32 TrackIndex = INDEX_NONE;
if (BoneIdx != INDEX_NONE && BoneIdx < TrackMap.Num() && TrackMap[BoneIdx].BoneTreeIndex == BoneIdx)
{
TrackIndex = BoneIdx;
}
else
{
// This shouldn't happen but I need a fallback
// Don't currently want to reconstruct the map inversely
for (int i = 0; i < TrackMap.Num(); ++i)
{
if (TrackMap[i].BoneTreeIndex == BoneIdx)
{
TrackIndex = i;
break;
}
}
}
if (TrackIndex != INDEX_NONE)
{
FSkeletonPoseBoneIndex BoneIndex(TrackMap[TrackIndex].BoneTreeIndex);
if (BoneIndex.IsValid())
{
MyAnimSequence->GetBoneTransform(BoneTransform, BoneIndex, /*AnimTime*/ tracklen, bUseRawDataOnly);
return BoneTransform;
}
}
else
{
return FTransform::Identity;
}
}
return FTransform::Identity;
}
#endif
void UHandSocketComponent::OnRegister()
{
#if WITH_EDITORONLY_DATA
AActor* MyOwner = GetOwner();
if (bShowVisualizationMesh && (MyOwner != nullptr) && !IsRunningCommandlet())
{
if (HandVisualizerComponent == nullptr && bShowVisualizationMesh)
{
HandVisualizerComponent = NewObject<UPoseableMeshComponent>(MyOwner, NAME_None, RF_Transactional | RF_TextExportTransient);
HandVisualizerComponent->SetupAttachment(this);
HandVisualizerComponent->SetIsVisualizationComponent(true);
HandVisualizerComponent->SetCollisionProfileName(UCollisionProfile::NoCollision_ProfileName);
HandVisualizerComponent->CastShadow = false;
HandVisualizerComponent->CreationMethod = CreationMethod;
//HandVisualizerComponent->SetCollisionEnabled(ECollisionEnabled::NoCollision);
HandVisualizerComponent->SetComponentTickEnabled(false);
HandVisualizerComponent->SetHiddenInGame(true);
HandVisualizerComponent->RegisterComponentWithWorld(GetWorld());
//HandVisualizerComponent->SetUsingAbsoluteScale(true);
}
else if (!bShowVisualizationMesh && HandVisualizerComponent)
{
HideVisualizationMesh();
}
if (HandVisualizerComponent)
{
bTickedPose = false;
if (VisualizationMesh)
{
if (HandPreviewMaterial)
{
HandVisualizerComponent->SetMaterial(0, (UMaterialInterface*)HandPreviewMaterial);
}
HandVisualizerComponent->SetSkinnedAssetAndUpdate(VisualizationMesh);
}
PositionVisualizationMesh();
PoseVisualizationToAnimation(true);
}
}
#endif // WITH_EDITORONLY_DATA
Super::OnRegister();
}
#if WITH_EDITORONLY_DATA
void UHandSocketComponent::PositionVisualizationMesh()
{
if (!HandVisualizerComponent)
{
return;
}
if (USceneComponent* ParentAttach = this->GetAttachParent())
{
FTransform relTrans = this->GetRelativeTransform();
if (bDecoupled != bDecoupleMeshPlacement)
{
if (bDecoupleMeshPlacement)
{
HandRelativePlacement = HandRelativePlacement * GetRelativeTransform();
}
else
{
HandRelativePlacement = HandRelativePlacement.GetRelativeTransform(GetRelativeTransform());
}
}
FTransform HandPlacement = GetHandRelativePlacement();
FTransform ReturnTrans = (HandPlacement * relTrans);
if (bMirrorVisualizationMesh)//(bFlipForLeftHand && !bIsRightHand))
{
MirrorHandTransform(ReturnTrans, relTrans);
}
if ((bLeftHandDominant && !bMirrorVisualizationMesh) || (!bLeftHandDominant && bMirrorVisualizationMesh))
{
ReturnTrans.SetScale3D(ReturnTrans.GetScale3D() * MirroredScale);
}
HandVisualizerComponent->SetRelativeTransform(ReturnTrans.GetRelativeTransform(relTrans)/*newRel*/);
}
}
void UHandSocketComponent::HideVisualizationMesh()
{
if (!bShowVisualizationMesh && HandVisualizerComponent)
{
HandVisualizerComponent->SetVisibility(false);
HandVisualizerComponent->DestroyComponent();
HandVisualizerComponent = nullptr;
}
}
#endif
#if WITH_EDITORONLY_DATA
void UHandSocketComponent::PoseVisualizationToAnimation(bool bForceRefresh)
{
if (!HandVisualizerComponent || !HandVisualizerComponent->GetSkinnedAsset())
return;
TArray<FTransform> LocalPoses;
if (!HandTargetAnimation)
{
// Store local poses for posing
LocalPoses = HandVisualizerComponent->GetSkinnedAsset()->GetSkeleton()->GetRefLocalPoses();
}
TArray<FName> BonesNames;
HandVisualizerComponent->GetBoneNames(BonesNames);
int32 Bones = HandVisualizerComponent->GetNumBones();
for (int32 i = 0; i < Bones; i++)
{
if (!HandTargetAnimation && !bUseCustomPoseDeltas)
{
if (HandVisualizerComponent->GetBoneSpaceTransforms().Num() > 0)
{
HandVisualizerComponent->ResetBoneTransformByName(BonesNames[i]);
}
continue;
}
FName ParentBone = HandVisualizerComponent->GetParentBone(BonesNames[i]);
FTransform ParentTrans = FTransform::Identity;
if (ParentBone != NAME_None)
{
ParentTrans = HandVisualizerComponent->GetBoneTransformByName(ParentBone, EBoneSpaces::ComponentSpace);
}
FQuat DeltaQuat = FQuat::Identity;
if (bUseCustomPoseDeltas)
{
for (FBPVRHandPoseBonePair BonePairC : CustomPoseDeltas)
{
if (BonePairC.BoneName == BonesNames[i])
{
DeltaQuat = BonePairC.DeltaPose;
DeltaQuat.Normalize();
break;
}
}
}
FTransform BoneTrans = FTransform::Identity;
if (HandTargetAnimation)
{
BoneTrans = GetBoneTransformAtTime(HandTargetAnimation, /*FLT_MAX,*/ i, BonesNames[i], false); // true;
}
else
{
BoneTrans = LocalPoses[i];
}
BoneTrans = BoneTrans * ParentTrans;// *HandVisualizerComponent->GetComponentTransform();
BoneTrans.NormalizeRotation();
//DeltaQuat *= HandVisualizerComponent->GetComponentTransform().GetRotation().Inverse();
BoneTrans.ConcatenateRotation(DeltaQuat);
BoneTrans.NormalizeRotation();
HandVisualizerComponent->SetBoneTransformByName(BonesNames[i], BoneTrans, EBoneSpaces::ComponentSpace);
}
if (HandVisualizerComponent && (!bTickedPose || bForceRefresh))
{
// Tick Pose first
if (HandVisualizerComponent->IsRegistered())
{
bTickedPose = true;
HandVisualizerComponent->TickPose(1.0f, false);
if (HandVisualizerComponent->LeaderPoseComponent.IsValid())
{
HandVisualizerComponent->UpdateFollowerComponent();
}
else
{
HandVisualizerComponent->RefreshBoneTransforms(&HandVisualizerComponent->PrimaryComponentTick);
}
}
}
}
void UHandSocketComponent::AddReferencedObjects(UObject* InThis, FReferenceCollector& Collector)
{
UHandSocketComponent* This = CastChecked<UHandSocketComponent>(InThis);
Collector.AddReferencedObject(This->HandVisualizerComponent);
Super::AddReferencedObjects(InThis, Collector);
}
void UHandSocketComponent::OnComponentDestroyed(bool bDestroyingHierarchy)
{
Super::OnComponentDestroyed(bDestroyingHierarchy);
if (HandVisualizerComponent)
{
HandVisualizerComponent->DestroyComponent();
}
}
#endif
void UHandSocketComponent::GetLifetimeReplicatedProps(TArray< class FLifetimeProperty > & OutLifetimeProps) const
{
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
DOREPLIFETIME(UHandSocketComponent, bRepGameplayTags);
DOREPLIFETIME(UHandSocketComponent, bReplicateMovement);
DOREPLIFETIME_CONDITION(UHandSocketComponent, GameplayTags, COND_Custom);
}
void UHandSocketComponent::PreReplication(IRepChangedPropertyTracker & ChangedPropertyTracker)
{
Super::PreReplication(ChangedPropertyTracker);
// Don't replicate if set to not do it
DOREPLIFETIME_ACTIVE_OVERRIDE_FAST(UHandSocketComponent, GameplayTags, bRepGameplayTags);
DOREPLIFETIME_ACTIVE_OVERRIDE_FAST(USceneComponent, RelativeLocation, bReplicateMovement);
DOREPLIFETIME_ACTIVE_OVERRIDE_FAST(USceneComponent, RelativeRotation, bReplicateMovement);
DOREPLIFETIME_ACTIVE_OVERRIDE_FAST(USceneComponent, RelativeScale3D, bReplicateMovement);
}
//=============================================================================
UHandSocketComponent::~UHandSocketComponent()
{
}
#if WITH_EDITOR
void UHandSocketComponent::PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent)
{
Super::PostEditChangeProperty(PropertyChangedEvent);
FProperty* PropertyThatChanged = PropertyChangedEvent.Property;
if (PropertyThatChanged != nullptr)
{
#if WITH_EDITORONLY_DATA
if (PropertyThatChanged->GetFName() == GET_MEMBER_NAME_CHECKED(UHandSocketComponent, HandTargetAnimation) ||
PropertyThatChanged->GetFName() == GET_MEMBER_NAME_CHECKED(UHandSocketComponent, VisualizationMesh)
)
{
PositionVisualizationMesh();
PoseVisualizationToAnimation(true);
}
else if (PropertyThatChanged->GetFName() == GET_MEMBER_NAME_CHECKED(UHandSocketComponent, CustomPoseDeltas))
{
PoseVisualizationToAnimation(true);
}
else if (PropertyThatChanged->GetFName() == GET_MEMBER_NAME_CHECKED(UHandSocketComponent, HandRelativePlacement))
{
PositionVisualizationMesh();
}
else if (PropertyThatChanged->GetFName() == GET_MEMBER_NAME_CHECKED(UHandSocketComponent, bShowVisualizationMesh))
{
HideVisualizationMesh();
}
#endif
}
}
#endif
UHandSocketComponent* UHandSocketComponent::GetHandSocketComponentFromObject(UObject* ObjectToCheck, FName SocketName)
{
if (AActor* OwningActor = Cast<AActor>(ObjectToCheck))
{
if (USceneComponent* OwningRoot = Cast<USceneComponent>(OwningActor->GetRootComponent()))
{
TArray<USceneComponent*> AttachChildren = OwningRoot->GetAttachChildren();
for (USceneComponent* AttachChild : AttachChildren)
{
if (AttachChild && AttachChild->IsA<UHandSocketComponent>() && AttachChild->GetFName() == SocketName)
{
return Cast<UHandSocketComponent>(AttachChild);
}
}
}
}
else if (USceneComponent* OwningRoot = Cast<USceneComponent>(ObjectToCheck))
{
TArray<USceneComponent*> AttachChildren = OwningRoot->GetAttachChildren();
for (USceneComponent* AttachChild : AttachChildren)
{
if (AttachChild && AttachChild->IsA<UHandSocketComponent>() && AttachChild->GetFName() == SocketName)
{
return Cast<UHandSocketComponent>(AttachChild);
}
}
}
return nullptr;
}

View file

@ -0,0 +1,416 @@
// Copyright 1998-2016 Epic Games, Inc. All Rights Reserved.
#include "Interactibles/VRButtonComponent.h"
#include UE_INLINE_GENERATED_CPP_BY_NAME(VRButtonComponent)
#include "Net/UnrealNetwork.h"
//#include "VRGripInterface.h"
#include "GripMotionControllerComponent.h"
#include "GameFramework/Character.h"
//=============================================================================
UVRButtonComponent::UVRButtonComponent(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{
this->SetGenerateOverlapEvents(true);
this->PrimaryComponentTick.bStartWithTickEnabled = false;
PrimaryComponentTick.bCanEverTick = true;
LastToggleTime = 0.0f;
DepressDistance = 8.0f;
ButtonEngageDepth = 8.0f;
DepressSpeed = 50.0f;
ButtonAxis = EVRInteractibleAxis::Axis_Z;
ButtonType = EVRButtonType::Btn_Toggle_Return;
MinTimeBetweenEngaging = 0.1f;
bIsEnabled = true;
StateChangeAuthorityType = EVRStateChangeAuthorityType::CanChangeState_All;
bButtonState = false;
this->SetCollisionResponseToAllChannels(ECR_Overlap);
bSkipOverlapFiltering = false;
InitialRelativeTransform = FTransform::Identity;
bReplicateMovement = false;
}
//=============================================================================
UVRButtonComponent::~UVRButtonComponent()
{
}
void UVRButtonComponent::GetLifetimeReplicatedProps(TArray< class FLifetimeProperty > & OutLifetimeProps) const
{
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
DOREPLIFETIME(UVRButtonComponent, InitialRelativeTransform);
DOREPLIFETIME(UVRButtonComponent, bReplicateMovement);
DOREPLIFETIME(UVRButtonComponent, StateChangeAuthorityType);
DOREPLIFETIME_CONDITION(UVRButtonComponent, bButtonState, COND_InitialOnly);
}
void UVRButtonComponent::PreReplication(IRepChangedPropertyTracker & ChangedPropertyTracker)
{
Super::PreReplication(ChangedPropertyTracker);
// Replicate the levers initial transform if we are replicating movement
//DOREPLIFETIME_ACTIVE_OVERRIDE(UVRButtonComponent, InitialRelativeTransform, bReplicateMovement);
DOREPLIFETIME_ACTIVE_OVERRIDE_FAST(USceneComponent, RelativeLocation, bReplicateMovement);
DOREPLIFETIME_ACTIVE_OVERRIDE_FAST(USceneComponent, RelativeRotation, bReplicateMovement);
DOREPLIFETIME_ACTIVE_OVERRIDE_FAST(USceneComponent, RelativeScale3D, bReplicateMovement);
}
void UVRButtonComponent::OnRegister()
{
Super::OnRegister();
ResetInitialButtonLocation();
}
void UVRButtonComponent::BeginPlay()
{
// Call the base class
Super::BeginPlay();
SetButtonToRestingPosition();
OnComponentBeginOverlap.AddUniqueDynamic(this, &UVRButtonComponent::OnOverlapBegin);
OnComponentEndOverlap.AddUniqueDynamic(this, &UVRButtonComponent::OnOverlapEnd);
}
void UVRButtonComponent::TickComponent(float DeltaTime, enum ELevelTick TickType, FActorComponentTickFunction *ThisTickFunction)
{
// Call supers tick (though I don't think any of the base classes to this actually implement it)
Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
const float WorldTime = GetWorld()->GetRealTimeSeconds();
if (IsValid(LocalInteractingComponent))
{
// If button was set to inactive during use
if (!bIsEnabled)
{
// Remove interacting component and return, next tick will begin lerping back
LocalInteractingComponent = nullptr;
return;
}
FTransform OriginalBaseTransform = CalcNewComponentToWorld(InitialRelativeTransform);
float CheckDepth = FMath::Clamp(GetAxisValue(InitialLocation) - GetAxisValue(OriginalBaseTransform.InverseTransformPosition(LocalInteractingComponent->GetComponentLocation())), 0.0f, DepressDistance);
if (CheckDepth > 0.0f)
{
float ClampMinDepth = 0.0f;
// If active and a toggled stay, then clamp min to the toggled stay location
if (ButtonType == EVRButtonType::Btn_Toggle_Stay && bButtonState)
ClampMinDepth = -(ButtonEngageDepth + (1.e-2f)); // + NOT_SO_UE_KINDA_SMALL_NUMBER
float NewDepth = FMath::Clamp(GetAxisValue(InitialComponentLoc) + (-CheckDepth), -DepressDistance, ClampMinDepth);
this->SetRelativeLocation(InitialRelativeTransform.TransformPosition(SetAxisValue(NewDepth)), false);
if (ButtonType == EVRButtonType::Btn_Toggle_Return || ButtonType == EVRButtonType::Btn_Toggle_Stay)
{
if ((StateChangeAuthorityType == EVRStateChangeAuthorityType::CanChangeState_All) ||
(StateChangeAuthorityType == EVRStateChangeAuthorityType::CanChangeState_Server && GetNetMode() < ENetMode::NM_Client) ||
(StateChangeAuthorityType == EVRStateChangeAuthorityType::CanChangeState_Owner && IsValid(LocalLastInteractingActor) && LocalLastInteractingActor->HasLocalNetOwner()))
{
if (!bToggledThisTouch && NewDepth <= (-ButtonEngageDepth) + UE_KINDA_SMALL_NUMBER && (WorldTime - LastToggleTime) >= MinTimeBetweenEngaging)
{
LastToggleTime = WorldTime;
bToggledThisTouch = true;
bButtonState = !bButtonState;
ReceiveButtonStateChanged(bButtonState, LocalLastInteractingActor.Get(), LocalLastInteractingComponent.Get());
OnButtonStateChanged.Broadcast(bButtonState, LocalLastInteractingActor.Get(), LocalLastInteractingComponent.Get());
}
}
}
}
}
else
{
// Std precision tolerance should be fine
if (this->GetRelativeLocation().Equals(GetTargetRelativeLocation()))
{
this->SetComponentTickEnabled(false);
OnButtonEndInteraction.Broadcast(LocalLastInteractingActor.Get(), LocalLastInteractingComponent.Get());
ReceiveButtonEndInteraction(LocalLastInteractingActor.Get(), LocalLastInteractingComponent.Get());
LocalInteractingComponent = nullptr; // Just reset it here so it only does it once
LocalLastInteractingComponent = nullptr;
}
else
this->SetRelativeLocation(FMath::VInterpConstantTo(this->GetRelativeLocation(), GetTargetRelativeLocation(), DeltaTime, DepressSpeed), false);
}
// Press buttons always get checked, both during press AND during lerping for if they are active or not.
if (ButtonType == EVRButtonType::Btn_Press)
{
if ((StateChangeAuthorityType == EVRStateChangeAuthorityType::CanChangeState_All) ||
(StateChangeAuthorityType == EVRStateChangeAuthorityType::CanChangeState_Server && GetNetMode() < ENetMode::NM_Client) ||
(StateChangeAuthorityType == EVRStateChangeAuthorityType::CanChangeState_Owner && IsValid(LocalLastInteractingActor) && LocalLastInteractingActor->HasLocalNetOwner()))
{
// Check for if we should set the state of the button, done here as for the press button the lerp counts for input
bool bCheckState = (GetAxisValue(InitialRelativeTransform.InverseTransformPosition(this->GetRelativeLocation())) <= (-ButtonEngageDepth) + UE_KINDA_SMALL_NUMBER);
if (bButtonState != bCheckState && (WorldTime - LastToggleTime) >= MinTimeBetweenEngaging)
{
LastToggleTime = WorldTime;
bButtonState = bCheckState;
ReceiveButtonStateChanged(bButtonState, LocalLastInteractingActor.Get(), LocalLastInteractingComponent.Get());
OnButtonStateChanged.Broadcast(bButtonState, LocalLastInteractingActor.Get(), LocalLastInteractingComponent.Get());
}
}
}
}
bool UVRButtonComponent::IsValidOverlap_Implementation(UPrimitiveComponent * OverlapComponent)
{
// Early out on the simple checks
if (!OverlapComponent || OverlapComponent == GetAttachParent() || OverlapComponent->GetAttachParent() == GetAttachParent())
return false;
// Should return faster checking for owning character
AActor * OverlapOwner = OverlapComponent->GetOwner();
if (OverlapOwner && OverlapOwner->IsA(ACharacter::StaticClass()))
return true;
// Because epic motion controllers are not owned by characters have to check here too in case someone implements it like that
// Now since our grip controllers are a subclass to the std ones we only need to check for the base one instead of both.
USceneComponent * OurAttachParent = OverlapComponent->GetAttachParent();
if (OurAttachParent && OurAttachParent->IsA(UMotionControllerComponent::StaticClass()))
return true;
// Now check for if it is a grippable object and if it is currently held
if (OverlapComponent->GetClass()->ImplementsInterface(UVRGripInterface::StaticClass()))
{
TArray<FBPGripPair> Controllers;
bool bIsHeld;
IVRGripInterface::Execute_IsHeld(OverlapComponent, Controllers, bIsHeld);
if (bIsHeld)
return true;
}
else if(OverlapOwner && OverlapOwner->GetClass()->ImplementsInterface(UVRGripInterface::StaticClass()))
{
TArray<FBPGripPair> Controllers;
bool bIsHeld;
IVRGripInterface::Execute_IsHeld(OverlapOwner, Controllers, bIsHeld);
if (bIsHeld)
return true;
}
return false;
}
void UVRButtonComponent::SetLastInteractingActor()
{
// Early out on the simple checks
if (!IsValid(LocalInteractingComponent) || LocalInteractingComponent == GetAttachParent() || LocalInteractingComponent->GetAttachParent() == GetAttachParent())
{
LocalLastInteractingActor = nullptr;
LocalLastInteractingComponent = nullptr;
return;
}
LocalLastInteractingComponent = LocalInteractingComponent;
// Should return faster checking for owning character
AActor * OverlapOwner = LocalInteractingComponent->GetOwner();
if (OverlapOwner && OverlapOwner->IsA(ACharacter::StaticClass()))
{
LocalLastInteractingActor = OverlapOwner;
return;
}
// Now check for if it is a grippable object and if it is currently held
if (LocalInteractingComponent->GetClass()->ImplementsInterface(UVRGripInterface::StaticClass()))
{
TArray<FBPGripPair> Controllers;
bool bIsHeld;
IVRGripInterface::Execute_IsHeld(LocalLastInteractingComponent.Get(), Controllers, bIsHeld);
if (bIsHeld && Controllers.Num())
{
AActor * ControllerOwner = Controllers[0].HoldingController != nullptr ? Controllers[0].HoldingController->GetOwner() : nullptr;
if (ControllerOwner)
{
LocalLastInteractingActor = ControllerOwner;
return;
}
}
}
else if (OverlapOwner && OverlapOwner->GetClass()->ImplementsInterface(UVRGripInterface::StaticClass()))
{
TArray<FBPGripPair> Controllers;
bool bIsHeld;
IVRGripInterface::Execute_IsHeld(OverlapOwner, Controllers, bIsHeld);
if (bIsHeld && Controllers.Num())
{
AActor * ControllerOwner = Controllers[0].HoldingController != nullptr ? Controllers[0].HoldingController->GetOwner() : nullptr;
if (ControllerOwner)
{
LocalLastInteractingActor = ControllerOwner;
return;
}
}
}
// Fall back to the owner, wasn't held and wasn't a character
if (OverlapOwner)
{
LocalLastInteractingActor = OverlapOwner;
return;
}
LocalLastInteractingActor = nullptr;
return;
}
void UVRButtonComponent::OnOverlapBegin(UPrimitiveComponent* OverlappedComp, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult)
{
// Other Actor is the actor that triggered the event. Check that is not ourself.
if (bIsEnabled && !IsValid(LocalInteractingComponent) && (bSkipOverlapFiltering || IsValidOverlap(OtherComp)))
{
LocalInteractingComponent = OtherComp;
FTransform OriginalBaseTransform = CalcNewComponentToWorld(InitialRelativeTransform);
FVector loc = LocalInteractingComponent->GetComponentLocation();
InitialLocation = OriginalBaseTransform.InverseTransformPosition(LocalInteractingComponent->GetComponentLocation());
InitialComponentLoc = OriginalBaseTransform.InverseTransformPosition(this->GetComponentLocation());
bToggledThisTouch = false;
this->SetComponentTickEnabled(true);
if (LocalInteractingComponent != LocalLastInteractingComponent.Get())
{
SetLastInteractingActor();
OnButtonBeginInteraction.Broadcast(LocalLastInteractingActor.Get(), LocalLastInteractingComponent.Get());
ReceiveButtonBeginInteraction(LocalLastInteractingActor.Get(), LocalLastInteractingComponent.Get());
}
}
}
void UVRButtonComponent::OnOverlapEnd(UPrimitiveComponent* OverlappedComp, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex)
{
if (IsValid(LocalInteractingComponent) && OtherComp == LocalInteractingComponent)
{
LocalInteractingComponent = nullptr;
}
}
FVector UVRButtonComponent::GetTargetRelativeLocation()
{
// If target is the half pressed
if (ButtonType == EVRButtonType::Btn_Toggle_Stay && bButtonState)
{
// 1.e-2f = MORE_UE_KINDA_SMALL_NUMBER
return InitialRelativeTransform.TransformPosition(SetAxisValue(-(ButtonEngageDepth + (1.e-2f))));
}
// Else return going all the way back
return InitialRelativeTransform.GetTranslation();
}
void UVRButtonComponent::SetButtonToRestingPosition(bool bLerpToPosition)
{
switch (ButtonType)
{
case EVRButtonType::Btn_Press:
{
}break;
case EVRButtonType::Btn_Toggle_Return:
{}break;
case EVRButtonType::Btn_Toggle_Stay:
{
if (!bLerpToPosition)
{
float ClampMinDepth = 0.0f;
// If active and a toggled stay, then clamp min to the toggled stay location
if (bButtonState)
ClampMinDepth = -(ButtonEngageDepth + (1.e-2f)); // + NOT_SO_UE_KINDA_SMALL_NUMBER
float NewDepth = FMath::Clamp(ClampMinDepth, -DepressDistance, ClampMinDepth);
this->SetRelativeLocation(InitialRelativeTransform.TransformPosition(SetAxisValue(NewDepth)), false);
}
else
this->SetComponentTickEnabled(true); // This will trigger the lerp to resting position
}break;
default:break;
}
}
void UVRButtonComponent::SetButtonState(bool bNewButtonState, bool bCallButtonChangedEvent, bool bSnapIntoPosition)
{
// No change
if (bButtonState == bNewButtonState)
return;
bButtonState = bNewButtonState;
SetButtonToRestingPosition(!bSnapIntoPosition);
LastToggleTime = GetWorld()->GetRealTimeSeconds();
if (bCallButtonChangedEvent)
{
ReceiveButtonStateChanged(bButtonState, LocalLastInteractingActor.Get(), LocalLastInteractingComponent.Get());
OnButtonStateChanged.Broadcast(bButtonState, LocalLastInteractingActor.Get(), LocalLastInteractingComponent.Get());
}
}
void UVRButtonComponent::ResetInitialButtonLocation()
{
// Get our initial relative transform to our parent (or not if un-parented).
InitialRelativeTransform = this->GetRelativeTransform();
}
bool UVRButtonComponent::IsButtonInUse()
{
return IsValid(LocalInteractingComponent);
}
float UVRButtonComponent::GetAxisValue(FVector CheckLocation)
{
switch (ButtonAxis)
{
case EVRInteractibleAxis::Axis_X:
return CheckLocation.X; break;
case EVRInteractibleAxis::Axis_Y:
return CheckLocation.Y; break;
case EVRInteractibleAxis::Axis_Z:
return CheckLocation.Z; break;
default:return 0.0f; break;
}
}
FVector UVRButtonComponent::SetAxisValue(float SetValue)
{
FVector vec = FVector::ZeroVector;
switch (ButtonAxis)
{
case EVRInteractibleAxis::Axis_X:
vec.X = SetValue; break;
case EVRInteractibleAxis::Axis_Y:
vec.Y = SetValue; break;
case EVRInteractibleAxis::Axis_Z:
vec.Z = SetValue; break;
}
return vec;
}

View file

@ -0,0 +1,596 @@
// Copyright 1998-2016 Epic Games, Inc. All Rights Reserved.
#include "Interactibles/VRDialComponent.h"
#include UE_INLINE_GENERATED_CPP_BY_NAME(VRDialComponent)
#include "VRExpansionFunctionLibrary.h"
#include "GripMotionControllerComponent.h"
#include "Net/UnrealNetwork.h"
//=============================================================================
UVRDialComponent::UVRDialComponent(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{
this->SetGenerateOverlapEvents(true);
this->PrimaryComponentTick.bStartWithTickEnabled = false;
PrimaryComponentTick.bCanEverTick = true;
bRepGameplayTags = false;
// Defaulting these true so that they work by default in networked environments
bReplicateMovement = true;
DialRotationAxis = EVRInteractibleAxis::Axis_Z;
InteractorRotationAxis = EVRInteractibleAxis::Axis_X;
bDialUsesAngleSnap = false;
bDialUseSnapAngleList = false;
SnapAngleThreshold = 45.0f;
SnapAngleIncrement = 45.0f;
LastSnapAngle = 0.0f;
RotationScaler = 1.0f;
ClockwiseMaximumDialAngle = 180.0f;
CClockwiseMaximumDialAngle = 180.0f;
bDenyGripping = false;
PrimarySlotRange = 100.f;
SecondarySlotRange = 100.f;
GripPriority = 1;
MovementReplicationSetting = EGripMovementReplicationSettings::ForceClientSideMovement;
BreakDistance = 100.0f;
bLerpBackOnRelease = false;
bSendDialEventsDuringLerp = false;
DialReturnSpeed = 90.0f;
bIsLerping = false;
bDialUseDirectHandRotation = false;
LastGripRot = 0.0f;
InitialGripRot = 0.f;
InitialRotBackEnd = 0.f;
bUseRollover = false;
}
//=============================================================================
UVRDialComponent::~UVRDialComponent()
{
}
void UVRDialComponent::GetLifetimeReplicatedProps(TArray< class FLifetimeProperty > & OutLifetimeProps) const
{
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
DOREPLIFETIME(UVRDialComponent, InitialRelativeTransform);
//DOREPLIFETIME_CONDITION(UVRDialComponent, bIsLerping, COND_InitialOnly);
DOREPLIFETIME(UVRDialComponent, bRepGameplayTags);
DOREPLIFETIME(UVRDialComponent, bReplicateMovement);
DOREPLIFETIME_CONDITION(UVRDialComponent, GameplayTags, COND_Custom);
}
void UVRDialComponent::PreReplication(IRepChangedPropertyTracker & ChangedPropertyTracker)
{
Super::PreReplication(ChangedPropertyTracker);
// Don't replicate if set to not do it
DOREPLIFETIME_ACTIVE_OVERRIDE_FAST(UVRDialComponent, GameplayTags, bRepGameplayTags);
DOREPLIFETIME_ACTIVE_OVERRIDE_FAST(USceneComponent, RelativeLocation, bReplicateMovement);
DOREPLIFETIME_ACTIVE_OVERRIDE_FAST(USceneComponent, RelativeRotation, bReplicateMovement);
DOREPLIFETIME_ACTIVE_OVERRIDE_FAST(USceneComponent, RelativeScale3D, bReplicateMovement);
}
void UVRDialComponent::OnRegister()
{
Super::OnRegister();
ResetInitialDialLocation(); // Load the original dial location
}
void UVRDialComponent::BeginPlay()
{
// Call the base class
Super::BeginPlay();
CalculateDialProgress();
bOriginalReplicatesMovement = bReplicateMovement;
}
void UVRDialComponent::TickComponent(float DeltaTime, enum ELevelTick TickType, FActorComponentTickFunction *ThisTickFunction)
{
if (bIsLerping)
{
if (bUseRollover)
{
this->SetDialAngle(FMath::FInterpConstantTo(CurRotBackEnd, 0.f, DeltaTime, DialReturnSpeed), bSendDialEventsDuringLerp);
}
else
{
// Flip lerp direction if we are on the other side
if (CurrentDialAngle > ClockwiseMaximumDialAngle)
this->SetDialAngle(FMath::FInterpConstantTo(CurRotBackEnd, 360.f, DeltaTime, DialReturnSpeed), bSendDialEventsDuringLerp);
else
this->SetDialAngle(FMath::FInterpConstantTo(CurRotBackEnd, 0.f, DeltaTime, DialReturnSpeed), bSendDialEventsDuringLerp);
}
if (CurRotBackEnd == 0.f)
{
this->SetComponentTickEnabled(false);
bIsLerping = false;
OnDialFinishedLerping.Broadcast();
ReceiveDialFinishedLerping();
}
}
else
{
this->SetComponentTickEnabled(false);
}
}
void UVRDialComponent::TickGrip_Implementation(UGripMotionControllerComponent * GrippingController, const FBPActorGripInformation & GripInformation, float DeltaTime)
{
// #TODO: Should this use a pivot rotation? it wouldn't make that much sense to me?
float DeltaRot = 0.0f;
if (!bDialUseDirectHandRotation)
{
FTransform CurrentRelativeTransform = InitialRelativeTransform * UVRInteractibleFunctionLibrary::Interactible_GetCurrentParentTransform(this);
FVector CurInteractorLocation = CurrentRelativeTransform.InverseTransformPosition(GrippingController->GetPivotLocation());
float NewRot = FRotator::ClampAxis(UVRInteractibleFunctionLibrary::GetAtan2Angle(DialRotationAxis, CurInteractorLocation));
//DeltaRot = RotationScaler * ( NewRot - LastGripRot);
DeltaRot = RotationScaler * FMath::FindDeltaAngleDegrees(LastGripRot, NewRot);
float LimitTest = FRotator::ClampAxis(((NewRot - InitialGripRot) + InitialRotBackEnd));
float MaxCheckValue = bUseRollover ? -CClockwiseMaximumDialAngle : 360.0f - CClockwiseMaximumDialAngle;
if (FMath::IsNearlyZero(CClockwiseMaximumDialAngle))
{
if (LimitTest > ClockwiseMaximumDialAngle && (CurRotBackEnd == ClockwiseMaximumDialAngle || CurRotBackEnd == 0.f))
{
DeltaRot = 0.f;
}
}
else if (FMath::IsNearlyZero(ClockwiseMaximumDialAngle))
{
if (LimitTest < MaxCheckValue && (CurRotBackEnd == MaxCheckValue || CurRotBackEnd == 0.f))
{
DeltaRot = 0.f;
}
}
else if (LimitTest > ClockwiseMaximumDialAngle && LimitTest < MaxCheckValue && (CurRotBackEnd == ClockwiseMaximumDialAngle || CurRotBackEnd == MaxCheckValue))
{
DeltaRot = 0.f;
}
LastGripRot = NewRot;
}
else
{
FRotator curRotation = GrippingController->GetComponentRotation();
DeltaRot = RotationScaler * UVRInteractibleFunctionLibrary::GetAxisValue(InteractorRotationAxis, (curRotation - LastRotation).GetNormalized());
LastRotation = curRotation;
}
AddDialAngle(DeltaRot, true);
// Handle the auto drop
if (BreakDistance > 0.f && GrippingController->HasGripAuthority(GripInformation) && FVector::DistSquared(InitialDropLocation, this->GetComponentTransform().InverseTransformPosition(GrippingController->GetPivotLocation())) >= FMath::Square(BreakDistance))
{
if (GrippingController->OnGripOutOfRange.IsBound())
{
uint8 GripID = GripInformation.GripID;
GrippingController->OnGripOutOfRange.Broadcast(GripInformation, GripInformation.GripDistance);
}
else
{
GrippingController->DropObjectByInterface(this, HoldingGrip.GripID);
}
return;
}
}
void UVRDialComponent::OnGrip_Implementation(UGripMotionControllerComponent * GrippingController, const FBPActorGripInformation & GripInformation)
{
FTransform CurrentRelativeTransform = InitialRelativeTransform * UVRInteractibleFunctionLibrary::Interactible_GetCurrentParentTransform(this);
// This lets me use the correct original location over the network without changes
FTransform ReversedRelativeTransform = FTransform(GripInformation.RelativeTransform.ToInverseMatrixWithScale());
FTransform CurrentTransform = this->GetComponentTransform();
FTransform RelativeToGripTransform = ReversedRelativeTransform * CurrentTransform;
//FTransform InitialTrans = RelativeToGripTransform.GetRelativeTransform(CurrentRelativeTransform);
InitialInteractorLocation = CurrentRelativeTransform.InverseTransformPosition(RelativeToGripTransform.GetTranslation());
InitialDropLocation = ReversedRelativeTransform.GetTranslation();
if (!bDialUseDirectHandRotation)
{
LastGripRot = FRotator::ClampAxis(UVRInteractibleFunctionLibrary::GetAtan2Angle(DialRotationAxis, InitialInteractorLocation));
InitialGripRot = LastGripRot;
InitialRotBackEnd = CurRotBackEnd;
}
else
{
LastRotation = RelativeToGripTransform.GetRotation().Rotator(); // Forcing into world space now so that initial can be correct over the network
}
bIsLerping = false;
//OnGripped.Broadcast(GrippingController, GripInformation);
}
void UVRDialComponent::OnGripRelease_Implementation(UGripMotionControllerComponent * ReleasingController, const FBPActorGripInformation & GripInformation, bool bWasSocketed)
{
if (bDialUsesAngleSnap && bDialUseSnapAngleList)
{
float closestAngle = 0.f;
float closestVal = FMath::Abs(closestAngle - CurRotBackEnd);
float closestValt = 0.f;
for (float val : DialSnapAngleList)
{
closestValt = FMath::Abs(val - CurRotBackEnd);
if (closestValt < closestVal)
{
closestAngle = val;
closestVal = closestValt;
}
}
if (closestAngle != LastSnapAngle)
{
this->SetRelativeRotation((FTransform(UVRInteractibleFunctionLibrary::SetAxisValueRot(DialRotationAxis, FMath::UnwindDegrees(closestAngle), FRotator::ZeroRotator)) * InitialRelativeTransform).Rotator());
CurrentDialAngle = FMath::RoundToFloat(closestAngle);
CurRotBackEnd = CurrentDialAngle;
if (!FMath::IsNearlyEqual(LastSnapAngle, CurrentDialAngle))
{
ReceiveDialHitSnapAngle(CurrentDialAngle);
OnDialHitSnapAngle.Broadcast(CurrentDialAngle);
LastSnapAngle = CurrentDialAngle;
}
}
}
else if (bDialUsesAngleSnap && SnapAngleIncrement > 0.f && FMath::Abs(FMath::Fmod(CurRotBackEnd, SnapAngleIncrement)) <= FMath::Min(SnapAngleIncrement, SnapAngleThreshold))
{
this->SetRelativeRotation((FTransform(UVRInteractibleFunctionLibrary::SetAxisValueRot(DialRotationAxis, FMath::GridSnap(CurRotBackEnd, SnapAngleIncrement), FRotator::ZeroRotator)) * InitialRelativeTransform).Rotator());
CurRotBackEnd = FMath::GridSnap(CurRotBackEnd, SnapAngleIncrement);
if (bUseRollover)
{
CurrentDialAngle = FMath::RoundToFloat(CurRotBackEnd);
}
else
{
CurrentDialAngle = FRotator::ClampAxis(FMath::RoundToFloat(CurRotBackEnd));
}
if (!FMath::IsNearlyEqual(LastSnapAngle, CurrentDialAngle))
{
ReceiveDialHitSnapAngle(CurrentDialAngle);
OnDialHitSnapAngle.Broadcast(CurrentDialAngle);
LastSnapAngle = CurrentDialAngle;
}
}
if (bLerpBackOnRelease)
{
bIsLerping = true;
this->SetComponentTickEnabled(true);
}
else
this->SetComponentTickEnabled(false);
//OnDropped.Broadcast(ReleasingController, GripInformation, bWasSocketed);
}
void UVRDialComponent::SetGripPriority(int NewGripPriority)
{
GripPriority = NewGripPriority;
}
void UVRDialComponent::OnChildGrip_Implementation(UGripMotionControllerComponent * GrippingController, const FBPActorGripInformation & GripInformation) {}
void UVRDialComponent::OnChildGripRelease_Implementation(UGripMotionControllerComponent * ReleasingController, const FBPActorGripInformation & GripInformation, bool bWasSocketed) {}
void UVRDialComponent::OnSecondaryGrip_Implementation(UGripMotionControllerComponent * GripOwningController, USceneComponent * SecondaryGripComponent, const FBPActorGripInformation & GripInformation) {}
void UVRDialComponent::OnSecondaryGripRelease_Implementation(UGripMotionControllerComponent * GripOwningController, USceneComponent * ReleasingSecondaryGripComponent, const FBPActorGripInformation & GripInformation) {}
void UVRDialComponent::OnUsed_Implementation() {}
void UVRDialComponent::OnEndUsed_Implementation() {}
void UVRDialComponent::OnSecondaryUsed_Implementation() {}
void UVRDialComponent::OnEndSecondaryUsed_Implementation() {}
void UVRDialComponent::OnInput_Implementation(FKey Key, EInputEvent KeyEvent) {}
bool UVRDialComponent::RequestsSocketing_Implementation(USceneComponent *& ParentToSocketTo, FName & OptionalSocketName, FTransform_NetQuantize & RelativeTransform) { return false; }
bool UVRDialComponent::DenyGripping_Implementation(UGripMotionControllerComponent * GripInitiator)
{
return bDenyGripping;
}
EGripInterfaceTeleportBehavior UVRDialComponent::TeleportBehavior_Implementation()
{
return EGripInterfaceTeleportBehavior::DropOnTeleport;
}
bool UVRDialComponent::SimulateOnDrop_Implementation()
{
return false;
}
/*EGripCollisionType UVRDialComponent::SlotGripType_Implementation()
{
return EGripCollisionType::CustomGrip;
}
EGripCollisionType UVRDialComponent::FreeGripType_Implementation()
{
return EGripCollisionType::CustomGrip;
}*/
EGripCollisionType UVRDialComponent::GetPrimaryGripType_Implementation(bool bIsSlot)
{
return EGripCollisionType::CustomGrip;
}
ESecondaryGripType UVRDialComponent::SecondaryGripType_Implementation()
{
return ESecondaryGripType::SG_None;
}
EGripMovementReplicationSettings UVRDialComponent::GripMovementReplicationType_Implementation()
{
return MovementReplicationSetting;
}
EGripLateUpdateSettings UVRDialComponent::GripLateUpdateSetting_Implementation()
{
return EGripLateUpdateSettings::LateUpdatesAlwaysOff;
}
/*float UVRDialComponent::GripStiffness_Implementation()
{
return 1500.0f;
}
float UVRDialComponent::GripDamping_Implementation()
{
return 200.0f;
}*/
void UVRDialComponent::GetGripStiffnessAndDamping_Implementation(float &GripStiffnessOut, float &GripDampingOut)
{
GripStiffnessOut = 0.0f;
GripDampingOut = 0.0f;
}
FBPAdvGripSettings UVRDialComponent::AdvancedGripSettings_Implementation()
{
return FBPAdvGripSettings(GripPriority);
}
float UVRDialComponent::GripBreakDistance_Implementation()
{
return BreakDistance;
}
/*void UVRDialComponent::ClosestSecondarySlotInRange_Implementation(FVector WorldLocation, bool & bHadSlotInRange, FTransform & SlotWorldTransform, UGripMotionControllerComponent * CallingController, FName OverridePrefix)
{
bHadSlotInRange = false;
}
void UVRDialComponent::ClosestPrimarySlotInRange_Implementation(FVector WorldLocation, bool & bHadSlotInRange, FTransform & SlotWorldTransform, UGripMotionControllerComponent * CallingController, FName OverridePrefix)
{
bHadSlotInRange = false;
}*/
void UVRDialComponent::ClosestGripSlotInRange_Implementation(FVector WorldLocation, bool bSecondarySlot, bool & bHadSlotInRange, FTransform & SlotWorldTransform, FName & SlotName, UGripMotionControllerComponent * CallingController, FName OverridePrefix)
{
if (OverridePrefix.IsNone())
bSecondarySlot ? OverridePrefix = "VRGripS" : OverridePrefix = "VRGripP";
UVRExpansionFunctionLibrary::GetGripSlotInRangeByTypeName_Component(OverridePrefix, this, WorldLocation, bSecondarySlot ? SecondarySlotRange : PrimarySlotRange, bHadSlotInRange, SlotWorldTransform, SlotName, CallingController);
}
bool UVRDialComponent::AllowsMultipleGrips_Implementation()
{
return false;
}
void UVRDialComponent::IsHeld_Implementation(TArray<FBPGripPair> & CurHoldingControllers, bool & bCurIsHeld)
{
CurHoldingControllers.Empty();
if (HoldingGrip.IsValid())
{
CurHoldingControllers.Add(HoldingGrip);
bCurIsHeld = bIsHeld;
}
else
{
bCurIsHeld = false;
}
}
void UVRDialComponent::Native_NotifyThrowGripDelegates(UGripMotionControllerComponent* Controller, bool bGripped, const FBPActorGripInformation& GripInformation, bool bWasSocketed)
{
if (bGripped)
{
OnGripped.Broadcast(Controller, GripInformation);
}
else
{
OnDropped.Broadcast(Controller, GripInformation, bWasSocketed);
}
}
void UVRDialComponent::SetHeld_Implementation(UGripMotionControllerComponent * NewHoldingController, uint8 GripID, bool bNewIsHeld)
{
if (bNewIsHeld)
{
HoldingGrip = FBPGripPair(NewHoldingController, GripID);
if (MovementReplicationSetting != EGripMovementReplicationSettings::ForceServerSideMovement)
{
if(!bIsHeld)
bOriginalReplicatesMovement = bReplicateMovement;
bReplicateMovement = false;
}
}
else
{
HoldingGrip.Clear();
if (MovementReplicationSetting != EGripMovementReplicationSettings::ForceServerSideMovement)
{
bReplicateMovement = bOriginalReplicatesMovement;
}
}
bIsHeld = bNewIsHeld;
}
/*FBPInteractionSettings UVRDialComponent::GetInteractionSettings_Implementation()
{
return FBPInteractionSettings();
}*/
bool UVRDialComponent::GetGripScripts_Implementation(TArray<UVRGripScriptBase*> & ArrayReference)
{
return false;
}
void UVRDialComponent::SetDialAngle(float DialAngle, bool bCallEvents)
{
CurRotBackEnd = DialAngle;
AddDialAngle(0.0f, bCallEvents);
}
void UVRDialComponent::AddDialAngle(float DialAngleDelta, bool bCallEvents, bool bSkipSettingRot)
{
//FindDeltaAngleDegrees
/** Utility to ensure angle is between +/- 180 degrees by unwinding. */
//static float UnwindDegrees(float A)
float MaxCheckValue = bUseRollover ? -CClockwiseMaximumDialAngle : 360.0f - CClockwiseMaximumDialAngle;
float DeltaRot = DialAngleDelta;
float tempCheck = bUseRollover ? CurRotBackEnd + DeltaRot : FRotator::ClampAxis(CurRotBackEnd + DeltaRot);
// Clamp it to the boundaries
if (FMath::IsNearlyZero(CClockwiseMaximumDialAngle))
{
CurRotBackEnd = FMath::Clamp(CurRotBackEnd + DeltaRot, 0.0f, ClockwiseMaximumDialAngle);
}
else if (FMath::IsNearlyZero(ClockwiseMaximumDialAngle))
{
if (bUseRollover)
{
CurRotBackEnd = FMath::Clamp(CurRotBackEnd + DeltaRot, -CClockwiseMaximumDialAngle, 0.0f);
}
else
{
if (CurRotBackEnd < MaxCheckValue)
CurRotBackEnd = FMath::Clamp(360.0f + DeltaRot, MaxCheckValue, 360.0f);
else
CurRotBackEnd = FMath::Clamp(CurRotBackEnd + DeltaRot, MaxCheckValue, 360.0f);
}
}
else if(!bUseRollover && tempCheck > ClockwiseMaximumDialAngle && tempCheck < MaxCheckValue)
{
if (CurRotBackEnd < MaxCheckValue)
{
CurRotBackEnd = ClockwiseMaximumDialAngle;
}
else
{
CurRotBackEnd = MaxCheckValue;
}
}
else if (bUseRollover)
{
if (tempCheck > ClockwiseMaximumDialAngle)
{
CurRotBackEnd = ClockwiseMaximumDialAngle;
}
else if (tempCheck < MaxCheckValue)
{
CurRotBackEnd = MaxCheckValue;
}
else
{
CurRotBackEnd = tempCheck;
}
}
else
{
CurRotBackEnd = tempCheck;
}
if (bDialUsesAngleSnap && bDialUseSnapAngleList)
{
float closestAngle = 0.f;
// Always default 0.0f to the list
float closestVal = FMath::Abs(closestAngle - CurRotBackEnd);
float closestValt = 0.f;
for (float val : DialSnapAngleList)
{
closestValt = FMath::Abs(val - CurRotBackEnd);
if (closestValt < closestVal)
{
closestAngle = val;
closestVal = closestValt;
}
}
if (closestAngle != LastSnapAngle)
{
if (!bSkipSettingRot)
this->SetRelativeRotation((FTransform(UVRInteractibleFunctionLibrary::SetAxisValueRot(DialRotationAxis, FMath::UnwindDegrees(closestAngle), FRotator::ZeroRotator)) * InitialRelativeTransform).Rotator());
CurrentDialAngle = FMath::RoundToFloat(closestAngle);
if (bCallEvents && !FMath::IsNearlyEqual(LastSnapAngle, CurrentDialAngle))
{
ReceiveDialHitSnapAngle(CurrentDialAngle);
OnDialHitSnapAngle.Broadcast(CurrentDialAngle);
}
LastSnapAngle = CurrentDialAngle;
}
}
else if (bDialUsesAngleSnap && SnapAngleIncrement > 0.f && FMath::Abs(FMath::Fmod(CurRotBackEnd, SnapAngleIncrement)) <= FMath::Min(SnapAngleIncrement, SnapAngleThreshold))
{
if (!bSkipSettingRot)
this->SetRelativeRotation((FTransform(UVRInteractibleFunctionLibrary::SetAxisValueRot(DialRotationAxis, FMath::UnwindDegrees(FMath::GridSnap(CurRotBackEnd, SnapAngleIncrement)), FRotator::ZeroRotator)) * InitialRelativeTransform).Rotator());
CurrentDialAngle = FMath::RoundToFloat(FMath::GridSnap(CurRotBackEnd, SnapAngleIncrement));
if (bCallEvents && !FMath::IsNearlyEqual(LastSnapAngle, CurrentDialAngle))
{
ReceiveDialHitSnapAngle(CurrentDialAngle);
OnDialHitSnapAngle.Broadcast(CurrentDialAngle);
}
LastSnapAngle = CurrentDialAngle;
}
else
{
if (!bSkipSettingRot)
this->SetRelativeRotation((FTransform(UVRInteractibleFunctionLibrary::SetAxisValueRot(DialRotationAxis, FMath::UnwindDegrees(CurRotBackEnd), FRotator::ZeroRotator)) * InitialRelativeTransform).Rotator());
CurrentDialAngle = FMath::RoundToFloat(CurRotBackEnd);
}
}
void UVRDialComponent::ResetInitialDialLocation()
{
// Get our initial relative transform to our parent (or not if un-parented).
InitialRelativeTransform = this->GetRelativeTransform();
CurRotBackEnd = 0.0f;
CalculateDialProgress();
}
void UVRDialComponent::CalculateDialProgress()
{
FTransform CurRelativeTransform = this->GetComponentTransform().GetRelativeTransform(UVRInteractibleFunctionLibrary::Interactible_GetCurrentParentTransform(this));
LastGripRot = UVRInteractibleFunctionLibrary::GetDeltaAngleFromTransforms(DialRotationAxis, InitialRelativeTransform, CurRelativeTransform);
CurRotBackEnd = LastGripRot;
AddDialAngle(0.0f, false, true);
}

View file

@ -0,0 +1,8 @@
// Fill out your copyright notice in the Description page of Project Settings.
#include "Interactibles/VRInteractibleFunctionLibrary.h"
#include UE_INLINE_GENERATED_CPP_BY_NAME(VRInteractibleFunctionLibrary)
//#include "Engine/Engine.h"
//General Log
DEFINE_LOG_CATEGORY(VRInteractibleFunctionLibraryLog);

View file

@ -0,0 +1,819 @@
// Copyright 1998-2016 Epic Games, Inc. All Rights Reserved.
#include "Interactibles/VRLeverComponent.h"
#include UE_INLINE_GENERATED_CPP_BY_NAME(VRLeverComponent)
#include "GripMotionControllerComponent.h"
#include "VRExpansionFunctionLibrary.h"
#include "Net/UnrealNetwork.h"
//=============================================================================
UVRLeverComponent::UVRLeverComponent(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{
this->SetGenerateOverlapEvents(true);
this->PrimaryComponentTick.bStartWithTickEnabled = false;
PrimaryComponentTick.bCanEverTick = true;
bRepGameplayTags = false;
// Defaulting these true so that they work by default in networked environments
bReplicateMovement = true;
MovementReplicationSetting = EGripMovementReplicationSettings::ForceClientSideMovement;
BreakDistance = 100.0f;
Stiffness = 1500.0f;
Damping = 200.0f;
//SceneIndex = 0;
ParentComponent = nullptr;
LeverRotationAxis = EVRInteractibleLeverAxis::Axis_X;
LeverLimitNegative = 0.0f;
LeverLimitPositive = 90.0f;
FlightStickYawLimit = 180.0f;
bLeverState = false;
LeverTogglePercentage = 0.8f;
LastDeltaAngle = 0.0f;
FullCurrentAngle = 0.0f;
LeverReturnTypeWhenReleased = EVRInteractibleLeverReturnType::ReturnToZero;
LeverReturnSpeed = 50.0f;
MomentumAtDrop = 0.0f;
LeverMomentumFriction = 5.0f;
MaxLeverMomentum = 180.0f;
FramesToAverage = 3;
bBlendAxisValuesByAngleThreshold = false;
AngleThreshold = 90.0f;
LastLeverAngle = 0.0f;
bSendLeverEventsDuringLerp = false;
InitialRelativeTransform = FTransform::Identity;
InitialInteractorLocation = FVector::ZeroVector;
InteractorOffsetTransform = FTransform::Identity;
AllCurrentLeverAngles = FRotator::ZeroRotator;
InitialGripRot = 0.0f;
qRotAtGrab = FQuat::Identity;
bIsLerping = false;
bUngripAtTargetRotation = false;
bDenyGripping = false;
bIsLocked = false;
bAutoDropWhenLocked = true;
PrimarySlotRange = 100.f;
SecondarySlotRange = 100.f;
GripPriority = 1;
// Set to only overlap with things so that its not ruined by touching over actors
this->SetCollisionResponseToAllChannels(ECollisionResponse::ECR_Overlap);
}
//=============================================================================
UVRLeverComponent::~UVRLeverComponent()
{
}
void UVRLeverComponent::GetLifetimeReplicatedProps(TArray< class FLifetimeProperty > & OutLifetimeProps) const
{
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
DOREPLIFETIME(UVRLeverComponent, InitialRelativeTransform);
//DOREPLIFETIME_CONDITION(UVRLeverComponent, bIsLerping, COND_InitialOnly);
DOREPLIFETIME(UVRLeverComponent, bRepGameplayTags);
DOREPLIFETIME(UVRLeverComponent, bReplicateMovement);
DOREPLIFETIME_CONDITION(UVRLeverComponent, GameplayTags, COND_Custom);
}
void UVRLeverComponent::PreReplication(IRepChangedPropertyTracker & ChangedPropertyTracker)
{
Super::PreReplication(ChangedPropertyTracker);
// Replicate the levers initial transform if we are replicating movement
//DOREPLIFETIME_ACTIVE_OVERRIDE(UVRLeverComponent, InitialRelativeTransform, bReplicateMovement);
// Don't replicate if set to not do it
DOREPLIFETIME_ACTIVE_OVERRIDE_FAST(UVRLeverComponent, GameplayTags, bRepGameplayTags);
DOREPLIFETIME_ACTIVE_OVERRIDE_FAST(USceneComponent, RelativeLocation, bReplicateMovement);
DOREPLIFETIME_ACTIVE_OVERRIDE_FAST(USceneComponent, RelativeRotation, bReplicateMovement);
DOREPLIFETIME_ACTIVE_OVERRIDE_FAST(USceneComponent, RelativeScale3D, bReplicateMovement);
}
void UVRLeverComponent::OnRegister()
{
Super::OnRegister();
ResetInitialLeverLocation(); // Load the original lever location
}
void UVRLeverComponent::BeginPlay()
{
// Call the base class
Super::BeginPlay();
ReCalculateCurrentAngle(true);
bOriginalReplicatesMovement = bReplicateMovement;
}
void UVRLeverComponent::TickComponent(float DeltaTime, enum ELevelTick TickType, FActorComponentTickFunction *ThisTickFunction)
{
// Call supers tick (though I don't think any of the base classes to this actually implement it)
Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
bool bWasLerping = bIsLerping;
// If we are locked then end the lerp, no point
if (bIsLocked)
{
if (bWasLerping)
{
bIsLerping = false;
// If we start lerping while locked, just end it
OnLeverFinishedLerping.Broadcast(CurrentLeverAngle);
ReceiveLeverFinishedLerping(CurrentLeverAngle);
}
return;
}
if (bIsLerping)
{
FTransform CurRelativeTransform = this->GetComponentTransform().GetRelativeTransform(UVRInteractibleFunctionLibrary::Interactible_GetCurrentParentTransform(this));
switch (LeverRotationAxis)
{
case EVRInteractibleLeverAxis::Axis_X:
case EVRInteractibleLeverAxis::Axis_Y:
case EVRInteractibleLeverAxis::Axis_Z:
{
LerpAxis(FullCurrentAngle, DeltaTime);
}break;
case EVRInteractibleLeverAxis::Axis_XY:
case EVRInteractibleLeverAxis::FlightStick_XY:
{
// Only supporting LerpToZero with this mode currently
FQuat LerpedQuat = FMath::QInterpConstantTo(CurRelativeTransform.GetRelativeTransform(InitialRelativeTransform).GetRotation(), FQuat::Identity, DeltaTime, FMath::DegreesToRadians(LeverReturnSpeed));
if (LerpedQuat.IsIdentity())
{
this->SetComponentTickEnabled(false);
bIsLerping = false;
bReplicateMovement = bOriginalReplicatesMovement;
this->SetRelativeRotation(InitialRelativeTransform.Rotator());
}
else
{
this->SetRelativeRotation((FTransform(LerpedQuat) * InitialRelativeTransform).GetRotation());
}
}break;
default:break;
}
}
FTransform CurrentRelativeTransform = this->GetComponentTransform().GetRelativeTransform(UVRInteractibleFunctionLibrary::Interactible_GetCurrentParentTransform(this));
CalculateCurrentAngle(CurrentRelativeTransform);
if (!bWasLerping && LeverReturnTypeWhenReleased == EVRInteractibleLeverReturnType::RetainMomentum)
{
// Rolling average across num samples
MomentumAtDrop -= MomentumAtDrop / FramesToAverage;
MomentumAtDrop += ((FullCurrentAngle - LastLeverAngle) / DeltaTime) / FramesToAverage;
MomentumAtDrop = FMath::Min(MaxLeverMomentum, MomentumAtDrop);
LastLeverAngle = FullCurrentAngle;
}
// Check for events and set current state and check for auto drop
ProccessCurrentState(bWasLerping, true, true);
// If the lerping state changed from the above
if (bWasLerping && !bIsLerping)
{
OnLeverFinishedLerping.Broadcast(CurrentLeverAngle);
ReceiveLeverFinishedLerping(CurrentLeverAngle);
}
}
void UVRLeverComponent::ProccessCurrentState(bool bWasLerping, bool bThrowEvents, bool bCheckAutoDrop)
{
bool bNewLeverState = (!FMath::IsNearlyZero(LeverLimitNegative) && FullCurrentAngle <= -(LeverLimitNegative * LeverTogglePercentage)) || (!FMath::IsNearlyZero(LeverLimitPositive) && FullCurrentAngle >= (LeverLimitPositive * LeverTogglePercentage));
//if (FMath::Abs(CurrentLeverAngle) >= LeverLimit )
if (bNewLeverState != bLeverState)
{
bLeverState = bNewLeverState;
if (bThrowEvents && (bSendLeverEventsDuringLerp || !bWasLerping))
{
ReceiveLeverStateChanged(bLeverState, FullCurrentAngle >= 0.0f ? EVRInteractibleLeverEventType::LeverPositive : EVRInteractibleLeverEventType::LeverNegative, CurrentLeverAngle, FullCurrentAngle);
OnLeverStateChanged.Broadcast(bLeverState, FullCurrentAngle >= 0.0f ? EVRInteractibleLeverEventType::LeverPositive : EVRInteractibleLeverEventType::LeverNegative, CurrentLeverAngle, FullCurrentAngle);
}
if (bCheckAutoDrop)
{
if (!bWasLerping && bUngripAtTargetRotation && bLeverState && HoldingGrip.IsValid())
{
FBPActorGripInformation GripInformation;
EBPVRResultSwitch result;
HoldingGrip.HoldingController->GetGripByID(GripInformation, HoldingGrip.GripID, result);
if (result == EBPVRResultSwitch::OnSucceeded && HoldingGrip.HoldingController->HasGripAuthority(GripInformation))
{
HoldingGrip.HoldingController->DropObjectByInterface(this, HoldingGrip.GripID);
}
}
}
}
}
void UVRLeverComponent::OnUnregister()
{
Super::OnUnregister();
}
bool UVRLeverComponent::CheckAutoDrop(UGripMotionControllerComponent* GrippingController, const FBPActorGripInformation& GripInformation)
{
// Converted to a relative value now so it should be correct
if (BreakDistance > 0.f && GrippingController->HasGripAuthority(GripInformation) && FVector::DistSquared(InitialInteractorDropLocation, this->GetComponentTransform().InverseTransformPosition(GrippingController->GetPivotLocation())) >= FMath::Square(BreakDistance))
{
if (GrippingController->OnGripOutOfRange.IsBound())
{
uint8 GripID = GripInformation.GripID;
GrippingController->OnGripOutOfRange.Broadcast(GripInformation, GripInformation.GripDistance);
}
else
{
GrippingController->DropObjectByInterface(this, HoldingGrip.GripID);
}
return true;
}
return false;
}
void UVRLeverComponent::TickGrip_Implementation(UGripMotionControllerComponent * GrippingController, const FBPActorGripInformation & GripInformation, float DeltaTime)
{
if (bIsLocked)
{
if (bAutoDropWhenLocked)
{
// Check if we should auto drop
CheckAutoDrop(GrippingController, GripInformation);
}
return;
}
// Handle manual tracking here
FTransform ParentTransform = UVRInteractibleFunctionLibrary::Interactible_GetCurrentParentTransform(this);
FTransform CurrentRelativeTransform = InitialRelativeTransform * ParentTransform;
FTransform PivotTransform = GrippingController->GetPivotTransform();
FVector CurInteractorLocation = (InteractorOffsetTransform * PivotTransform).GetRelativeTransform(CurrentRelativeTransform).GetTranslation();
switch (LeverRotationAxis)
{
case EVRInteractibleLeverAxis::Axis_XY:
case EVRInteractibleLeverAxis::FlightStick_XY:
{
FRotator Rot;
FVector nAxis;
float nAngle = 0.0f;
FQuat::FindBetweenVectors(qRotAtGrab.UnrotateVector(InitialInteractorLocation), CurInteractorLocation).ToAxisAndAngle(nAxis, nAngle);
float MaxAngle = FMath::DegreesToRadians(LeverLimitPositive);
bool bWasClamped = nAngle > MaxAngle;
nAngle = FMath::Clamp(nAngle, 0.0f, MaxAngle);
Rot = FQuat(nAxis, nAngle).Rotator();
if (LeverRotationAxis == EVRInteractibleLeverAxis::FlightStick_XY)
{
// Store our projected relative transform
FTransform CalcTransform = (FTransform(Rot) * InitialRelativeTransform);
// Fixup yaw if this is a flight stick
if (bWasClamped)
{
// If we clamped the angle due to limits then lets re-project the hand back to get the correct facing again
// This is only when things have been clamped to avoid the extra calculations
FTransform NewPivTrans = PivotTransform.GetRelativeTransform((CalcTransform * ParentTransform));
CurInteractorLocation = NewPivTrans.GetTranslation();
FVector OffsetVal = CurInteractorLocation + NewPivTrans.GetRotation().RotateVector(InteractorOffsetTransform.GetTranslation());
OffsetVal.Z = 0;
CurInteractorLocation -= OffsetVal;
}
else
{
CurInteractorLocation = (CalcTransform * ParentTransform).InverseTransformPosition(GrippingController->GetPivotLocation());
}
float CurrentLeverYawAngle = FRotator::NormalizeAxis(UVRInteractibleFunctionLibrary::GetAtan2Angle(EVRInteractibleAxis::Axis_Z, CurInteractorLocation, InitialGripRot));
if (FlightStickYawLimit < 180.0f)
{
CurrentLeverYawAngle = FMath::Clamp(CurrentLeverYawAngle, -FlightStickYawLimit, FlightStickYawLimit);
}
FQuat newLocalRot = CalcTransform.GetRotation() * FQuat(FVector::UpVector, FMath::DegreesToRadians(CurrentLeverYawAngle));
this->SetRelativeRotation(newLocalRot.Rotator());
}
else
{
this->SetRelativeRotation((FTransform(Rot) * InitialRelativeTransform).Rotator());
}
}
break;
case EVRInteractibleLeverAxis::Axis_X:
case EVRInteractibleLeverAxis::Axis_Y:
case EVRInteractibleLeverAxis::Axis_Z:
{
float DeltaAngle = CalcAngle(LeverRotationAxis, CurInteractorLocation);
LastDeltaAngle = DeltaAngle;
FTransform CalcTransform = (FTransform(UVRInteractibleFunctionLibrary::SetAxisValueRot((EVRInteractibleAxis)LeverRotationAxis, DeltaAngle, FRotator::ZeroRotator)) * InitialRelativeTransform);
this->SetRelativeRotation(CalcTransform.Rotator());
}break;
default:break;
}
// Recalc current angle
CurrentRelativeTransform = this->GetComponentTransform().GetRelativeTransform(UVRInteractibleFunctionLibrary::Interactible_GetCurrentParentTransform(this));
CalculateCurrentAngle(CurrentRelativeTransform);
// Check for events and set current state and check for auto drop
ProccessCurrentState(bIsLerping, true, true);
// Check if we should auto drop
CheckAutoDrop(GrippingController, GripInformation);
}
void UVRLeverComponent::OnGrip_Implementation(UGripMotionControllerComponent * GrippingController, const FBPActorGripInformation & GripInformation)
{
ParentComponent = this->GetAttachParent();
FTransform CurrentRelativeTransform = InitialRelativeTransform * UVRInteractibleFunctionLibrary::Interactible_GetCurrentParentTransform(this);
// This lets me use the correct original location over the network without changes
FTransform ReversedRelativeTransform = FTransform(GripInformation.RelativeTransform.ToInverseMatrixWithScale());
FTransform CurrentTransform = this->GetComponentTransform();
FTransform RelativeToGripTransform = FTransform::Identity;
if (LeverRotationAxis == EVRInteractibleLeverAxis::FlightStick_XY)
{
// Offset the grip to the same height on the cross axis and centered on the lever
FVector InitialInteractorOffset = ReversedRelativeTransform.GetTranslation();
FTransform InitTrans = ReversedRelativeTransform;
InitialInteractorOffset.X = 0;
InitialInteractorOffset.Y = 0;
InteractorOffsetTransform = ReversedRelativeTransform;
InteractorOffsetTransform.AddToTranslation(-InitialInteractorOffset);
InteractorOffsetTransform = FTransform(InteractorOffsetTransform.ToInverseMatrixWithScale());
InitialInteractorOffset = ReversedRelativeTransform.GetTranslation();
InitialInteractorOffset.Z = 0;
InitTrans.AddToTranslation(-InitialInteractorOffset);
RelativeToGripTransform = InitTrans * CurrentTransform;
}
else
{
RelativeToGripTransform = ReversedRelativeTransform * CurrentTransform;
InteractorOffsetTransform = FTransform::Identity;
}
InitialInteractorLocation = CurrentRelativeTransform.InverseTransformPosition(RelativeToGripTransform.GetTranslation());
InitialInteractorDropLocation = ReversedRelativeTransform.GetTranslation();
switch (LeverRotationAxis)
{
case EVRInteractibleLeverAxis::Axis_XY:
{
qRotAtGrab = this->GetComponentTransform().GetRelativeTransform(CurrentRelativeTransform).GetRotation();
}break;
case EVRInteractibleLeverAxis::FlightStick_XY:
{
qRotAtGrab = this->GetComponentTransform().GetRelativeTransform(CurrentRelativeTransform).GetRotation();
InitialGripRot = UVRInteractibleFunctionLibrary::GetAtan2Angle(EVRInteractibleAxis::Axis_Z, ReversedRelativeTransform.GetTranslation());
}break;
case EVRInteractibleLeverAxis::Axis_X:
case EVRInteractibleLeverAxis::Axis_Y:
{
// Get our initial interactor rotation
InitialGripRot = UVRInteractibleFunctionLibrary::GetAtan2Angle((EVRInteractibleAxis)LeverRotationAxis, InitialInteractorLocation);
}break;
case EVRInteractibleLeverAxis::Axis_Z:
{
// Get our initial interactor rotation
InitialGripRot = UVRInteractibleFunctionLibrary::GetAtan2Angle((EVRInteractibleAxis)LeverRotationAxis, InitialInteractorLocation);
}break;
default:break;
}
// Get out current rotation at grab
RotAtGrab = UVRInteractibleFunctionLibrary::GetDeltaAngleFromTransforms((EVRInteractibleAxis)LeverRotationAxis, CurrentRelativeTransform, CurrentTransform);
LastLeverAngle = CurrentLeverAngle;
bIsLerping = false;
bIsInFirstTick = true;
MomentumAtDrop = 0.0f;
this->SetComponentTickEnabled(true);
//OnGripped.Broadcast(GrippingController, GripInformation);
}
void UVRLeverComponent::OnGripRelease_Implementation(UGripMotionControllerComponent * ReleasingController, const FBPActorGripInformation & GripInformation, bool bWasSocketed)
{
if (LeverReturnTypeWhenReleased != EVRInteractibleLeverReturnType::Stay)
{
bIsLerping = true;
this->SetComponentTickEnabled(true);
if (MovementReplicationSetting != EGripMovementReplicationSettings::ForceServerSideMovement)
bReplicateMovement = false;
}
else
{
this->SetComponentTickEnabled(false);
bReplicateMovement = bOriginalReplicatesMovement;
}
//OnDropped.Broadcast(ReleasingController, GripInformation, bWasSocketed);
}
void UVRLeverComponent::SetGripPriority(int NewGripPriority)
{
GripPriority = NewGripPriority;
}
void UVRLeverComponent::SetIsLocked(bool bNewLockedState)
{
bIsLocked = bNewLockedState;
}
void UVRLeverComponent::OnChildGrip_Implementation(UGripMotionControllerComponent * GrippingController, const FBPActorGripInformation & GripInformation) {}
void UVRLeverComponent::OnChildGripRelease_Implementation(UGripMotionControllerComponent * ReleasingController, const FBPActorGripInformation & GripInformation, bool bWasSocketed) {}
void UVRLeverComponent::OnSecondaryGrip_Implementation(UGripMotionControllerComponent * GripOwningController, USceneComponent * SecondaryGripComponent, const FBPActorGripInformation & GripInformation) {}
void UVRLeverComponent::OnSecondaryGripRelease_Implementation(UGripMotionControllerComponent * GripOwningController, USceneComponent * ReleasingSecondaryGripComponent, const FBPActorGripInformation & GripInformation) {}
void UVRLeverComponent::OnUsed_Implementation() {}
void UVRLeverComponent::OnEndUsed_Implementation() {}
void UVRLeverComponent::OnSecondaryUsed_Implementation() {}
void UVRLeverComponent::OnEndSecondaryUsed_Implementation() {}
void UVRLeverComponent::OnInput_Implementation(FKey Key, EInputEvent KeyEvent) {}
bool UVRLeverComponent::RequestsSocketing_Implementation(USceneComponent *& ParentToSocketTo, FName & OptionalSocketName, FTransform_NetQuantize & RelativeTransform) { return false; }
bool UVRLeverComponent::DenyGripping_Implementation(UGripMotionControllerComponent * GripInitiator)
{
return bDenyGripping;
}
EGripInterfaceTeleportBehavior UVRLeverComponent::TeleportBehavior_Implementation()
{
return EGripInterfaceTeleportBehavior::DropOnTeleport;
}
bool UVRLeverComponent::SimulateOnDrop_Implementation()
{
return false;
}
EGripCollisionType UVRLeverComponent::GetPrimaryGripType_Implementation(bool bIsSlot)
{
return EGripCollisionType::CustomGrip;
}
ESecondaryGripType UVRLeverComponent::SecondaryGripType_Implementation()
{
return ESecondaryGripType::SG_None;
}
EGripMovementReplicationSettings UVRLeverComponent::GripMovementReplicationType_Implementation()
{
return MovementReplicationSetting;
}
EGripLateUpdateSettings UVRLeverComponent::GripLateUpdateSetting_Implementation()
{
return EGripLateUpdateSettings::LateUpdatesAlwaysOff;
}
/*float UVRLeverComponent::GripStiffness_Implementation()
{
return Stiffness;
}
float UVRLeverComponent::GripDamping_Implementation()
{
return Damping;
}*/
void UVRLeverComponent::GetGripStiffnessAndDamping_Implementation(float &GripStiffnessOut, float &GripDampingOut)
{
GripStiffnessOut = Stiffness;
GripDampingOut = Damping;
}
FBPAdvGripSettings UVRLeverComponent::AdvancedGripSettings_Implementation()
{
return FBPAdvGripSettings(GripPriority);
}
float UVRLeverComponent::GripBreakDistance_Implementation()
{
return BreakDistance;
}
/*void UVRLeverComponent::ClosestSecondarySlotInRange_Implementation(FVector WorldLocation, bool & bHadSlotInRange, FTransform & SlotWorldTransform, UGripMotionControllerComponent * CallingController, FName OverridePrefix)
{
bHadSlotInRange = false;
}
void UVRLeverComponent::ClosestPrimarySlotInRange_Implementation(FVector WorldLocation, bool & bHadSlotInRange, FTransform & SlotWorldTransform, UGripMotionControllerComponent * CallingController, FName OverridePrefix)
{
bHadSlotInRange = false;
}*/
void UVRLeverComponent::ClosestGripSlotInRange_Implementation(FVector WorldLocation, bool bSecondarySlot, bool & bHadSlotInRange, FTransform & SlotWorldTransform, FName & SlotName, UGripMotionControllerComponent * CallingController, FName OverridePrefix)
{
if (OverridePrefix.IsNone())
bSecondarySlot ? OverridePrefix = "VRGripS" : OverridePrefix = "VRGripP";
UVRExpansionFunctionLibrary::GetGripSlotInRangeByTypeName_Component(OverridePrefix, this, WorldLocation, bSecondarySlot ? SecondarySlotRange : PrimarySlotRange, bHadSlotInRange, SlotWorldTransform, SlotName, CallingController);
}
bool UVRLeverComponent::AllowsMultipleGrips_Implementation()
{
return false;
}
void UVRLeverComponent::IsHeld_Implementation(TArray<FBPGripPair> & CurHoldingControllers, bool & bCurIsHeld)
{
CurHoldingControllers.Empty();
if (HoldingGrip.IsValid())
{
CurHoldingControllers.Add(HoldingGrip);
bCurIsHeld = bIsHeld;
}
else
{
bCurIsHeld = false;
}
}
void UVRLeverComponent::Native_NotifyThrowGripDelegates(UGripMotionControllerComponent* Controller, bool bGripped, const FBPActorGripInformation& GripInformation, bool bWasSocketed)
{
if (bGripped)
{
OnGripped.Broadcast(Controller, GripInformation);
}
else
{
OnDropped.Broadcast(Controller, GripInformation, bWasSocketed);
}
}
void UVRLeverComponent::SetHeld_Implementation(UGripMotionControllerComponent * NewHoldingController, uint8 GripID, bool bNewIsHeld)
{
if (bNewIsHeld)
{
HoldingGrip = FBPGripPair(NewHoldingController, GripID);
if (MovementReplicationSetting != EGripMovementReplicationSettings::ForceServerSideMovement)
{
if (!bIsHeld && !bIsLerping)
bOriginalReplicatesMovement = bReplicateMovement;
bReplicateMovement = false;
}
}
else
{
HoldingGrip.Clear();
if (MovementReplicationSetting != EGripMovementReplicationSettings::ForceServerSideMovement)
{
bReplicateMovement = bOriginalReplicatesMovement;
}
}
bIsHeld = bNewIsHeld;
}
/*FBPInteractionSettings UVRLeverComponent::GetInteractionSettings_Implementation()
{
return FBPInteractionSettings();
}*/
bool UVRLeverComponent::GetGripScripts_Implementation(TArray<UVRGripScriptBase*> & ArrayReference)
{
return false;
}
float UVRLeverComponent::ReCalculateCurrentAngle(bool bAllowThrowingEvents)
{
FTransform CurRelativeTransform = this->GetComponentTransform().GetRelativeTransform(UVRInteractibleFunctionLibrary::Interactible_GetCurrentParentTransform(this));
CalculateCurrentAngle(CurRelativeTransform);
ProccessCurrentState(bIsLerping, bAllowThrowingEvents, bAllowThrowingEvents);
return CurrentLeverAngle;
}
void UVRLeverComponent::SetLeverAngle(float NewAngle, FVector DualAxisForwardVector, bool bAllowThrowingEvents)
{
NewAngle = -NewAngle; // Need to inverse the sign
FVector ForwardVector = DualAxisForwardVector;
switch (LeverRotationAxis)
{
case EVRInteractibleLeverAxis::Axis_X:
ForwardVector = FVector(FMath::Sign(NewAngle), 0.0f, 0.0f); break;
case EVRInteractibleLeverAxis::Axis_Y:
ForwardVector = FVector(0.0f, FMath::Sign(NewAngle), 0.0f); break;
case EVRInteractibleLeverAxis::Axis_Z:
ForwardVector = FVector(0.0f, 0.0f, FMath::Sign(NewAngle)); break;
default:break;
}
FQuat NewLeverRotation(ForwardVector, FMath::DegreesToRadians(FMath::Abs(NewAngle)));
this->SetRelativeTransform(FTransform(NewLeverRotation) * InitialRelativeTransform);
ReCalculateCurrentAngle(bAllowThrowingEvents);
}
void UVRLeverComponent::ResetInitialLeverLocation(bool bAllowThrowingEvents)
{
// Get our initial relative transform to our parent (or not if un-parented).
InitialRelativeTransform = this->GetRelativeTransform();
CalculateCurrentAngle(InitialRelativeTransform);
ProccessCurrentState(bIsLerping, bAllowThrowingEvents, bAllowThrowingEvents);
}
void UVRLeverComponent::CalculateCurrentAngle(FTransform & CurrentTransform)
{
float Angle;
switch (LeverRotationAxis)
{
case EVRInteractibleLeverAxis::Axis_XY:
case EVRInteractibleLeverAxis::FlightStick_XY:
{
FTransform RelativeToSpace = CurrentTransform.GetRelativeTransform(InitialRelativeTransform);
FQuat CurrentRelRot = RelativeToSpace.GetRotation();// CurrentTransform.GetRotation();
FVector UpVec = CurrentRelRot.GetUpVector();
CurrentLeverForwardVector = FVector::VectorPlaneProject(UpVec, FVector::UpVector);
CurrentLeverForwardVector.Normalize();
FullCurrentAngle = FMath::RadiansToDegrees(FMath::Acos(FVector::DotProduct(UpVec, FVector::UpVector)));
CurrentLeverAngle = FMath::RoundToFloat(FullCurrentAngle);
AllCurrentLeverAngles.Roll = FMath::Sign(UpVec.Y) * FMath::RadiansToDegrees(FMath::Acos(FVector::DotProduct(FVector(0.0f, UpVec.Y, UpVec.Z), FVector::UpVector)));
AllCurrentLeverAngles.Pitch = FMath::Sign(UpVec.X) * FMath::RadiansToDegrees(FMath::Acos(FVector::DotProduct(FVector(UpVec.X, 0.0f, UpVec.Z), FVector::UpVector)));
if (bBlendAxisValuesByAngleThreshold)
{
FVector ProjectedLoc = FVector(UpVec.X, UpVec.Y, 0.0f).GetSafeNormal();
AllCurrentLeverAngles.Pitch *= FMath::Clamp(1.0f - (FMath::Abs(FMath::RadiansToDegrees(FMath::Acos(FVector::DotProduct(ProjectedLoc, FMath::Sign(UpVec.X) * FVector::ForwardVector)))) / AngleThreshold), 0.0f, 1.0f);
AllCurrentLeverAngles.Roll *= FMath::Clamp(1.0f - (FMath::Abs(FMath::RadiansToDegrees(FMath::Acos(FVector::DotProduct(ProjectedLoc, FMath::Sign(UpVec.Y) * FVector::RightVector)))) / AngleThreshold), 0.0f, 1.0f);
}
AllCurrentLeverAngles.Roll = FMath::RoundToFloat(AllCurrentLeverAngles.Roll);
AllCurrentLeverAngles.Pitch = FMath::RoundToFloat(AllCurrentLeverAngles.Pitch);
if (LeverRotationAxis == EVRInteractibleLeverAxis::FlightStick_XY)
{
AllCurrentLeverAngles.Yaw = FRotator::NormalizeAxis(FMath::RoundToFloat(UVRExpansionFunctionLibrary::GetHMDPureYaw_I(CurrentRelRot.Rotator()).Yaw));
if (FlightStickYawLimit < 180.0f)
{
AllCurrentLeverAngles.Yaw = FMath::Clamp(AllCurrentLeverAngles.Yaw, -FlightStickYawLimit, FlightStickYawLimit);
}
}
else
AllCurrentLeverAngles.Yaw = 0.0f;
}break;
default:
{
Angle = UVRInteractibleFunctionLibrary::GetDeltaAngleFromTransforms((EVRInteractibleAxis)LeverRotationAxis, InitialRelativeTransform, CurrentTransform);
FullCurrentAngle = Angle;
CurrentLeverAngle = FMath::RoundToFloat(FullCurrentAngle);
CurrentLeverForwardVector = UVRInteractibleFunctionLibrary::SetAxisValueVec((EVRInteractibleAxis)LeverRotationAxis, FMath::Sign(Angle));
AllCurrentLeverAngles = UVRInteractibleFunctionLibrary::SetAxisValueRot((EVRInteractibleAxis)LeverRotationAxis, CurrentLeverAngle, FRotator::ZeroRotator);
}break;
}
}
void UVRLeverComponent::LerpAxis(float CurrentAngle, float DeltaTime)
{
float TargetAngle = 0.0f;
float FinalReturnSpeed = LeverReturnSpeed;
switch (LeverReturnTypeWhenReleased)
{
case EVRInteractibleLeverReturnType::LerpToMax:
{
if (CurrentAngle >= 0)
TargetAngle = FMath::RoundToFloat(LeverLimitPositive);
else
TargetAngle = -FMath::RoundToFloat(LeverLimitNegative);
}break;
case EVRInteractibleLeverReturnType::LerpToMaxIfOverThreshold:
{
if ((!FMath::IsNearlyZero(LeverLimitPositive) && CurrentAngle >= (LeverLimitPositive * LeverTogglePercentage)))
TargetAngle = FMath::RoundToFloat(LeverLimitPositive);
else if ((!FMath::IsNearlyZero(LeverLimitNegative) && CurrentAngle <= -(LeverLimitNegative * LeverTogglePercentage)))
TargetAngle = -FMath::RoundToFloat(LeverLimitNegative);
}break;
case EVRInteractibleLeverReturnType::RetainMomentum:
{
if (FMath::IsNearlyZero(MomentumAtDrop * DeltaTime, 0.1f))
{
MomentumAtDrop = 0.0f;
this->SetComponentTickEnabled(false);
bIsLerping = false;
bReplicateMovement = bOriginalReplicatesMovement;
return;
}
else
{
MomentumAtDrop = FMath::FInterpTo(MomentumAtDrop, 0.0f, DeltaTime, LeverMomentumFriction);
FinalReturnSpeed = FMath::Abs(MomentumAtDrop);
if (MomentumAtDrop >= 0.0f)
TargetAngle = FMath::RoundToFloat(LeverLimitPositive);
else
TargetAngle = -FMath::RoundToFloat(LeverLimitNegative);
}
}break;
case EVRInteractibleLeverReturnType::ReturnToZero:
default:
{}break;
}
//float LerpedVal = FMath::FixedTurn(CurrentAngle, TargetAngle, FinalReturnSpeed * DeltaTime);
float LerpedVal = FMath::FInterpConstantTo(CurrentAngle, TargetAngle, DeltaTime, FinalReturnSpeed);
if (FMath::IsNearlyEqual(LerpedVal, TargetAngle))
{
if (LeverRestitution > 0.0f)
{
MomentumAtDrop = -(MomentumAtDrop * LeverRestitution);
FTransform CalcTransform = (FTransform(UVRInteractibleFunctionLibrary::SetAxisValueRot((EVRInteractibleAxis)LeverRotationAxis, TargetAngle, FRotator::ZeroRotator)) * InitialRelativeTransform);
this->SetRelativeRotation(CalcTransform.Rotator());
}
else
{
this->SetComponentTickEnabled(false);
bIsLerping = false;
bReplicateMovement = bOriginalReplicatesMovement;
FTransform CalcTransform = (FTransform(UVRInteractibleFunctionLibrary::SetAxisValueRot((EVRInteractibleAxis)LeverRotationAxis, TargetAngle, FRotator::ZeroRotator)) * InitialRelativeTransform);
this->SetRelativeRotation(CalcTransform.Rotator());
}
}
else
{
FTransform CalcTransform = (FTransform(UVRInteractibleFunctionLibrary::SetAxisValueRot((EVRInteractibleAxis)LeverRotationAxis, LerpedVal, FRotator::ZeroRotator)) * InitialRelativeTransform);
this->SetRelativeRotation(CalcTransform.Rotator());
}
}
float UVRLeverComponent::CalcAngle(EVRInteractibleLeverAxis AxisToCalc, FVector CurInteractorLocation, bool bSkipLimits)
{
float ReturnAxis = 0.0f;
ReturnAxis = UVRInteractibleFunctionLibrary::GetAtan2Angle((EVRInteractibleAxis)AxisToCalc, CurInteractorLocation, InitialGripRot);
if (bSkipLimits)
return ReturnAxis;
if (LeverLimitPositive > 0.0f && LeverLimitNegative > 0.0f && FMath::IsNearlyEqual(LeverLimitNegative, 180.f, 0.01f) && FMath::IsNearlyEqual(LeverLimitPositive, 180.f, 0.01f))
{
// Don't run the clamping or the flip detection, we are a 360 degree lever
}
else
{
ReturnAxis = FMath::ClampAngle(FRotator::NormalizeAxis(RotAtGrab + ReturnAxis), -LeverLimitNegative, LeverLimitPositive);
// Ignore rotations that would flip the angle of the lever to the other side, with a 90 degree allowance
if (!bIsInFirstTick && ((LeverLimitPositive > 0.0f && LastDeltaAngle >= LeverLimitPositive) || (LeverLimitNegative > 0.0f && LastDeltaAngle <= -LeverLimitNegative)) && FMath::Sign(LastDeltaAngle) != FMath::Sign(ReturnAxis))
{
ReturnAxis = LastDeltaAngle;
}
}
bIsInFirstTick = false;
return ReturnAxis;
}

View file

@ -0,0 +1,584 @@
// Copyright 1998-2016 Epic Games, Inc. All Rights Reserved.
#include "Interactibles/VRMountComponent.h"
#include UE_INLINE_GENERATED_CPP_BY_NAME(VRMountComponent)
#include "VRExpansionFunctionLibrary.h"
#include "GripMotionControllerComponent.h"
//#include "PhysicsPublic.h"
//#include "PhysicsEngine/ConstraintInstance.h"
#include "Net/UnrealNetwork.h"
//=============================================================================
UVRMountComponent::UVRMountComponent(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{
this->SetGenerateOverlapEvents(true);
this->PrimaryComponentTick.bStartWithTickEnabled = false;
PrimaryComponentTick.bCanEverTick = true;
bRepGameplayTags = false;
// Defaulting these true so that they work by default in networked environments
bReplicateMovement = true;
MovementReplicationSetting = EGripMovementReplicationSettings::ForceClientSideMovement;
BreakDistance = 100.0f;
Stiffness = 1500.0f;
Damping = 200.0f;
MountRotationAxis = EVRInteractibleMountAxis::Axis_XZ;
InitialRelativeTransform = FTransform::Identity;
InitialInteractorLocation = FVector::ZeroVector;
InitialGripRot = 0.0f;
qRotAtGrab = FQuat::Identity;
bDenyGripping = false;
PrimarySlotRange = 100.f;
SecondarySlotRange = 100.f;
GripPriority = 1;
FlipingZone = 0.4;
FlipReajustYawSpeed = 7.7;
// Set to only overlap with things so that its not ruined by touching over actors
this->SetCollisionResponseToAllChannels(ECollisionResponse::ECR_Overlap);
}
//=============================================================================
UVRMountComponent::~UVRMountComponent()
{
}
void UVRMountComponent::GetLifetimeReplicatedProps(TArray< class FLifetimeProperty > & OutLifetimeProps) const
{
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
DOREPLIFETIME(UVRMountComponent, bRepGameplayTags);
DOREPLIFETIME(UVRMountComponent, bReplicateMovement);
DOREPLIFETIME_CONDITION(UVRMountComponent, GameplayTags, COND_Custom);
}
void UVRMountComponent::PreReplication(IRepChangedPropertyTracker & ChangedPropertyTracker)
{
Super::PreReplication(ChangedPropertyTracker);
// Don't replicate if set to not do it
DOREPLIFETIME_ACTIVE_OVERRIDE_FAST(UVRMountComponent, GameplayTags, bRepGameplayTags);
DOREPLIFETIME_ACTIVE_OVERRIDE_FAST(USceneComponent, RelativeLocation, bReplicateMovement);
DOREPLIFETIME_ACTIVE_OVERRIDE_FAST(USceneComponent, RelativeRotation, bReplicateMovement);
DOREPLIFETIME_ACTIVE_OVERRIDE_FAST(USceneComponent, RelativeScale3D, bReplicateMovement);
}
void UVRMountComponent::OnRegister()
{
Super::OnRegister();
ResetInitialMountLocation(); // Load the original mount location
}
void UVRMountComponent::BeginPlay()
{
// Call the base class
Super::BeginPlay();
}
void UVRMountComponent::TickComponent(float DeltaTime, enum ELevelTick TickType, FActorComponentTickFunction *ThisTickFunction)
{
// Call supers tick (though I don't think any of the base classes to this actually implement it)
Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
}
void UVRMountComponent::OnUnregister()
{
Super::OnUnregister();
}
void UVRMountComponent::TickGrip_Implementation(UGripMotionControllerComponent * GrippingController, const FBPActorGripInformation & GripInformation, float DeltaTime)
{
// Handle manual tracking here
FTransform CurrentRelativeTransform = InitialRelativeTransform * UVRInteractibleFunctionLibrary::Interactible_GetCurrentParentTransform(this);
FVector CurInteractorLocation = CurrentRelativeTransform.InverseTransformPosition(GrippingController->GetPivotLocation());
switch (MountRotationAxis)
{
case EVRInteractibleMountAxis::Axis_XZ:
{
//The current Mount to target location vector.
FVector MountToTarget;
//In case the Mount is initially gripped on the back (negative of relative x axis)
if (GrippedOnBack)
{
CurInteractorLocation = CurInteractorLocation *-1;
}
//Rotate the Initial Grip relative to the Forward Axis so it represents the current correct vector after Mount is rotated.
FRotator RelativeRot = GetRelativeRotation();
FVector CurToForwardAxisVec = FRotator(RelativeRot.Pitch, RelativeRot.Yaw, TwistDiff).RotateVector(InitialGripToForwardVec);
//The Current InteractorLocation based on current interactor location to intersection point on sphere with forward axis.
CurInteractorLocation = (CurInteractorLocation.GetSafeNormal() * InitialInteractorLocation.Size() + CurToForwardAxisVec).GetSafeNormal()*CurInteractorLocation.Size();
FRotator Rot;
//If we are inside the fliping zone defined from Cone Area on top or bottom. ToDo: This probably can be combined with later FlipZone.
if (bIsInsideFrontFlipingZone || bIsInsideBackFlipZone)
{
// Entering the flip zone for the first time, a initial forward plane is made to ajust currentinteractorlocation relative x and y. This immitates a mechanical pull/push
if (!bFirstEntryToHalfFlipZone)
{
//Unmodified Right vector can still be used here to create the plane, before it will be changed later.
ForwardPullPlane = FPlane(FVector::ZeroVector, FVector(EntryRightVec.X, EntryRightVec.Y, 0).GetSafeNormal());
bFirstEntryToHalfFlipZone = true;
bLerpingOutOfFlipZone = false;
CurInterpGripLoc = CurInteractorLocation;
CurPointOnForwardPlane = FPlane::PointPlaneProject(CurInteractorLocation, ForwardPullPlane);
}
LastPointOnForwardPlane = CurPointOnForwardPlane;
CurPointOnForwardPlane = FPlane::PointPlaneProject(CurInteractorLocation, ForwardPullPlane);
FVector ForwardPlainDiffVec = CurPointOnForwardPlane - LastPointOnForwardPlane;
//Add the difference to how much we moved trough the forward plane since the last frame.
CurInterpGripLoc += ForwardPlainDiffVec;
// InterpTo the current projected point on forward plane over time when inside the FlipZone.
CurInterpGripLoc = FMath::VInterpConstantTo(CurInterpGripLoc, CurPointOnForwardPlane, GetWorld()->GetDeltaSeconds(), 50);
//The current location of the motion controller projected on to the forward plane. Only x and y is used from interpolation.
MountToTarget = FVector(CurInterpGripLoc.X, CurInterpGripLoc.Y, CurInteractorLocation.Z).GetSafeNormal();
//Save the CurInteractorLocation once to modify roll with it later
CurInteractorLocation = FVector(CurInterpGripLoc.X, CurInterpGripLoc.Y, CurInteractorLocation.Z);
}
else
{
//When going out of whole FlipZone Lerp yaw back to curinteractorlocation in case relative entry xy deviates to much from when leaving area
if (bLerpingOutOfFlipZone)
{
//If projected point on plane is not close enough to the "real" motion controller location lerp
if ((CurInterpGripLoc - CurInteractorLocation).Size() >= 1.0f)
{
//How fast yaw should rotate back when leaving flipzone. ToDo: For LerpOutSpeed maybe better us distance deviation
LerpOutAlpha = FMath::Clamp(LerpOutAlpha + FlipReajustYawSpeed / 100.0f, 0.0f, 1.0f);
//Lerp
CurInterpGripLoc = CurInterpGripLoc + LerpOutAlpha * (CurInteractorLocation - CurInterpGripLoc);
//The new vector to make rotation from.
MountToTarget = CurInterpGripLoc.GetSafeNormal();
//We left the flipzone completly, set everything back to normal.
if (LerpOutAlpha >= 0.97f)
{
bLerpingOutOfFlipZone = false;
bIsInsideBackFlipZone = false;
}
}
else
{
//If we are already near the real controller location just snap back
bLerpingOutOfFlipZone = false;
bIsInsideBackFlipZone = false;
MountToTarget = CurInteractorLocation.GetSafeNormal();
}
}
else
{
//There is still a possibility here maybe to be inside backflipzone, but Mount is actually already outside. So just use unmodified rotation.
MountToTarget = CurInteractorLocation.GetSafeNormal();
bIsInsideBackFlipZone = false;
}
}
//Setting the relative rotation once before using "accumulated" relative rotation to calculate roll onto it.
Rot = MountToTarget.Rotation();
this->SetRelativeRotation((FTransform(FRotator(Rot.Pitch, Rot.Yaw, 0))*InitialRelativeTransform).Rotator());
FVector nAxis;
float FlipAngle;
FQuat::FindBetweenVectors(FVector(0, 0, 1), CurInteractorLocation.GetSafeNormal()).ToAxisAndAngle(nAxis, FlipAngle);
// This part takes care of the roll rotation ff Mount is inside flipping zone on top or on bottom.
if (FlipAngle < FlipingZone || FlipAngle > PI - FlipingZone)
{
//When entering FlipZone for the first time setup initial properties
if (!bIsInsideFrontFlipingZone && !bIsInsideBackFlipZone)
{
//We entered the FrontFlipzone
bIsInsideFrontFlipingZone = true;
//Up and Right Vector when entering the FlipZone. Parent Rotation is accounted for.
if (USceneComponent * ParentComp = GetAttachParent())
{
EntryUpVec = ParentComp->GetComponentRotation().UnrotateVector(GetUpVector());
EntryRightVec = ParentComp->GetComponentRotation().UnrotateVector(GetRightVector());
}
else
{
EntryUpVec = GetUpVector();
EntryRightVec = GetRightVector();
}
//Only relative x and y is important for a Mount which has XY rotation limitation for the flip plane.
EntryUpXYNeg = FVector(EntryUpVec.X, EntryUpVec.Y, 0).GetSafeNormal()*-1;
//If flipping over the bottom
if (FlipAngle > PI - FlipingZone)
{
EntryUpXYNeg *= -1;
}
//A Plane perpendicular to the FlipZone relative EntryPoint XY UpVector. This plane determines when the roll has to be turned by 180 degree
FlipPlane = FPlane(FVector::ZeroVector, EntryUpXYNeg);
}
//CurInteractor vector to its projected point on flipplane
FVector CurInteractorToFlipPlaneVec = CurInteractorLocation - FPlane::PointPlaneProject(CurInteractorLocation, FlipPlane);
if (bIsInsideFrontFlipingZone)
{
//If Mount rotation is on or over flipplane but still inside frontflipzone flip the roll.
if (FVector::DotProduct(CurInteractorToFlipPlaneVec, EntryUpXYNeg) <= 0)
{
bIsInsideFrontFlipingZone = false;
bIsInsideBackFlipZone = true;
bIsFlipped = !bIsFlipped;
if (bIsFlipped)
{
TwistDiff = 180;
}
else
{
TwistDiff = 0;
}
}
else
{
//If Mount Rotation is still inside FrontFlipZone ajust the roll so it looks naturally when moving the Mount against the FlipPlane
FVector RelativeUpVec = GetUpVector();
if(USceneComponent * ParentComp = GetAttachParent())
RelativeUpVec = ParentComp->GetComponentRotation().UnrotateVector(RelativeUpVec);
FVector CurrentUpVec = FVector(RelativeUpVec.X, RelativeUpVec.Y, 0).GetSafeNormal();
//If rotating over the top ajust relative UpVector
if (FlipAngle < FlipingZone)
{
CurrentUpVec *= -1;
}
float EntryTwist = FMath::Atan2(EntryUpXYNeg.Y, EntryUpXYNeg.X);
float CurTwist = FMath::Atan2(CurrentUpVec.Y, CurrentUpVec.X);
//Rotate the roll so relative up vector x y looks at the flip plane
if (bIsFlipped)
{
TwistDiff = FMath::RadiansToDegrees(EntryTwist - CurTwist - PI);
}
else
{
TwistDiff = FMath::RadiansToDegrees(EntryTwist - CurTwist);
}
}
}
else
{
//If Inside Back Flip Zone just flip the roll. ToDo: Dont just ajust roll to 0 or 180. Calculate Twist diff according to up vector to FlipPlane.
if (bIsInsideBackFlipZone)
{
if (FVector::DotProduct(CurInteractorToFlipPlaneVec, EntryUpXYNeg) >= 0)
{
bIsInsideFrontFlipingZone = true;
bIsInsideBackFlipZone = false;
bIsFlipped = !bIsFlipped;
if (bIsFlipped)
{
TwistDiff = 180;
}
else
{
TwistDiff = 0;
}
}
}
}
}
else
{
//If the Mount went into the flipping zone and back out without going over flip plane reset roll
bIsInsideFrontFlipingZone = false;
bIsInsideBackFlipZone = false;
if (bIsFlipped)
{
TwistDiff = 180;
}
else
{
TwistDiff = 0;
}
if (bFirstEntryToHalfFlipZone)
{
//If never left FlipZone before but doing now it now, reset first time entry bool. ToDo: Maybe better to do this elsewhere.
bFirstEntryToHalfFlipZone = false;
LerpOutAlpha = 0;
//We left the flipzone so rotate yaw back from interpolated controller position on forward pull plane back to "real" one.
bLerpingOutOfFlipZone = true;
}
}
//Add Roll modifications to accumulated LocalRotation.
this->AddLocalRotation(FRotator(0, 0, -TwistDiff));
}break;
default:break;
}
// #TODO: This drop code is incorrect, it is based off of the initial point and not the location at grip - revise it at some point
// Also set it to after rotation
if (BreakDistance > 0.f && GrippingController->HasGripAuthority(GripInformation) && FVector::DistSquared(InitialInteractorDropLocation, this->GetComponentTransform().InverseTransformPosition(GrippingController->GetPivotLocation())) >= FMath::Square(BreakDistance))
{
if (GrippingController->OnGripOutOfRange.IsBound())
{
uint8 GripID = GripInformation.GripID;
GrippingController->OnGripOutOfRange.Broadcast(GripInformation, GripInformation.GripDistance);
}
else
{
GrippingController->DropObjectByInterface(this, HoldingGrip.GripID);
}
return;
}
}
void UVRMountComponent::OnGrip_Implementation(UGripMotionControllerComponent * GrippingController, const FBPActorGripInformation & GripInformation)
{
FTransform CurrentRelativeTransform = InitialRelativeTransform * UVRInteractibleFunctionLibrary::Interactible_GetCurrentParentTransform(this);
// This lets me use the correct original location over the network without changes
FTransform ReversedRelativeTransform = FTransform(GripInformation.RelativeTransform.ToInverseMatrixWithScale());
FTransform RelativeToGripTransform = ReversedRelativeTransform * this->GetComponentTransform();
//continue here CurToForwardAxis is based on last gripped location ---> change this
InitialInteractorLocation = CurrentRelativeTransform.InverseTransformPosition(RelativeToGripTransform.GetTranslation());
InitialInteractorDropLocation = ReversedRelativeTransform.GetTranslation();
switch (MountRotationAxis)
{
case EVRInteractibleMountAxis::Axis_XZ:
{
//spaceharry
qRotAtGrab = this->GetComponentTransform().GetRelativeTransform(CurrentRelativeTransform).GetRotation();
FVector ForwardVectorToUse = GetForwardVector();
if (USceneComponent * ParentComp = GetAttachParent())
{
ForwardVectorToUse = ParentComp->GetComponentRotation().UnrotateVector(ForwardVectorToUse);
}
InitialForwardVector = InitialInteractorLocation.Size() * ForwardVectorToUse;
if (FVector::DotProduct(InitialInteractorLocation, ForwardVectorToUse) <= 0)
{
GrippedOnBack = true;
InitialGripToForwardVec = (InitialForwardVector + InitialInteractorLocation);
}
else
{
InitialGripToForwardVec = InitialForwardVector - InitialInteractorLocation;
GrippedOnBack = false;
}
FRotator RelativeRot = GetRelativeRotation();
InitialGripToForwardVec = FRotator(RelativeRot.Pitch, RelativeRot.Yaw, TwistDiff).UnrotateVector(InitialGripToForwardVec);
}break;
default:break;
}
this->SetComponentTickEnabled(true);
}
void UVRMountComponent::OnGripRelease_Implementation(UGripMotionControllerComponent * ReleasingController, const FBPActorGripInformation & GripInformation, bool bWasSocketed)
{
this->SetComponentTickEnabled(false);
}
void UVRMountComponent::SetGripPriority(int NewGripPriority)
{
GripPriority = NewGripPriority;
}
void UVRMountComponent::OnChildGrip_Implementation(UGripMotionControllerComponent * GrippingController, const FBPActorGripInformation & GripInformation) {}
void UVRMountComponent::OnChildGripRelease_Implementation(UGripMotionControllerComponent * ReleasingController, const FBPActorGripInformation & GripInformation, bool bWasSocketed) {}
void UVRMountComponent::OnSecondaryGrip_Implementation(UGripMotionControllerComponent * GripOwningController, USceneComponent * SecondaryGripComponent, const FBPActorGripInformation & GripInformation) {}
void UVRMountComponent::OnSecondaryGripRelease_Implementation(UGripMotionControllerComponent * GripOwningController, USceneComponent * ReleasingSecondaryGripComponent, const FBPActorGripInformation & GripInformation) {}
void UVRMountComponent::OnUsed_Implementation() {}
void UVRMountComponent::OnEndUsed_Implementation() {}
void UVRMountComponent::OnSecondaryUsed_Implementation() {}
void UVRMountComponent::OnEndSecondaryUsed_Implementation() {}
void UVRMountComponent::OnInput_Implementation(FKey Key, EInputEvent KeyEvent) {}
bool UVRMountComponent::RequestsSocketing_Implementation(USceneComponent *& ParentToSocketTo, FName & OptionalSocketName, FTransform_NetQuantize & RelativeTransform) { return false; }
bool UVRMountComponent::DenyGripping_Implementation(UGripMotionControllerComponent * GripInitiator)
{
return bDenyGripping;
}
EGripInterfaceTeleportBehavior UVRMountComponent::TeleportBehavior_Implementation()
{
return EGripInterfaceTeleportBehavior::DropOnTeleport;
}
bool UVRMountComponent::SimulateOnDrop_Implementation()
{
return false;
}
EGripCollisionType UVRMountComponent::GetPrimaryGripType_Implementation(bool bIsSlot)
{
return EGripCollisionType::CustomGrip;
}
ESecondaryGripType UVRMountComponent::SecondaryGripType_Implementation()
{
return ESecondaryGripType::SG_None;
}
EGripMovementReplicationSettings UVRMountComponent::GripMovementReplicationType_Implementation()
{
return MovementReplicationSetting;
}
EGripLateUpdateSettings UVRMountComponent::GripLateUpdateSetting_Implementation()
{
return EGripLateUpdateSettings::LateUpdatesAlwaysOff;
}
/*float UVRMountComponent::GripStiffness_Implementation()
{
return Stiffness;
}
float UVRMountComponent::GripDamping_Implementation()
{
return Damping;
}*/
void UVRMountComponent::GetGripStiffnessAndDamping_Implementation(float &GripStiffnessOut, float &GripDampingOut)
{
GripStiffnessOut = Stiffness;
GripDampingOut = Damping;
}
FBPAdvGripSettings UVRMountComponent::AdvancedGripSettings_Implementation()
{
return FBPAdvGripSettings(GripPriority);
}
float UVRMountComponent::GripBreakDistance_Implementation()
{
return BreakDistance;
}
/*void UVRMountComponent::ClosestSecondarySlotInRange_Implementation(FVector WorldLocation, bool & bHadSlotInRange, FTransform & SlotWorldTransform, UGripMotionControllerComponent * CallingController, FName OverridePrefix)
{
bHadSlotInRange = false;
}
void UVRMountComponent::ClosestPrimarySlotInRange_Implementation(FVector WorldLocation, bool & bHadSlotInRange, FTransform & SlotWorldTransform, UGripMotionControllerComponent * CallingController, FName OverridePrefix)
{
bHadSlotInRange = false;
}*/
void UVRMountComponent::ClosestGripSlotInRange_Implementation(FVector WorldLocation, bool bSecondarySlot, bool & bHadSlotInRange, FTransform & SlotWorldTransform, FName & SlotName, UGripMotionControllerComponent * CallingController, FName OverridePrefix)
{
if (OverridePrefix.IsNone())
bSecondarySlot ? OverridePrefix = "VRGripS" : OverridePrefix = "VRGripP";
UVRExpansionFunctionLibrary::GetGripSlotInRangeByTypeName_Component(OverridePrefix, this, WorldLocation, bSecondarySlot ? SecondarySlotRange : PrimarySlotRange, bHadSlotInRange, SlotWorldTransform, SlotName, CallingController);
}
bool UVRMountComponent::AllowsMultipleGrips_Implementation()
{
return false;
}
void UVRMountComponent::IsHeld_Implementation(TArray<FBPGripPair> & CurHoldingControllers, bool & bCurIsHeld)
{
CurHoldingControllers.Empty();
if (HoldingGrip.IsValid())
{
CurHoldingControllers.Add(HoldingGrip);
bCurIsHeld = bIsHeld;
}
else
{
bCurIsHeld = false;
}
}
void UVRMountComponent::SetHeld_Implementation(UGripMotionControllerComponent * NewHoldingController, uint8 GripID, bool bNewIsHeld)
{
if (bNewIsHeld)
{
HoldingGrip = FBPGripPair(NewHoldingController, GripID);
if (MovementReplicationSetting != EGripMovementReplicationSettings::ForceServerSideMovement)
{
if (!bIsHeld)
bOriginalReplicatesMovement = bReplicateMovement;
bReplicateMovement = false;
}
}
else
{
HoldingGrip.Clear();
if (MovementReplicationSetting != EGripMovementReplicationSettings::ForceServerSideMovement)
{
bReplicateMovement = bOriginalReplicatesMovement;
}
}
bIsHeld = bNewIsHeld;
}
/*FBPInteractionSettings UVRMountComponent::GetInteractionSettings_Implementation()
{
return FBPInteractionSettings();
}*/
bool UVRMountComponent::GetGripScripts_Implementation(TArray<UVRGripScriptBase*> & ArrayReference)
{
return false;
}

View file

@ -0,0 +1,944 @@
// Copyright 1998-2016 Epic Games, Inc. All Rights Reserved.
#include "Interactibles/VRSliderComponent.h"
#include UE_INLINE_GENERATED_CPP_BY_NAME(VRSliderComponent)
#include "VRExpansionFunctionLibrary.h"
#include "Components/SplineComponent.h"
#include "GripMotionControllerComponent.h"
#include "Net/UnrealNetwork.h"
//=============================================================================
UVRSliderComponent::UVRSliderComponent(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{
this->SetGenerateOverlapEvents(true);
this->PrimaryComponentTick.bStartWithTickEnabled = false;
PrimaryComponentTick.bCanEverTick = true;
bRepGameplayTags = false;
// Defaulting these true so that they work by default in networked environments
bReplicateMovement = true;
MovementReplicationSetting = EGripMovementReplicationSettings::ForceClientSideMovement;
BreakDistance = 100.0f;
InitialRelativeTransform = FTransform::Identity;
bDenyGripping = false;
bUpdateInTick = false;
bPassThrough = false;
MinSlideDistance = FVector::ZeroVector;
MaxSlideDistance = FVector(10.0f, 0.f, 0.f);
SliderRestitution = 0.0f;
CurrentSliderProgress = 0.0f;
LastSliderProgress = FVector::ZeroVector;//0.0f;
SplineLastSliderProgress = 0.0f;
MomentumAtDrop = FVector::ZeroVector;// 0.0f;
SplineMomentumAtDrop = 0.0f;
SliderMomentumFriction = 3.0f;
MaxSliderMomentum = 1.0f;
FramesToAverage = 3;
InitialInteractorLocation = FVector::ZeroVector;
InitialGripLoc = FVector::ZeroVector;
bSlideDistanceIsInParentSpace = true;
bUseLegacyLogic = false;
bIsLocked = false;
bAutoDropWhenLocked = true;
SplineComponentToFollow = nullptr;
bFollowSplineRotationAndScale = false;
SplineLerpType = EVRInteractibleSliderLerpType::Lerp_None;
SplineLerpValue = 8.f;
PrimarySlotRange = 100.f;
SecondarySlotRange = 100.f;
GripPriority = 1;
LastSliderProgressState = -1.0f;
LastInputKey = 0.0f;
bSliderUsesSnapPoints = false;
SnapIncrement = 0.1f;
SnapThreshold = 0.1f;
bIncrementProgressBetweenSnapPoints = false;
EventThrowThreshold = 1.0f;
bHitEventThreshold = false;
// Set to only overlap with things so that its not ruined by touching over actors
this->SetCollisionResponseToAllChannels(ECollisionResponse::ECR_Overlap);
}
//=============================================================================
UVRSliderComponent::~UVRSliderComponent()
{
}
void UVRSliderComponent::GetLifetimeReplicatedProps(TArray< class FLifetimeProperty > & OutLifetimeProps) const
{
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
DOREPLIFETIME(UVRSliderComponent, InitialRelativeTransform);
DOREPLIFETIME(UVRSliderComponent, SplineComponentToFollow);
//DOREPLIFETIME_CONDITION(UVRSliderComponent, bIsLerping, COND_InitialOnly);
DOREPLIFETIME(UVRSliderComponent, bRepGameplayTags);
DOREPLIFETIME(UVRSliderComponent, bReplicateMovement);
DOREPLIFETIME_CONDITION(UVRSliderComponent, GameplayTags, COND_Custom);
}
void UVRSliderComponent::PreReplication(IRepChangedPropertyTracker & ChangedPropertyTracker)
{
Super::PreReplication(ChangedPropertyTracker);
// Replicate the levers initial transform if we are replicating movement
//DOREPLIFETIME_ACTIVE_OVERRIDE(UVRSliderComponent, InitialRelativeTransform, bReplicateMovement);
//DOREPLIFETIME_ACTIVE_OVERRIDE(UVRSliderComponent, SplineComponentToFollow, bReplicateMovement);
// Don't replicate if set to not do it
DOREPLIFETIME_ACTIVE_OVERRIDE_FAST(UVRSliderComponent, GameplayTags, bRepGameplayTags);
DOREPLIFETIME_ACTIVE_OVERRIDE_FAST(USceneComponent, RelativeLocation, bReplicateMovement);
DOREPLIFETIME_ACTIVE_OVERRIDE_FAST(USceneComponent, RelativeRotation, bReplicateMovement);
DOREPLIFETIME_ACTIVE_OVERRIDE_FAST(USceneComponent, RelativeScale3D, bReplicateMovement);
}
void UVRSliderComponent::OnRegister()
{
Super::OnRegister();
// Init the slider settings
if (USplineComponent * ParentSpline = Cast<USplineComponent>(GetAttachParent()))
{
SetSplineComponentToFollow(ParentSpline);
}
else
{
ResetInitialSliderLocation();
}
}
void UVRSliderComponent::BeginPlay()
{
// Call the base class
Super::BeginPlay();
CalculateSliderProgress();
bOriginalReplicatesMovement = bReplicateMovement;
}
void UVRSliderComponent::TickComponent(float DeltaTime, enum ELevelTick TickType, FActorComponentTickFunction *ThisTickFunction)
{
// Call supers tick (though I don't think any of the base classes to this actually implement it)
Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
if (bIsHeld && bUpdateInTick && HoldingGrip.HoldingController)
{
FBPActorGripInformation GripInfo;
EBPVRResultSwitch Result;
HoldingGrip.HoldingController->GetGripByID(GripInfo, HoldingGrip.GripID, Result);
if (Result == EBPVRResultSwitch::OnSucceeded)
{
bPassThrough = true;
TickGrip_Implementation(HoldingGrip.HoldingController, GripInfo, DeltaTime);
bPassThrough = false;
}
return;
}
// If we are locked then end the lerp, no point
if (bIsLocked)
{
// Notify the end user
OnSliderFinishedLerping.Broadcast(CurrentSliderProgress);
ReceiveSliderFinishedLerping(CurrentSliderProgress);
this->SetComponentTickEnabled(false);
bReplicateMovement = bOriginalReplicatesMovement;
return;
}
if (bIsLerping)
{
if ((SplineComponentToFollow && FMath::IsNearlyZero((SplineMomentumAtDrop * DeltaTime), 0.00001f)) || (!SplineComponentToFollow && (MomentumAtDrop * DeltaTime).IsNearlyZero(0.00001f)))
{
bIsLerping = false;
}
else
{
if (this->SplineComponentToFollow)
{
SplineMomentumAtDrop = FMath::FInterpTo(SplineMomentumAtDrop, 0.0f, DeltaTime, SliderMomentumFriction);
float newProgress = CurrentSliderProgress + (SplineMomentumAtDrop * DeltaTime);
if (newProgress < 0.0f || FMath::IsNearlyEqual(newProgress, 0.0f, 0.00001f))
{
if (SliderRestitution > 0.0f)
{
// Reverse the momentum
SplineMomentumAtDrop = -(SplineMomentumAtDrop * SliderRestitution);
this->SetSliderProgress(0.0f);
}
else
{
bIsLerping = false;
this->SetSliderProgress(0.0f);
}
}
else if (newProgress > 1.0f || FMath::IsNearlyEqual(newProgress, 1.0f, 0.00001f))
{
if (SliderRestitution > 0.0f)
{
// Reverse the momentum
SplineMomentumAtDrop = -(SplineMomentumAtDrop * SliderRestitution);
this->SetSliderProgress(1.0f);
}
else
{
bIsLerping = false;
this->SetSliderProgress(1.0f);
}
}
else
{
this->SetSliderProgress(newProgress);
}
}
else
{
MomentumAtDrop = FMath::VInterpTo(MomentumAtDrop, FVector::ZeroVector, DeltaTime, SliderMomentumFriction);
FVector ClampedLocation = ClampSlideVector(InitialRelativeTransform.InverseTransformPosition(this->GetRelativeLocation()) + (MomentumAtDrop * DeltaTime));
this->SetRelativeLocation(InitialRelativeTransform.TransformPosition(ClampedLocation));
CurrentSliderProgress = GetCurrentSliderProgress(bSlideDistanceIsInParentSpace ? ClampedLocation * InitialRelativeTransform.GetScale3D() : ClampedLocation);
float newProgress = CurrentSliderProgress;
if (SliderRestitution > 0.0f)
{
// Implement bounce
FVector CurLoc = ClampedLocation;
if (
(FMath::Abs(MinSlideDistance.X) > 0.0f && CurLoc.X <= -FMath::Abs(this->MinSlideDistance.X)) ||
(FMath::Abs(MinSlideDistance.Y) > 0.0f && CurLoc.Y <= -FMath::Abs(this->MinSlideDistance.Y)) ||
(FMath::Abs(MinSlideDistance.Z) > 0.0f && CurLoc.Z <= -FMath::Abs(this->MinSlideDistance.Z)) ||
(FMath::Abs(MaxSlideDistance.X) > 0.0f && CurLoc.X >= FMath::Abs(this->MaxSlideDistance.X)) ||
(FMath::Abs(MaxSlideDistance.Y) > 0.0f && CurLoc.Y >= FMath::Abs(this->MaxSlideDistance.Y)) ||
(FMath::Abs(MaxSlideDistance.Z) > 0.0f && CurLoc.Z >= FMath::Abs(this->MaxSlideDistance.Z))
)
{
MomentumAtDrop = (-MomentumAtDrop * SliderRestitution);
}
}
else
{
if (newProgress < 0.0f || FMath::IsNearlyEqual(newProgress, 0.0f, 0.00001f))
{
bIsLerping = false;
this->SetSliderProgress(0.0f);
}
else if (newProgress > 1.0f || FMath::IsNearlyEqual(newProgress, 1.0f, 0.00001f))
{
bIsLerping = false;
this->SetSliderProgress(1.0f);
}
}
}
}
if (!bIsLerping)
{
// Notify the end user
OnSliderFinishedLerping.Broadcast(CurrentSliderProgress);
ReceiveSliderFinishedLerping(CurrentSliderProgress);
this->SetComponentTickEnabled(false);
bReplicateMovement = bOriginalReplicatesMovement;
}
// Check for the hit point always
CheckSliderProgress();
}
}
void UVRSliderComponent::TickGrip_Implementation(UGripMotionControllerComponent * GrippingController, const FBPActorGripInformation & GripInformation, float DeltaTime)
{
// Skip this tick if its not triggered from the pass through
if (bUpdateInTick && !bPassThrough)
return;
// If the sliders progress is locked then just exit early
if (bIsLocked)
{
if (bAutoDropWhenLocked)
{
// Check if we should auto drop
CheckAutoDrop(GrippingController, GripInformation);
}
return;
}
// Handle manual tracking here
FTransform ParentTransform = UVRInteractibleFunctionLibrary::Interactible_GetCurrentParentTransform(this);
FTransform CurrentRelativeTransform = InitialRelativeTransform * ParentTransform;
FVector CurInteractorLocation = CurrentRelativeTransform.InverseTransformPosition(GrippingController->GetPivotLocation());
FVector CalculatedLocation = InitialGripLoc + (CurInteractorLocation - InitialInteractorLocation);
float SplineProgress = CurrentSliderProgress;
if (SplineComponentToFollow != nullptr)
{
FVector WorldCalculatedLocation = CurrentRelativeTransform.TransformPosition(CalculatedLocation);
float ClosestKey = SplineComponentToFollow->FindInputKeyClosestToWorldLocation(WorldCalculatedLocation);
if (bSliderUsesSnapPoints)
{
float SplineLength = SplineComponentToFollow->GetSplineLength();
SplineProgress = GetCurrentSliderProgress(WorldCalculatedLocation, true, ClosestKey);
SplineProgress = UVRInteractibleFunctionLibrary::Interactible_GetThresholdSnappedValue(SplineProgress, SnapIncrement, SnapThreshold);
const int32 NumPoints = SplineComponentToFollow->SplineCurves.Position.Points.Num();
if (SplineComponentToFollow->SplineCurves.Position.Points.Num() > 1)
{
ClosestKey = SplineComponentToFollow->SplineCurves.ReparamTable.Eval(SplineProgress * SplineLength, 0.0f);
}
WorldCalculatedLocation = SplineComponentToFollow->GetLocationAtSplineInputKey(ClosestKey, ESplineCoordinateSpace::World);
}
bool bLerpToNewKey = true;
bool bChangedLocation = false;
if (bEnforceSplineLinearity && LastInputKey >= 0.0f &&
FMath::Abs((FMath::TruncToFloat(ClosestKey) - FMath::TruncToFloat(LastInputKey))) > 1.0f &&
(!bSliderUsesSnapPoints || (SplineProgress - CurrentSliderProgress > SnapIncrement))
)
{
bLerpToNewKey = false;
}
else
{
LerpedKey = ClosestKey;
}
if (bFollowSplineRotationAndScale)
{
FTransform trans;
if (SplineLerpType != EVRInteractibleSliderLerpType::Lerp_None && LastInputKey >= 0.0f && !FMath::IsNearlyEqual(LerpedKey, LastInputKey))
{
GetLerpedKey(LerpedKey, DeltaTime);
trans = SplineComponentToFollow->GetTransformAtSplineInputKey(LerpedKey, ESplineCoordinateSpace::World, true);
bChangedLocation = true;
}
else if (bLerpToNewKey)
{
trans = SplineComponentToFollow->FindTransformClosestToWorldLocation(WorldCalculatedLocation, ESplineCoordinateSpace::World, true);
bChangedLocation = true;
}
if (bChangedLocation)
{
trans.MultiplyScale3D(InitialRelativeTransform.GetScale3D());
trans = trans * ParentTransform.Inverse();
this->SetRelativeTransform(trans);
}
}
else
{
FVector WorldLocation;
if (SplineLerpType != EVRInteractibleSliderLerpType::Lerp_None && LastInputKey >= 0.0f && !FMath::IsNearlyEqual(LerpedKey, LastInputKey))
{
GetLerpedKey(LerpedKey, DeltaTime);
WorldLocation = SplineComponentToFollow->GetLocationAtSplineInputKey(LerpedKey, ESplineCoordinateSpace::World);
bChangedLocation = true;
}
else if (bLerpToNewKey)
{
WorldLocation = SplineComponentToFollow->FindLocationClosestToWorldLocation(WorldCalculatedLocation, ESplineCoordinateSpace::World);
bChangedLocation = true;
}
if (bChangedLocation)
this->SetRelativeLocation(ParentTransform.InverseTransformPosition(WorldLocation));
}
CurrentSliderProgress = GetCurrentSliderProgress(WorldCalculatedLocation, true, bLerpToNewKey ? LerpedKey : ClosestKey);
if (bLerpToNewKey)
{
LastInputKey = LerpedKey;
}
}
else
{
FVector ClampedLocation = ClampSlideVector(CalculatedLocation);
this->SetRelativeLocation(InitialRelativeTransform.TransformPosition(ClampedLocation));
CurrentSliderProgress = GetCurrentSliderProgress(bSlideDistanceIsInParentSpace ? ClampedLocation * InitialRelativeTransform.GetScale3D() : ClampedLocation);
}
if (SliderBehaviorWhenReleased == EVRInteractibleSliderDropBehavior::RetainMomentum)
{
if (SplineComponentToFollow)
{
// Rolling average across num samples
SplineMomentumAtDrop -= SplineMomentumAtDrop / FramesToAverage;
SplineMomentumAtDrop += ((CurrentSliderProgress - SplineLastSliderProgress) / DeltaTime) / FramesToAverage;
float momentumSign = FMath::Sign(SplineMomentumAtDrop);
SplineMomentumAtDrop = momentumSign * FMath::Min(MaxSliderMomentum, FMath::Abs(SplineMomentumAtDrop));
SplineLastSliderProgress = CurrentSliderProgress;
}
else
{
// Rolling average across num samples
MomentumAtDrop -= MomentumAtDrop / FramesToAverage;
FVector CurProgress = InitialRelativeTransform.InverseTransformPosition(this->GetRelativeLocation());
if (bSlideDistanceIsInParentSpace)
CurProgress *= FVector(1.0f) / InitialRelativeTransform.GetScale3D();
MomentumAtDrop += ((/*CurrentSliderProgress*/CurProgress - LastSliderProgress) / DeltaTime) / FramesToAverage;
//MomentumAtDrop = FMath::Min(MaxSliderMomentum, MomentumAtDrop);
LastSliderProgress = CurProgress;//CurrentSliderProgress;
}
}
CheckSliderProgress();
// Check if we should auto drop
CheckAutoDrop(GrippingController, GripInformation);
}
bool UVRSliderComponent::CheckAutoDrop(UGripMotionControllerComponent * GrippingController, const FBPActorGripInformation & GripInformation)
{
// Converted to a relative value now so it should be correct
if (BreakDistance > 0.f && GrippingController->HasGripAuthority(GripInformation) && FVector::DistSquared(InitialDropLocation, this->GetComponentTransform().InverseTransformPosition(GrippingController->GetPivotLocation())) >= FMath::Square(BreakDistance))
{
if (GrippingController->OnGripOutOfRange.IsBound())
{
uint8 GripID = GripInformation.GripID;
GrippingController->OnGripOutOfRange.Broadcast(GripInformation, GripInformation.GripDistance);
}
else
{
GrippingController->DropObjectByInterface(this, HoldingGrip.GripID);
}
return true;
}
return false;
}
void UVRSliderComponent::CheckSliderProgress()
{
// Skip first check, this will skip an event throw on rounded
if (LastSliderProgressState < 0.0f)
{
// Skip first tick, this is our resting position
if (!bSliderUsesSnapPoints)
LastSliderProgressState = FMath::RoundToFloat(CurrentSliderProgress); // Ensure it is rounded to 0 or 1
else
LastSliderProgressState = CurrentSliderProgress;
}
else if ((LastSliderProgressState != CurrentSliderProgress) || bHitEventThreshold)
{
float ModValue = FMath::Fmod(CurrentSliderProgress, SnapIncrement);
if ((!bSliderUsesSnapPoints && (CurrentSliderProgress == 1.0f || CurrentSliderProgress == 0.0f)) ||
(bSliderUsesSnapPoints && SnapIncrement > 0.f && (FMath::IsNearlyEqual(ModValue, 0.0f, 0.001f) || FMath::IsNearlyEqual(ModValue, SnapIncrement, 0.00001f)))
)
{
// I am working with exacts here because of the clamping, it should actually work with no precision issues
// I wanted to ABS(Last-Cur) == 1.0 but it would cause an initial miss on whatever one last was inited to.
if (!bSliderUsesSnapPoints)
LastSliderProgressState = FMath::RoundToFloat(CurrentSliderProgress); // Ensure it is rounded to 0 or 1
else
LastSliderProgressState = CurrentSliderProgress;
ReceiveSliderHitPoint(LastSliderProgressState);
OnSliderHitPoint.Broadcast(LastSliderProgressState);
bHitEventThreshold = false;
}
}
if (FMath::Abs(LastSliderProgressState - CurrentSliderProgress) >= (bSliderUsesSnapPoints ? FMath::Min(EventThrowThreshold, SnapIncrement / 2.0f) : EventThrowThreshold))
{
bHitEventThreshold = true;
}
}
void UVRSliderComponent::OnGrip_Implementation(UGripMotionControllerComponent * GrippingController, const FBPActorGripInformation & GripInformation)
{
FTransform CurrentRelativeTransform = InitialRelativeTransform * UVRInteractibleFunctionLibrary::Interactible_GetCurrentParentTransform(this);
// This lets me use the correct original location over the network without changes
FTransform ReversedRelativeTransform = FTransform(GripInformation.RelativeTransform.ToInverseMatrixWithScale());
FTransform RelativeToGripTransform = ReversedRelativeTransform * this->GetComponentTransform();
InitialInteractorLocation = CurrentRelativeTransform.InverseTransformPosition(RelativeToGripTransform.GetTranslation());
InitialGripLoc = InitialRelativeTransform.InverseTransformPosition(this->GetRelativeLocation());
InitialDropLocation = ReversedRelativeTransform.GetTranslation();
LastInputKey = -1.0f;
LerpedKey = 0.0f;
bHitEventThreshold = false;
//LastSliderProgressState = -1.0f;
LastSliderProgress = InitialGripLoc;//CurrentSliderProgress;
SplineLastSliderProgress = CurrentSliderProgress;
bIsLerping = false;
MomentumAtDrop = FVector::ZeroVector;//0.0f;
SplineMomentumAtDrop = 0.0f;
if (GripInformation.GripMovementReplicationSetting != EGripMovementReplicationSettings::ForceServerSideMovement)
{
bReplicateMovement = false;
}
if (bUpdateInTick)
SetComponentTickEnabled(true);
//OnGripped.Broadcast(GrippingController, GripInformation);
}
void UVRSliderComponent::OnGripRelease_Implementation(UGripMotionControllerComponent * ReleasingController, const FBPActorGripInformation & GripInformation, bool bWasSocketed)
{
//this->SetComponentTickEnabled(false);
// #TODO: Handle letting go and how lerping works, specifically with the snap points it may be an issue
if (SliderBehaviorWhenReleased != EVRInteractibleSliderDropBehavior::Stay)
{
bIsLerping = true;
this->SetComponentTickEnabled(true);
FVector Len = (MinSlideDistance.GetAbs() + MaxSlideDistance.GetAbs());
if(bSlideDistanceIsInParentSpace)
Len *= (FVector(1.0f) / InitialRelativeTransform.GetScale3D());
float TotalDistance = Len.Size();
if (!SplineComponentToFollow)
{
if (MaxSliderMomentum * TotalDistance < MomentumAtDrop.Size())
{
MomentumAtDrop = MomentumAtDrop.GetSafeNormal() * (TotalDistance * MaxSliderMomentum);
}
}
if(MovementReplicationSetting != EGripMovementReplicationSettings::ForceServerSideMovement)
bReplicateMovement = false;
}
else
{
this->SetComponentTickEnabled(false);
bReplicateMovement = bOriginalReplicatesMovement;
}
//OnDropped.Broadcast(ReleasingController, GripInformation, bWasSocketed);
}
void UVRSliderComponent::SetIsLocked(bool bNewLockedState)
{
bIsLocked = bNewLockedState;
}
void UVRSliderComponent::SetGripPriority(int NewGripPriority)
{
GripPriority = NewGripPriority;
}
void UVRSliderComponent::OnChildGrip_Implementation(UGripMotionControllerComponent * GrippingController, const FBPActorGripInformation & GripInformation) {}
void UVRSliderComponent::OnChildGripRelease_Implementation(UGripMotionControllerComponent * ReleasingController, const FBPActorGripInformation & GripInformation, bool bWasSocketed) {}
void UVRSliderComponent::OnSecondaryGrip_Implementation(UGripMotionControllerComponent * GripOwningController, USceneComponent * SecondaryGripComponent, const FBPActorGripInformation & GripInformation) {}
void UVRSliderComponent::OnSecondaryGripRelease_Implementation(UGripMotionControllerComponent * GripOwningController, USceneComponent * ReleasingSecondaryGripComponent, const FBPActorGripInformation & GripInformation) {}
void UVRSliderComponent::OnUsed_Implementation() {}
void UVRSliderComponent::OnEndUsed_Implementation() {}
void UVRSliderComponent::OnSecondaryUsed_Implementation() {}
void UVRSliderComponent::OnEndSecondaryUsed_Implementation() {}
void UVRSliderComponent::OnInput_Implementation(FKey Key, EInputEvent KeyEvent) {}
bool UVRSliderComponent::RequestsSocketing_Implementation(USceneComponent *& ParentToSocketTo, FName & OptionalSocketName, FTransform_NetQuantize & RelativeTransform) { return false; }
bool UVRSliderComponent::DenyGripping_Implementation(UGripMotionControllerComponent * GripInitiator)
{
return bDenyGripping;
}
EGripInterfaceTeleportBehavior UVRSliderComponent::TeleportBehavior_Implementation()
{
return EGripInterfaceTeleportBehavior::DropOnTeleport;
}
bool UVRSliderComponent::SimulateOnDrop_Implementation()
{
return false;
}
/*EGripCollisionType UVRSliderComponent::SlotGripType_Implementation()
{
return EGripCollisionType::CustomGrip;
}
EGripCollisionType UVRSliderComponent::FreeGripType_Implementation()
{
return EGripCollisionType::CustomGrip;
}*/
EGripCollisionType UVRSliderComponent::GetPrimaryGripType_Implementation(bool bIsSlot)
{
return EGripCollisionType::CustomGrip;
}
ESecondaryGripType UVRSliderComponent::SecondaryGripType_Implementation()
{
return ESecondaryGripType::SG_None;
}
EGripMovementReplicationSettings UVRSliderComponent::GripMovementReplicationType_Implementation()
{
return MovementReplicationSetting;
}
EGripLateUpdateSettings UVRSliderComponent::GripLateUpdateSetting_Implementation()
{
return EGripLateUpdateSettings::LateUpdatesAlwaysOff;
}
/*float UVRSliderComponent::GripStiffness_Implementation()
{
return 0.0f;
}
float UVRSliderComponent::GripDamping_Implementation()
{
return 0.0f;
}*/
void UVRSliderComponent::GetGripStiffnessAndDamping_Implementation(float &GripStiffnessOut, float &GripDampingOut)
{
GripStiffnessOut = 0.0f;
GripDampingOut = 0.0f;
}
FBPAdvGripSettings UVRSliderComponent::AdvancedGripSettings_Implementation()
{
return FBPAdvGripSettings(GripPriority);
}
float UVRSliderComponent::GripBreakDistance_Implementation()
{
return BreakDistance;
}
/*void UVRSliderComponent::ClosestSecondarySlotInRange_Implementation(FVector WorldLocation, bool & bHadSlotInRange, FTransform & SlotWorldTransform, UGripMotionControllerComponent * CallingController, FName OverridePrefix)
{
bHadSlotInRange = false;
}
void UVRSliderComponent::ClosestPrimarySlotInRange_Implementation(FVector WorldLocation, bool & bHadSlotInRange, FTransform & SlotWorldTransform, UGripMotionControllerComponent * CallingController, FName OverridePrefix)
{
bHadSlotInRange = false;
}*/
void UVRSliderComponent::ClosestGripSlotInRange_Implementation(FVector WorldLocation, bool bSecondarySlot, bool & bHadSlotInRange, FTransform & SlotWorldTransform, FName & SlotName, UGripMotionControllerComponent * CallingController, FName OverridePrefix)
{
if (OverridePrefix.IsNone())
bSecondarySlot ? OverridePrefix = "VRGripS" : OverridePrefix = "VRGripP";
UVRExpansionFunctionLibrary::GetGripSlotInRangeByTypeName_Component(OverridePrefix, this, WorldLocation, bSecondarySlot ? SecondarySlotRange : PrimarySlotRange, bHadSlotInRange, SlotWorldTransform, SlotName, CallingController);
}
bool UVRSliderComponent::AllowsMultipleGrips_Implementation()
{
return false;
}
void UVRSliderComponent::IsHeld_Implementation(TArray<FBPGripPair> & CurHoldingControllers, bool & bCurIsHeld)
{
CurHoldingControllers.Empty();
if (HoldingGrip.IsValid())
{
CurHoldingControllers.Add(HoldingGrip);
bCurIsHeld = bIsHeld;
}
else
{
bCurIsHeld = false;
}
}
void UVRSliderComponent::Native_NotifyThrowGripDelegates(UGripMotionControllerComponent* Controller, bool bGripped, const FBPActorGripInformation& GripInformation, bool bWasSocketed)
{
if (bGripped)
{
OnGripped.Broadcast(Controller, GripInformation);
}
else
{
OnDropped.Broadcast(Controller, GripInformation, bWasSocketed);
}
}
void UVRSliderComponent::SetHeld_Implementation(UGripMotionControllerComponent * NewHoldingController, uint8 GripID, bool bNewIsHeld)
{
if (bNewIsHeld)
{
HoldingGrip = FBPGripPair(NewHoldingController, GripID);
if (MovementReplicationSetting != EGripMovementReplicationSettings::ForceServerSideMovement)
{
if (!bIsHeld && !bIsLerping)
bOriginalReplicatesMovement = bReplicateMovement;
bReplicateMovement = false;
}
}
else
{
HoldingGrip.Clear();
if (MovementReplicationSetting != EGripMovementReplicationSettings::ForceServerSideMovement)
{
bReplicateMovement = bOriginalReplicatesMovement;
}
}
bIsHeld = bNewIsHeld;
}
/*FBPInteractionSettings UVRSliderComponent::GetInteractionSettings_Implementation()
{
return FBPInteractionSettings();
}*/
bool UVRSliderComponent::GetGripScripts_Implementation(TArray<UVRGripScriptBase*> & ArrayReference)
{
return false;
}
FVector UVRSliderComponent::ClampSlideVector(FVector ValueToClamp)
{
FVector fScaleFactor = FVector(1.0f);
if (bSlideDistanceIsInParentSpace)
fScaleFactor = fScaleFactor / InitialRelativeTransform.GetScale3D();
FVector MinScale = (bUseLegacyLogic ? MinSlideDistance : MinSlideDistance.GetAbs()) * fScaleFactor;
FVector Dist = (bUseLegacyLogic ? (MinSlideDistance + MaxSlideDistance) : (MinSlideDistance.GetAbs() + MaxSlideDistance.GetAbs())) * fScaleFactor;
FVector Progress = (ValueToClamp - (-MinScale)) / Dist;
if (bSliderUsesSnapPoints)
{
Progress.X = FMath::Clamp(UVRInteractibleFunctionLibrary::Interactible_GetThresholdSnappedValue(Progress.X, SnapIncrement, SnapThreshold), 0.0f, 1.0f);
Progress.Y = FMath::Clamp(UVRInteractibleFunctionLibrary::Interactible_GetThresholdSnappedValue(Progress.Y, SnapIncrement, SnapThreshold), 0.0f, 1.0f);
Progress.Z = FMath::Clamp(UVRInteractibleFunctionLibrary::Interactible_GetThresholdSnappedValue(Progress.Z, SnapIncrement, SnapThreshold), 0.0f, 1.0f);
}
else
{
Progress.X = FMath::Clamp(Progress.X, 0.f, 1.f);
Progress.Y = FMath::Clamp(Progress.Y, 0.f, 1.f);
Progress.Z = FMath::Clamp(Progress.Z, 0.f, 1.f);
}
return (Progress * Dist) - (MinScale);
}
float UVRSliderComponent::GetDistanceAlongSplineAtSplineInputKey(float InKey) const
{
const int32 NumPoints = SplineComponentToFollow->SplineCurves.Position.Points.Num();
const int32 NumSegments = SplineComponentToFollow->IsClosedLoop() ? NumPoints : NumPoints - 1;
if ((InKey >= 0) && (InKey < NumSegments))
{
const int32 ReparamPrevIndex = static_cast<int32>(InKey * SplineComponentToFollow->ReparamStepsPerSegment);
const int32 ReparamNextIndex = ReparamPrevIndex + 1;
const float Alpha = (InKey * SplineComponentToFollow->ReparamStepsPerSegment) - static_cast<float>(ReparamPrevIndex);
const float PrevDistance = SplineComponentToFollow->SplineCurves.ReparamTable.Points[ReparamPrevIndex].InVal;
const float NextDistance = SplineComponentToFollow->SplineCurves.ReparamTable.Points[ReparamNextIndex].InVal;
// ReparamTable assumes that distance and input keys have a linear relationship in-between entries.
return FMath::Lerp(PrevDistance, NextDistance, Alpha);
}
else if (InKey >= NumSegments)
{
return SplineComponentToFollow->SplineCurves.GetSplineLength();
}
return 0.0f;
}
float UVRSliderComponent::GetCurrentSliderProgress(FVector CurLocation, bool bUseKeyInstead, float CurKey)
{
if (SplineComponentToFollow != nullptr)
{
// In this case it is a world location
float ClosestKey = CurKey;
if (!bUseKeyInstead)
ClosestKey = SplineComponentToFollow->FindInputKeyClosestToWorldLocation(CurLocation);
/*int32 primaryKey = FMath::TruncToInt(ClosestKey);
float distance1 = SplineComponentToFollow->GetDistanceAlongSplineAtSplinePoint(primaryKey);
float distance2 = SplineComponentToFollow->GetDistanceAlongSplineAtSplinePoint(primaryKey + 1);
float FinalDistance = ((distance2 - distance1) * (ClosestKey - (float)primaryKey)) + distance1;
return FMath::Clamp(FinalDistance / SplineComponentToFollow->GetSplineLength(), 0.0f, 1.0f);*/
float SplineLength = SplineComponentToFollow->GetSplineLength();
return GetDistanceAlongSplineAtSplineInputKey(ClosestKey) / SplineLength;
}
// Should need the clamp normally, but if someone is manually setting locations it could go out of bounds
float Progress = 0.f;
if (bUseLegacyLogic)
{
Progress = FMath::Clamp(FVector::Dist(-MinSlideDistance, CurLocation) / FVector::Dist(-MinSlideDistance, MaxSlideDistance), 0.0f, 1.0f);
}
else
{
Progress = FMath::Clamp(FVector::Dist(-MinSlideDistance.GetAbs(), CurLocation) / FVector::Dist(-MinSlideDistance.GetAbs(), MaxSlideDistance.GetAbs()), 0.0f, 1.0f);
}
if (bSliderUsesSnapPoints && SnapThreshold < SnapIncrement)
{
if (FMath::Fmod(Progress, SnapIncrement) < SnapThreshold)
{
Progress = FMath::GridSnap(Progress, SnapIncrement);
}
else if(!bIncrementProgressBetweenSnapPoints)
{
Progress = CurrentSliderProgress;
}
}
return Progress;
}
void UVRSliderComponent::GetLerpedKey(float &ClosestKey, float DeltaTime)
{
switch (SplineLerpType)
{
case EVRInteractibleSliderLerpType::Lerp_Interp:
{
ClosestKey = FMath::FInterpTo(LastInputKey, ClosestKey, DeltaTime, SplineLerpValue);
}break;
case EVRInteractibleSliderLerpType::Lerp_InterpConstantTo:
{
ClosestKey = FMath::FInterpConstantTo(LastInputKey, ClosestKey, DeltaTime, SplineLerpValue);
}break;
default: break;
}
}
void UVRSliderComponent::SetSplineComponentToFollow(USplineComponent * SplineToFollow)
{
SplineComponentToFollow = SplineToFollow;
if (SplineToFollow != nullptr)
ResetToParentSplineLocation();
else
CalculateSliderProgress();
}
void UVRSliderComponent::ResetInitialSliderLocation()
{
// Get our initial relative transform to our parent (or not if un-parented).
InitialRelativeTransform = this->GetRelativeTransform();
ResetToParentSplineLocation();
if (SplineComponentToFollow == nullptr)
CurrentSliderProgress = GetCurrentSliderProgress(FVector(0, 0, 0));
}
void UVRSliderComponent::ResetToParentSplineLocation()
{
if (SplineComponentToFollow != nullptr)
{
FTransform ParentTransform = UVRInteractibleFunctionLibrary::Interactible_GetCurrentParentTransform(this);
FTransform WorldTransform = SplineComponentToFollow->FindTransformClosestToWorldLocation(this->GetComponentLocation(), ESplineCoordinateSpace::World, true);
if (bFollowSplineRotationAndScale)
{
WorldTransform.MultiplyScale3D(InitialRelativeTransform.GetScale3D());
WorldTransform = WorldTransform * ParentTransform.Inverse();
this->SetRelativeTransform(WorldTransform);
}
else
{
this->SetWorldLocation(WorldTransform.GetLocation());
}
CurrentSliderProgress = GetCurrentSliderProgress(WorldTransform.GetLocation());
}
}
void UVRSliderComponent::SetSliderProgress(float NewSliderProgress)
{
NewSliderProgress = FMath::Clamp(NewSliderProgress, 0.0f, 1.0f);
if (SplineComponentToFollow != nullptr)
{
FTransform ParentTransform = UVRInteractibleFunctionLibrary::Interactible_GetCurrentParentTransform(this);
float splineProgress = SplineComponentToFollow->GetSplineLength() * NewSliderProgress;
if (bFollowSplineRotationAndScale)
{
FTransform trans = SplineComponentToFollow->GetTransformAtDistanceAlongSpline(splineProgress, ESplineCoordinateSpace::World, true);
trans.MultiplyScale3D(InitialRelativeTransform.GetScale3D());
trans = trans * ParentTransform.Inverse();
this->SetRelativeTransform(trans);
}
else
{
this->SetRelativeLocation(ParentTransform.InverseTransformPosition(SplineComponentToFollow->GetLocationAtDistanceAlongSpline(splineProgress, ESplineCoordinateSpace::World)));
}
}
else // Not a spline follow
{
// Doing it min+max because the clamp value subtracts the min value
FVector CalculatedLocation = bUseLegacyLogic ? FMath::Lerp(-MinSlideDistance, MaxSlideDistance, NewSliderProgress) : FMath::Lerp(-MinSlideDistance.GetAbs(), MaxSlideDistance.GetAbs(), NewSliderProgress);
if (bSlideDistanceIsInParentSpace)
CalculatedLocation *= FVector(1.0f) / InitialRelativeTransform.GetScale3D();
FVector ClampedLocation = ClampSlideVector(CalculatedLocation);
//if (bSlideDistanceIsInParentSpace)
// this->SetRelativeLocation(InitialRelativeTransform.TransformPositionNoScale(ClampedLocation));
//else
this->SetRelativeLocation(InitialRelativeTransform.TransformPosition(ClampedLocation));
}
CurrentSliderProgress = NewSliderProgress;
}
float UVRSliderComponent::CalculateSliderProgress()
{
if (this->SplineComponentToFollow != nullptr)
{
CurrentSliderProgress = GetCurrentSliderProgress(this->GetComponentLocation());
}
else
{
FTransform ParentTransform = UVRInteractibleFunctionLibrary::Interactible_GetCurrentParentTransform(this);
FTransform CurrentRelativeTransform = InitialRelativeTransform * ParentTransform;
FVector CalculatedLocation = CurrentRelativeTransform.InverseTransformPosition(this->GetComponentLocation());
CurrentSliderProgress = GetCurrentSliderProgress(bSlideDistanceIsInParentSpace ? CalculatedLocation * InitialRelativeTransform.GetScale3D() : CalculatedLocation);
}
return CurrentSliderProgress;
}

View file

@ -0,0 +1,398 @@
// Copyright 1998-2016 Epic Games, Inc. All Rights Reserved.
#include "Misc/BucketUpdateSubsystem.h"
#include UE_INLINE_GENERATED_CPP_BY_NAME(BucketUpdateSubsystem)
bool UBucketUpdateSubsystem::AddObjectToBucket(int32 UpdateHTZ, UObject* InObject, FName FunctionName)
{
if (!InObject || UpdateHTZ < 1)
return false;
return BucketContainer.AddBucketObject(UpdateHTZ, InObject, FunctionName);
}
bool UBucketUpdateSubsystem::K2_AddObjectToBucket(int32 UpdateHTZ, UObject* InObject, FName FunctionName)
{
if (!InObject || UpdateHTZ < 1)
return false;
return BucketContainer.AddBucketObject(UpdateHTZ, InObject, FunctionName);
}
bool UBucketUpdateSubsystem::K2_AddObjectEventToBucket(FDynamicBucketUpdateTickSignature Delegate, int32 UpdateHTZ)
{
if (!Delegate.IsBound())
return false;
return BucketContainer.AddBucketObject(UpdateHTZ, Delegate);
}
bool UBucketUpdateSubsystem::RemoveObjectFromBucketByFunctionName(UObject* InObject, FName FunctionName)
{
if (!InObject)
return false;
return BucketContainer.RemoveBucketObject(InObject, FunctionName);
}
bool UBucketUpdateSubsystem::RemoveObjectFromBucketByEvent(FDynamicBucketUpdateTickSignature Delegate)
{
if (!Delegate.IsBound())
return false;
return BucketContainer.RemoveBucketObject(Delegate);
}
bool UBucketUpdateSubsystem::RemoveObjectFromAllBuckets(UObject* InObject)
{
if (!InObject)
return false;
return BucketContainer.RemoveObjectFromAllBuckets(InObject);
}
bool UBucketUpdateSubsystem::IsObjectFunctionInBucket(UObject* InObject, FName FunctionName)
{
if (!InObject)
return false;
return BucketContainer.IsObjectFunctionInBucket(InObject, FunctionName);
}
bool UBucketUpdateSubsystem::IsActive()
{
return BucketContainer.bNeedsUpdate;
}
void UBucketUpdateSubsystem::Tick(float DeltaTime)
{
BucketContainer.UpdateBuckets(DeltaTime);
}
bool UBucketUpdateSubsystem::IsTickable() const
{
return BucketContainer.bNeedsUpdate;
}
UWorld* UBucketUpdateSubsystem::GetTickableGameObjectWorld() const
{
return GetWorld();
}
bool UBucketUpdateSubsystem::IsTickableInEditor() const
{
return false;
}
bool UBucketUpdateSubsystem::IsTickableWhenPaused() const
{
return false;
}
ETickableTickType UBucketUpdateSubsystem::GetTickableTickType() const
{
if (IsTemplate(RF_ClassDefaultObject))
return ETickableTickType::Never;
return ETickableTickType::Conditional;
}
TStatId UBucketUpdateSubsystem::GetStatId() const
{
RETURN_QUICK_DECLARE_CYCLE_STAT(UVRGripScriptBase, STATGROUP_Tickables);
}
bool FUpdateBucketDrop::ExecuteBoundCallback()
{
if (NativeCallback.IsBound())
{
return NativeCallback.Execute();
}
else if (DynamicCallback.IsBound())
{
DynamicCallback.Execute();
return true;
}
return false;
}
bool FUpdateBucketDrop::IsBoundToObjectFunction(UObject * Obj, FName & FuncName)
{
return (NativeCallback.IsBoundToObject(Obj) && FunctionName == FuncName);
}
bool FUpdateBucketDrop::IsBoundToObjectDelegate(FDynamicBucketUpdateTickSignature & DynEvent)
{
return DynamicCallback == DynEvent;
}
bool FUpdateBucketDrop::IsBoundToObject(UObject * Obj)
{
return (NativeCallback.IsBoundToObject(Obj) || DynamicCallback.IsBoundToObject(Obj));
}
FUpdateBucketDrop::FUpdateBucketDrop()
{
FunctionName = NAME_None;
}
FUpdateBucketDrop::FUpdateBucketDrop(FDynamicBucketUpdateTickSignature & DynCallback)
{
DynamicCallback = DynCallback;
}
FUpdateBucketDrop::FUpdateBucketDrop(UObject * Obj, FName FuncName)
{
if (Obj && Obj->FindFunction(FuncName))
{
FunctionName = FuncName;
NativeCallback.BindUFunction(Obj, FunctionName);
}
else
{
FunctionName = NAME_None;
}
}
bool FUpdateBucket::Update(float DeltaTime)
{
//#TODO: Need to consider batching / spreading out load if there are a lot of updating objects in the bucket
if (Callbacks.Num() < 1)
return false;
// Check for if this bucket is ready to fire events
nUpdateCount += DeltaTime;
if (nUpdateCount >= nUpdateRate)
{
nUpdateCount = 0.0f;
for (int i = Callbacks.Num() - 1; i >= 0; --i)
{
if (Callbacks[i].ExecuteBoundCallback())
{
// If this returns true then we keep it in the queue
continue;
}
// Remove the callback, it is complete or invalid
Callbacks.RemoveAt(i);
}
}
return Callbacks.Num() > 0;
}
void FUpdateBucketContainer::UpdateBuckets(float DeltaTime)
{
TArray<uint32> BucketsToRemove;
for(auto& Bucket : ReplicationBuckets)
{
if (!Bucket.Value.Update(DeltaTime))
{
// Add Bucket to list to remove at end of update
BucketsToRemove.Add(Bucket.Key);
}
}
// Remove unused buckets so that they don't get ticked
for (const uint32 Key : BucketsToRemove)
{
ReplicationBuckets.Remove(Key);
}
if (ReplicationBuckets.Num() < 1)
bNeedsUpdate = false;
}
bool FUpdateBucketContainer::AddBucketObject(uint32 UpdateHTZ, UObject* InObject, FName FunctionName)
{
if (!InObject || InObject->FindFunction(FunctionName) == nullptr || UpdateHTZ < 1)
return false;
// First verify that this object isn't already contained in a bucket, if it is then erase it so that we can replace it below
RemoveBucketObject(InObject, FunctionName);
if (ReplicationBuckets.Contains(UpdateHTZ))
{
ReplicationBuckets[UpdateHTZ].Callbacks.Add(FUpdateBucketDrop(InObject, FunctionName));
}
else
{
FUpdateBucket & newBucket = ReplicationBuckets.Add(UpdateHTZ, FUpdateBucket(UpdateHTZ));
ReplicationBuckets[UpdateHTZ].Callbacks.Add(FUpdateBucketDrop(InObject, FunctionName));
}
if (ReplicationBuckets.Num() > 0)
bNeedsUpdate = true;
return true;
}
bool FUpdateBucketContainer::AddBucketObject(uint32 UpdateHTZ, FDynamicBucketUpdateTickSignature &Delegate)
{
if (!Delegate.IsBound() || UpdateHTZ < 1)
return false;
// First verify that this object isn't already contained in a bucket, if it is then erase it so that we can replace it below
RemoveBucketObject(Delegate);
if (ReplicationBuckets.Contains(UpdateHTZ))
{
ReplicationBuckets[UpdateHTZ].Callbacks.Add(FUpdateBucketDrop(Delegate));
}
else
{
FUpdateBucket & newBucket = ReplicationBuckets.Add(UpdateHTZ, FUpdateBucket(UpdateHTZ));
ReplicationBuckets[UpdateHTZ].Callbacks.Add(FUpdateBucketDrop(Delegate));
}
if (ReplicationBuckets.Num() > 0)
bNeedsUpdate = true;
return true;
}
bool FUpdateBucketContainer::RemoveBucketObject(UObject * ObjectToRemove, FName FunctionName)
{
if (!ObjectToRemove || ObjectToRemove->FindFunction(FunctionName) == nullptr)
return false;
// Store if we ended up removing it
bool bRemovedObject = false;
TArray<uint32> BucketsToRemove;
for (auto& Bucket : ReplicationBuckets)
{
for (int i = Bucket.Value.Callbacks.Num() - 1; i >= 0; --i)
{
if (Bucket.Value.Callbacks[i].IsBoundToObjectFunction(ObjectToRemove, FunctionName))
{
Bucket.Value.Callbacks.RemoveAt(i);
bRemovedObject = true;
// Leave the loop, this is called in add as well so we should never get duplicate entries
break;
}
}
if (bRemovedObject)
{
break;
}
}
return bRemovedObject;
}
bool FUpdateBucketContainer::RemoveBucketObject(FDynamicBucketUpdateTickSignature &DynEvent)
{
if (!DynEvent.IsBound())
return false;
// Store if we ended up removing it
bool bRemovedObject = false;
TArray<uint32> BucketsToRemove;
for (auto& Bucket : ReplicationBuckets)
{
for (int i = Bucket.Value.Callbacks.Num() - 1; i >= 0; --i)
{
if (Bucket.Value.Callbacks[i].IsBoundToObjectDelegate(DynEvent))
{
Bucket.Value.Callbacks.RemoveAt(i);
bRemovedObject = true;
// Leave the loop, this is called in add as well so we should never get duplicate entries
break;
}
}
if (bRemovedObject)
{
break;
}
}
return bRemovedObject;
}
bool FUpdateBucketContainer::RemoveObjectFromAllBuckets(UObject * ObjectToRemove)
{
if (!ObjectToRemove)
return false;
// Store if we ended up removing it
bool bRemovedObject = false;
TArray<uint32> BucketsToRemove;
for (auto& Bucket : ReplicationBuckets)
{
for (int i = Bucket.Value.Callbacks.Num() - 1; i >= 0; --i)
{
if (Bucket.Value.Callbacks[i].IsBoundToObject(ObjectToRemove))
{
Bucket.Value.Callbacks.RemoveAt(i);
bRemovedObject = true;
}
}
}
return bRemovedObject;
}
bool FUpdateBucketContainer::IsObjectInBucket(UObject * ObjectToRemove)
{
if (!ObjectToRemove)
return false;
for (auto& Bucket : ReplicationBuckets)
{
for (int i = Bucket.Value.Callbacks.Num() - 1; i >= 0; --i)
{
if (Bucket.Value.Callbacks[i].IsBoundToObject(ObjectToRemove))
{
return true;
}
}
}
return false;
}
bool FUpdateBucketContainer::IsObjectFunctionInBucket(UObject * ObjectToRemove, FName FunctionName)
{
if (!ObjectToRemove)
return false;
for (auto& Bucket : ReplicationBuckets)
{
for (int i = Bucket.Value.Callbacks.Num() - 1; i >= 0; --i)
{
if (Bucket.Value.Callbacks[i].IsBoundToObjectFunction(ObjectToRemove, FunctionName))
{
return true;
}
}
}
return false;
}
bool FUpdateBucketContainer::IsObjectDelegateInBucket(FDynamicBucketUpdateTickSignature &DynEvent)
{
if (!DynEvent.IsBound())
return false;
for (auto& Bucket : ReplicationBuckets)
{
for (int i = Bucket.Value.Callbacks.Num() - 1; i >= 0; --i)
{
if (Bucket.Value.Callbacks[i].IsBoundToObjectDelegate(DynEvent))
{
return true;
}
}
}
return false;
}

View file

@ -0,0 +1,588 @@
// Copyright 1998-2016 Epic Games, Inc. All Rights Reserved.
#include "Misc/CollisionIgnoreSubsystem.h"
#include UE_INLINE_GENERATED_CPP_BY_NAME(CollisionIgnoreSubsystem)
#include "Components/SkeletalMeshComponent.h"
#include "Runtime/Engine/Classes/Kismet/GameplayStatics.h"
#include "VRGlobalSettings.h"
//#include "Chaos/ParticleHandle.h"
#include "PhysicsEngine/PhysicsAsset.h"
#include "PhysicsEngine/PhysicsAsset.h"
#include "Physics/Experimental/PhysScene_Chaos.h"
#include "Chaos/KinematicGeometryParticles.h"
#include "PhysicsProxy/SingleParticlePhysicsProxy.h"
#include "PBDRigidsSolver.h"
#include "Chaos/ContactModification.h"
DEFINE_LOG_CATEGORY(VRE_CollisionIgnoreLog);
void FCollisionIgnoreSubsystemAsyncCallback::OnContactModification_Internal(Chaos::FCollisionContactModifier& Modifier)
{
const FSimCallbackInputVR* Input = GetConsumerInput_Internal();
if (Input && Input->bIsInitialized)
{
for (Chaos::FContactPairModifierIterator ContactIterator = Modifier.Begin(); ContactIterator; ++ContactIterator)
{
if (ContactIterator.IsValid())
{
Chaos::TVec2<Chaos::FGeometryParticleHandle*> Pair = ContactIterator->GetParticlePair();
Chaos::TPBDRigidParticleHandle<Chaos::FReal, 3>* ParticleHandle0 = Pair[0]->CastToRigidParticle();
Chaos::TPBDRigidParticleHandle<Chaos::FReal, 3>* ParticleHandle1 = Pair[1]->CastToRigidParticle();
// Chaos has some sort of bug here, the constraint flags are cleared, and for that matter the
// ID0 and ID1 pairs losing collision ignore shouldn't happen as they are still valid always
// Don't use contact modification until after we see if the incoming chaos fixes for constraint collision ignore
// is resolved.
if (ParticleHandle0 && ParticleHandle1)
{
// This lets us pull the transform at time of collision, collision events use the first contact
// for the information to throw out so we should be able to pull it here and keep it for that pair for the frame
//FTransform particleHandle = Chaos::FRigidTransform3(ParticleHandle0->X(), ParticleHandle0->R());
//FTransform particleHandle2 = Chaos::FRigidTransform3(ParticleHandle0->X(), ParticleHandle1->R());
//bool bHasCollisionFlag = ParticleHandle0->HasCollisionConstraintFlag(Chaos::ECollisionConstraintFlags::CCF_BroadPhaseIgnoreCollisions);
//bool bHadCollisionFlag2 = ParticleHandle1->HasCollisionConstraintFlag(Chaos::ECollisionConstraintFlags::CCF_BroadPhaseIgnoreCollisions);
//if (bHasCollisionFlag && bHadCollisionFlag2)
{
FChaosParticlePair SearchPair(ParticleHandle0, ParticleHandle1);
if (Input->ParticlePairs.Contains(SearchPair))
{
ContactIterator->Disable();
}
}
}
}
}
}
}
void UCollisionIgnoreSubsystem::ConstructInput()
{
if (ContactModifierCallback)
{
FSimCallbackInputVR* Input = ContactModifierCallback->GetProducerInputData_External();
if (Input->bIsInitialized == false)
{
Input->bIsInitialized = true;
}
// Clear out the pair array
Input->Reset();
for (TPair<FCollisionPrimPair, FCollisionIgnorePairArray>& CollisionPairArray : CollisionTrackedPairs)
{
for (FCollisionIgnorePair& IgnorePair : CollisionPairArray.Value.PairArray)
{
if (IgnorePair.Actor1 && IgnorePair.Actor2)
{
Input->ParticlePairs.Add(FChaosParticlePair(IgnorePair.Actor1->GetHandle_LowLevel()->CastToRigidParticle(), IgnorePair.Actor2->GetHandle_LowLevel()->CastToRigidParticle()));
}
}
}
}
}
void UCollisionIgnoreSubsystem::UpdateTimer(bool bChangesWereMade)
{
RemovedPairs.Reset();
const UVRGlobalSettings& VRSettings = *GetDefault<UVRGlobalSettings>();
if (CollisionTrackedPairs.Num() > 0)
{
if (!UpdateHandle.IsValid())
{
// Setup the heartbeat on 1htz checks
GetWorld()->GetTimerManager().SetTimer(UpdateHandle, this, &UCollisionIgnoreSubsystem::CheckActiveFilters, VRSettings.CollisionIgnoreSubsystemUpdateRate, true, VRSettings.CollisionIgnoreSubsystemUpdateRate);
if (VRSettings.bUseCollisionModificationForCollisionIgnore && !ContactModifierCallback)
{
if (UWorld* World = GetWorld())
{
if (FPhysScene* PhysScene = World->GetPhysicsScene())
{
// Register a callback
ContactModifierCallback = PhysScene->GetSolver()->CreateAndRegisterSimCallbackObject_External<FCollisionIgnoreSubsystemAsyncCallback>(/*true*/);
}
}
}
}
// Need to only add input when changes are made
if (VRSettings.bUseCollisionModificationForCollisionIgnore && ContactModifierCallback && bChangesWereMade)
{
ConstructInput();
}
}
else if (UpdateHandle.IsValid())
{
GetWorld()->GetTimerManager().ClearTimer(UpdateHandle);
if (VRSettings.bUseCollisionModificationForCollisionIgnore && ContactModifierCallback)
{
//FSimCallbackInputVR* Input = ContactModifierCallback->GetProducerInputData_External();
//Input->bIsInitialized = false;
if (UWorld* World = GetWorld())
{
if (FPhysScene* PhysScene = World->GetPhysicsScene())
{
// UnRegister a callback
PhysScene->GetSolver()->UnregisterAndFreeSimCallbackObject_External(ContactModifierCallback);
ContactModifierCallback = nullptr;
}
}
}
}
}
void UCollisionIgnoreSubsystem::CheckActiveFilters()
{
bool bMadeChanges = false;
for (TPair<FCollisionPrimPair, FCollisionIgnorePairArray>& KeyPair : CollisionTrackedPairs)
{
// First check for invalid primitives
if (!IsValid(KeyPair.Key.Prim1) || !IsValid(KeyPair.Key.Prim2))
{
// If we don't have a map element for this pair, then add it now
if (!RemovedPairs.Contains(KeyPair.Key))
{
RemovedPairs.Add(KeyPair.Key, KeyPair.Value);
bMadeChanges = true;
}
continue; // skip remaining checks as we have invalid primitives anyway
}
// Skip this section but leave the code intact in case i need it eventually
#if false
// Now check for lost physics handles
// Implement later
if (FPhysScene* PhysScene = KeyPair.Key.Prim1->GetWorld()->GetPhysicsScene())
{
Chaos::FIgnoreCollisionManager& IgnoreCollisionManager = PhysScene->GetSolver()->GetEvolution()->GetBroadPhase().GetIgnoreCollisionManager();
FPhysicsCommand::ExecuteWrite(PhysScene, [&]()
{
using namespace Chaos;
for (int i = KeyPair.Value.PairArray.Num() - 1; i >= 0; i--)
{
FPhysicsActorHandle hand1 = KeyPair.Value.PairArray[i].Actor1;
FPhysicsActorHandle hand2 = KeyPair.Value.PairArray[i].Actor2;
/*if (bIgnoreCollision)
{
if (!IgnoreCollisionManager.IgnoresCollision(ID0, ID1))
{
TPBDRigidParticleHandle<FReal, 3>* ParticleHandle0 = ApplicableBodies[i].BInstance->ActorHandle->GetHandle_LowLevel()->CastToRigidParticle();
TPBDRigidParticleHandle<FReal, 3>* ParticleHandle1 = ApplicableBodies2[j].BInstance->ActorHandle->GetHandle_LowLevel()->CastToRigidParticle();
if (ParticleHandle0 && ParticleHandle1)
{
ParticleHandle0->AddCollisionConstraintFlag(Chaos::ECollisionConstraintFlags::CCF_BroadPhaseIgnoreCollisions);
IgnoreCollisionManager.AddIgnoreCollisionsFor(ID0, ID1);
ParticleHandle1->AddCollisionConstraintFlag(Chaos::ECollisionConstraintFlags::CCF_BroadPhaseIgnoreCollisions);
IgnoreCollisionManager.AddIgnoreCollisionsFor(ID1, ID0);
}
}
}*/
if (
(!KeyPair.Value.PairArray[i].Actor1 || !FPhysicsInterface::IsValid(KeyPair.Value.PairArray[i].Actor1)) ||
(!KeyPair.Value.PairArray[i].Actor2 || !FPhysicsInterface::IsValid(KeyPair.Value.PairArray[i].Actor2))
)
{
// We will either be re-initing or deleting from here so it will always involve changes
bMadeChanges = true;
FName Bone1 = KeyPair.Value.PairArray[i].BoneName1;
FName Bone2 = KeyPair.Value.PairArray[i].BoneName2;
FBodyInstance* Inst1 = KeyPair.Key.Prim1->GetBodyInstance(Bone1);
FBodyInstance* Inst2 = KeyPair.Key.Prim2->GetBodyInstance(Bone2);
if (Inst1 && Inst2)
{
// We still have the bones available, lets go ahead and re-init for them
KeyPair.Value.PairArray.RemoveAt(i);
SetComponentCollisionIgnoreState(false, false, KeyPair.Key.Prim1, Bone1, KeyPair.Key.Prim2, Bone2, true, false);
}
else
{
// Its not still available, lets remove the pair.
KeyPair.Value.PairArray.RemoveAt(i);
}
}
else
{
Chaos::FUniqueIdx ID0 = KeyPair.Value.PairArray[i].Actor1->GetParticle_LowLevel()->UniqueIdx();
Chaos::FUniqueIdx ID1 = KeyPair.Value.PairArray[i].Actor2->GetParticle_LowLevel()->UniqueIdx();
if (!IgnoreCollisionManager.IgnoresCollision(ID0, ID1))
{
auto* pHandle1 = KeyPair.Value.PairArray[i].Actor1->GetHandle_LowLevel();
auto* pHandle2 = KeyPair.Value.PairArray[i].Actor2->GetHandle_LowLevel();
if (pHandle1 && pHandle2)
{
TPBDRigidParticleHandle<FReal, 3>* ParticleHandle0 = pHandle1->CastToRigidParticle();
TPBDRigidParticleHandle<FReal, 3>* ParticleHandle1 = pHandle2->CastToRigidParticle();
if (ParticleHandle0 && ParticleHandle1)
{
ParticleHandle0->AddCollisionConstraintFlag(Chaos::ECollisionConstraintFlags::CCF_BroadPhaseIgnoreCollisions);
IgnoreCollisionManager.AddIgnoreCollisionsFor(ID0, ID1);
ParticleHandle1->AddCollisionConstraintFlag(Chaos::ECollisionConstraintFlags::CCF_BroadPhaseIgnoreCollisions);
IgnoreCollisionManager.AddIgnoreCollisionsFor(ID1, ID0);
}
}
}
}
}
});
}
#endif
// If there are no pairs left
if (KeyPair.Value.PairArray.Num() < 1)
{
// Try and remove it, chaos should be cleaning up the ignore setups
if (!RemovedPairs.Contains(KeyPair.Key))
{
RemovedPairs.Add(KeyPair.Key, KeyPair.Value);
}
}
}
/*#if WITH_CHAOS
if (FPhysScene* PhysScene2 = GetWorld()->GetPhysicsScene())
{
Chaos::FIgnoreCollisionManager& IgnoreCollisionManager = PhysScene2->GetSolver()->GetEvolution()->GetBroadPhase().GetIgnoreCollisionManager();
int32 ExternalTimestamp = PhysScene2->GetSolver()->GetMarshallingManager().GetExternalTimestamp_External();
Chaos::FIgnoreCollisionManager::FPendingMap IgnoreSet = IgnoreCollisionManager.GetPendingActivationsForGameThread(ExternalTimestamp);
Chaos::FIgnoreCollisionManager::FDeactivationSet DeactiveSet = IgnoreCollisionManager.GetPendingDeactivationsForGameThread(ExternalTimestamp);
//Chaos::FIgnoreCollisionManager::FDeactivationSet IgnoreSet =
// Prints out the list of items currently being re-activated after one of their pairs died.
// Chaos automatically cleans up here, I don't need to do anything.
GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Red, FString::Printf(TEXT("Pending activate: %i - Pending deativate: %i"), IgnoreSet.Num(), DeactiveSet.Num()));
}
#endif*/
for (const TPair<FCollisionPrimPair, FCollisionIgnorePairArray>& KeyPair : RemovedPairs)
{
if (CollisionTrackedPairs.Contains(KeyPair.Key))
{
CollisionTrackedPairs[KeyPair.Key].PairArray.Empty();
CollisionTrackedPairs.Remove(KeyPair.Key);
}
}
UpdateTimer(bMadeChanges);
}
void UCollisionIgnoreSubsystem::RemoveComponentCollisionIgnoreState(UPrimitiveComponent* Prim1)
{
if (!Prim1)
return;
TMap<FCollisionPrimPair, FCollisionIgnorePairArray> PairsToRemove;
for (const TPair<FCollisionPrimPair, FCollisionIgnorePairArray>& KeyPair : CollisionTrackedPairs)
{
// First check for invalid primitives
if (KeyPair.Key.Prim1 == Prim1 || KeyPair.Key.Prim2 == Prim1)
{
// If we don't have a map element for this pair, then add it now
if (!PairsToRemove.Contains(KeyPair.Key))
{
PairsToRemove.Add(KeyPair.Key, KeyPair.Value);
}
}
}
for (const TPair<FCollisionPrimPair, FCollisionIgnorePairArray>& KeyPair : PairsToRemove)
{
if (CollisionTrackedPairs.Contains(KeyPair.Key))
{
for (const FCollisionIgnorePair& newIgnorePair : KeyPair.Value.PairArray)
{
// Clear out current ignores
SetComponentCollisionIgnoreState(false, false, KeyPair.Key.Prim1, newIgnorePair.BoneName1, KeyPair.Key.Prim2, newIgnorePair.BoneName2, false, false);
}
/*if (CollisionTrackedPairs.Contains(KeyPair.Key))
{
// Ensure we are empty
CollisionTrackedPairs[KeyPair.Key].PairArray.Empty();
CollisionTrackedPairs.Remove(KeyPair.Key);
}*/
}
}
UpdateTimer(true);
}
bool UCollisionIgnoreSubsystem::IsComponentIgnoringCollision(UPrimitiveComponent* Prim1)
{
if (!Prim1)
return false;
for (const TPair<FCollisionPrimPair, FCollisionIgnorePairArray>& KeyPair : CollisionTrackedPairs)
{
// First check for invalid primitives
if (KeyPair.Key.Prim1 == Prim1 || KeyPair.Key.Prim2 == Prim1)
{
return true;
}
}
return false;
}
bool UCollisionIgnoreSubsystem::AreComponentsIgnoringCollisions(UPrimitiveComponent* Prim1, UPrimitiveComponent* Prim2)
{
if (!Prim1 || !Prim2)
return false;
TSet<FCollisionPrimPair> CurrentKeys;
int numKeys = CollisionTrackedPairs.GetKeys(CurrentKeys);
FCollisionPrimPair SearchPair;
SearchPair.Prim1 = Prim1;
SearchPair.Prim2 = Prim2;
// This checks if we exist already as well as provides an index
if (FCollisionPrimPair* ExistingPair = CurrentKeys.Find(SearchPair))
{
// These components are ignoring collision
return true;
}
return false;
}
void UCollisionIgnoreSubsystem::InitiateIgnore()
{
}
bool UCollisionIgnoreSubsystem::HasCollisionIgnorePairs()
{
return CollisionTrackedPairs.Num() > 0;
}
void UCollisionIgnoreSubsystem::SetComponentCollisionIgnoreState(bool bIterateChildren1, bool bIterateChildren2, UPrimitiveComponent* Prim1, FName OptionalBoneName1, UPrimitiveComponent* Prim2, FName OptionalBoneName2, bool bIgnoreCollision, bool bCheckFilters)
{
if (!Prim1 || !Prim2)
{
UE_LOG(VRE_CollisionIgnoreLog, Error, TEXT("Set Objects Ignore Collision called with invalid object(s)!!"));
return;
}
if (Prim1->GetCollisionEnabled() == ECollisionEnabled::NoCollision || Prim2->GetCollisionEnabled() == ECollisionEnabled::NoCollision)
{
UE_LOG(VRE_CollisionIgnoreLog, Error, TEXT("Set Objects Ignore Collision called with one or more objects with no collision!! %s, %s"), *Prim1->GetName(), *Prim2->GetName());
return;
}
if (Prim1->Mobility == EComponentMobility::Static || Prim2->Mobility == EComponentMobility::Static)
{
UE_LOG(VRE_CollisionIgnoreLog, Error, TEXT("Set Objects Ignore Collision called with at least one static mobility object (cannot ignore collision with it)!!"));
if (bIgnoreCollision)
{
return;
}
}
USkeletalMeshComponent* SkeleMesh = nullptr;
USkeletalMeshComponent* SkeleMesh2 = nullptr;
if (bIterateChildren1)
{
SkeleMesh = Cast<USkeletalMeshComponent>(Prim1);
}
if (bIterateChildren2)
{
SkeleMesh2 = Cast<USkeletalMeshComponent>(Prim2);
}
struct BodyPairStore
{
FBodyInstance* BInstance;
FName BName;
BodyPairStore(FBodyInstance* BI, FName BoneName)
{
BInstance = BI;
BName = BoneName;
}
};
TArray<BodyPairStore> ApplicableBodies;
if (SkeleMesh)
{
UPhysicsAsset* PhysAsset = SkeleMesh ? SkeleMesh->GetPhysicsAsset() : nullptr;
if (PhysAsset)
{
int32 NumBodiesFound = SkeleMesh->ForEachBodyBelow(OptionalBoneName1, true, false, [PhysAsset, &ApplicableBodies](FBodyInstance* BI)
{
const FName IterBodyName = PhysAsset->SkeletalBodySetups[BI->InstanceBodyIndex]->BoneName;
ApplicableBodies.Add(BodyPairStore(BI, IterBodyName));
});
}
}
else
{
FBodyInstance* Inst1 = Prim1->GetBodyInstance(OptionalBoneName1);
if (Inst1)
{
ApplicableBodies.Add(BodyPairStore(Inst1, OptionalBoneName1));
}
}
TArray<BodyPairStore> ApplicableBodies2;
if (SkeleMesh2)
{
UPhysicsAsset* PhysAsset = SkeleMesh2 ? SkeleMesh2->GetPhysicsAsset() : nullptr;
if (PhysAsset)
{
int32 NumBodiesFound = SkeleMesh2->ForEachBodyBelow(OptionalBoneName2, true, false, [PhysAsset, &ApplicableBodies2](FBodyInstance* BI)
{
const FName IterBodyName = PhysAsset->SkeletalBodySetups[BI->InstanceBodyIndex]->BoneName;
ApplicableBodies2.Add(BodyPairStore(BI, IterBodyName));
});
}
}
else
{
FBodyInstance* Inst1 = Prim2->GetBodyInstance(OptionalBoneName2);
if (Inst1)
{
ApplicableBodies2.Add(BodyPairStore(Inst1, OptionalBoneName2));
}
}
FCollisionPrimPair newPrimPair;
newPrimPair.Prim1 = Prim1;
newPrimPair.Prim2 = Prim2;
// Check our active filters and handle inconsistencies before we run the next logic
// (This prevents cases where null ptrs get added too)
if (bCheckFilters)
{
CheckActiveFilters();
}
// If we don't have a map element for this pair, then add it now
if (bIgnoreCollision && !CollisionTrackedPairs.Contains(newPrimPair))
{
CollisionTrackedPairs.Add(newPrimPair, FCollisionIgnorePairArray());
}
else if (!bIgnoreCollision && !CollisionTrackedPairs.Contains(newPrimPair))
{
// Early out, we don't even have this pair to remove it
return;
}
for (int i = 0; i < ApplicableBodies.Num(); ++i)
{
for (int j = 0; j < ApplicableBodies2.Num(); ++j)
{
if (ApplicableBodies[i].BInstance && ApplicableBodies2[j].BInstance && ApplicableBodies[i].BInstance->ActorHandle && ApplicableBodies2[j].BInstance->ActorHandle)
{
if (FPhysScene* PhysScene = Prim1->GetWorld()->GetPhysicsScene())
{
FCollisionIgnorePair newIgnorePair;
newIgnorePair.Actor1 = ApplicableBodies[i].BInstance->ActorHandle;
newIgnorePair.BoneName1 = ApplicableBodies[i].BName;
newIgnorePair.Actor2 = ApplicableBodies2[j].BInstance->ActorHandle;
newIgnorePair.BoneName2 = ApplicableBodies2[j].BName;
//Chaos::FUniqueIdx ID0 = ApplicableBodies[i].BInstance->ActorHandle->GetParticle_LowLevel()->UniqueIdx();
//Chaos::FUniqueIdx ID1 = ApplicableBodies2[j].BInstance->ActorHandle->GetParticle_LowLevel()->UniqueIdx();
auto* pHandle1 = ApplicableBodies[i].BInstance->ActorHandle->GetHandle_LowLevel();
auto* pHandle2 = ApplicableBodies2[j].BInstance->ActorHandle->GetHandle_LowLevel();
Chaos::FIgnoreCollisionManager& IgnoreCollisionManager = PhysScene->GetSolver()->GetEvolution()->GetBroadPhase().GetIgnoreCollisionManager();
FPhysicsCommand::ExecuteWrite(PhysScene, [&]()
{
using namespace Chaos;
if (bIgnoreCollision && pHandle1 && pHandle2)
{
if (!IgnoreCollisionManager.IgnoresCollision(pHandle1, pHandle2))
{
IgnoreCollisionManager.AddIgnoreCollisions(pHandle1, pHandle2);
TSet<FCollisionPrimPair> CurrentKeys;
int numKeys = CollisionTrackedPairs.GetKeys(CurrentKeys);
// This checks if we exist already as well as provides an index
if (FCollisionPrimPair* CurrentPair = CurrentKeys.Find(newPrimPair))
{
// Check if the current one has the same primitive ordering as the new check
if (CurrentPair->Prim1 != newPrimPair.Prim1)
{
// If not then lets flip the elements around in order to match it
newIgnorePair.FlipElements();
}
CollisionTrackedPairs[newPrimPair].PairArray.AddUnique(newIgnorePair);
}
}
}
else if (pHandle1 && pHandle2)
{
if (IgnoreCollisionManager.IgnoresCollision(pHandle1, pHandle2))
{
IgnoreCollisionManager.RemoveIgnoreCollisions(pHandle1, pHandle2);
CollisionTrackedPairs[newPrimPair].PairArray.Remove(newIgnorePair);
if (CollisionTrackedPairs[newPrimPair].PairArray.Num() < 1)
{
CollisionTrackedPairs.Remove(newPrimPair);
}
// If we don't have a map element for this pair, then add it now
if (!RemovedPairs.Contains(newPrimPair))
{
RemovedPairs.Add(newPrimPair, FCollisionIgnorePairArray());
}
RemovedPairs[newPrimPair].PairArray.AddUnique(newIgnorePair);
}
}
});
}
}
}
}
// Update our timer state
UpdateTimer(true);
}

View file

@ -0,0 +1,677 @@
// Copyright 1998-2016 Epic Games, Inc. All Rights Reserved.
#include "Misc/OptionalRepSkeletalMeshActor.h"
#include UE_INLINE_GENERATED_CPP_BY_NAME(OptionalRepSkeletalMeshActor)
#include "TimerManager.h"
#include "Net/UnrealNetwork.h"
#include "PhysicsReplication.h"
#include "PhysicsEngine/PhysicsAsset.h"
#if WITH_PUSH_MODEL
#include "Net/Core/PushModel/PushModel.h"
#endif
UNoRepSphereComponent::UNoRepSphereComponent(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{
this->SetIsReplicatedByDefault(true);
this->PrimaryComponentTick.bCanEverTick = false;
SphereRadius = 4.0f;
SetCollisionEnabled(ECollisionEnabled::PhysicsOnly);
SetCollisionResponseToAllChannels(ECR_Ignore);
//SetAllMassScale(0.0f); Engine hates calling this in constructor
BodyInstance.bOverrideMass = true;
BodyInstance.SetMassOverride(0.f);
}
void UNoRepSphereComponent::GetLifetimeReplicatedProps(TArray< class FLifetimeProperty >& OutLifetimeProps) const
{
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
DOREPLIFETIME(UNoRepSphereComponent, bReplicateMovement);
RESET_REPLIFETIME_CONDITION_PRIVATE_PROPERTY(USceneComponent, AttachParent, COND_InitialOnly);
RESET_REPLIFETIME_CONDITION_PRIVATE_PROPERTY(USceneComponent, AttachSocketName, COND_InitialOnly);
RESET_REPLIFETIME_CONDITION_PRIVATE_PROPERTY(USceneComponent, AttachChildren, COND_InitialOnly);
RESET_REPLIFETIME_CONDITION_PRIVATE_PROPERTY(USceneComponent, RelativeLocation, COND_InitialOnly);
RESET_REPLIFETIME_CONDITION_PRIVATE_PROPERTY(USceneComponent, RelativeRotation, COND_InitialOnly);
RESET_REPLIFETIME_CONDITION_PRIVATE_PROPERTY(USceneComponent, RelativeScale3D, COND_InitialOnly);
//DISABLE_REPLICATED_PRIVATE_PROPERTY(AActor, AttachmentReplication);
}
void UNoRepSphereComponent::PreReplication(IRepChangedPropertyTracker& ChangedPropertyTracker)
{
Super::PreReplication(ChangedPropertyTracker);
}
void FSkeletalMeshComponentEndPhysicsTickFunctionVR::ExecuteTick(float DeltaTime, enum ELevelTick TickType, ENamedThreads::Type CurrentThread, const FGraphEventRef& MyCompletionGraphEvent)
{
//QUICK_SCOPE_CYCLE_COUNTER(FSkeletalMeshComponentEndPhysicsTickFunction_ExecuteTick);
//CSV_SCOPED_TIMING_STAT_EXCLUSIVE(Physics);
FActorComponentTickFunction::ExecuteTickHelper(TargetVR, /*bTickInEditor=*/ false, DeltaTime, TickType, [this](float DilatedTime)
{
TargetVR->EndPhysicsTickComponentVR(*this);
});
}
FString FSkeletalMeshComponentEndPhysicsTickFunctionVR::DiagnosticMessage()
{
if (TargetVR)
{
return TargetVR->GetFullName() + TEXT("[EndPhysicsTickVR]");
}
return TEXT("<NULL>[EndPhysicsTick]");
}
FName FSkeletalMeshComponentEndPhysicsTickFunctionVR::DiagnosticContext(bool bDetailed)
{
return FName(TEXT("SkeletalMeshComponentEndPhysicsTickVR"));
}
UInversePhysicsSkeletalMeshComponent::UInversePhysicsSkeletalMeshComponent(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{
bReplicateMovement = true;
this->EndPhysicsTickFunction.bCanEverTick = false;
bReplicatePhysicsToAutonomousProxy = false;
EndPhysicsTickFunctionVR.TickGroup = TG_EndPhysics;
EndPhysicsTickFunctionVR.bCanEverTick = true;
EndPhysicsTickFunctionVR.bStartWithTickEnabled = true;
}
void UInversePhysicsSkeletalMeshComponent::PreReplication(IRepChangedPropertyTracker& ChangedPropertyTracker)
{
Super::PreReplication(ChangedPropertyTracker);
// Don't replicate if set to not do it
DOREPLIFETIME_ACTIVE_OVERRIDE_FAST(USceneComponent, RelativeLocation, bReplicateMovement);
DOREPLIFETIME_ACTIVE_OVERRIDE_FAST(USceneComponent, RelativeRotation, bReplicateMovement);
DOREPLIFETIME_ACTIVE_OVERRIDE_FAST(USceneComponent, RelativeScale3D, bReplicateMovement);
}
void UInversePhysicsSkeletalMeshComponent::EndPhysicsTickComponentVR(FSkeletalMeshComponentEndPhysicsTickFunctionVR& ThisTickFunction)
{
//IMPORTANT!
//
// The decision on whether to use EndPhysicsTickComponent or not is made by ShouldRunEndPhysicsTick()
// Any changes that are made to EndPhysicsTickComponent that affect whether it should be run or not
// have to be reflected in ShouldRunEndPhysicsTick() as well
// if physics is disabled on dedicated server, no reason to be here.
if (!bEnablePhysicsOnDedicatedServer && IsRunningDedicatedServer())
{
FinalizeBoneTransform();
return;
}
if (IsRegistered() && IsSimulatingPhysics() && RigidBodyIsAwake())
{
if (bNotifySyncComponentToRBPhysics)
{
OnSyncComponentToRBPhysics();
}
SyncComponentToRBPhysics();
}
// this used to not run if not rendered, but that causes issues such as bounds not updated
// causing it to not rendered, at the end, I think we should blend body positions
// for example if you're only simulating, this has to happen all the time
// whether looking at it or not, otherwise
// @todo better solution is to check if it has moved by changing SyncComponentToRBPhysics to return true if anything modified
// and run this if that is true or rendered
// that will at least reduce the chance of mismatch
// generally if you move your actor position, this has to happen to approximately match their bounds
if (ShouldBlendPhysicsBones())
{
if (IsRegistered())
{
BlendInPhysicsInternalVR(ThisTickFunction);
}
}
}
void UInversePhysicsSkeletalMeshComponent::BlendInPhysicsInternalVR(FTickFunction& ThisTickFunction)
{
check(IsInGameThread());
// Can't do anything without a SkeletalMesh
if (!GetSkinnedAsset())
{
return;
}
// We now have all the animations blended together and final relative transforms for each bone.
// If we don't have or want any physics, we do nothing.
if (Bodies.Num() > 0 && CollisionEnabledHasPhysics(GetCollisionEnabled()))
{
//HandleExistingParallelEvaluationTask(/*bBlockOnTask = */ true, /*bPerformPostAnimEvaluation =*/ true);
// start parallel work
//check(!IsValidRef(ParallelAnimationEvaluationTask));
const bool bParallelBlend = false;// !!CVarUseParallelBlendPhysics.GetValueOnGameThread() && FApp::ShouldUseThreadingForPerformance();
if (bParallelBlend)
{
/*SwapEvaluationContextBuffers();
ParallelAnimationEvaluationTask = TGraphTask<FParallelBlendPhysicsTask>::CreateTask().ConstructAndDispatchWhenReady(this);
// set up a task to run on the game thread to accept the results
FGraphEventArray Prerequistes;
Prerequistes.Add(ParallelAnimationEvaluationTask);
check(!IsValidRef(ParallelBlendPhysicsCompletionTask));
ParallelBlendPhysicsCompletionTask = TGraphTask<FParallelBlendPhysicsCompletionTask>::CreateTask(&Prerequistes).ConstructAndDispatchWhenReady(this);
ThisTickFunction.GetCompletionHandle()->DontCompleteUntil(ParallelBlendPhysicsCompletionTask);*/
}
else
{
PRAGMA_DISABLE_DEPRECATION_WARNINGS
PerformBlendPhysicsBonesVR(RequiredBones, GetEditableComponentSpaceTransforms(), BoneSpaceTransforms);
PRAGMA_ENABLE_DEPRECATION_WARNINGS
FinalizeAnimationUpdateVR();
}
}
}
void UInversePhysicsSkeletalMeshComponent::FinalizeAnimationUpdateVR()
{
//SCOPE_CYCLE_COUNTER(STAT_FinalizeAnimationUpdate);
// Flip bone buffer and send 'post anim' notification
FinalizeBoneTransform();
if (!bSimulationUpdatesChildTransforms || !IsSimulatingPhysics()) //If we simulate physics the call to MoveComponent already updates the children transforms. If we are confident that animation will not be needed this can be skipped. TODO: this should be handled at the scene component layer
{
//SCOPE_CYCLE_COUNTER(STAT_FinalizeAnimationUpdate_UpdateChildTransforms);
// Update Child Transform - The above function changes bone transform, so will need to update child transform
// But only children attached to us via a socket.
UpdateChildTransforms(EUpdateTransformFlags::OnlyUpdateIfUsingSocket);
}
if (bUpdateOverlapsOnAnimationFinalize)
{
//SCOPE_CYCLE_COUNTER(STAT_FinalizeAnimationUpdate_UpdateOverlaps);
// animation often change overlap.
UpdateOverlaps();
}
// update bounds
// *NOTE* This is a private var, I have to remove it for this temp fix
/*if (bSkipBoundsUpdateWhenInterpolating)
{
if (AnimEvaluationContext.bDoEvaluation)
{
//SCOPE_CYCLE_COUNTER(STAT_FinalizeAnimationUpdate_UpdateBounds);
// Cached local bounds are now out of date
InvalidateCachedBounds();
UpdateBounds();
}
}
else*/
{
//SCOPE_CYCLE_COUNTER(STAT_FinalizeAnimationUpdate_UpdateBounds);
// Cached local bounds are now out of date
InvalidateCachedBounds();
UpdateBounds();
}
// Need to send new bounds to
MarkRenderTransformDirty();
// New bone positions need to be sent to render thread
MarkRenderDynamicDataDirty();
// If we have any Slave Components, they need to be refreshed as well.
RefreshFollowerComponents();
}
void UInversePhysicsSkeletalMeshComponent::GetWeldedBodies(TArray<FBodyInstance*>& OutWeldedBodies, TArray<FName>& OutLabels, bool bIncludingAutoWeld)
{
UPhysicsAsset* PhysicsAsset = GetPhysicsAsset();
for (int32 BodyIdx = 0; BodyIdx < Bodies.Num(); ++BodyIdx)
{
FBodyInstance* BI = Bodies[BodyIdx];
if (BI && (BI->WeldParent != nullptr || (bIncludingAutoWeld && BI->bAutoWeld)))
{
OutWeldedBodies.Add(BI);
if (PhysicsAsset)
{
if (UBodySetup* PhysicsAssetBodySetup = PhysicsAsset->SkeletalBodySetups[BodyIdx])
{
OutLabels.Add(PhysicsAssetBodySetup->BoneName);
}
else
{
OutLabels.Add(NAME_None);
}
}
else
{
OutLabels.Add(NAME_None);
}
}
}
for (USceneComponent* Child : GetAttachChildren())
{
if (UPrimitiveComponent* PrimChild = Cast<UPrimitiveComponent>(Child))
{
PrimChild->GetWeldedBodies(OutWeldedBodies, OutLabels, bIncludingAutoWeld);
}
}
}
FBodyInstance* UInversePhysicsSkeletalMeshComponent::GetBodyInstance(FName BoneName, bool bGetWelded, int32) const
{
UPhysicsAsset* const PhysicsAsset = GetPhysicsAsset();
FBodyInstance* BodyInst = NULL;
if (PhysicsAsset != NULL)
{
// A name of NAME_None indicates 'root body'
if (BoneName == NAME_None)
{
if (Bodies.IsValidIndex(RootBodyData.BodyIndex))
{
BodyInst = Bodies[RootBodyData.BodyIndex];
}
}
// otherwise, look for the body
else
{
int32 BodyIndex = PhysicsAsset->FindBodyIndex(BoneName);
if (Bodies.IsValidIndex(BodyIndex))
{
BodyInst = Bodies[BodyIndex];
}
}
BodyInst = (bGetWelded && BodyInstance.WeldParent) ? BodyInstance.WeldParent : BodyInst;
}
return BodyInst;
}
struct FAssetWorldBoneTM
{
FTransform TM; // Should never contain scaling.
bool bUpToDate; // If this equals PhysAssetUpdateNum, then the matrix is up to date.
};
typedef TArray<FAssetWorldBoneTM, TMemStackAllocator<alignof(FAssetWorldBoneTM)>> TAssetWorldBoneTMArray;
void UpdateWorldBoneTMVR(TAssetWorldBoneTMArray& WorldBoneTMs, const TArray<FTransform>& InBoneSpaceTransforms, int32 BoneIndex, USkeletalMeshComponent* SkelComp, const FTransform& LocalToWorldTM, const FVector& Scale3D)
{
// If its already up to date - do nothing
if (WorldBoneTMs[BoneIndex].bUpToDate)
{
return;
}
FTransform ParentTM, RelTM;
if (BoneIndex == 0)
{
// If this is the root bone, we use the mesh component LocalToWorld as the parent transform.
ParentTM = LocalToWorldTM;
}
else
{
// If not root, use our cached world-space bone transforms.
int32 ParentIndex = SkelComp->GetSkeletalMeshAsset()->GetRefSkeleton().GetParentIndex(BoneIndex);
UpdateWorldBoneTMVR(WorldBoneTMs, InBoneSpaceTransforms, ParentIndex, SkelComp, LocalToWorldTM, Scale3D);
ParentTM = WorldBoneTMs[ParentIndex].TM;
}
if (InBoneSpaceTransforms.IsValidIndex(BoneIndex))
{
RelTM = InBoneSpaceTransforms[BoneIndex];
RelTM.ScaleTranslation(Scale3D);
WorldBoneTMs[BoneIndex].TM = RelTM * ParentTM;
WorldBoneTMs[BoneIndex].bUpToDate = true;
}
}
void UInversePhysicsSkeletalMeshComponent::PerformBlendPhysicsBonesVR(const TArray<FBoneIndexType>& InRequiredBones, TArray<FTransform>& InOutComponentSpaceTransforms, TArray<FTransform>& InOutBoneSpaceTransforms)
{
//SCOPE_CYCLE_COUNTER(STAT_BlendInPhysics);
// Get drawscale from Owner (if there is one)
FVector TotalScale3D = GetComponentTransform().GetScale3D();
FVector RecipScale3D = TotalScale3D.Reciprocal();
UPhysicsAsset* const PhysicsAsset = GetPhysicsAsset();
check(PhysicsAsset);
if (InOutComponentSpaceTransforms.Num() == 0)
{
return;
}
// Get the scene, and do nothing if we can't get one.
FPhysScene* PhysScene = nullptr;
if (GetWorld() != nullptr)
{
PhysScene = GetWorld()->GetPhysicsScene();
}
if (PhysScene == nullptr)
{
return;
}
FMemMark Mark(FMemStack::Get());
// Make sure scratch space is big enough.
TAssetWorldBoneTMArray WorldBoneTMs;
WorldBoneTMs.AddZeroed(InOutComponentSpaceTransforms.Num());
FTransform LocalToWorldTM = GetComponentTransform();
// PhysAnim.cpp - PerformBlendPhysicsBones
// This fixes the simulated inversed scaled skeletal mesh bug
LocalToWorldTM.SetScale3D(LocalToWorldTM.GetScale3D().GetSignVector());
LocalToWorldTM.NormalizeRotation();
// Original implementation stomps negative scale values
//LocalToWorldTM.RemoveScaling();
struct FBodyTMPair
{
FBodyInstance* BI;
FTransform TM;
};
FPhysicsCommand::ExecuteRead(this, [&]()
{
bool bSetParentScale = false;
const bool bSimulatedRootBody = Bodies.IsValidIndex(RootBodyData.BodyIndex) && Bodies[RootBodyData.BodyIndex]->IsInstanceSimulatingPhysics();
const FTransform NewComponentToWorld = bSimulatedRootBody ? GetComponentTransformFromBodyInstance(Bodies[RootBodyData.BodyIndex]) : FTransform::Identity;
// For each bone - see if we need to provide some data for it.
for (int32 i = 0; i < InRequiredBones.Num(); i++)
{
int32 BoneIndex = InRequiredBones[i];
// See if this is a physics bone..
int32 BodyIndex = PhysicsAsset->FindBodyIndex(GetSkeletalMeshAsset()->GetRefSkeleton().GetBoneName(BoneIndex));
// need to update back to physics so that physics knows where it was after blending
FBodyInstance* PhysicsAssetBodyInstance = nullptr;
// If so - get its world space matrix and its parents world space matrix and calc relative atom.
if (BodyIndex != INDEX_NONE)
{
#if !(UE_BUILD_SHIPPING || UE_BUILD_TEST)
// tracking down TTP 280421. Remove this if this doesn't happen.
if (!ensure(Bodies.IsValidIndex(BodyIndex)))
{
UE_LOG(LogPhysics, Warning, TEXT("%s(Mesh %s, PhysicsAsset %s)"),
*GetName(), *GetNameSafe(GetSkeletalMeshAsset()), *GetNameSafe(PhysicsAsset));
UE_LOG(LogPhysics, Warning, TEXT(" - # of BodySetup (%d), # of Bodies (%d), Invalid BodyIndex(%d)"),
PhysicsAsset->SkeletalBodySetups.Num(), Bodies.Num(), BodyIndex);
continue;
}
#endif
PhysicsAssetBodyInstance = Bodies[BodyIndex];
//if simulated body copy back and blend with animation
if (PhysicsAssetBodyInstance->IsInstanceSimulatingPhysics())
{
FTransform PhysTM = PhysicsAssetBodyInstance->GetUnrealWorldTransform_AssumesLocked();
// Store this world-space transform in cache.
WorldBoneTMs[BoneIndex].TM = PhysTM;
WorldBoneTMs[BoneIndex].bUpToDate = true;
float UsePhysWeight = (bBlendPhysics) ? 1.f : PhysicsAssetBodyInstance->PhysicsBlendWeight;
// if the body instance is disabled, then we want to use the animation transform and ignore the physics one
if (PhysicsAssetBodyInstance->IsPhysicsDisabled())
{
UsePhysWeight = 0.0f;
}
// Find this bones parent matrix.
FTransform ParentWorldTM;
// if we wan't 'full weight' we just find
if (UsePhysWeight > 0.f)
{
if (!(ensure(InOutBoneSpaceTransforms.Num())))
{
continue;
}
if (BoneIndex == 0)
{
ParentWorldTM = LocalToWorldTM;
}
else
{
// If not root, get parent TM from cache (making sure its up-to-date).
int32 ParentIndex = GetSkeletalMeshAsset()->GetRefSkeleton().GetParentIndex(BoneIndex);
UpdateWorldBoneTMVR(WorldBoneTMs, InOutBoneSpaceTransforms, ParentIndex, this, LocalToWorldTM, TotalScale3D);
ParentWorldTM = WorldBoneTMs[ParentIndex].TM;
}
// Then calc rel TM and convert to atom.
FTransform RelTM = PhysTM.GetRelativeTransform(ParentWorldTM);
RelTM.RemoveScaling();
FQuat RelRot(RelTM.GetRotation());
FVector RelPos = RecipScale3D * RelTM.GetLocation();
FTransform PhysAtom = FTransform(RelRot, RelPos, InOutBoneSpaceTransforms[BoneIndex].GetScale3D());
// Now blend in this atom. See if we are forcing this bone to always be blended in
InOutBoneSpaceTransforms[BoneIndex].Blend(InOutBoneSpaceTransforms[BoneIndex], PhysAtom, UsePhysWeight);
if (!bSetParentScale)
{
//We must update RecipScale3D based on the atom scale of the root
TotalScale3D *= InOutBoneSpaceTransforms[0].GetScale3D();
RecipScale3D = TotalScale3D.Reciprocal();
bSetParentScale = true;
}
}
}
}
if (!(ensure(BoneIndex < InOutComponentSpaceTransforms.Num())))
{
continue;
}
// Update SpaceBases entry for this bone now
if (BoneIndex == 0)
{
if (!(ensure(InOutBoneSpaceTransforms.Num())))
{
continue;
}
InOutComponentSpaceTransforms[0] = InOutBoneSpaceTransforms[0];
}
else
{
if (bLocalSpaceKinematics || BodyIndex == INDEX_NONE || Bodies[BodyIndex]->IsInstanceSimulatingPhysics())
{
if (!(ensure(BoneIndex < InOutBoneSpaceTransforms.Num())))
{
continue;
}
const int32 ParentIndex = GetSkeletalMeshAsset()->GetRefSkeleton().GetParentIndex(BoneIndex);
InOutComponentSpaceTransforms[BoneIndex] = InOutBoneSpaceTransforms[BoneIndex] * InOutComponentSpaceTransforms[ParentIndex];
/**
* Normalize rotations.
* We want to remove any loss of precision due to accumulation of error.
* i.e. A componentSpace transform is the accumulation of all of its local space parents. The further down the chain, the greater the error.
* SpaceBases are used by external systems, we feed this to Physics, send this to gameplay through bone and socket queries, etc.
* So this is a good place to make sure all transforms are normalized.
*/
InOutComponentSpaceTransforms[BoneIndex].NormalizeRotation();
}
else if (bSimulatedRootBody)
{
InOutComponentSpaceTransforms[BoneIndex] = Bodies[BodyIndex]->GetUnrealWorldTransform_AssumesLocked().GetRelativeTransform(NewComponentToWorld);
}
}
}
}); //end scope for read lock
}
void UInversePhysicsSkeletalMeshComponent::RegisterEndPhysicsTick(bool bRegister)
{
// For testing if the engine fix is live yet or not
//return Super::RegisterEndPhysicsTick(bRegister);
if (bRegister != EndPhysicsTickFunctionVR.IsTickFunctionRegistered())
{
if (bRegister)
{
if (SetupActorComponentTickFunction(&EndPhysicsTickFunctionVR))
{
EndPhysicsTickFunctionVR.TargetVR = this;
// Make sure our EndPhysicsTick gets called after physics simulation is finished
UWorld* World = GetWorld();
if (World != nullptr)
{
EndPhysicsTickFunctionVR.AddPrerequisite(World, World->EndPhysicsTickFunction);
}
}
}
else
{
EndPhysicsTickFunctionVR.UnRegisterTickFunction();
}
}
}
void UInversePhysicsSkeletalMeshComponent::TickComponent(float DeltaTime, enum ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction)
{
//CSV_SCOPED_TIMING_STAT_EXCLUSIVE(Animation);
bool bShouldRunPhysTick = (bEnablePhysicsOnDedicatedServer || !IsNetMode(NM_DedicatedServer)) && // Early out if we are on a dedicated server and not running physics.
((IsSimulatingPhysics() && RigidBodyIsAwake()) || ShouldBlendPhysicsBones());
RegisterEndPhysicsTick(PrimaryComponentTick.IsTickFunctionRegistered() && bShouldRunPhysTick);
//UpdateEndPhysicsTickRegisteredState();
RegisterClothTick(PrimaryComponentTick.IsTickFunctionRegistered() && ShouldRunClothTick());
//UpdateClothTickRegisteredState();
// If we are suspended, we will not simulate clothing, but as clothing is simulated in local space
// relative to a root bone we need to extract simulation positions as this bone could be animated.
/*if (bClothingSimulationSuspended && this->GetClothingSimulation() && this->GetClothingSimulation()->ShouldSimulate())
{
//CSV_SCOPED_TIMING_STAT(Animation, Cloth);
this->GetClothingSimulation()->GetSimulationData(CurrentSimulationData, this, Cast<USkeletalMeshComponent>(MasterPoseComponent.Get()));
}*/
Super::Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
PendingRadialForces.Reset();
// Update bOldForceRefPose
bOldForceRefPose = bForceRefpose;
static const auto CVarAnimationDelaysEndGroup = IConsoleManager::Get().FindTConsoleVariableDataInt(TEXT("tick.AnimationDelaysEndGroup"));
/** Update the end group and tick priority */
const bool bDoLateEnd = CVarAnimationDelaysEndGroup->GetValueOnGameThread() > 0;
const bool bRequiresPhysics = EndPhysicsTickFunctionVR.IsTickFunctionRegistered();
const ETickingGroup EndTickGroup = bDoLateEnd && !bRequiresPhysics ? TG_PostPhysics : TG_PrePhysics;
if (ThisTickFunction)
{
ThisTickFunction->EndTickGroup = TG_PostPhysics;// EndTickGroup;
// Note that if animation is so long that we are blocked in EndPhysics we may want to reduce the priority. However, there is a risk that this function will not go wide early enough.
// This requires profiling and is very game dependent so cvar for now makes sense
static const auto CVarHiPriSkinnedMeshesTicks = IConsoleManager::Get().FindTConsoleVariableDataInt(TEXT("tick.HiPriSkinnedMeshes"));
bool bDoHiPri = CVarHiPriSkinnedMeshesTicks->GetValueOnGameThread() > 0;
if (ThisTickFunction->bHighPriority != bDoHiPri)
{
ThisTickFunction->SetPriorityIncludingPrerequisites(bDoHiPri);
}
}
// If we are waiting for ParallelEval to complete or if we require Physics,
// then FinalizeBoneTransform will be called and Anim events will be dispatched there.
// We prefer doing it there so these events are triggered once we have a new updated pose.
// Note that it's possible that FinalizeBoneTransform has already been called here if not using ParallelUpdate.
// or it's possible that it hasn't been called at all if we're skipping Evaluate due to not being visible.
// ConditionallyDispatchQueuedAnimEvents will catch that and only Dispatch events if not already done.
if (!IsRunningParallelEvaluation() && !bRequiresPhysics)
{
/////////////////////////////////////////////////////////////////////////////
// Notify / Event Handling!
// This can do anything to our component (including destroy it)
// Any code added after this point needs to take that into account
/////////////////////////////////////////////////////////////////////////////
ConditionallyDispatchQueuedAnimEvents();
}
}
void UInversePhysicsSkeletalMeshComponent::GetLifetimeReplicatedProps(TArray< class FLifetimeProperty > & OutLifetimeProps) const
{
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
DOREPLIFETIME(UInversePhysicsSkeletalMeshComponent, bReplicateMovement);
}
AOptionalRepGrippableSkeletalMeshActor::AOptionalRepGrippableSkeletalMeshActor(const FObjectInitializer& ObjectInitializer) :
Super(ObjectInitializer.SetDefaultSubobjectClass<UInversePhysicsSkeletalMeshComponent>(TEXT("SkeletalMeshComponent0")))
{
bIgnoreAttachmentReplication = false;
bIgnorePhysicsReplication = false;
}
void AOptionalRepGrippableSkeletalMeshActor::GetLifetimeReplicatedProps(TArray< class FLifetimeProperty >& OutLifetimeProps) const
{
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
DOREPLIFETIME(AOptionalRepGrippableSkeletalMeshActor, bIgnoreAttachmentReplication);
DOREPLIFETIME(AOptionalRepGrippableSkeletalMeshActor, bIgnorePhysicsReplication);
if (bIgnoreAttachmentReplication)
{
RESET_REPLIFETIME_CONDITION_PRIVATE_PROPERTY(AActor, AttachmentReplication, COND_InitialOnly);
}
//DISABLE_REPLICATED_PRIVATE_PROPERTY(AActor, AttachmentReplication);
}
void AOptionalRepGrippableSkeletalMeshActor::OnRep_ReplicateMovement()
{
if (bIgnorePhysicsReplication)
{
return;
}
Super::OnRep_ReplicateMovement();
}
void AOptionalRepGrippableSkeletalMeshActor::PostNetReceivePhysicState()
{
if (bIgnorePhysicsReplication)
{
return;
}
Super::PostNetReceivePhysicState();
}

View file

@ -0,0 +1,383 @@
#include "Misc/VREPhysicalAnimationComponent.h"
#include UE_INLINE_GENERATED_CPP_BY_NAME(VREPhysicalAnimationComponent)
#include "SceneManagement.h"
#include "Components/SkeletalMeshComponent.h"
#include "PhysicsEngine/PhysicsAsset.h"
#include "PhysicsEngine/ConstraintInstance.h"
#include "ReferenceSkeleton.h"
#include "DrawDebugHelpers.h"
#if ENABLE_DRAW_DEBUG
#include "Chaos/ImplicitObject.h"
#include "Chaos/TriangleMeshImplicitObject.h"
#endif
#include "Physics/PhysicsInterfaceCore.h"
#include "Physics/PhysicsInterfaceTypes.h"
UVREPhysicalAnimationComponent::UVREPhysicalAnimationComponent(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{
bAutoSetPhysicsSleepSensitivity = true;
SleepThresholdMultiplier = 0.0f;
}
/*void UVREPhysicalAnimationComponent::CustomPhysics(float DeltaTime, FBodyInstance* BodyInstance)
{
//UpdateWeldedBoneDriver(DeltaTime);
}*/
/*void UVREPhysicalAnimationComponent::OnWeldedMassUpdated(FBodyInstance* BodyInstance)
{
// If our mass changed then our body was altered, lets re-init
SetupWeldedBoneDriver(true);
}*/
void UVREPhysicalAnimationComponent::SetWeldedBoneDriverPaused(bool bPaused)
{
bIsPaused = bPaused;
}
bool UVREPhysicalAnimationComponent::IsWeldedBoneDriverPaused()
{
return bIsPaused;
}
void UVREPhysicalAnimationComponent::RefreshWeldedBoneDriver()
{
SetupWeldedBoneDriver_Implementation(true);
}
void UVREPhysicalAnimationComponent::SetupWeldedBoneDriver(TArray<FName> BaseBoneNames)
{
if (BaseBoneNames.Num())
BaseWeldedBoneDriverNames = BaseBoneNames;
SetupWeldedBoneDriver_Implementation(false);
}
FTransform UVREPhysicalAnimationComponent::GetWorldSpaceRefBoneTransform(FReferenceSkeleton& RefSkel, int32 BoneIndex, int32 ParentBoneIndex)
{
FTransform BoneTransform;
if (BoneIndex > 0 && BoneIndex != ParentBoneIndex)
{
BoneTransform = RefSkel.GetRefBonePose()[BoneIndex];
FMeshBoneInfo BoneInfo = RefSkel.GetRefBoneInfo()[BoneIndex];
if (BoneInfo.ParentIndex != 0 && BoneInfo.ParentIndex != ParentBoneIndex)
{
BoneTransform *= GetWorldSpaceRefBoneTransform(RefSkel, BoneInfo.ParentIndex, ParentBoneIndex);
}
}
return BoneTransform;
}
// #TODO: support off scaling
FTransform UVREPhysicalAnimationComponent::GetRefPoseBoneRelativeTransform(USkeletalMeshComponent* SkeleMesh, FName BoneName, FName ParentBoneName)
{
FTransform BoneTransform;
if (SkeleMesh && !BoneName.IsNone() && !ParentBoneName.IsNone())
{
//SkelMesh->ClearRefPoseOverride();
FReferenceSkeleton RefSkel;
RefSkel = SkeleMesh->GetSkinnedAsset()->GetRefSkeleton();
BoneTransform = GetWorldSpaceRefBoneTransform(RefSkel, RefSkel.FindBoneIndex(BoneName), RefSkel.FindBoneIndex(ParentBoneName));
}
return BoneTransform;
}
void UVREPhysicalAnimationComponent::SetupWeldedBoneDriver_Implementation(bool bReInit)
{
TArray<FWeldedBoneDriverData> OriginalData;
if (bReInit)
{
OriginalData = BoneDriverMap;
}
BoneDriverMap.Empty();
USkeletalMeshComponent* SkeleMesh = GetSkeletalMesh();
if (!SkeleMesh || !SkeleMesh->Bodies.Num())
return;
// Get ref pose position and walk up the tree to the welded root to get our relative base pose.
//SkeleMesh->GetRefPosePosition()
UPhysicsAsset* PhysAsset = SkeleMesh ? SkeleMesh->GetPhysicsAsset() : nullptr;
if (PhysAsset && SkeleMesh->GetSkinnedAsset())
{
for (FName BaseWeldedBoneDriverName : BaseWeldedBoneDriverNames)
{
int32 ParentBodyIdx = PhysAsset->FindBodyIndex(BaseWeldedBoneDriverName);
if (FBodyInstance* ParentBody = (ParentBodyIdx == INDEX_NONE ? nullptr : SkeleMesh->Bodies[ParentBodyIdx]))
{
// Build map of bodies that we want to control.
FPhysicsActorHandle& ActorHandle = ParentBody->WeldParent ? ParentBody->WeldParent->GetPhysicsActorHandle() : ParentBody->GetPhysicsActorHandle();
if (FPhysicsInterface::IsValid(ActorHandle) /*&& FPhysicsInterface::IsRigidBody(ActorHandle)*/)
{
FPhysicsCommand::ExecuteWrite(ActorHandle, [&](FPhysicsActorHandle& Actor)
{
//TArray<FPhysicsShapeHandle> Shapes;
PhysicsInterfaceTypes::FInlineShapeArray Shapes;
FPhysicsInterface::GetAllShapes_AssumedLocked(Actor, Shapes);
for (FPhysicsShapeHandle& Shape : Shapes)
{
if (ParentBody->WeldParent)
{
const FBodyInstance* OriginalBI = ParentBody->WeldParent->GetOriginalBodyInstance(Shape);
if (OriginalBI != ParentBody)
{
// Not originally our shape
continue;
}
}
FKShapeElem* ShapeElem = FChaosUserData::Get<FKShapeElem>(FPhysicsInterface::GetUserData(Shape));
if (ShapeElem)
{
FName TargetBoneName = ShapeElem->GetName();
int32 BoneIdx = SkeleMesh->GetBoneIndex(TargetBoneName);
if (BoneIdx != INDEX_NONE)
{
FWeldedBoneDriverData DriverData;
DriverData.BoneName = TargetBoneName;
//DriverData.ShapeHandle = Shape;
if (bReInit && OriginalData.Num() - 1 >= BoneDriverMap.Num())
{
DriverData.RelativeTransform = OriginalData[BoneDriverMap.Num()].RelativeTransform;
}
else
{
FTransform BoneTransform = FTransform::Identity;
if (SkeleMesh->GetBoneIndex(TargetBoneName) != INDEX_NONE)
BoneTransform = GetRefPoseBoneRelativeTransform(SkeleMesh, TargetBoneName, BaseWeldedBoneDriverName).Inverse();
//FTransform BoneTransform = SkeleMesh->GetSocketTransform(TargetBoneName, ERelativeTransformSpace::RTS_World);
// Calc shape global pose
//FTransform RelativeTM = FPhysicsInterface::GetLocalTransform(Shape) * FPhysicsInterface::GetGlobalPose_AssumesLocked(ActorHandle);
//RelativeTM = RelativeTM * BoneTransform.Inverse();
//BoneTransform.SetScale3D(FVector(1.0f));
DriverData.RelativeTransform = FPhysicsInterface::GetLocalTransform(Shape) * BoneTransform;
}
BoneDriverMap.Add(DriverData);
}
}
}
if (bAutoSetPhysicsSleepSensitivity && !ParentBody->WeldParent && BoneDriverMap.Num() > 0)
{
ParentBody->SleepFamily = ESleepFamily::Custom;
ParentBody->CustomSleepThresholdMultiplier = SleepThresholdMultiplier;
float SleepEnergyThresh = FPhysicsInterface::GetSleepEnergyThreshold_AssumesLocked(Actor);
SleepEnergyThresh *= ParentBody->GetSleepThresholdMultiplier();
FPhysicsInterface::SetSleepEnergyThreshold_AssumesLocked(Actor, SleepEnergyThresh);
}
});
}
}
}
}
}
void UVREPhysicalAnimationComponent::TickComponent(float DeltaTime, enum ELevelTick TickType, FActorComponentTickFunction *ThisTickFunction)
{
// Make sure base physical animation component runs its logic
Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
UpdateWeldedBoneDriver(DeltaTime);
}
void UVREPhysicalAnimationComponent::UpdateWeldedBoneDriver(float DeltaTime)
{
if (!BoneDriverMap.Num())
return;
USkeletalMeshComponent* SkeleMesh = GetSkeletalMesh();
if (!SkeleMesh || !SkeleMesh->Bodies.Num())// || (!SkeleMesh->IsSimulatingPhysics(BaseWeldedBoneDriverNames) && !SkeleMesh->IsWelded()))
return;
UPhysicsAsset* PhysAsset = SkeleMesh ? SkeleMesh->GetPhysicsAsset() : nullptr;
if(PhysAsset && SkeleMesh->GetSkinnedAsset())
{
for (FName BaseWeldedBoneDriverName : BaseWeldedBoneDriverNames)
{
int32 ParentBodyIdx = PhysAsset->FindBodyIndex(BaseWeldedBoneDriverName);
if (FBodyInstance* ParentBody = (ParentBodyIdx == INDEX_NONE ? nullptr : SkeleMesh->Bodies[ParentBodyIdx]))
{
// Allow it to run even when not simulating physics, if we have a welded root then it needs to animate anyway
//if (!ParentBody->IsInstanceSimulatingPhysics() && !ParentBody->WeldParent)
// return;
FPhysicsActorHandle& ActorHandle = ParentBody->WeldParent ? ParentBody->WeldParent->GetPhysicsActorHandle() : ParentBody->GetPhysicsActorHandle();
if (FPhysicsInterface::IsValid(ActorHandle) /*&& FPhysicsInterface::IsRigidBody(ActorHandle)*/)
{
#if ENABLE_DRAW_DEBUG
if (bDebugDrawCollision)
{
Chaos::FDebugDrawQueue::GetInstance().SetConsumerActive(this, true); // Need to deactivate this later as well
Chaos::FDebugDrawQueue::GetInstance().SetMaxCost(20000);
//Chaos::FDebugDrawQueue::GetInstance().SetRegionOfInterest(SkeleMesh->GetComponentLocation(), 1000.0f);
Chaos::FDebugDrawQueue::GetInstance().SetEnabled(true);
}
#endif
bool bModifiedBody = false;
FPhysicsCommand::ExecuteWrite(ActorHandle, [&](FPhysicsActorHandle& Actor)
{
PhysicsInterfaceTypes::FInlineShapeArray Shapes;
FPhysicsInterface::GetAllShapes_AssumedLocked(Actor, Shapes);
FTransform GlobalPose = FPhysicsInterface::GetGlobalPose_AssumesLocked(ActorHandle);
FTransform GlobalPoseInv = GlobalPose.Inverse();
#if ENABLE_DRAW_DEBUG
if (bDebugDrawCollision)
{
Chaos::FDebugDrawQueue::GetInstance().SetRegionOfInterest(GlobalPose.GetLocation(), 100.0f);
}
#endif
for (FPhysicsShapeHandle& Shape : Shapes)
{
if (ParentBody->WeldParent)
{
const FBodyInstance* OriginalBI = ParentBody->WeldParent->GetOriginalBodyInstance(Shape);
if (OriginalBI != ParentBody)
{
// Not originally our shape
continue;
}
}
// Log the shapes name to match to the bone
FName TargetBoneName = NAME_None;
if (FKShapeElem* ShapeElem = FChaosUserData::Get<FKShapeElem>(FPhysicsInterface::GetUserData(Shape)))
{
TargetBoneName = ShapeElem->GetName();
}
else
{
// Cant find the matching shape
continue;
}
if (FWeldedBoneDriverData* WeldedData = BoneDriverMap.FindByKey(TargetBoneName/*Shape*/))
{
bModifiedBody = true;
FTransform Trans = SkeleMesh->GetSocketTransform(WeldedData->BoneName, ERelativeTransformSpace::RTS_World);
// This fixes a bug with simulating inverse scaled meshes
//Trans.SetScale3D(FVector(1.f) * Trans.GetScale3D().GetSignVector());
FTransform GlobalTransform = WeldedData->RelativeTransform * Trans;
FTransform RelativeTM = GlobalTransform * GlobalPoseInv;
if (!WeldedData->LastLocal.Equals(RelativeTM))
{
FPhysicsInterface::SetLocalTransform(Shape, RelativeTM);
WeldedData->LastLocal = RelativeTM;
}
}
#if ENABLE_DRAW_DEBUG
if (bDebugDrawCollision)
{
const Chaos::FImplicitObject* ShapeImplicit = Shape.Shape->GetGeometry().Get();
Chaos::EImplicitObjectType Type = ShapeImplicit->GetType();
FTransform shapeTransform = FPhysicsInterface::GetLocalTransform(Shape);
FTransform FinalTransform = shapeTransform * GlobalPose;
Chaos::FRigidTransform3 RigTransform(FinalTransform);
Chaos::DebugDraw::DrawShape(RigTransform, ShapeImplicit, Chaos::FShapeOrShapesArray(), FColor::White);
}
#endif
}
});
#if ENABLE_DRAW_DEBUG
if (bDebugDrawCollision)
{
// Get the latest commands
TArray<Chaos::FLatentDrawCommand> DrawCommands;
Chaos::FDebugDrawQueue::GetInstance().ExtractAllElements(DrawCommands);
if (DrawCommands.Num())
{
DebugDrawMesh(DrawCommands);
}
Chaos::FDebugDrawQueue::GetInstance().SetConsumerActive(this, false); // Need to deactivate this later as well
Chaos::FDebugDrawQueue::GetInstance().SetEnabled(false);
}
#endif
}
}
}
}
}
#if ENABLE_DRAW_DEBUG
void UVREPhysicalAnimationComponent::DebugDrawMesh(const TArray<Chaos::FLatentDrawCommand>& DrawCommands)
{
UWorld* World = this->GetWorld();
// Draw all the captured elements in the viewport
for (const Chaos::FLatentDrawCommand& Command : DrawCommands)
{
switch (Command.Type)
{
case Chaos::FLatentDrawCommand::EDrawType::Point:
DrawDebugPoint(World, Command.LineStart, Command.Thickness, Command.Color, Command.bPersistentLines, -1.f, Command.DepthPriority);
break;
case Chaos::FLatentDrawCommand::EDrawType::Line:
DrawDebugLine(World, Command.LineStart, Command.LineEnd, Command.Color, Command.bPersistentLines, -1.0f, 0.f, 0.f);
break;
case Chaos::FLatentDrawCommand::EDrawType::DirectionalArrow:
DrawDebugDirectionalArrow(World, Command.LineStart, Command.LineEnd, Command.ArrowSize, Command.Color, Command.bPersistentLines, -1.f, Command.DepthPriority, Command.Thickness);
break;
case Chaos::FLatentDrawCommand::EDrawType::Sphere:
DrawDebugSphere(World, Command.LineStart, Command.Radius, Command.Segments, Command.Color, Command.bPersistentLines, -1.f, Command.DepthPriority, Command.Thickness);
break;
case Chaos::FLatentDrawCommand::EDrawType::Box:
DrawDebugBox(World, Command.Center, Command.Extent, Command.Rotation, Command.Color, Command.bPersistentLines, -1.f, Command.DepthPriority, Command.Thickness);
break;
case Chaos::FLatentDrawCommand::EDrawType::String:
DrawDebugString(World, Command.TextLocation, Command.Text, Command.TestBaseActor, Command.Color, -1.f, Command.bDrawShadow, Command.FontScale);
break;
case Chaos::FLatentDrawCommand::EDrawType::Circle:
{
FMatrix M = FRotationMatrix::MakeFromYZ(Command.YAxis, Command.ZAxis);
M.SetOrigin(Command.Center);
DrawDebugCircle(World, M, Command.Radius, Command.Segments, Command.Color, Command.bPersistentLines, -1.f, Command.DepthPriority, Command.Thickness, Command.bDrawAxis);
break;
}
case Chaos::FLatentDrawCommand::EDrawType::Capsule:
DrawDebugCapsule(World, Command.Center, Command.HalfHeight, Command.Radius, Command.Rotation, Command.Color, Command.bPersistentLines, -1.f, Command.DepthPriority, Command.Thickness);
default:
break;
}
}
}
#endif

View file

@ -0,0 +1,201 @@
// Copyright 1998-2016 Epic Games, Inc. All Rights Reserved.
#include "Misc/VRLogComponent.h"
#include UE_INLINE_GENERATED_CPP_BY_NAME(VRLogComponent)
//#include "Engine/Engine.h"
/* Top of File */
#define LOCTEXT_NAMESPACE "VRLogComponent"
//=============================================================================
UVRLogComponent::UVRLogComponent(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{
PrimaryComponentTick.bCanEverTick = false;
MaxLineLength = 130;
MaxStoredMessages = 10000;
}
//=============================================================================
UVRLogComponent::~UVRLogComponent()
{
}
void UVRLogComponent::SetConsoleText(FString Text)
{
UConsole* ViewportConsole = (GEngine->GameViewport != nullptr) ? GEngine->GameViewport->ViewportConsole : nullptr;
if (!ViewportConsole)
return;
// Using append because UpdatePrecompletedInputLine is private and append calls it
ViewportConsole->SetInputText("");
ViewportConsole->AppendInputText(Text);
}
void UVRLogComponent::SendKeyEventToConsole(FKey Key, EInputEvent KeyEvent)
{
UConsole* ViewportConsole = (GEngine->GameViewport != nullptr) ? GEngine->GameViewport->ViewportConsole : nullptr;
if (!ViewportConsole)
return;
ViewportConsole->FakeGotoState(FName(TEXT("Typing")));
ViewportConsole->InputKey(IPlatformInputDeviceMapper::Get().GetDefaultInputDevice(), Key, KeyEvent);
ViewportConsole->FakeGotoState(NAME_None);
}
void UVRLogComponent::AppendTextToConsole(FString Text, bool bReturnAtEnd)
{
UConsole* ViewportConsole = (GEngine->GameViewport != nullptr) ? GEngine->GameViewport->ViewportConsole : nullptr;
if (!ViewportConsole)
return;
ViewportConsole->AppendInputText(Text);
if (bReturnAtEnd)
{
ViewportConsole->FakeGotoState(FName(TEXT("Typing")));
ViewportConsole->InputKey(IPlatformInputDeviceMapper::Get().GetDefaultInputDevice(), EKeys::Enter, EInputEvent::IE_Released);
ViewportConsole->FakeGotoState(NAME_None);
}
}
bool UVRLogComponent::DrawConsoleToRenderTarget2D(EBPVRConsoleDrawType DrawType, UTextureRenderTarget2D * Texture, float ScrollOffset, bool bForceDraw)
{
if (!bForceDraw && DrawType == EBPVRConsoleDrawType::VRConsole_Draw_OutputLogOnly && !OutputLogHistory.bIsDirty)
{
return false;
}
//LastRenderedOutputLogSize
// check(WorldContextObject);
UWorld* World = GetWorld();//GEngine->GetWorldFromContextObject(WorldContextObject, false);
if (!World)
return false;
// Create or find the canvas object to use to render onto the texture. Multiple canvas render target textures can share the same canvas.
UCanvas* Canvas = World->GetCanvasForRenderingToTarget();
if (!Canvas)
return false;
// Create the FCanvas which does the actual rendering.
//const ERHIFeatureLevel::Type FeatureLevel = World != nullptr ? World->FeatureLevel : GMaxRHIFeatureLevel;
FCanvas * RenderCanvas = new FCanvas(
Texture->GameThread_GetRenderTargetResource(),
nullptr,
World,
World->GetFeatureLevel(),
// Draw immediately so that interleaved SetVectorParameter (etc) function calls work as expected
FCanvas::CDM_ImmediateDrawing);
Canvas->Init(Texture->GetSurfaceWidth(), Texture->GetSurfaceHeight(), nullptr, RenderCanvas);
Canvas->Update();
switch (DrawType)
{
//case EBPVRConsoleDrawType::VRConsole_Draw_ConsoleAndOutputLog: DrawConsole(true, Canvas); DrawOutputLog(true, Canvas); break;
case EBPVRConsoleDrawType::VRConsole_Draw_ConsoleOnly: DrawConsole(false, Canvas); break;
case EBPVRConsoleDrawType::VRConsole_Draw_OutputLogOnly: DrawOutputLog(false, Canvas, ScrollOffset); break;
default: break;
}
// Clean up and flush the rendering canvas.
Canvas->Canvas = nullptr;
RenderCanvas->Flush_GameThread();
delete RenderCanvas;
RenderCanvas = nullptr;
// It renders without this, is it actually required?
// Enqueue the rendering command to copy the freshly rendering texture resource back to the render target RHI
// so that the texture is updated and available for rendering.
/*ENQUEUE_UNIQUE_RENDER_COMMAND_ONEPARAMETER
(
CanvasRenderTargetResolveCommand,
FTextureRenderTargetResource*,
RenderTargetResource,
Texture->GameThread_GetRenderTargetResource(),
{
RHICmdList.CopyToResolveTarget(RenderTargetResource->GetRenderTargetTexture(), RenderTargetResource->TextureRHI, true, FResolveParams());
}
);*/
return true;
}
void UVRLogComponent::DrawConsole(bool bLowerHalf, UCanvas* Canvas)
{
UConsole* ViewportConsole = (GEngine->GameViewport != nullptr) ? GEngine->GameViewport->ViewportConsole : nullptr;
if (!ViewportConsole)
return;
ViewportConsole->PostRender_Console_Open(Canvas);
}
void UVRLogComponent::DrawOutputLog(bool bUpperHalf, UCanvas* Canvas, float ScrollOffset)
{
UFont* Font = GEngine->GetSmallFont();// GEngine->GetTinyFont();//GEngine->GetSmallFont();
// determine the height of the text
float xl, yl;
Canvas->StrLen(Font, TEXT("M"), xl, yl);
float Height = FMath::FloorToFloat(Canvas->ClipY);// *0.75f);
// Background
FLinearColor BackgroundColor = FColor::Black.ReinterpretAsLinear();
BackgroundColor.A = 1.0f;
FCanvasTileItem ConsoleTile(FVector2D(0, 0.0f), GBlackTexture, FVector2D(Canvas->ClipX, Canvas->ClipY), FVector2D(0.0f, 0.0f), FVector2D(1.0f, 1.0f), BackgroundColor);
// Preserve alpha to allow single-pass composite
ConsoleTile.BlendMode = SE_BLEND_AlphaBlend;
Canvas->DrawItem(ConsoleTile);
FCanvasTextItem ConsoleText(FVector2D(0, 0 + Height - 5 - yl), FText::FromString(TEXT("")), Font, FColor::Emerald);
const TArray< TSharedPtr<FVRLogMessage> > LoggedMessages = OutputLogHistory.GetMessages();
int32 ScrollPos = 0;
if(ScrollOffset > 0 && LoggedMessages.Num() > 1)
ScrollPos = FMath::Clamp(FMath::RoundToInt(LoggedMessages.Num() * ScrollOffset ) , 0, LoggedMessages.Num() - 1);
float Xpos = 0.0f;
float Ypos = 0.0f;
for (int i = LoggedMessages.Num() - (1 + ScrollPos); i >= 0 && Ypos <= Height - yl; i--)//auto &Message : LoggedMessages)
{
switch (LoggedMessages[i]->Verbosity)
{
case ELogVerbosity::Error:
case ELogVerbosity::Fatal: ConsoleText.SetColor(FLinearColor(0.7f,0.1f,0.1f)); break;
case ELogVerbosity::Warning: ConsoleText.SetColor(FLinearColor(0.5f,0.5f,0.0f)); break;
case ELogVerbosity::Log:
default: ConsoleText.SetColor(FLinearColor(0.8f,0.8f,0.8f));
}
Ypos += yl;
ConsoleText.Text = FText::Format(NSLOCTEXT("VRLogComponent", "ConsoleFormat", "{0}"), FText::FromString(*LoggedMessages[i]->Message));
Canvas->DrawItem(ConsoleText, 0, Height - Ypos);
}
OutputLogHistory.bIsDirty = false;
}
#undef LOCTEXT_NAMESPACE
/* Bottom of File */

View file

@ -0,0 +1,124 @@
// Copyright 1998-2016 Epic Games, Inc. All Rights Reserved.
#include "Misc/VRPlayerStart.h"
#include UE_INLINE_GENERATED_CPP_BY_NAME(VRPlayerStart)
#include "Components/CapsuleComponent.h"
#include "Components/BillboardComponent.h"
AVRPlayerStart::AVRPlayerStart(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{
VRRootComp = CreateDefaultSubobject<USceneComponent>(TEXT("VRRootComp"));
VRRootComp->Mobility = EComponentMobility::Static;
RootComponent = VRRootComp;
UCapsuleComponent * CapsuleComp = GetCapsuleComponent();
if (CapsuleComp && VRRootComp)
{
CapsuleComp->SetupAttachment(VRRootComp);
CapsuleComp->SetRelativeLocation(FVector(0.f,0.f,CapsuleComp->GetScaledCapsuleHalfHeight()));
}
}
void AVRPlayerStart::GetSimpleCollisionCylinder(float& CollisionRadius, float& CollisionHalfHeight) const
{
UCapsuleComponent * CapsuleComp = GetCapsuleComponent();
if (CapsuleComp != nullptr && CapsuleComp->IsRegistered() && CapsuleComp->IsCollisionEnabled())
{
// Note: assuming vertical orientation
CapsuleComp->GetScaledCapsuleSize(CollisionRadius, CollisionHalfHeight);
}
else
{
Super::GetSimpleCollisionCylinder(CollisionRadius, CollisionHalfHeight);
}
}
void AVRPlayerStart::FindBase()
{
if (GetWorld()->HasBegunPlay())
{
return;
}
if (ShouldBeBased())
{
UCapsuleComponent * CapsuleComp = GetCapsuleComponent();
if (!CapsuleComp)
return;
// not using find base, because don't want to fail if LD has navigationpoint slightly interpenetrating floor
FHitResult Hit(1.f);
const float Radius = CapsuleComp->GetScaledCapsuleRadius();
FVector const CollisionSlice(Radius, Radius, 1.f);
// check for placement
float ScaledHalfHeight = CapsuleComp->GetScaledCapsuleHalfHeight();
const FVector TraceStart = GetActorLocation() + FVector(0.f, 0.f, ScaledHalfHeight);
const FVector TraceEnd = GetActorLocation() - FVector(0.f, 0.f, 2.f * ScaledHalfHeight);
GetWorld()->SweepSingleByObjectType(Hit, TraceStart, TraceEnd, FQuat::Identity, FCollisionObjectQueryParams(ECC_WorldStatic), FCollisionShape::MakeBox(CollisionSlice), FCollisionQueryParams(SCENE_QUERY_STAT(NavFindBase), false));
// @fixme, ensure object is on the navmesh?
// if( Hit.Actor != NULL )
// {
// if (Hit.Normal.Z > Scout->WalkableFloorZ)
// {
// const FVector HitLocation = TraceStart + (TraceEnd - TraceStart) * Hit.Time;
// TeleportTo(HitLocation + FVector(0.f,0.f,CapsuleComponent->GetScaledCapsuleHalfHeight()-2.f), GetActorRotation(), false, true);
// }
// else
// {
// Hit.Actor = NULL;
// }
// }
if (GetGoodSprite())
{
GetGoodSprite()->SetVisibility(true);
}
if (GetBadSprite())
{
GetBadSprite()->SetVisibility(false);
}
}
}
void AVRPlayerStart::Validate()
{
if (ShouldBeBased() && (GetGoodSprite() || GetBadSprite()))
{
UCapsuleComponent * CapsuleComp = GetCapsuleComponent();
if (!CapsuleComp)
return;
FVector OrigLocation = GetActorLocation();
const float Radius = CapsuleComp->GetScaledCapsuleRadius();
FVector const Slice(Radius, Radius, 1.f);
bool bResult = true;
// Check for adjustment
FHitResult Hit(ForceInit);
float ScaledHalfHeight = CapsuleComp->GetScaledCapsuleHalfHeight();
const FVector TraceStart = GetActorLocation() + FVector(0.f, 0.f, ScaledHalfHeight);
const FVector TraceEnd = GetActorLocation() - FVector(0.f, 0.f, 4.f * ScaledHalfHeight);
GetWorld()->SweepSingleByChannel(Hit, TraceStart, TraceEnd, FQuat::Identity, ECC_Pawn, FCollisionShape::MakeBox(Slice), FCollisionQueryParams(SCENE_QUERY_STAT(NavObjectBase_Validate), false, this));
if (Hit.bBlockingHit)
{
const FVector HitLocation = TraceStart + (TraceEnd - TraceStart) * Hit.Time;
FVector Dest = HitLocation - FVector(0.f, 0.f, /*CapsuleComponent->GetScaledCapsuleHalfHeight() -*/ 2.f);
// Move actor (TEST ONLY) to see if navigation point moves
TeleportTo(Dest, GetActorRotation(), false, true);
// If only adjustment was down towards the floor, then it is a valid placement
FVector NewLocation = GetActorLocation();
bResult = (NewLocation.X == OrigLocation.X &&
NewLocation.Y == OrigLocation.Y &&
NewLocation.Z <= OrigLocation.Z);
// Move actor back to original position
TeleportTo(OrigLocation, GetActorRotation(), false, true);
}
// Update sprites by result
if (GetGoodSprite())
{
GetGoodSprite()->SetVisibility(bResult);
}
if (GetBadSprite())
{
GetBadSprite()->SetVisibility(!bResult);
}
}
// Force update of icon
MarkComponentsRenderStateDirty();
}

View file

@ -0,0 +1,256 @@
// Copyright 1998-2016 Epic Games, Inc. All Rights Reserved.
#include "ParentRelativeAttachmentComponent.h"
#include UE_INLINE_GENERATED_CPP_BY_NAME(ParentRelativeAttachmentComponent)
#include "VRBaseCharacter.h"
#include "VRCharacter.h"
#include "IXRTrackingSystem.h"
#include "VRRootComponent.h"
//#include "Runtime/Engine/Private/EnginePrivate.h"
//#include "VRSimpleCharacter.h"
//#include "VRCharacter.h"
UParentRelativeAttachmentComponent::UParentRelativeAttachmentComponent(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{
PrimaryComponentTick.bCanEverTick = true;
PrimaryComponentTick.bStartWithTickEnabled = true;
// Let it sit in DuringPhysics like is the default
//PrimaryComponentTick.TickGroup = TG_PrePhysics;
bWantsInitializeComponent = true;
SetRelativeScale3D(FVector(1.0f, 1.0f, 1.0f));
SetRelativeLocation(FVector::ZeroVector);
YawTolerance = 0.0f;
//bOffsetByHMD = false;
bLerpTransition = true;
LerpSpeed = 100.0f;
LastLerpVal = 0.0f;
LerpTarget = 0.0f;
bWasSetOnce = false;
LeftControllerTrans = FTransform::Identity;
RightControllerTrans = FTransform::Identity;
bIgnoreRotationFromParent = false;
bUpdateInCharacterMovement = true;
bIsPaused = false;
CustomOffset = FVector::ZeroVector;
//YawRotationMethod = EVR_PRC_RotationMethod::PRC_ROT_HMD;
}
void UParentRelativeAttachmentComponent::InitializeComponent()
{
Super::InitializeComponent();
// Update our tracking
if (!bUseFeetLocation && IsValid(AttachChar)) // New case to early out and with less calculations
{
SetRelativeTransform(AttachChar->VRReplicatedCamera->GetRelativeTransform());
}
}
void UParentRelativeAttachmentComponent::OnAttachmentChanged()
{
if (AVRCharacter* CharacterOwner = Cast<AVRCharacter>(this->GetOwner()))
{
AttachChar = CharacterOwner;
}
else
{
AttachChar = nullptr;
}
if (AVRBaseCharacter * BaseCharacterOwner = Cast<AVRBaseCharacter>(this->GetOwner()))
{
AttachBaseChar = BaseCharacterOwner;
}
else
{
AttachBaseChar = nullptr;
}
Super::OnAttachmentChanged();
}
void UParentRelativeAttachmentComponent::SetPaused(bool bNewPaused, bool bZeroOutRotation, bool bZeroOutLocation)
{
if (bNewPaused != bIsPaused)
{
bIsPaused = bNewPaused;
if (bIsPaused && (bZeroOutLocation || bZeroOutRotation))
{
FVector NewLoc = this->GetRelativeLocation();
FRotator NewRot = this->GetRelativeRotation();
if (bZeroOutLocation)
{
NewLoc = FVector::ZeroVector;
}
if (bZeroOutRotation)
{
NewRot = FRotator::ZeroRotator;
}
SetRelativeLocationAndRotation(NewLoc, NewRot);
}
}
}
void UParentRelativeAttachmentComponent::SetRelativeRotAndLoc(FVector NewRelativeLocation, FRotator NewRelativeRotation, float DeltaTime)
{
RunSampling(NewRelativeRotation, NewRelativeLocation);
if (bUseFeetLocation)
{
FVector TotalOffset = CustomOffset;
if (bUseCenterAsFeetLocation && AttachChar && AttachChar->VRRootReference)
{
TotalOffset.Z += AttachChar->VRRootReference->GetUnscaledCapsuleHalfHeight();
}
if (!bIgnoreRotationFromParent)
{
SetRelativeLocationAndRotation(
FVector(NewRelativeLocation.X, NewRelativeLocation.Y, 0.0f) + TotalOffset,
GetCalculatedRotation(NewRelativeRotation, DeltaTime)
);
}
else
{
SetRelativeLocation(FVector(NewRelativeLocation.X, NewRelativeLocation.Y, 0.0f) + TotalOffset);
}
}
else
{
if (!bIgnoreRotationFromParent)
{
SetRelativeLocationAndRotation(
NewRelativeLocation + CustomOffset,
GetCalculatedRotation(NewRelativeRotation, DeltaTime)
); // Use the HMD height instead
}
else
{
SetRelativeLocation(NewRelativeLocation + CustomOffset); // Use the HMD height instead
}
}
}
void UParentRelativeAttachmentComponent::UpdateTracking(float DeltaTime)
{
// We are paused, do not update tracking anymore
if (bIsPaused)
return;
if (OptionalWaistTrackingParent.IsValid())
{
//#TODO: bOffsetByHMD not supported with this currently, fix it, need to check for both camera and HMD
FTransform TrackedParentWaist = IVRTrackedParentInterface::Default_GetWaistOrientationAndPosition(OptionalWaistTrackingParent);
if (bUseFeetLocation)
{
TrackedParentWaist.SetTranslation(TrackedParentWaist.GetTranslation() * FVector(1.0f, 1.0f, 0.0f));
if (!bIgnoreRotationFromParent)
{
FRotator InverseRot = UVRExpansionFunctionLibrary::GetHMDPureYaw_I(TrackedParentWaist.Rotator());
TrackedParentWaist.SetRotation(GetCalculatedRotation(InverseRot, DeltaTime));
}
}
TrackedParentWaist.AddToTranslation(CustomOffset);
SetRelativeTransform(TrackedParentWaist);
}
else if (IsValid(AttachChar)) // New case to early out and with less calculations
{
if (AttachChar->bRetainRoomscale)
{
SetRelativeRotAndLoc(AttachChar->VRRootReference->curCameraLoc, AttachChar->VRRootReference->StoredCameraRotOffset, DeltaTime);
}
else
{
FVector CameraLoc = FVector(0.0f, 0.0f, AttachChar->VRRootReference->curCameraLoc.Z);
CameraLoc += AttachChar->VRRootReference->StoredCameraRotOffset.RotateVector(FVector(-AttachChar->VRRootReference->VRCapsuleOffset.X, -AttachChar->VRRootReference->VRCapsuleOffset.Y, 0.0f));
SetRelativeRotAndLoc(CameraLoc, AttachChar->VRRootReference->StoredCameraRotOffset, DeltaTime);
}
}
else if (IsLocallyControlled() && GEngine->XRSystem.IsValid() && GEngine->XRSystem->IsHeadTrackingAllowed())
{
FQuat curRot;
FVector curCameraLoc;
if (GEngine->XRSystem->GetCurrentPose(IXRTrackingSystem::HMDDeviceId, curRot, curCameraLoc))
{
/*if (bOffsetByHMD)
{
curCameraLoc.X = 0;
curCameraLoc.Y = 0;
}*/
if (!bIgnoreRotationFromParent)
{
FRotator InverseRot = UVRExpansionFunctionLibrary::GetHMDPureYaw_I(curRot.Rotator());
SetRelativeRotAndLoc(curCameraLoc, InverseRot, DeltaTime);
}
else
SetRelativeRotAndLoc(curCameraLoc, FRotator::ZeroRotator, DeltaTime);
}
}
else if (IsValid(AttachBaseChar))
{
if (AttachBaseChar->VRReplicatedCamera)
{
if (!bIgnoreRotationFromParent)
{
FRotator InverseRot = UVRExpansionFunctionLibrary::GetHMDPureYaw(AttachBaseChar->VRReplicatedCamera->GetRelativeRotation());
SetRelativeRotAndLoc(AttachBaseChar->VRReplicatedCamera->GetRelativeLocation(), InverseRot, DeltaTime);
}
else
SetRelativeRotAndLoc(AttachBaseChar->VRReplicatedCamera->GetRelativeLocation(), FRotator::ZeroRotator, DeltaTime);
}
}
else if (AActor* owner = this->GetOwner())
{
if (UCameraComponent* CameraOwner = owner->FindComponentByClass<UCameraComponent>())
{
if (!bIgnoreRotationFromParent)
{
FRotator InverseRot = UVRExpansionFunctionLibrary::GetHMDPureYaw(CameraOwner->GetRelativeRotation());
SetRelativeRotAndLoc(CameraOwner->GetRelativeLocation(), InverseRot, DeltaTime);
}
else
SetRelativeRotAndLoc(CameraOwner->GetRelativeLocation(), FRotator::ZeroRotator, DeltaTime);
}
}
}
void UParentRelativeAttachmentComponent::TickComponent(float DeltaTime, enum ELevelTick TickType, FActorComponentTickFunction *ThisTickFunction)
{
if (!bUpdateInCharacterMovement || !IsValid(AttachChar))
{
UpdateTracking(DeltaTime);
}
else
{
UCharacterMovementComponent * CharMove = AttachChar->GetCharacterMovement();
if (!CharMove || !CharMove->IsComponentTickEnabled() || !CharMove->IsActive())
{
// Our character movement isn't handling our updates, lets do it ourself.
UpdateTracking(DeltaTime);
}
}
Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
}

View file

@ -0,0 +1,573 @@
// Copyright 1998-2016 Epic Games, Inc. All Rights Reserved.
#include "ReplicatedVRCameraComponent.h"
#include UE_INLINE_GENERATED_CPP_BY_NAME(ReplicatedVRCameraComponent)
#include "Net/UnrealNetwork.h"
#include "VRBaseCharacter.h"
#include "VRCharacter.h"
#include "VRRootComponent.h"
#include "IXRTrackingSystem.h"
#include "IXRCamera.h"
#include "Rendering/MotionVectorSimulation.h"
UReplicatedVRCameraComponent::UReplicatedVRCameraComponent(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{
PrimaryComponentTick.bCanEverTick = true;
PrimaryComponentTick.bStartWithTickEnabled = true;
//PrimaryComponentTick.TickGroup = TG_PrePhysics;
SetIsReplicatedByDefault(true);
SetRelativeScale3D(FVector(1.0f, 1.0f, 1.0f));
// Default 100 htz update rate, same as the 100htz update rate of rep_notify, will be capped to 90/45 though because of vsync on HMD
//bReplicateTransform = true;
NetUpdateRate = 100.0f; // 100 htz is default
NetUpdateCount = 0.0f;
bUsePawnControlRotation = false;
bAutoSetLockToHmd = true;
bScaleTracking = false;
TrackingScaler = FVector(1.0f);
//bOffsetByHMD = false;
bLimitMinHeight = false;
MinimumHeightAllowed = 0.0f;
bLimitMaxHeight = false;
MaxHeightAllowed = 300.f;
bLimitBounds = false;
// Just shy of 20' distance from the center of tracked space
MaximumTrackedBounds = 1028;
bSetPositionDuringTick = false;
bSmoothReplicatedMotion = false;
bLerpingPosition = false;
bReppedOnce = false;
OverrideSendTransform = nullptr;
LastRelativePosition = FTransform::Identity;
bSampleVelocityInWorldSpace = false;
bHadValidFirstVelocity = false;
//bUseVRNeckOffset = true;
//VRNeckOffset = FTransform(FRotator::ZeroRotator, FVector(15.0f,0,0), FVector(1.0f));
}
//=============================================================================
void UReplicatedVRCameraComponent::GetLifetimeReplicatedProps(TArray< class FLifetimeProperty > & OutLifetimeProps) const
{
// I am skipping the Scene component replication here
// Generally components aren't set to replicate anyway and I need it to NOT pass the Relative position through the network
// There isn't much in the scene component to replicate anyway
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
DISABLE_REPLICATED_PRIVATE_PROPERTY(USceneComponent, RelativeLocation);
DISABLE_REPLICATED_PRIVATE_PROPERTY(USceneComponent, RelativeRotation);
DISABLE_REPLICATED_PRIVATE_PROPERTY(USceneComponent, RelativeScale3D);
// Skipping the owner with this as the owner will use the location directly
DOREPLIFETIME_CONDITION(UReplicatedVRCameraComponent, ReplicatedCameraTransform, COND_SkipOwner);
DOREPLIFETIME(UReplicatedVRCameraComponent, NetUpdateRate);
DOREPLIFETIME(UReplicatedVRCameraComponent, bSmoothReplicatedMotion);
//DOREPLIFETIME(UReplicatedVRCameraComponent, bReplicateTransform);
}
// Just skipping this, it generates warnings for attached meshes when using this method of denying transform replication
/*void UReplicatedVRCameraComponent::PreReplication(IRepChangedPropertyTracker & ChangedPropertyTracker)
{
Super::PreReplication(ChangedPropertyTracker);
// Don't ever replicate these, they are getting replaced by my custom send anyway
DOREPLIFETIME_ACTIVE_OVERRIDE(USceneComponent, RelativeLocation, false);
DOREPLIFETIME_ACTIVE_OVERRIDE(USceneComponent, RelativeRotation, false);
DOREPLIFETIME_ACTIVE_OVERRIDE(USceneComponent, RelativeScale3D, false);
}*/
void UReplicatedVRCameraComponent::Server_SendCameraTransform_Implementation(FBPVRComponentPosRep NewTransform)
{
// Store new transform and trigger OnRep_Function
ReplicatedCameraTransform = NewTransform;
// Don't call on rep on the server if the server controls this controller
if (!bHasAuthority)
{
OnRep_ReplicatedCameraTransform();
}
}
bool UReplicatedVRCameraComponent::Server_SendCameraTransform_Validate(FBPVRComponentPosRep NewTransform)
{
return true;
// Optionally check to make sure that player is inside of their bounds and deny it if they aren't?
}
/*bool UReplicatedVRCameraComponent::IsServer()
{
if (GEngine != nullptr && GWorld != nullptr)
{
switch (GEngine->GetNetMode(GWorld))
{
case NM_Client:
{return false; } break;
case NM_DedicatedServer:
case NM_ListenServer:
default:
{return true; } break;
}
}
return false;
}*/
void UReplicatedVRCameraComponent::OnAttachmentChanged()
{
if (AVRCharacter* CharacterOwner = Cast<AVRCharacter>(this->GetOwner()))
{
AttachChar = CharacterOwner;
}
else
{
AttachChar = nullptr;
}
Super::OnAttachmentChanged();
}
bool UReplicatedVRCameraComponent::HasTrackingParameters()
{
return /*bOffsetByHMD ||*/ bScaleTracking || bLimitMaxHeight || bLimitMinHeight || bLimitBounds || (AttachChar && !AttachChar->bRetainRoomscale);
}
void UReplicatedVRCameraComponent::ApplyTrackingParameters(FVector &OriginalPosition, bool bSkipLocZero)
{
// I'm keeping the original values here as it lets me send them out for seated mode
if (!bSkipLocZero /*&& ((AttachChar && !AttachChar->bRetainRoomscale))*/)
{
OriginalPosition.X = 0;
OriginalPosition.Y = 0;
}
if (bLimitBounds)
{
OriginalPosition.X = FMath::Clamp(OriginalPosition.X, -MaximumTrackedBounds, MaximumTrackedBounds);
OriginalPosition.Y = FMath::Clamp(OriginalPosition.Y, -MaximumTrackedBounds, MaximumTrackedBounds);
}
if (bScaleTracking)
{
OriginalPosition *= TrackingScaler;
}
if (bLimitMaxHeight)
{
OriginalPosition.Z = FMath::Min(MaxHeightAllowed, OriginalPosition.Z);
}
if (bLimitMinHeight)
{
OriginalPosition.Z = FMath::Max(MinimumHeightAllowed, OriginalPosition.Z);
}
}
void UReplicatedVRCameraComponent::UpdateTracking(float DeltaTime)
{
bHasAuthority = IsLocallyControlled();
// Don't do any of the below if we aren't the authority
if (bHasAuthority)
{
// For non view target positional updates (third party and the like)
if (bSetPositionDuringTick && bLockToHmd && GEngine->XRSystem.IsValid() && GEngine->XRSystem->IsHeadTrackingAllowedForWorld(*GetWorld()))
{
//ResetRelativeTransform();
FQuat Orientation;
FVector Position;
if (GEngine->XRSystem->GetCurrentPose(IXRTrackingSystem::HMDDeviceId, Orientation, Position))
{
if (HasTrackingParameters())
{
ApplyTrackingParameters(Position);
}
ReplicatedCameraTransform.Position = Position;
ReplicatedCameraTransform.Rotation = Orientation.Rotator();
if (IsValid(AttachChar) && !AttachChar->bRetainRoomscale)
{
// Zero out camera posiiton
Position.X = 0.0f;
Position.Y = 0.0f;
FRotator StoredCameraRotOffset = FRotator::ZeroRotator;
if (AttachChar->VRMovementReference->GetReplicatedMovementMode() == EVRConjoinedMovementModes::C_VRMOVE_Seated)
{
AttachChar->SeatInformation.InitialRelCameraTransform.Rotator();
}
else
{
StoredCameraRotOffset = UVRExpansionFunctionLibrary::GetHMDPureYaw_I(Orientation.Rotator());
}
Position += StoredCameraRotOffset.RotateVector(FVector(-AttachChar->VRRootReference->VRCapsuleOffset.X, -AttachChar->VRRootReference->VRCapsuleOffset.Y, 0.0f));
}
SetRelativeTransform(FTransform(Orientation, Position));
}
}
}
else
{
// Run any networked smoothing
RunNetworkedSmoothing(DeltaTime);
}
// Save out the component velocity from this and last frame
if(bHadValidFirstVelocity || !LastRelativePosition.Equals(FTransform::Identity))
{
bHadValidFirstVelocity = true;
ComponentVelocity = ((bSampleVelocityInWorldSpace ? GetComponentLocation() : GetRelativeLocation()) - LastRelativePosition.GetTranslation()) / DeltaTime;
}
LastRelativePosition = bSampleVelocityInWorldSpace ? this->GetComponentTransform() : this->GetRelativeTransform();
}
void UReplicatedVRCameraComponent::RunNetworkedSmoothing(float DeltaTime)
{
FVector RetainPositionOffset(0.0f, 0.0f, ReplicatedCameraTransform.Position.Z);
if (AttachChar && !AttachChar->bRetainRoomscale)
{
FRotator StoredCameraRotOffset = FRotator::ZeroRotator;
if (AttachChar->VRMovementReference->GetReplicatedMovementMode() == EVRConjoinedMovementModes::C_VRMOVE_Seated)
{
AttachChar->SeatInformation.InitialRelCameraTransform.Rotator();
}
else
{
StoredCameraRotOffset = UVRExpansionFunctionLibrary::GetHMDPureYaw_I(ReplicatedCameraTransform.Rotation);
}
RetainPositionOffset += StoredCameraRotOffset.RotateVector(FVector(-AttachChar->VRRootReference->VRCapsuleOffset.X, -AttachChar->VRRootReference->VRCapsuleOffset.Y, 0.0f));
}
if (bLerpingPosition)
{
if (!bUseExponentialSmoothing)
{
NetUpdateCount += DeltaTime;
float LerpVal = FMath::Clamp(NetUpdateCount / (1.0f / NetUpdateRate), 0.0f, 1.0f);
if (LerpVal >= 1.0f)
{
if (AttachChar && !AttachChar->bRetainRoomscale)
{
SetRelativeLocationAndRotation(RetainPositionOffset, ReplicatedCameraTransform.Rotation);
}
else
{
SetRelativeLocationAndRotation(ReplicatedCameraTransform.Position, ReplicatedCameraTransform.Rotation);
}
// Stop lerping, wait for next update if it is delayed or lost then it will hitch here
// Actual prediction might be something to consider in the future, but rough to do in VR
// considering the speed and accuracy of movements
// would like to consider sub stepping but since there is no server rollback...not sure how useful it would be
// and might be perf taxing enough to not make it worth it.
bLerpingPosition = false;
NetUpdateCount = 0.0f;
}
else
{
if (AttachChar && !AttachChar->bRetainRoomscale)
{
// Removed variables to speed this up a bit
SetRelativeLocationAndRotation(
FMath::Lerp(LastUpdatesRelativePosition, RetainPositionOffset, LerpVal),
FMath::Lerp(LastUpdatesRelativeRotation, ReplicatedCameraTransform.Rotation, LerpVal)
);
}
else
{
// Removed variables to speed this up a bit
SetRelativeLocationAndRotation(
FMath::Lerp(LastUpdatesRelativePosition, (FVector)ReplicatedCameraTransform.Position, LerpVal),
FMath::Lerp(LastUpdatesRelativeRotation, ReplicatedCameraTransform.Rotation, LerpVal)
);
}
}
}
else // Exponential Smoothing
{
if (InterpolationSpeed <= 0.f)
{
if (AttachChar && !AttachChar->bRetainRoomscale)
{
SetRelativeLocationAndRotation(RetainPositionOffset, ReplicatedCameraTransform.Rotation);
}
else
{
SetRelativeLocationAndRotation((FVector)ReplicatedCameraTransform.Position, ReplicatedCameraTransform.Rotation);
}
bLerpingPosition = false;
return;
}
const float Alpha = FMath::Clamp(DeltaTime * InterpolationSpeed, 0.f, 1.f);
FTransform NA = FTransform(GetRelativeRotation(), GetRelativeLocation(), FVector(1.0f));
FTransform NB = FTransform::Identity;
if (AttachChar && !AttachChar->bRetainRoomscale)
{
NB = FTransform(ReplicatedCameraTransform.Rotation, RetainPositionOffset, FVector(1.0f));
}
else
{
NB = FTransform(ReplicatedCameraTransform.Rotation, (FVector)ReplicatedCameraTransform.Position, FVector(1.0f));
}
NA.NormalizeRotation();
NB.NormalizeRotation();
NA.Blend(NA, NB, Alpha);
// If we are nearly equal then snap to final position
if (NA.EqualsNoScale(NB))
{
if (AttachChar && !AttachChar->bRetainRoomscale)
{
SetRelativeLocationAndRotation(RetainPositionOffset, ReplicatedCameraTransform.Rotation);
}
else
{
SetRelativeLocationAndRotation(ReplicatedCameraTransform.Position, ReplicatedCameraTransform.Rotation);
}
}
else // Else just keep going
{
SetRelativeLocationAndRotation(NA.GetTranslation(), NA.Rotator());
}
}
}
}
void UReplicatedVRCameraComponent::TickComponent(float DeltaTime, enum ELevelTick TickType, FActorComponentTickFunction *ThisTickFunction)
{
Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
if (!bUpdateInCharacterMovement || !IsValid(AttachChar))
{
UpdateTracking(DeltaTime);
}
else
{
UCharacterMovementComponent* CharMove = AttachChar->GetCharacterMovement();
if (!CharMove || !CharMove->IsComponentTickEnabled() || !CharMove->IsActive() || (!CharMove->PrimaryComponentTick.bTickEvenWhenPaused && GetWorld()->IsPaused()))
{
// Our character movement isn't handling our updates, lets do it ourself.
UpdateTracking(DeltaTime);
}
}
if (bHasAuthority)
{
// Send changes
if (this->GetIsReplicated())
{
FRotator RelativeRot = GetRelativeRotation();
FVector RelativeLoc = GetRelativeLocation();
// Don't rep if no changes
if (!RelativeLoc.Equals(LastUpdatesRelativePosition) || !RelativeRot.Equals(LastUpdatesRelativeRotation))
{
NetUpdateCount += DeltaTime;
if (NetUpdateCount >= (1.0f / NetUpdateRate))
{
NetUpdateCount = 0.0f;
// Already stored out now, only do this for FPS debug characters
if (bFPSDebugMode)
{
ReplicatedCameraTransform.Position = RelativeLoc;
ReplicatedCameraTransform.Rotation = RelativeRot;
}
if (GetNetMode() == NM_Client)
{
AVRBaseCharacter* OwningChar = Cast<AVRBaseCharacter>(GetOwner());
if (OverrideSendTransform != nullptr && OwningChar != nullptr)
{
(OwningChar->* (OverrideSendTransform))(ReplicatedCameraTransform);
}
else
{
// Don't bother with any of this if not replicating transform
//if (bHasAuthority && bReplicateTransform)
Server_SendCameraTransform(ReplicatedCameraTransform);
}
}
LastUpdatesRelativeRotation = RelativeRot;
LastUpdatesRelativePosition = RelativeLoc;
}
}
}
}
}
void UReplicatedVRCameraComponent::HandleXRCamera()
{
bool bIsLocallyControlled = IsLocallyControlled();
if (bAutoSetLockToHmd)
{
if (bIsLocallyControlled)
bLockToHmd = true;
else
bLockToHmd = false;
}
if (bIsLocallyControlled && GEngine && GEngine->XRSystem.IsValid() && GetWorld() && GetWorld()->WorldType != EWorldType::Editor)
{
IXRTrackingSystem* XRSystem = GEngine->XRSystem.Get();
auto XRCamera = XRSystem->GetXRCamera();
if (XRCamera.IsValid())
{
if (XRSystem->IsHeadTrackingAllowedForWorld(*GetWorld()))
{
const FTransform ParentWorld = CalcNewComponentToWorld(FTransform());
XRCamera->SetupLateUpdate(ParentWorld, this, bLockToHmd == 0);
if (bLockToHmd)
{
FQuat Orientation;
FVector Position;
if (XRCamera->UpdatePlayerCamera(Orientation, Position))
{
if (HasTrackingParameters())
{
ApplyTrackingParameters(Position);
}
ReplicatedCameraTransform.Position = Position;
ReplicatedCameraTransform.Rotation = Orientation.Rotator();
if (IsValid(AttachChar) && !AttachChar->bRetainRoomscale)
{
// Zero out XY for non retained
Position.X = 0.0f;
Position.Y = 0.0f;
//FRotator OffsetRotator =
if (AttachChar->VRMovementReference->GetReplicatedMovementMode() != EVRConjoinedMovementModes::C_VRMOVE_Seated)
{
AttachChar->SeatInformation.InitialRelCameraTransform.Rotator();
FRotator StoredCameraRotOffset = FRotator::ZeroRotator;
if (AttachChar->VRMovementReference->GetReplicatedMovementMode() == EVRConjoinedMovementModes::C_VRMOVE_Seated)
{
AttachChar->SeatInformation.InitialRelCameraTransform.Rotator();
}
else
{
StoredCameraRotOffset = UVRExpansionFunctionLibrary::GetHMDPureYaw_I(Orientation.Rotator());
}
Position += StoredCameraRotOffset.RotateVector(FVector(-AttachChar->VRRootReference->VRCapsuleOffset.X, -AttachChar->VRRootReference->VRCapsuleOffset.Y, 0.0f));
}
}
SetRelativeTransform(FTransform(Orientation, Position));
}
else
{
SetRelativeScale3D(FVector(1.0f));
//ResetRelativeTransform(); stop doing this, it is problematic
// Let the camera freeze in the last position instead
// Setting scale by itself makes sure we don't get camera scaling but keeps the last location and rotation alive
}
}
// #TODO: Check back on this, was moved here in 4.20 but shouldn't it be inside of bLockToHMD?
XRCamera->OverrideFOV(this->FieldOfView);
}
}
}
}
void UReplicatedVRCameraComponent::OnRep_ReplicatedCameraTransform()
{
if (GetNetMode() < ENetMode::NM_Client && HasTrackingParameters())
{
// Ensure that we clamp to the expected values from the client
ApplyTrackingParameters(ReplicatedCameraTransform.Position, true);
}
FVector CameraPosition = ReplicatedCameraTransform.Position;
if (AttachChar && !AttachChar->bRetainRoomscale)
{
CameraPosition.X = 0;
CameraPosition.Y = 0;
FRotator StoredCameraRotOffset = FRotator::ZeroRotator;
if (AttachChar->VRMovementReference->GetReplicatedMovementMode() == EVRConjoinedMovementModes::C_VRMOVE_Seated)
{
AttachChar->SeatInformation.InitialRelCameraTransform.Rotator();
}
else
{
StoredCameraRotOffset = UVRExpansionFunctionLibrary::GetHMDPureYaw_I(ReplicatedCameraTransform.Rotation);
}
CameraPosition += StoredCameraRotOffset.RotateVector(FVector(-AttachChar->VRRootReference->VRCapsuleOffset.X, -AttachChar->VRRootReference->VRCapsuleOffset.Y, 0.0f));
}
if (bSmoothReplicatedMotion)
{
if (bReppedOnce)
{
bLerpingPosition = true;
NetUpdateCount = 0.0f;
LastUpdatesRelativePosition = this->GetRelativeLocation();
LastUpdatesRelativeRotation = this->GetRelativeRotation();
if (bUseExponentialSmoothing)
{
FVector OldToNewVector = CameraPosition - LastUpdatesRelativePosition;
float NewDistance = OldToNewVector.SizeSquared();
// Too far, snap to the new value
if (NewDistance >= FMath::Square(NetworkNoSmoothUpdateDistance))
{
SetRelativeLocationAndRotation(CameraPosition, ReplicatedCameraTransform.Rotation);
bLerpingPosition = false;
}
// Outside of the buffer distance, snap within buffer and keep smoothing from there
else if (NewDistance >= FMath::Square(NetworkMaxSmoothUpdateDistance))
{
FVector Offset = (OldToNewVector.Size() - NetworkMaxSmoothUpdateDistance) * OldToNewVector.GetSafeNormal();
SetRelativeLocation(LastUpdatesRelativePosition + Offset);
}
}
}
else
{
SetRelativeLocationAndRotation(CameraPosition, ReplicatedCameraTransform.Rotation);
bReppedOnce = true;
}
}
else
SetRelativeLocationAndRotation(CameraPosition, ReplicatedCameraTransform.Rotation);
}

View file

@ -0,0 +1,142 @@
// Copyright 1998-2016 Epic Games, Inc. All Rights Reserved.
#include "VRAIController.h"
#include UE_INLINE_GENERATED_CPP_BY_NAME(VRAIController)
//#include "VRBPDatatypes.h"
#include "VRBaseCharacter.h"
#include "Components/CapsuleComponent.h"
#include "NetworkingDistanceConstants.h" // Needed for the LinOfSightTo function override to work
#include "Navigation/CrowdFollowingComponent.h"
//#include "Runtime/Engine/Private/EnginePrivate.h"
FVector AVRAIController::GetFocalPointOnActor(const AActor *Actor) const
{
if (const AVRBaseCharacter * VRChar = Cast<const AVRBaseCharacter>(Actor))
{
return VRChar->GetVRLocation_Inline();
}
else
return Super::GetFocalPointOnActor(Actor); // Actor != nullptr ? Actor->GetActorLocation() : FAISystem::InvalidLocation;
}
bool AVRAIController::LineOfSightTo(const AActor* Other, FVector ViewPoint, bool bAlternateChecks) const
{
if (Other == nullptr)
{
return false;
}
if (ViewPoint.IsZero())
{
FRotator ViewRotation;
GetActorEyesViewPoint(ViewPoint, ViewRotation);
// if we still don't have a view point we simply fail
if (ViewPoint.IsZero())
{
return false;
}
}
static FName NAME_LineOfSight = FName(TEXT("LineOfSight"));
FVector TargetLocation = Other->GetTargetLocation(GetPawn());
FCollisionQueryParams CollisionParams(NAME_LineOfSight, true, this->GetPawn());
CollisionParams.AddIgnoredActor(Other);
bool bHit = GetWorld()->LineTraceTestByChannel(ViewPoint, TargetLocation, ECC_Visibility, CollisionParams);
if (!bHit)
{
return true;
}
// if other isn't using a cylinder for collision and isn't a Pawn (which already requires an accurate cylinder for AI)
// then don't go any further as it likely will not be tracing to the correct location
const APawn * OtherPawn = Cast<const APawn>(Other);
if (!OtherPawn && Cast<UCapsuleComponent>(Other->GetRootComponent()) == NULL)
{
return false;
}
// Changed this up to support my VR Characters
const AVRBaseCharacter * VRChar = Cast<const AVRBaseCharacter>(Other);
const FVector OtherActorLocation = VRChar != nullptr ? VRChar->GetVRLocation_Inline() : Other->GetActorLocation();
const FVector::FReal DistSq = (OtherActorLocation - ViewPoint).SizeSquared();
if (DistSq > FARSIGHTTHRESHOLDSQUARED)
{
return false;
}
if (!OtherPawn && (DistSq > NEARSIGHTTHRESHOLDSQUARED))
{
return false;
}
float OtherRadius, OtherHeight;
Other->GetSimpleCollisionCylinder(OtherRadius, OtherHeight);
if (!bAlternateChecks || !bLOSflag)
{
//try viewpoint to head
bHit = GetWorld()->LineTraceTestByChannel(ViewPoint, OtherActorLocation + FVector(0.f, 0.f, OtherHeight), ECC_Visibility, CollisionParams);
if (!bHit)
{
return true;
}
}
if (!bSkipExtraLOSChecks && (!bAlternateChecks || bLOSflag))
{
// only check sides if width of other is significant compared to distance
if (OtherRadius * OtherRadius / (OtherActorLocation - ViewPoint).SizeSquared() < 0.0001f)
{
return false;
}
//try checking sides - look at dist to four side points, and cull furthest and closest
FVector Points[4];
Points[0] = OtherActorLocation - FVector(OtherRadius, -1 * OtherRadius, 0);
Points[1] = OtherActorLocation + FVector(OtherRadius, OtherRadius, 0);
Points[2] = OtherActorLocation - FVector(OtherRadius, OtherRadius, 0);
Points[3] = OtherActorLocation + FVector(OtherRadius, -1 * OtherRadius, 0);
int32 IndexMin = 0;
int32 IndexMax = 0;
FVector::FReal CurrentMax = (Points[0] - ViewPoint).SizeSquared();
FVector::FReal CurrentMin = CurrentMax;
for (int32 PointIndex = 1; PointIndex < 4; PointIndex++)
{
const FVector::FReal NextSize = (Points[PointIndex] - ViewPoint).SizeSquared();
if (NextSize > CurrentMin)
{
CurrentMin = NextSize;
IndexMax = PointIndex;
}
else if (NextSize < CurrentMax)
{
CurrentMax = NextSize;
IndexMin = PointIndex;
}
}
for (int32 PointIndex = 0; PointIndex < 4; PointIndex++)
{
if ((PointIndex != IndexMin) && (PointIndex != IndexMax))
{
bHit = GetWorld()->LineTraceTestByChannel(ViewPoint, Points[PointIndex], ECC_Visibility, CollisionParams);
if (!bHit)
{
return true;
}
}
}
}
return false;
}
AVRDetourCrowdAIController::AVRDetourCrowdAIController(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer.SetDefaultSubobjectClass<UCrowdFollowingComponent>(TEXT("PathFollowingComponent")))
{
}

View file

@ -0,0 +1,220 @@
// Fill out your copyright notice in the Description page of Project Settings.
#include "VRBPDatatypes.h"
#include UE_INLINE_GENERATED_CPP_BY_NAME(VRBPDatatypes)
#include "Chaos/ChaosEngineInterface.h"
namespace VRDataTypeCVARs
{
// Doing it this way because I want as little rep and perf impact as possible and sampling a static var is that.
// This is to specifically help out very rare cases, DON'T USE THIS UNLESS YOU HAVE NO CHOICE
static int32 RepHighPrecisionTransforms = 0;
FAutoConsoleVariableRef CVarRepHighPrecisionTransforms(
TEXT("vrexp.RepHighPrecisionTransforms"),
RepHighPrecisionTransforms,
TEXT("When on, will rep Quantized transforms at full precision, WARNING use at own risk, if this isn't the same setting client & server then it will crash.\n")
TEXT("0: Disable, 1: Enable"),
ECVF_Default);
}
bool FTransform_NetQuantize::NetSerialize(FArchive& Ar, class UPackageMap* Map, bool& bOutSuccess)
{
bOutSuccess = true;
FVector rTranslation;
FVector rScale3D;
//FQuat rRotation;
FRotator rRotation;
uint16 ShortPitch = 0;
uint16 ShortYaw = 0;
uint16 ShortRoll = 0;
bool bUseHighPrecision = VRDataTypeCVARs::RepHighPrecisionTransforms > 0;
if (Ar.IsSaving())
{
// Because transforms can be vectorized or not, need to use the inline retrievers
rTranslation = this->GetTranslation();
rScale3D = this->GetScale3D();
rRotation = this->Rotator();//this->GetRotation();
if (bUseHighPrecision)
{
Ar << rTranslation;
Ar << rScale3D;
Ar << rRotation;
}
else
{
// Translation set to 2 decimal precision
bOutSuccess &= SerializePackedVector<100, 30>(rTranslation, Ar);
// Scale set to 2 decimal precision, had it 1 but realized that I used two already even
bOutSuccess &= SerializePackedVector<100, 30>(rScale3D, Ar);
// Rotation converted to FRotator and short compressed, see below for conversion reason
// FRotator already serializes compressed short by default but I can save a func call here
rRotation.SerializeCompressedShort(Ar);
}
//Ar << rRotation;
// If I converted it to a rotator and serialized as shorts I would save 6 bytes.
// I am unsure about a safe method of compressed serializing a quat, though I read through smallest three
// Epic already drops W from the Quat and reconstructs it after the send.
// Converting to rotator first may have conversion issues and has a perf cost....needs testing, epic attempts to handle
// Singularities in their conversion but I haven't tested it in all circumstances
//rRotation.SerializeCompressedShort(Ar);
}
else // If loading
{
if (bUseHighPrecision)
{
Ar << rTranslation;
Ar << rScale3D;
Ar << rRotation;
}
else
{
bOutSuccess &= SerializePackedVector<100, 30>(rTranslation, Ar);
bOutSuccess &= SerializePackedVector<100, 30>(rScale3D, Ar);
rRotation.SerializeCompressedShort(Ar);
}
//Ar << rRotation;
// Set it
this->SetComponents(rRotation.Quaternion(), rTranslation, rScale3D);
this->NormalizeRotation();
}
return bOutSuccess;
}
// ** Euro Low Pass Filter ** //
void FBPEuroLowPassFilter::ResetSmoothingFilter()
{
RawFilter.bFirstTime = true;
DeltaFilter.bFirstTime = true;
}
FVector FBPEuroLowPassFilter::RunFilterSmoothing(const FVector &InRawValue, const float &InDeltaTime)
{
if (InDeltaTime <= 0.0f)
{
// Invalid delta time, return the in value
return InRawValue;
}
// Calculate the delta, if this is the first time then there is no delta
const FVector Delta = RawFilter.bFirstTime == true ? FVector::ZeroVector : (InRawValue - RawFilter.PreviousRaw) * 1.0f / InDeltaTime;
// Filter the delta to get the estimated
const FVector Estimated = DeltaFilter.Filter(Delta, FVector(DeltaFilter.CalculateAlphaTau(DeltaCutoff, InDeltaTime)));
// Use the estimated to calculate the cutoff
const FVector Cutoff = DeltaFilter.CalculateCutoff(Estimated, MinCutoff, CutoffSlope);
// Filter passed value
return RawFilter.Filter(InRawValue, RawFilter.CalculateAlpha(Cutoff, InDeltaTime));
}
void FBPEuroLowPassFilterQuat::ResetSmoothingFilter()
{
RawFilter.bFirstTime = true;
DeltaFilter.bFirstTime = true;
}
FQuat FBPEuroLowPassFilterQuat::RunFilterSmoothing(const FQuat& InRawValue, const float& InDeltaTime)
{
if (InDeltaTime <= 0.0f)
{
// Invalid delta time, return the in value
return InRawValue;
}
FQuat NewInVal = InRawValue;
if (!RawFilter.bFirstTime)
{
// fix axial flipping, from unity open 1 Euro implementation
FVector4 PrevQuatAsVector(RawFilter.Previous.X, RawFilter.Previous.Y, RawFilter.Previous.Z, RawFilter.Previous.W);
FVector4 CurrQuatAsVector(InRawValue.X, InRawValue.Y, InRawValue.Z, InRawValue.W);
if ((PrevQuatAsVector - CurrQuatAsVector).SizeSquared() > 2)
NewInVal = FQuat(-InRawValue.X, -InRawValue.Y, -InRawValue.Z, -InRawValue.W);
}
// Calculate the delta, if this is the first time then there is no delta
FQuat Delta = FQuat::Identity;
if (!RawFilter.bFirstTime)
{
Delta = (NewInVal - RawFilter.PreviousRaw) * (1.0f / InDeltaTime);
}
float AlphaTau = DeltaFilter.CalculateAlphaTau(DeltaCutoff, InDeltaTime);
FQuat AlphaTauQ(AlphaTau, AlphaTau, AlphaTau, AlphaTau);
const FQuat Estimated = DeltaFilter.Filter(Delta, AlphaTauQ);
// Use the estimated to calculate the cutoff
const FQuat Cutoff = DeltaFilter.CalculateCutoff(Estimated, MinCutoff, CutoffSlope);
// Filter passed value
return RawFilter.Filter(NewInVal, RawFilter.CalculateAlpha(Cutoff, InDeltaTime)).GetNormalized();// .GetNormalized();
}
void FBPEuroLowPassFilterTrans::ResetSmoothingFilter()
{
RawFilter.bFirstTime = true;
DeltaFilter.bFirstTime = true;
}
FTransform FBPEuroLowPassFilterTrans::RunFilterSmoothing(const FTransform& InRawValue, const float& InDeltaTime)
{
if (InDeltaTime <= 0.0f)
{
// Invalid delta time, return the in value
return InRawValue;
}
FTransform NewInVal = InRawValue;
if (!RawFilter.bFirstTime)
{
FQuat TransQ = NewInVal.GetRotation();
FQuat PrevQ = RawFilter.Previous.GetRotation();
// fix axial flipping, from unity open 1 Euro implementation
FVector4 PrevQuatAsVector(PrevQ.X, PrevQ.Y, PrevQ.Z, PrevQ.W);
FVector4 CurrQuatAsVector(TransQ.X, TransQ.Y, TransQ.Z, TransQ.W);
if ((PrevQuatAsVector - CurrQuatAsVector).SizeSquared() > 2)
NewInVal.SetRotation(FQuat(-TransQ.X, -TransQ.Y, -TransQ.Z, -TransQ.W));
}
// Calculate the delta, if this is the first time then there is no delta
FTransform Delta = FTransform::Identity;
float Frequency = 1.0f / InDeltaTime;
if (!RawFilter.bFirstTime)
{
Delta.SetLocation((NewInVal.GetLocation() - RawFilter.PreviousRaw.GetLocation()) * Frequency);
Delta.SetRotation((NewInVal.GetRotation() - RawFilter.PreviousRaw.GetRotation()) * Frequency);
Delta.SetScale3D((NewInVal.GetScale3D() - RawFilter.PreviousRaw.GetScale3D()) * Frequency);
}
float AlphaTau = DeltaFilter.CalculateAlphaTau(DeltaCutoff, InDeltaTime);
FTransform AlphaTauQ(FQuat(AlphaTau, AlphaTau, AlphaTau, AlphaTau), FVector(AlphaTau), FVector(AlphaTau));
const FTransform Estimated = DeltaFilter.Filter(Delta, AlphaTauQ);
// Use the estimated to calculate the cutoff
const FTransform Cutoff = DeltaFilter.CalculateCutoff(Estimated, MinCutoff, CutoffSlope);
FTransform NewTrans = RawFilter.Filter(NewInVal, RawFilter.CalculateAlpha(Cutoff, InDeltaTime));
NewTrans.NormalizeRotation();
// Filter passed value
return NewTrans;
}

View file

@ -0,0 +1,248 @@
// Copyright 1998-2016 Epic Games, Inc. All Rights Reserved.
#include "VRCharacter.h"
#include UE_INLINE_GENERATED_CPP_BY_NAME(VRCharacter)
#include "NavigationSystem.h"
#include "VRBPDatatypes.h"
//#include "GripMotionControllerComponent.h"
//#include "VRExpansionFunctionLibrary.h"
//#include "ReplicatedVRCameraComponent.h"
//#include "ParentRelativeAttachmentComponent.h"
#include "VRRootComponent.h"
#include "VRCharacterMovementComponent.h"
#include "GameFramework/Controller.h"
#include "Runtime/Launch/Resources/Version.h"
#include "VRPathFollowingComponent.h"
//#include "Runtime/Engine/Private/EnginePrivate.h"
DEFINE_LOG_CATEGORY(LogVRCharacter);
AVRCharacter::AVRCharacter(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer.SetDefaultSubobjectClass<UVRRootComponent>(ACharacter::CapsuleComponentName).SetDefaultSubobjectClass<UVRCharacterMovementComponent>(ACharacter::CharacterMovementComponentName))
{
VRRootReference = NULL;
if (GetCapsuleComponent())
{
VRRootReference = Cast<UVRRootComponent>(GetCapsuleComponent());
VRRootReference->SetCapsuleSize(20.0f, 96.0f);
//VRRootReference->VRCapsuleOffset = FVector(-8.0f, 0.0f, 0.0f);
VRRootReference->SetCollisionResponseToAllChannels(ECollisionResponse::ECR_Ignore);
VRRootReference->SetCollisionResponseToChannel(ECollisionChannel::ECC_WorldStatic, ECollisionResponse::ECR_Block);
}
VRMovementReference = NULL;
if (GetMovementComponent())
{
VRMovementReference = Cast<UVRBaseCharacterMovementComponent>(GetMovementComponent());
//AddTickPrerequisiteComponent(this->GetCharacterMovement());
}
}
FVector AVRCharacter::GetTeleportLocation(FVector OriginalLocation)
{
if (!bRetainRoomscale)
{
return OriginalLocation + FVector(0.f, 0.f, VRRootReference->GetScaledCapsuleHalfHeight());
}
else
{
FVector modifier = VRRootReference->OffsetComponentToWorld.GetLocation() - this->GetActorLocation();
modifier.Z = 0.0f; // Null out Z
return OriginalLocation - modifier;
}
}
bool AVRCharacter::TeleportTo(const FVector& DestLocation, const FRotator& DestRotation, bool bIsATest, bool bNoCheck)
{
bool bTeleportSucceeded = Super::TeleportTo(DestLocation, DestRotation, bIsATest, bNoCheck);
if (bTeleportSucceeded)
{
NotifyOfTeleport();
}
return bTeleportSucceeded;
}
FVector AVRCharacter::GetNavAgentLocation() const
{
FVector AgentLocation = FNavigationSystem::InvalidLocation;
if (GetCharacterMovement() != nullptr)
{
if (VRMovementReference)
{
AgentLocation = VRMovementReference->GetActorFeetLocationVR();
}
else
AgentLocation = GetCharacterMovement()->GetActorFeetLocation();
}
if (FNavigationSystem::IsValidLocation(AgentLocation) == false /*&& GetCapsuleComponent() != nullptr*/)
{
if (VRRootReference)
{
AgentLocation = VRRootReference->OffsetComponentToWorld.GetLocation() - FVector(0, 0, VRRootReference->GetScaledCapsuleHalfHeight());
}
else if(GetCapsuleComponent() != nullptr)
AgentLocation = GetActorLocation() - FVector(0, 0, GetCapsuleComponent()->GetScaledCapsuleHalfHeight());
}
return AgentLocation;
}
void AVRCharacter::ExtendedSimpleMoveToLocation(const FVector& GoalLocation, float AcceptanceRadius, bool bStopOnOverlap, bool bUsePathfinding, bool bProjectDestinationToNavigation, bool bCanStrafe, TSubclassOf<UNavigationQueryFilter> FilterClass, bool bAllowPartialPaths)
{
UNavigationSystemV1* NavSys = Controller ? FNavigationSystem::GetCurrent<UNavigationSystemV1>(Controller->GetWorld()) : nullptr;
if (NavSys == nullptr || Controller == nullptr )
{
UE_LOG(LogVRCharacter, Warning, TEXT("UVRCharacter::ExtendedSimpleMoveToLocation called for NavSys:%s Controller:%s (if any of these is None then there's your problem"),
*GetNameSafe(NavSys), *GetNameSafe(Controller));
return;
}
UPathFollowingComponent* PFollowComp = nullptr;
//Controller->InitNavigationControl(PFollowComp);
if (Controller)
{
// New for 4.20, spawning the missing path following component here if there isn't already one
PFollowComp = Controller->FindComponentByClass<UPathFollowingComponent>();
if (PFollowComp == nullptr)
{
PFollowComp = NewObject<UVRPathFollowingComponent>(Controller);
PFollowComp->RegisterComponentWithWorld(Controller->GetWorld());
PFollowComp->Initialize();
}
}
if (PFollowComp == nullptr)
{
UE_LOG(LogVRCharacter, Warning, TEXT("ExtendedSimpleMoveToLocation - No PathFollowingComponent Found"));
return;
}
if (!PFollowComp->IsPathFollowingAllowed())
{
UE_LOG(LogVRCharacter, Warning, TEXT("ExtendedSimpleMoveToLocation - Path Following Movement Is Not Set To Allowed"));
return;
}
EPathFollowingReachMode ReachMode;
if (bStopOnOverlap)
ReachMode = EPathFollowingReachMode::OverlapAgent;
else
ReachMode = EPathFollowingReachMode::ExactLocation;
bool bAlreadyAtGoal = false;
if(UVRPathFollowingComponent * pathcomp = Cast<UVRPathFollowingComponent>(PFollowComp))
bAlreadyAtGoal = pathcomp->HasReached(GoalLocation, /*EPathFollowingReachMode::OverlapAgent*/ReachMode);
else
bAlreadyAtGoal = PFollowComp->HasReached(GoalLocation, /*EPathFollowingReachMode::OverlapAgent*/ReachMode);
// script source, keep only one move request at time
if (PFollowComp->GetStatus() != EPathFollowingStatus::Idle)
{
if (GetNetMode() == ENetMode::NM_Client)
{
// Stop the movement here, not keeping the velocity because it bugs out for clients, might be able to fix.
PFollowComp->AbortMove(*NavSys, FPathFollowingResultFlags::ForcedScript | FPathFollowingResultFlags::NewRequest
, FAIRequestID::AnyRequest, /*bAlreadyAtGoal ? */EPathFollowingVelocityMode::Reset /*: EPathFollowingVelocityMode::Keep*/);
}
else
{
PFollowComp->AbortMove(*NavSys, FPathFollowingResultFlags::ForcedScript | FPathFollowingResultFlags::NewRequest
, FAIRequestID::AnyRequest, bAlreadyAtGoal ? EPathFollowingVelocityMode::Reset : EPathFollowingVelocityMode::Keep);
}
}
if (bAlreadyAtGoal)
{
PFollowComp->RequestMoveWithImmediateFinish(EPathFollowingResult::Success);
}
else
{
const ANavigationData* NavData = NavSys->GetNavDataForProps(Controller->GetNavAgentPropertiesRef());
if (NavData)
{
FPathFindingQuery Query(Controller, *NavData, Controller->GetNavAgentLocation(), GoalLocation);
FPathFindingResult Result = NavSys->FindPathSync(Query);
if (Result.IsSuccessful())
{
FAIMoveRequest MoveReq(GoalLocation);
MoveReq.SetUsePathfinding(bUsePathfinding);
MoveReq.SetAllowPartialPath(bAllowPartialPaths);
MoveReq.SetProjectGoalLocation(bProjectDestinationToNavigation);
MoveReq.SetNavigationFilter(*FilterClass ? FilterClass : DefaultNavigationFilterClass);
MoveReq.SetAcceptanceRadius(AcceptanceRadius);
MoveReq.SetReachTestIncludesAgentRadius(bStopOnOverlap);
MoveReq.SetCanStrafe(bCanStrafe);
MoveReq.SetReachTestIncludesGoalRadius(true);
PFollowComp->RequestMove(/*FAIMoveRequest(GoalLocation)*/MoveReq, Result.Path);
}
else if (PFollowComp->GetStatus() != EPathFollowingStatus::Idle)
{
PFollowComp->RequestMoveWithImmediateFinish(EPathFollowingResult::Invalid);
}
}
}
}
FVector AVRCharacter::GetTargetHeightOffset()
{
return bRetainRoomscale ? FVector::ZeroVector : VRRootReference->GetTargetHeightOffset();
}
void AVRCharacter::RegenerateOffsetComponentToWorld(bool bUpdateBounds, bool bCalculatePureYaw)
{
if (VRRootReference)
{
VRRootReference->GenerateOffsetToWorld(bUpdateBounds, bCalculatePureYaw);
}
}
void AVRCharacter::SetCharacterSizeVR(float NewRadius, float NewHalfHeight, bool bUpdateOverlaps)
{
if (VRRootReference)
{
VRRootReference->SetCapsuleSizeVR(NewRadius, NewHalfHeight, bUpdateOverlaps);
if (GetNetMode() < ENetMode::NM_Client)
ReplicatedCapsuleHeight.CapsuleHeight = VRRootReference->GetUnscaledCapsuleHalfHeight();
}
else
{
Super::SetCharacterSizeVR(NewRadius, NewHalfHeight, bUpdateOverlaps);
}
}
void AVRCharacter::SetCharacterHalfHeightVR(float HalfHeight, bool bUpdateOverlaps)
{
if (VRRootReference)
{
VRRootReference->SetCapsuleHalfHeightVR(HalfHeight, bUpdateOverlaps);
if (GetNetMode() < ENetMode::NM_Client)
ReplicatedCapsuleHeight.CapsuleHeight = VRRootReference->GetUnscaledCapsuleHalfHeight();
}
else
{
Super::SetCharacterHalfHeightVR(HalfHeight, bUpdateOverlaps);
}
}
FVector AVRCharacter::GetProjectedVRLocation() const
{
if (VRRootReference)
{
return OffsetComponentToWorld.TransformPosition(-FVector(VRRootReference->VRCapsuleOffset.X, VRRootReference->VRCapsuleOffset.Y, 0.0f));
}
else
{
return AVRBaseCharacter::GetProjectedVRLocation();
}
}

View file

@ -0,0 +1,784 @@
// Fill out your copyright notice in the Description page of Project Settings.
#include "VRExpansionFunctionLibrary.h"
#include UE_INLINE_GENERATED_CPP_BY_NAME(VRExpansionFunctionLibrary)
#include "DrawDebugHelpers.h"
#include "Engine/Engine.h"
#include "IXRTrackingSystem.h"
#include "IHeadMountedDisplay.h"
#include "InputCoreTypes.h"
#include "Grippables/HandSocketComponent.h"
#include "Misc/CollisionIgnoreSubsystem.h"
#include "Components/SplineComponent.h"
#include "Components/SplineMeshComponent.h"
#include "Components/PrimitiveComponent.h"
#include "GripMotionControllerComponent.h"
//#include "IMotionController.h"
//#include "HeadMountedDisplayFunctionLibrary.h"
#include "Grippables/GrippablePhysicsReplication.h"
#include "GameplayTagContainer.h"
#include "XRMotionControllerBase.h"
//#include "IHeadMountedDisplay.h"
#include "Chaos/ParticleHandle.h"
#include "Chaos/KinematicGeometryParticles.h"
#include "Chaos/ParticleHandle.h"
#include "PhysicsProxy/SingleParticlePhysicsProxy.h"
#include "PBDRigidsSolver.h"
#if WITH_EDITOR
#include "Editor/UnrealEd/Classes/Editor/EditorEngine.h"
#endif
//General Log
DEFINE_LOG_CATEGORY(VRExpansionFunctionLibraryLog);
UGameViewportClient* UVRExpansionFunctionLibrary::GetGameViewportClient(UObject* WorldContextObject)
{
if (WorldContextObject)
{
return WorldContextObject->GetWorld()->GetGameViewport();
}
return nullptr;
}
void UVRExpansionFunctionLibrary::SetActorsIgnoreAllCollision(UObject* WorldContextObject, AActor* Actor1, AActor* Actor2, bool bIgnoreCollision)
{
TInlineComponentArray<UPrimitiveComponent*> PrimitiveComponents1;
Actor1->GetComponents<UPrimitiveComponent>(PrimitiveComponents1);
TInlineComponentArray<UPrimitiveComponent*> PrimitiveComponents2;
Actor2->GetComponents<UPrimitiveComponent>(PrimitiveComponents2);
UCollisionIgnoreSubsystem* CollisionIgnoreSubsystem = WorldContextObject->GetWorld()->GetSubsystem<UCollisionIgnoreSubsystem>();
if (CollisionIgnoreSubsystem)
{
// Just a temp flag to only check state on the first component pair
bool bIgnorefirst = true;
for (int i = 0; i < PrimitiveComponents1.Num(); ++i)
{
for (int j = 0; j < PrimitiveComponents2.Num(); ++j)
{
// Don't ignore collision if one is already invalid
if (!IsValid(PrimitiveComponents1[i]) || !IsValid(PrimitiveComponents2[j]))
{
continue;
}
// Don't ignore collision if no collision on at least one of the objects
if (PrimitiveComponents1[i]->GetCollisionEnabled() == ECollisionEnabled::NoCollision || PrimitiveComponents2[j]->GetCollisionEnabled() == ECollisionEnabled::NoCollision)
{
if (!bIgnoreCollision)
{
if (CollisionIgnoreSubsystem->AreComponentsIgnoringCollisions(PrimitiveComponents1[i], PrimitiveComponents2[j]))
{
UE_LOG(VRExpansionFunctionLibraryLog, Error, TEXT("Set Actors Ignore Collision called with at least one object set to no collision that are ignoring collision already!! %s, %s"),*PrimitiveComponents1[i]->GetName(), *PrimitiveComponents2[j]->GetName());
}
}
continue;
}
CollisionIgnoreSubsystem->SetComponentCollisionIgnoreState(true, true, PrimitiveComponents1[i], NAME_None, PrimitiveComponents2[j], NAME_None, bIgnoreCollision, bIgnorefirst);
bIgnorefirst = false;
}
}
}
}
void UVRExpansionFunctionLibrary::SetObjectsIgnoreCollision(UObject* WorldContextObject, UPrimitiveComponent* Prim1, FName OptionalBoneName1, bool bAddChildBones1, UPrimitiveComponent* Prim2, FName OptionalBoneName2, bool bAddChildBones2, bool bIgnoreCollision)
{
UCollisionIgnoreSubsystem* CollisionIgnoreSubsystem = WorldContextObject->GetWorld()->GetSubsystem<UCollisionIgnoreSubsystem>();
if (CollisionIgnoreSubsystem)
{
CollisionIgnoreSubsystem->SetComponentCollisionIgnoreState(bAddChildBones1, bAddChildBones2, Prim1, OptionalBoneName1, Prim2, OptionalBoneName2, bIgnoreCollision, true);
}
}
bool UVRExpansionFunctionLibrary::IsComponentIgnoringCollision(UObject* WorldContextObject, UPrimitiveComponent* Prim1)
{
UCollisionIgnoreSubsystem* CollisionIgnoreSubsystem = WorldContextObject->GetWorld()->GetSubsystem<UCollisionIgnoreSubsystem>();
if (CollisionIgnoreSubsystem)
{
return CollisionIgnoreSubsystem->IsComponentIgnoringCollision(Prim1);
}
return false;
}
bool UVRExpansionFunctionLibrary::AreComponentsIgnoringCollisions(UObject* WorldContextObject, UPrimitiveComponent* Prim1, UPrimitiveComponent * Prim2)
{
UCollisionIgnoreSubsystem* CollisionIgnoreSubsystem = WorldContextObject->GetWorld()->GetSubsystem<UCollisionIgnoreSubsystem>();
if (CollisionIgnoreSubsystem && CollisionIgnoreSubsystem->CollisionTrackedPairs.Num() > 0)
{
return CollisionIgnoreSubsystem->AreComponentsIgnoringCollisions(Prim1, Prim2);
}
return false;
}
void UVRExpansionFunctionLibrary::RemoveObjectCollisionIgnore(UObject* WorldContextObject, UPrimitiveComponent* Prim1)
{
UCollisionIgnoreSubsystem* CollisionIgnoreSubsystem = WorldContextObject->GetWorld()->GetSubsystem<UCollisionIgnoreSubsystem>();
if (CollisionIgnoreSubsystem)
{
CollisionIgnoreSubsystem->RemoveComponentCollisionIgnoreState(Prim1);
}
}
void UVRExpansionFunctionLibrary::RemoveActorCollisionIgnore(UObject* WorldContextObject, AActor* Actor1)
{
TInlineComponentArray<UPrimitiveComponent*> PrimitiveComponents1;
Actor1->GetComponents<UPrimitiveComponent>(PrimitiveComponents1);
UCollisionIgnoreSubsystem* CollisionIgnoreSubsystem = WorldContextObject->GetWorld()->GetSubsystem<UCollisionIgnoreSubsystem>();
if (CollisionIgnoreSubsystem)
{
for (int i = 0; i < PrimitiveComponents1.Num(); ++i)
{
CollisionIgnoreSubsystem->RemoveComponentCollisionIgnoreState(PrimitiveComponents1[i]);
}
}
}
void UVRExpansionFunctionLibrary::LowPassFilter_RollingAverage(FVector lastAverage, FVector newSample, FVector& newAverage, int32 numSamples)
{
newAverage = lastAverage;
newAverage -= newAverage / numSamples;
newAverage += newSample / numSamples;
}
void UVRExpansionFunctionLibrary::LowPassFilter_Exponential(FVector lastAverage, FVector newSample, FVector& newAverage, float sampleFactor)
{
newAverage = (newSample * sampleFactor) + ((1 - sampleFactor) * lastAverage);
}
bool UVRExpansionFunctionLibrary::GetIsActorMovable(AActor* ActorToCheck)
{
if (!ActorToCheck)
return false;
if (USceneComponent* rootComp = ActorToCheck->GetRootComponent())
{
return rootComp->Mobility == EComponentMobility::Movable;
}
return false;
}
void UVRExpansionFunctionLibrary::GetGripSlotInRangeByTypeName(FName SlotType, AActor* Actor, FVector WorldLocation, float MaxRange, bool& bHadSlotInRange, FTransform& SlotWorldTransform, FName& SlotName, UGripMotionControllerComponent* QueryController)
{
bHadSlotInRange = false;
SlotWorldTransform = FTransform::Identity;
SlotName = NAME_None;
UHandSocketComponent* TargetHandSocket = nullptr;
if (!Actor)
return;
if (USceneComponent* rootComp = Actor->GetRootComponent())
{
GetGripSlotInRangeByTypeName_Component(SlotType, rootComp, WorldLocation, MaxRange, bHadSlotInRange, SlotWorldTransform, SlotName, QueryController);
}
}
void UVRExpansionFunctionLibrary::GetGripSlotInRangeByTypeName_Component(FName SlotType, USceneComponent* Component, FVector WorldLocation, float MaxRange, bool& bHadSlotInRange, FTransform& SlotWorldTransform, FName& SlotName, UGripMotionControllerComponent* QueryController)
{
bHadSlotInRange = false;
SlotWorldTransform = FTransform::Identity;
SlotName = NAME_None;
UHandSocketComponent* TargetHandSocket = nullptr;
if (!Component)
return;
FVector RelativeWorldLocation = Component->GetComponentTransform().InverseTransformPosition(WorldLocation);
MaxRange = FMath::Square(MaxRange);
float ClosestSlotDistance = -0.1f;
TArray<FName> SocketNames = Component->GetAllSocketNames();
FString GripIdentifier = SlotType.ToString();
int foundIndex = 0;
for (int i = 0; i < SocketNames.Num(); ++i)
{
if (SocketNames[i].ToString().Contains(GripIdentifier, ESearchCase::IgnoreCase, ESearchDir::FromStart))
{
float vecLen = FVector::DistSquared(RelativeWorldLocation, Component->GetSocketTransform(SocketNames[i], ERelativeTransformSpace::RTS_Component).GetLocation());
if (MaxRange >= vecLen && (ClosestSlotDistance < 0.0f || vecLen < ClosestSlotDistance))
{
ClosestSlotDistance = vecLen;
bHadSlotInRange = true;
foundIndex = i;
}
}
}
TArray<USceneComponent*> AttachChildren = Component->GetAttachChildren();
TArray<UHandSocketComponent*> RotationallyMatchingHandSockets;
for (USceneComponent* AttachChild : AttachChildren)
{
if (AttachChild && AttachChild->IsA<UHandSocketComponent>())
{
if (UHandSocketComponent* SocketComp = Cast<UHandSocketComponent>(AttachChild))
{
if (SocketComp->bDisabled)
continue;
FName BoneName = SocketComp->GetAttachSocketName();
FString SlotPrefix = BoneName != NAME_None ? BoneName.ToString() + SocketComp->SlotPrefix.ToString() : SocketComp->SlotPrefix.ToString();
if (SlotPrefix.Contains(GripIdentifier, ESearchCase::IgnoreCase, ESearchDir::FromStart))
{
FVector SocketRelativeLocation = Component->GetComponentTransform().InverseTransformPosition(SocketComp->GetHandSocketTransform(QueryController, true).GetLocation());
float vecLen = FVector::DistSquared(RelativeWorldLocation, SocketRelativeLocation);
//float vecLen = FVector::DistSquared(RelativeWorldLocation, SocketComp->GetRelativeLocation());
if (SocketComp->bAlwaysInRange)
{
if (SocketComp->bMatchRotation)
{
RotationallyMatchingHandSockets.Add(SocketComp);
}
else
{
TargetHandSocket = SocketComp;
ClosestSlotDistance = vecLen;
bHadSlotInRange = true;
}
}
else
{
float RangeVal = (SocketComp->OverrideDistance > 0.0f ? FMath::Square(SocketComp->OverrideDistance) : MaxRange);
if (RangeVal >= vecLen && (ClosestSlotDistance < 0.0f || vecLen < ClosestSlotDistance))
{
if (SocketComp->bMatchRotation)
{
RotationallyMatchingHandSockets.Add(SocketComp);
}
else
{
TargetHandSocket = SocketComp;
ClosestSlotDistance = vecLen;
bHadSlotInRange = true;
}
}
}
}
}
}
}
// Try and sort through any hand sockets flagged as rotationally matched
if (RotationallyMatchingHandSockets.Num() > 0)
{
FQuat ControllerRot = QueryController->GetPivotTransform().GetRotation();
//FQuat ClosestQuat = RotationallyMatchingHandSockets[0]->GetComponentTransform().GetRotation();
FQuat ClosestQuat = RotationallyMatchingHandSockets[0]->GetHandSocketTransform(QueryController, true).GetRotation();
TargetHandSocket = RotationallyMatchingHandSockets[0];
bHadSlotInRange = true;
ClosestSlotDistance = ControllerRot.AngularDistance(ClosestQuat);
for (int i = 1; i < RotationallyMatchingHandSockets.Num(); i++)
{
//float CheckDistance = ControllerRot.AngularDistance(RotationallyMatchingHandSockets[i]->GetComponentTransform().GetRotation());
float CheckDistance = ControllerRot.AngularDistance(RotationallyMatchingHandSockets[i]->GetHandSocketTransform(QueryController, true).GetRotation());
if (CheckDistance < ClosestSlotDistance)
{
TargetHandSocket = RotationallyMatchingHandSockets[i];
ClosestSlotDistance = CheckDistance;
}
}
}
if (bHadSlotInRange)
{
if (TargetHandSocket)
{
SlotWorldTransform = TargetHandSocket->GetHandSocketTransform(QueryController);
SlotName = TargetHandSocket->GetFName();
SlotWorldTransform.SetScale3D(FVector(1.0f));
}
else
{
SlotWorldTransform = Component->GetSocketTransform(SocketNames[foundIndex]);
SlotName = SocketNames[foundIndex];
SlotWorldTransform.SetScale3D(FVector(1.0f));
}
}
}
bool UVRExpansionFunctionLibrary::GetHandFromMotionSourceName(FName MotionSource, EControllerHand& Hand)
{
Hand = EControllerHand::Left;
if (FXRMotionControllerBase::GetHandEnumForSourceName(MotionSource, Hand))
{
return true;
}
return false;
}
FRotator UVRExpansionFunctionLibrary::GetHMDPureYaw(FRotator HMDRotation)
{
return GetHMDPureYaw_I(HMDRotation);
}
EBPHMDWornState UVRExpansionFunctionLibrary::GetIsHMDWorn()
{
if (GEngine->XRSystem.IsValid() && GEngine->XRSystem->GetHMDDevice())
{
return (EBPHMDWornState)GEngine->XRSystem->GetHMDDevice()->GetHMDWornState();
}
return EBPHMDWornState::Unknown;
}
bool UVRExpansionFunctionLibrary::GetIsHMDConnected()
{
return GEngine->XRSystem.IsValid() && GEngine->XRSystem->GetHMDDevice() && GEngine->XRSystem->GetHMDDevice()->IsHMDConnected();
}
EBPHMDDeviceType UVRExpansionFunctionLibrary::GetHMDType()
{
if (GEngine && GEngine->XRSystem.IsValid())
{
/*
if (GEngine && GEngine->XRSystem.IsValid())
{
Ar.Logf(*GEngine->XRSystem->GetVersionString());
}
*/
// #TODO 4.19: Figure out a way to replace this...its broken now
/*IHeadMountedDisplay* HMDDevice = GEngine->XRSystem->GetHMDDevice();
if (HMDDevice)
{
EHMDDeviceType::Type HMDDeviceType = HMDDevice->GetHMDDeviceType();
switch (HMDDeviceType)
{
case EHMDDeviceType::DT_ES2GenericStereoMesh: return EBPHMDDeviceType::DT_ES2GenericStereoMesh; break;
case EHMDDeviceType::DT_GearVR: return EBPHMDDeviceType::DT_GearVR; break;
case EHMDDeviceType::DT_Morpheus: return EBPHMDDeviceType::DT_Morpheus; break;
case EHMDDeviceType::DT_OculusRift: return EBPHMDDeviceType::DT_OculusRift; break;
case EHMDDeviceType::DT_SteamVR: return EBPHMDDeviceType::DT_SteamVR; break;
case EHMDDeviceType::DT_GoogleVR: return EBPHMDDeviceType::DT_GoogleVR; break;
}
}*/
// There are no device type entries for these now....
// Does the device type go away soon leaving only FNames?
// #TODO: 4.19?
// GearVR doesn't even return anything gut OculusHMD in FName currently.
static const FName SteamVRSystemName(TEXT("SteamVR"));
static const FName OculusSystemName(TEXT("OculusHMD"));
static const FName PSVRSystemName(TEXT("PSVR"));
static const FName OSVRSystemName(TEXT("OSVR"));
static const FName GoogleARCoreSystemName(TEXT("FGoogleARCoreHMD"));
static const FName AppleARKitSystemName(TEXT("AppleARKit"));
static const FName GoogleVRHMDSystemName(TEXT("FGoogleVRHMD"));
FName DeviceName(NAME_None);
DeviceName = GEngine->XRSystem->GetSystemName();
if (DeviceName == FName(TEXT("SimpleHMD")))
return EBPHMDDeviceType::DT_ES2GenericStereoMesh;
else if (DeviceName == SteamVRSystemName)
return EBPHMDDeviceType::DT_SteamVR;
else if (DeviceName == OculusSystemName)
return EBPHMDDeviceType::DT_OculusHMD;
else if (DeviceName == PSVRSystemName)
return EBPHMDDeviceType::DT_PSVR;
else if (DeviceName == OSVRSystemName)
return EBPHMDDeviceType::DT_SteamVR;
else if (DeviceName == GoogleARCoreSystemName)
return EBPHMDDeviceType::DT_GoogleARCore;
else if (DeviceName == AppleARKitSystemName)
return EBPHMDDeviceType::DT_AppleARKit;
else if (DeviceName == GoogleVRHMDSystemName)
return EBPHMDDeviceType::DT_GoogleVR;
}
// Default to unknown
return EBPHMDDeviceType::DT_Unknown;
}
bool UVRExpansionFunctionLibrary::IsInVREditorPreviewOrGame()
{
#if WITH_EDITOR
if (GIsEditor)
{
if (UEditorEngine* EdEngine = Cast<UEditorEngine>(GEngine))
{
TOptional<FPlayInEditorSessionInfo> PlayInfo = EdEngine->GetPlayInEditorSessionInfo();
if (PlayInfo.IsSet())
{
return PlayInfo->OriginalRequestParams.SessionPreviewTypeOverride == EPlaySessionPreviewType::VRPreview;
}
else
{
return false;
}
}
}
#endif
// Is not an editor build, default to true here
return true;
}
bool UVRExpansionFunctionLibrary::IsInVREditorPreview()
{
#if WITH_EDITOR
if (GIsEditor)
{
if (UEditorEngine* EdEngine = Cast<UEditorEngine>(GEngine))
{
TOptional<FPlayInEditorSessionInfo> PlayInfo = EdEngine->GetPlayInEditorSessionInfo();
if (PlayInfo.IsSet())
{
return PlayInfo->OriginalRequestParams.SessionPreviewTypeOverride == EPlaySessionPreviewType::VRPreview;
}
else
{
return false;
}
}
}
#endif
// Is not an editor build, default to false here
return false;
}
void UVRExpansionFunctionLibrary::NonAuthorityMinimumAreaRectangle(class UObject* WorldContextObject, const TArray<FVector>& InVerts, const FVector& SampleSurfaceNormal, FVector& OutRectCenter, FRotator& OutRectRotation, float& OutSideLengthX, float& OutSideLengthY, bool bDebugDraw)
{
float MinArea = -1.f;
float CurrentArea = -1.f;
FVector SupportVectorA, SupportVectorB;
FVector RectSideA, RectSideB;
float MinDotResultA, MinDotResultB, MaxDotResultA, MaxDotResultB;
FVector TestEdge;
float TestEdgeDot = 0.f;
FVector PolyNormal(0.f, 0.f, 1.f);
TArray<int32> PolyVertIndices;
// Bail if we receive an empty InVerts array
if (InVerts.Num() == 0)
{
return;
}
// Compute the approximate normal of the poly, using the direction of SampleSurfaceNormal for guidance
PolyNormal = (InVerts[InVerts.Num() / 3] - InVerts[0]) ^ (InVerts[InVerts.Num() * 2 / 3] - InVerts[InVerts.Num() / 3]);
if ((PolyNormal | SampleSurfaceNormal) < 0.f)
{
PolyNormal = -PolyNormal;
}
// Transform the sample points to 2D
FMatrix SurfaceNormalMatrix = FRotationMatrix::MakeFromZX(PolyNormal, FVector(1.f, 0.f, 0.f));
TArray<FVector> TransformedVerts;
OutRectCenter = FVector(0.f);
for (int32 Idx = 0; Idx < InVerts.Num(); ++Idx)
{
OutRectCenter += InVerts[Idx];
TransformedVerts.Add(SurfaceNormalMatrix.InverseTransformVector(InVerts[Idx]));
}
OutRectCenter /= InVerts.Num();
// Compute the convex hull of the sample points
ConvexHull2D::ComputeConvexHull(TransformedVerts, PolyVertIndices);
// Minimum area rectangle as computed by http://www.geometrictools.com/Documentation/MinimumAreaRectangle.pdf
for (int32 Idx = 1; Idx < PolyVertIndices.Num() - 1; ++Idx)
{
SupportVectorA = (TransformedVerts[PolyVertIndices[Idx]] - TransformedVerts[PolyVertIndices[Idx - 1]]).GetSafeNormal();
SupportVectorA.Z = 0.f;
SupportVectorB.X = -SupportVectorA.Y;
SupportVectorB.Y = SupportVectorA.X;
SupportVectorB.Z = 0.f;
MinDotResultA = MinDotResultB = MaxDotResultA = MaxDotResultB = 0.f;
for (int TestVertIdx = 1; TestVertIdx < PolyVertIndices.Num(); ++TestVertIdx)
{
TestEdge = TransformedVerts[PolyVertIndices[TestVertIdx]] - TransformedVerts[PolyVertIndices[0]];
TestEdgeDot = SupportVectorA | TestEdge;
if (TestEdgeDot < MinDotResultA)
{
MinDotResultA = TestEdgeDot;
}
else if (TestEdgeDot > MaxDotResultA)
{
MaxDotResultA = TestEdgeDot;
}
TestEdgeDot = SupportVectorB | TestEdge;
if (TestEdgeDot < MinDotResultB)
{
MinDotResultB = TestEdgeDot;
}
else if (TestEdgeDot > MaxDotResultB)
{
MaxDotResultB = TestEdgeDot;
}
}
CurrentArea = (MaxDotResultA - MinDotResultA) * (MaxDotResultB - MinDotResultB);
if (MinArea < 0.f || CurrentArea < MinArea)
{
MinArea = CurrentArea;
RectSideA = SupportVectorA * (MaxDotResultA - MinDotResultA);
RectSideB = SupportVectorB * (MaxDotResultB - MinDotResultB);
}
}
RectSideA = SurfaceNormalMatrix.TransformVector(RectSideA);
RectSideB = SurfaceNormalMatrix.TransformVector(RectSideB);
OutRectRotation = FRotationMatrix::MakeFromZX(PolyNormal, RectSideA).Rotator();
OutSideLengthX = RectSideA.Size();
OutSideLengthY = RectSideB.Size();
#if ENABLE_DRAW_DEBUG
if (bDebugDraw)
{
UWorld* World = (WorldContextObject) ? GEngine->GetWorldFromContextObject(WorldContextObject, EGetWorldErrorMode::LogAndReturnNull) : nullptr;
if (World != nullptr)
{
DrawDebugSphere(World, OutRectCenter, 10.f, 12, FColor::Yellow, true);
DrawDebugCoordinateSystem(World, OutRectCenter, SurfaceNormalMatrix.Rotator(), 100.f, true);
DrawDebugLine(World, OutRectCenter - RectSideA * 0.5f + FVector(0, 0, 10.f), OutRectCenter + RectSideA * 0.5f + FVector(0, 0, 10.f), FColor::Green, true, -1, 0, 5.f);
DrawDebugLine(World, OutRectCenter - RectSideB * 0.5f + FVector(0, 0, 10.f), OutRectCenter + RectSideB * 0.5f + FVector(0, 0, 10.f), FColor::Blue, true, -1, 0, 5.f);
}
else
{
FFrame::KismetExecutionMessage(TEXT("WorldContext required for MinimumAreaRectangle to draw a debug visualization."), ELogVerbosity::Warning);
}
}
#endif
}
bool UVRExpansionFunctionLibrary::EqualEqual_FBPActorGripInformation(const FBPActorGripInformation& A, const FBPActorGripInformation& B)
{
return A == B;
}
bool UVRExpansionFunctionLibrary::IsActiveGrip(const FBPActorGripInformation& Grip)
{
return Grip.IsActive();
}
FTransform_NetQuantize UVRExpansionFunctionLibrary::MakeTransform_NetQuantize(FVector Translation, FRotator Rotation, FVector Scale)
{
return FTransform_NetQuantize(Rotation, Translation, Scale);
}
void UVRExpansionFunctionLibrary::BreakTransform_NetQuantize(const FTransform_NetQuantize& InTransform, FVector& Translation, FRotator& Rotation, FVector& Scale)
{
Translation = InTransform.GetLocation();
Rotation = InTransform.Rotator();
Scale = InTransform.GetScale3D();
}
FTransform_NetQuantize UVRExpansionFunctionLibrary::Conv_TransformToTransformNetQuantize(const FTransform& InTransform)
{
return FTransform_NetQuantize(InTransform);
}
UGripMotionControllerComponent* UVRExpansionFunctionLibrary::Conv_GripPairToMotionController(const FBPGripPair& GripPair)
{
return GripPair.HoldingController;
}
uint8 UVRExpansionFunctionLibrary::Conv_GripPairToGripID(const FBPGripPair& GripPair)
{
return GripPair.GripID;
}
FVector_NetQuantize UVRExpansionFunctionLibrary::Conv_FVectorToFVectorNetQuantize(const FVector& InVector)
{
return FVector_NetQuantize(InVector);
}
FVector_NetQuantize UVRExpansionFunctionLibrary::MakeVector_NetQuantize(FVector InVector)
{
return FVector_NetQuantize(InVector);
}
FVector_NetQuantize10 UVRExpansionFunctionLibrary::Conv_FVectorToFVectorNetQuantize10(const FVector& InVector)
{
return FVector_NetQuantize10(InVector);
}
FVector_NetQuantize10 UVRExpansionFunctionLibrary::MakeVector_NetQuantize10(FVector InVector)
{
return FVector_NetQuantize10(InVector);
}
FVector_NetQuantize100 UVRExpansionFunctionLibrary::Conv_FVectorToFVectorNetQuantize100(const FVector& InVector)
{
return FVector_NetQuantize100(InVector);
}
FVector_NetQuantize100 UVRExpansionFunctionLibrary::MakeVector_NetQuantize100(FVector InVector)
{
return FVector_NetQuantize100(InVector);
}
USceneComponent* UVRExpansionFunctionLibrary::AddSceneComponentByClass(UObject* Outer, TSubclassOf<USceneComponent> Class, const FTransform& ComponentRelativeTransform)
{
if (Class != nullptr && Outer != nullptr)
{
USceneComponent* Component = NewObject<USceneComponent>(Outer, *Class);
if (Component != nullptr)
{
if (USceneComponent* ParentComp = Cast<USceneComponent>(Outer))
Component->SetupAttachment(ParentComp);
Component->RegisterComponent();
Component->SetRelativeTransform(ComponentRelativeTransform);
return Component;
}
else
{
return nullptr;
}
}
return nullptr;
}
void UVRExpansionFunctionLibrary::SmoothUpdateLaserSpline(USplineComponent* LaserSplineComponent, TArray<USplineMeshComponent*> LaserSplineMeshComponents, FVector InStartLocation, FVector InEndLocation, FVector InForward, float LaserRadius)
{
if (LaserSplineComponent == nullptr)
return;
LaserSplineComponent->ClearSplinePoints();
const FVector SmoothLaserDirection = InEndLocation - InStartLocation;
float Distance = SmoothLaserDirection.Size();
const FVector StraightLaserEndLocation = InStartLocation + (InForward * Distance);
const int32 NumLaserSplinePoints = LaserSplineMeshComponents.Num();
LaserSplineComponent->AddSplinePoint(InStartLocation, ESplineCoordinateSpace::World, false);
for (int32 Index = 1; Index < NumLaserSplinePoints; Index++)
{
float Alpha = (float)Index / (float)NumLaserSplinePoints;
Alpha = FMath::Sin(Alpha * PI * 0.5f);
const FVector PointOnStraightLaser = FMath::Lerp(InStartLocation, StraightLaserEndLocation, Alpha);
const FVector PointOnSmoothLaser = FMath::Lerp(InStartLocation, InEndLocation, Alpha);
const FVector PointBetweenLasers = FMath::Lerp(PointOnStraightLaser, PointOnSmoothLaser, Alpha);
LaserSplineComponent->AddSplinePoint(PointBetweenLasers, ESplineCoordinateSpace::World, false);
}
LaserSplineComponent->AddSplinePoint(InEndLocation, ESplineCoordinateSpace::World, false);
// Update all the segments of the spline
LaserSplineComponent->UpdateSpline();
const float LaserPointerRadius = LaserRadius;
Distance *= 0.0001f;
for (int32 Index = 0; Index < NumLaserSplinePoints; Index++)
{
USplineMeshComponent* SplineMeshComponent = LaserSplineMeshComponents[Index];
check(SplineMeshComponent != nullptr);
FVector StartLoc, StartTangent, EndLoc, EndTangent;
LaserSplineComponent->GetLocationAndTangentAtSplinePoint(Index, StartLoc, StartTangent, ESplineCoordinateSpace::Local);
LaserSplineComponent->GetLocationAndTangentAtSplinePoint(Index + 1, EndLoc, EndTangent, ESplineCoordinateSpace::Local);
const float AlphaIndex = (float)Index / (float)NumLaserSplinePoints;
const float AlphaDistance = Distance * AlphaIndex;
float Radius = LaserPointerRadius * ((AlphaIndex * AlphaDistance) + 1);
FVector2D LaserScale(Radius, Radius);
SplineMeshComponent->SetStartScale(LaserScale, false);
const float NextAlphaIndex = (float)(Index + 1) / (float)NumLaserSplinePoints;
const float NextAlphaDistance = Distance * NextAlphaIndex;
Radius = LaserPointerRadius * ((NextAlphaIndex * NextAlphaDistance) + 1);
LaserScale = FVector2D(Radius, Radius);
SplineMeshComponent->SetEndScale(LaserScale, false);
SplineMeshComponent->SetStartAndEnd(StartLoc, StartTangent, EndLoc, EndTangent, true);
}
}
bool UVRExpansionFunctionLibrary::MatchesAnyTagsWithDirectParentTag(FGameplayTag DirectParentTag, const FGameplayTagContainer& BaseContainer, const FGameplayTagContainer& OtherContainer)
{
TArray<FGameplayTag> BaseContainerTags;
BaseContainer.GetGameplayTagArray(BaseContainerTags);
for (const FGameplayTag& OtherTag : BaseContainerTags)
{
if (OtherTag.RequestDirectParent().MatchesTagExact(DirectParentTag))
{
if (OtherContainer.HasTagExact(OtherTag))
return true;
}
}
return false;
}
bool UVRExpansionFunctionLibrary::GetFirstGameplayTagWithExactParent(FGameplayTag DirectParentTag, const FGameplayTagContainer& BaseContainer, FGameplayTag& FoundTag)
{
TArray<FGameplayTag> BaseContainerTags;
BaseContainer.GetGameplayTagArray(BaseContainerTags);
for (const FGameplayTag& OtherTag : BaseContainerTags)
{
if (OtherTag.RequestDirectParent().MatchesTagExact(DirectParentTag))
{
FoundTag = OtherTag;
return true;
}
}
return false;
}
void UVRExpansionFunctionLibrary::ResetPeakLowPassFilter(UPARAM(ref) FBPLowPassPeakFilter& TargetPeakFilter)
{
TargetPeakFilter.Reset();
}
void UVRExpansionFunctionLibrary::UpdatePeakLowPassFilter(UPARAM(ref) FBPLowPassPeakFilter& TargetPeakFilter, FVector NewSample)
{
TargetPeakFilter.AddSample(NewSample);
}
FVector UVRExpansionFunctionLibrary::GetPeak_PeakLowPassFilter(UPARAM(ref) FBPLowPassPeakFilter& TargetPeakFilter)
{
return TargetPeakFilter.GetPeak();
}
void UVRExpansionFunctionLibrary::ResetEuroSmoothingFilter(UPARAM(ref) FBPEuroLowPassFilter& TargetEuroFilter)
{
TargetEuroFilter.ResetSmoothingFilter();
}
void UVRExpansionFunctionLibrary::RunEuroSmoothingFilter(UPARAM(ref) FBPEuroLowPassFilter& TargetEuroFilter, FVector InRawValue, const float DeltaTime, FVector & SmoothedValue)
{
SmoothedValue = TargetEuroFilter.RunFilterSmoothing(InRawValue, DeltaTime);
}

View file

@ -0,0 +1,57 @@
// Copyright 1998-2016 Epic Games, Inc. All Rights Reserved.
#include "VRExpansionPlugin.h"
#include "Grippables/GrippablePhysicsReplication.h"
#include "VRGlobalSettings.h"
#include "ISettingsContainer.h"
#include "ISettingsModule.h"
#include "ISettingsSection.h"
#include "Physics/Experimental/PhysScene_Chaos.h"
#define LOCTEXT_NAMESPACE "FVRExpansionPluginModule"
void FVRExpansionPluginModule::StartupModule()
{
// This code will execute after your module is loaded into memory; the exact timing is specified in the .uplugin file per-module
RegisterSettings();
FPhysScene_Chaos::PhysicsReplicationFactory = MakeShared<IPhysicsReplicationFactoryVR>();
}
void FVRExpansionPluginModule::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.
UnregisterSettings();
}
void FVRExpansionPluginModule::RegisterSettings()
{
if (ISettingsModule* SettingsModule = FModuleManager::GetModulePtr<ISettingsModule>("Settings"))
{
// Create the new category
ISettingsContainerPtr SettingsContainer = SettingsModule->GetContainer("Project");
SettingsModule->RegisterSettings("Project", "Plugins", "VRExpansionPlugin",
LOCTEXT("VRExpansionSettingsName", "VRExpansion Settings"),
LOCTEXT("VRExpansionSettingsDescription", "Configure global settings for the VRExpansionPlugin"),
GetMutableDefault<UVRGlobalSettings>());
}
}
void FVRExpansionPluginModule::UnregisterSettings()
{
// Ensure to unregister all of your registered settings here, hot-reload would
// otherwise yield unexpected results.
if (ISettingsModule* SettingsModule = FModuleManager::GetModulePtr<ISettingsModule>("Settings"))
{
SettingsModule->UnregisterSettings("Project", "Plugins", "VRExpansionPlugin");
}
}
#undef LOCTEXT_NAMESPACE
IMPLEMENT_MODULE(FVRExpansionPluginModule, VRExpansionPlugin)

View file

@ -0,0 +1,20 @@
// Copyright 1998-2016 Epic Games, Inc. All Rights Reserved.
//#include "VRExpansionPlugin.h"
//#include "EngineMinimal.h"
//#include "VRBPDatatypes.h"
//#include "GripMotionControllerComponent.h"
//#include "VRExpansionFunctionLibrary.h"
//#include "ReplicatedVRCameraComponent.h"
//#include "ParentRelativeAttachmentComponent.h"
//#include "VRRootComponent.h"
//#include "VRBaseCharacterMovementComponent.h"
//#include "VRCharacterMovementComponent.h"
//#include "VRCharacter.h"
//#include "VRPathFollowingComponent.h"
//#include "VRPlayerController.h"
//#include "VRGripInterface.h"
//#include "Runtime/Launch/Resources/Version.h"
// You should place include statements to your module's private header files here. You only need to
// add includes for headers that are used in most of your module's source files though.

View file

@ -0,0 +1,791 @@
#include "VRGestureComponent.h"
#include UE_INLINE_GENERATED_CPP_BY_NAME(VRGestureComponent)
#include "VRBaseCharacter.h"
#include "Components/SplineMeshComponent.h"
#include "Components/SplineComponent.h"
#include "Components/LineBatchComponent.h"
#include "DrawDebugHelpers.h"
#include "Algo/Reverse.h"
#include "TimerManager.h"
DECLARE_CYCLE_STAT(TEXT("TickGesture ~ TickingGesture"), STAT_TickGesture, STATGROUP_TickGesture);
UVRGestureComponent::UVRGestureComponent(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{
PrimaryComponentTick.bCanEverTick = false;
//PrimaryComponentTick.bStartWithTickEnabled = false;
//PrimaryComponentTick.TickGroup = TG_PrePhysics;
//PrimaryComponentTick.bTickEvenWhenPaused = false;
maxSlope = 3;// INT_MAX;
//globalThreshold = 10.0f;
SameSampleTolerance = 0.1f;
bGestureChanged = false;
MirroringHand = EVRGestureMirrorMode::GES_NoMirror;
bDrawSplinesCurved = true;
bGetGestureInWorldSpace = true;
SplineMeshScaler = FVector2D(1.f);
}
void UGesturesDatabase::FillSplineWithGesture(FVRGesture &Gesture, USplineComponent * SplineComponent, bool bCenterPointsOnSpline, bool bScaleToBounds, float OptionalBounds, bool bUseCurvedPoints, bool bFillInSplineMeshComponents, UStaticMesh * Mesh, UMaterial * MeshMat)
{
if (!SplineComponent || Gesture.Samples.Num() < 2)
return;
UWorld* InWorld = GEngine->GetWorldFromContextObject(SplineComponent, EGetWorldErrorMode::LogAndReturnNull);
if (!InWorld)
return;
SplineComponent->ClearSplinePoints(false);
FVector PointOffset = FVector::ZeroVector;
float Scaler = 1.0f;
if (bScaleToBounds && OptionalBounds > 0.0f)
{
Scaler = OptionalBounds / Gesture.GestureSize.GetSize().GetMax();
}
if (bCenterPointsOnSpline)
{
PointOffset = -Gesture.GestureSize.GetCenter();
}
int curIndex = 0;
for (int i = Gesture.Samples.Num() - 1; i >= 0; --i)
{
SplineComponent->AddSplinePoint((Gesture.Samples[i] + PointOffset) * Scaler, ESplineCoordinateSpace::Local, false);
curIndex++;
SplineComponent->SetSplinePointType(curIndex, bUseCurvedPoints ? ESplinePointType::Curve : ESplinePointType::Linear, false);
}
// Update spline now
SplineComponent->UpdateSpline();
if (bFillInSplineMeshComponents && Mesh != nullptr && MeshMat != nullptr)
{
TArray<USplineMeshComponent *> CurrentSplineChildren;
TArray<USceneComponent*> Children;
SplineComponent->GetChildrenComponents(false, Children);
for (auto Child : Children)
{
USplineMeshComponent* SplineMesh = Cast<USplineMeshComponent>(Child);
if (SplineMesh != nullptr && IsValid(SplineMesh))
{
CurrentSplineChildren.Add(SplineMesh);
}
}
if (CurrentSplineChildren.Num() > SplineComponent->GetNumberOfSplinePoints() - 1)
{
int diff = CurrentSplineChildren.Num() - (CurrentSplineChildren.Num() - (SplineComponent->GetNumberOfSplinePoints() -1));
for (int i = CurrentSplineChildren.Num()- 1; i >= diff; --i)
{
if (!CurrentSplineChildren[i]->IsBeingDestroyed())
{
CurrentSplineChildren[i]->SetVisibility(false);
CurrentSplineChildren[i]->Modify();
CurrentSplineChildren[i]->DestroyComponent();
CurrentSplineChildren.RemoveAt(i);
}
}
}
else
{
for (int i = CurrentSplineChildren.Num(); i < SplineComponent->GetNumberOfSplinePoints() -1; ++i)
{
USplineMeshComponent * newSplineMesh = NewObject<USplineMeshComponent>(SplineComponent);
newSplineMesh->RegisterComponentWithWorld(InWorld);
newSplineMesh->SetMobility(EComponentMobility::Movable);
CurrentSplineChildren.Add(newSplineMesh);
newSplineMesh->SetStaticMesh(Mesh);
newSplineMesh->SetMaterial(0, (UMaterialInterface*)MeshMat);
newSplineMesh->AttachToComponent(SplineComponent, FAttachmentTransformRules::SnapToTargetIncludingScale);
newSplineMesh->SetVisibility(true);
}
}
for(int i=0; i<SplineComponent->GetNumberOfSplinePoints() - 1; i++)
{
CurrentSplineChildren[i]->SetStartAndEnd(SplineComponent->GetLocationAtSplinePoint(i, ESplineCoordinateSpace::Local),
SplineComponent->GetTangentAtSplinePoint(i, ESplineCoordinateSpace::Local),
SplineComponent->GetLocationAtSplinePoint(i + 1, ESplineCoordinateSpace::Local),
SplineComponent->GetTangentAtSplinePoint(i + 1, ESplineCoordinateSpace::Local),
true);
}
}
}
void UVRGestureComponent::BeginRecording(bool bRunDetection, bool bFlattenGesture, bool bDrawGesture, bool bDrawAsSpline, int SamplingHTZ, int SampleBufferSize, float ClampingTolerance)
{
RecordingBufferSize = SampleBufferSize;
RecordingDelta = 1.0f / SamplingHTZ;
RecordingClampingTolerance = ClampingTolerance;
bDrawRecordingGesture = bDrawGesture;
bDrawRecordingGestureAsSpline = bDrawAsSpline;
bRecordingFlattenGesture = bFlattenGesture;
GestureLog.GestureSize.Init();
// Reset does the reserve already
GestureLog.Samples.Reset(RecordingBufferSize);
CurrentState = bRunDetection ? EVRGestureState::GES_Detecting : EVRGestureState::GES_Recording;
if (TargetCharacter != nullptr)
{
OriginatingTransform = TargetCharacter->OffsetComponentToWorld;
if (!bGetGestureInWorldSpace)
{
ParentRelativeTransform = OriginatingTransform.GetRelativeTransform(TargetCharacter->GetActorTransform());
}
else
{
ParentRelativeTransform = FTransform::Identity;
}
}
else if (AVRBaseCharacter * own = Cast<AVRBaseCharacter>(GetOwner()))
{
TargetCharacter = own;
OriginatingTransform = TargetCharacter->OffsetComponentToWorld;
if (!bGetGestureInWorldSpace)
{
ParentRelativeTransform = OriginatingTransform.GetRelativeTransform(TargetCharacter->GetActorTransform());
}
else
{
ParentRelativeTransform = FTransform::Identity;
}
}
else
OriginatingTransform = this->GetComponentTransform();
StartVector = OriginatingTransform.InverseTransformPosition(this->GetComponentLocation());
// Reinit the drawing spline
if (!bDrawAsSpline || !bDrawGesture)
RecordingGestureDraw.Clear(); // Not drawing or not as a spline, remove the components if they exist
else
{
RecordingGestureDraw.Reset(); // Otherwise just clear points and hide mesh components
if (RecordingGestureDraw.SplineComponent == nullptr)
{
RecordingGestureDraw.SplineComponent = NewObject<USplineComponent>(GetAttachParent());
RecordingGestureDraw.SplineComponent->RegisterComponentWithWorld(GetWorld());
RecordingGestureDraw.SplineComponent->SetMobility(EComponentMobility::Movable);
}
RecordingGestureDraw.SplineComponent->ClearSplinePoints(true);
if (!bGetGestureInWorldSpace && TargetCharacter != nullptr)
{
RecordingGestureDraw.SplineComponent->AttachToComponent(TargetCharacter->GetRootComponent(), FAttachmentTransformRules::KeepRelativeTransform);
RecordingGestureDraw.SplineComponent->SetRelativeLocationAndRotation(ParentRelativeTransform.TransformPosition(StartVector), ParentRelativeTransform.GetRotation());
}
else
{
RecordingGestureDraw.SplineComponent->AttachToComponent(GetAttachParent(), FAttachmentTransformRules::KeepRelativeTransform);
}
}
this->SetComponentTickEnabled(true);
if (!TickGestureTimer_Handle.IsValid())
GetWorld()->GetTimerManager().SetTimer(TickGestureTimer_Handle, this, &UVRGestureComponent::TickGesture, RecordingDelta, true);
}
void UVRGestureComponent::CaptureGestureFrame()
{
FTransform CalcedTransform = OriginatingTransform;
if (!bGetGestureInWorldSpace)
{
if (TargetCharacter != nullptr)
{
CalcedTransform = ParentRelativeTransform * TargetCharacter->GetActorTransform();
}
else if (AVRBaseCharacter* own = Cast<AVRBaseCharacter>(GetOwner()))
{
TargetCharacter = own;
CalcedTransform = ParentRelativeTransform * TargetCharacter->GetActorTransform();
}
}
FVector NewSample = CalcedTransform.InverseTransformPosition(this->GetComponentLocation()) - StartVector;
if (CurrentState == EVRGestureState::GES_Recording)
{
if (bRecordingFlattenGesture)
NewSample.X = 0;
if (RecordingClampingTolerance > 0.0f)
{
NewSample.X = FMath::GridSnap(NewSample.X, RecordingClampingTolerance);
NewSample.Y = FMath::GridSnap(NewSample.Y, RecordingClampingTolerance);
NewSample.Z = FMath::GridSnap(NewSample.Z, RecordingClampingTolerance);
}
}
// Add in newest sample at beginning (reverse order)
if (NewSample != FVector::ZeroVector && (GestureLog.Samples.Num() < 1 || !GestureLog.Samples[0].Equals(NewSample, SameSampleTolerance)))
{
bool bClearLatestSpline = false;
// Pop off oldest sample
if (GestureLog.Samples.Num() >= RecordingBufferSize)
{
GestureLog.Samples.Pop(false);
bClearLatestSpline = true;
}
GestureLog.GestureSize.Max.X = FMath::Max(NewSample.X, GestureLog.GestureSize.Max.X);
GestureLog.GestureSize.Max.Y = FMath::Max(NewSample.Y, GestureLog.GestureSize.Max.Y);
GestureLog.GestureSize.Max.Z = FMath::Max(NewSample.Z, GestureLog.GestureSize.Max.Z);
GestureLog.GestureSize.Min.X = FMath::Min(NewSample.X, GestureLog.GestureSize.Min.X);
GestureLog.GestureSize.Min.Y = FMath::Min(NewSample.Y, GestureLog.GestureSize.Min.Y);
GestureLog.GestureSize.Min.Z = FMath::Min(NewSample.Z, GestureLog.GestureSize.Min.Z);
if (bDrawRecordingGesture && bDrawRecordingGestureAsSpline && SplineMesh != nullptr && SplineMaterial != nullptr)
{
if (bClearLatestSpline)
RecordingGestureDraw.ClearLastPoint();
RecordingGestureDraw.SplineComponent->AddSplinePoint(NewSample, ESplineCoordinateSpace::Local, false);
int SplineIndex = RecordingGestureDraw.SplineComponent->GetNumberOfSplinePoints() - 1;
RecordingGestureDraw.SplineComponent->SetSplinePointType(SplineIndex, bDrawSplinesCurved ? ESplinePointType::Curve : ESplinePointType::Linear, true);
bool bFoundEmptyMesh = false;
USplineMeshComponent * MeshComp = nullptr;
int MeshIndex = 0;
for (int i = 0; i < RecordingGestureDraw.SplineMeshes.Num(); i++)
{
MeshIndex = i;
MeshComp = RecordingGestureDraw.SplineMeshes[i];
if (MeshComp == nullptr)
{
RecordingGestureDraw.SplineMeshes[i] = NewObject<USplineMeshComponent>(RecordingGestureDraw.SplineComponent);
MeshComp = RecordingGestureDraw.SplineMeshes[i];
MeshComp->RegisterComponentWithWorld(GetWorld());
MeshComp->SetMobility(EComponentMobility::Movable);
//MeshComp->SetStaticMesh(SplineMesh);
//MeshComp->SetMaterial(0, (UMaterialInterface*)SplineMaterial);
bFoundEmptyMesh = true;
break;
}
else if (!MeshComp->IsVisible())
{
bFoundEmptyMesh = true;
break;
}
}
if (!bFoundEmptyMesh)
{
USplineMeshComponent * newSplineMesh = NewObject<USplineMeshComponent>(RecordingGestureDraw.SplineComponent);
MeshComp = newSplineMesh;
MeshComp->RegisterComponentWithWorld(GetWorld());
MeshComp->SetMobility(EComponentMobility::Movable);
RecordingGestureDraw.SplineMeshes.Add(MeshComp);
MeshIndex = RecordingGestureDraw.SplineMeshes.Num() - 1;
//MeshComp->SetStaticMesh(SplineMesh);
//MeshComp->SetMaterial(0, (UMaterialInterface*)SplineMaterial);
if (!bGetGestureInWorldSpace && TargetCharacter)
MeshComp->AttachToComponent(TargetCharacter->GetRootComponent(), FAttachmentTransformRules::KeepRelativeTransform);
}
if (MeshComp != nullptr)
{
// Fill in last mesh component tangent and end pos
if (RecordingGestureDraw.LastIndexSet != MeshIndex && RecordingGestureDraw.SplineMeshes[RecordingGestureDraw.LastIndexSet] != nullptr)
{
RecordingGestureDraw.SplineMeshes[RecordingGestureDraw.LastIndexSet]->SetEndPosition(NewSample, false);
RecordingGestureDraw.SplineMeshes[RecordingGestureDraw.LastIndexSet]->SetEndTangent(RecordingGestureDraw.SplineComponent->GetTangentAtSplinePoint(SplineIndex, ESplineCoordinateSpace::Local), true);
}
// Re-init mesh and material on the spline mesh, won't do anything if its the same
MeshComp->SetStaticMesh(SplineMesh);
MeshComp->SetMaterial(0, (UMaterialInterface*)SplineMaterial);
MeshComp->SetStartScale(SplineMeshScaler);
MeshComp->SetEndScale(SplineMeshScaler);
MeshComp->SetStartAndEnd(NewSample,
RecordingGestureDraw.SplineComponent->GetTangentAtSplinePoint(SplineIndex, ESplineCoordinateSpace::Local),
NewSample,
FVector::ZeroVector,
true);
//if (bGetGestureInWorldSpace)
MeshComp->SetWorldLocationAndRotation(CalcedTransform.TransformPosition(StartVector), CalcedTransform.GetRotation());
//else
//MeshComp->SetRelativeLocationAndRotation(/*OriginatingTransform.TransformPosition(*/StartVector/*)*/, FQuat::Identity/*OriginatingTransform.GetRotation()*/);
RecordingGestureDraw.LastIndexSet = MeshIndex;
MeshComp->SetVisibility(true);
}
}
GestureLog.Samples.Insert(NewSample, 0);
bGestureChanged = true;
}
}
void UVRGestureComponent::TickGesture()
{
SCOPE_CYCLE_COUNTER(STAT_TickGesture);
switch (CurrentState)
{
case EVRGestureState::GES_Detecting:
{
CaptureGestureFrame();
RecognizeGesture(GestureLog);
bGestureChanged = false;
}break;
case EVRGestureState::GES_Recording:
{
CaptureGestureFrame();
}break;
case EVRGestureState::GES_None:
default: {}break;
}
if (bDrawRecordingGesture)
{
if (!bDrawRecordingGestureAsSpline)
{
FTransform DrawTransform = FTransform(StartVector) * OriginatingTransform;
// Setting the lifetime to the recording htz now, should remove the flicker.
DrawDebugGesture(this, DrawTransform, GestureLog, FColor::White, false, 0, RecordingDelta, 0.0f);
}
}
}
void UVRGestureComponent::RecognizeGesture(FVRGesture inputGesture)
{
if (!GesturesDB || inputGesture.Samples.Num() < 1 || !bGestureChanged)
return;
float minDist = MAX_FLT;
int OutGestureIndex = -1;
bool bMirrorGesture = false;
FVector Size = inputGesture.GestureSize.GetSize();
float Scaler = GesturesDB->TargetGestureScale / Size.GetMax();
float FinalScaler = Scaler;
for (int i = 0; i < GesturesDB->Gestures.Num(); i++)
{
FVRGesture &exampleGesture = GesturesDB->Gestures[i];
if (!exampleGesture.GestureSettings.bEnabled || exampleGesture.Samples.Num() < 1 || inputGesture.Samples.Num() < exampleGesture.GestureSettings.Minimum_Gesture_Length)
continue;
FinalScaler = exampleGesture.GestureSettings.bEnableScaling ? Scaler : 1.f;
bMirrorGesture = (MirroringHand != EVRGestureMirrorMode::GES_NoMirror && MirroringHand != EVRGestureMirrorMode::GES_MirrorBoth && MirroringHand == exampleGesture.GestureSettings.MirrorMode);
if (GetGestureDistance(inputGesture.Samples[0] * FinalScaler, exampleGesture.Samples[0], bMirrorGesture) < FMath::Square(exampleGesture.GestureSettings.firstThreshold))
{
float d = dtw(inputGesture, exampleGesture, bMirrorGesture, FinalScaler) / (exampleGesture.Samples.Num());
if (d < minDist && d < FMath::Square(exampleGesture.GestureSettings.FullThreshold))
{
minDist = d;
OutGestureIndex = i;
}
}
else if (exampleGesture.GestureSettings.MirrorMode == EVRGestureMirrorMode::GES_MirrorBoth)
{
bMirrorGesture = true;
if (GetGestureDistance(inputGesture.Samples[0] * FinalScaler, exampleGesture.Samples[0], bMirrorGesture) < FMath::Square(exampleGesture.GestureSettings.firstThreshold))
{
float d = dtw(inputGesture, exampleGesture, bMirrorGesture, FinalScaler) / (exampleGesture.Samples.Num());
if (d < minDist && d < FMath::Square(exampleGesture.GestureSettings.FullThreshold))
{
minDist = d;
OutGestureIndex = i;
}
}
}
/*if (exampleGesture.MirrorMode == EVRGestureMirrorMode::GES_MirrorBoth)
{
bMirrorGesture = true;
if (GetGestureDistance(inputGesture.Samples[0], exampleGesture.Samples[0], bMirrorGesture) < FMath::Square(exampleGesture.GestureSettings.firstThreshold))
{
float d = dtw(inputGesture, exampleGesture, bMirrorGesture) / (exampleGesture.Samples.Num());
if (d < minDist && d < FMath::Square(exampleGesture.GestureSettings.FullThreshold))
{
minDist = d;
OutGestureIndex = i;
}
}
}*/
}
if (/*minDist < FMath::Square(globalThreshold) && */OutGestureIndex != -1)
{
OnGestureDetected(GesturesDB->Gestures[OutGestureIndex].GestureType, /*minDist,*/ GesturesDB->Gestures[OutGestureIndex].Name, OutGestureIndex, GesturesDB, Size);
OnGestureDetected_Bind.Broadcast(GesturesDB->Gestures[OutGestureIndex].GestureType, /*minDist,*/ GesturesDB->Gestures[OutGestureIndex].Name, OutGestureIndex, GesturesDB, Size);
ClearRecording(); // Clear the recording out, we don't want to detect this gesture again with the same data
RecordingGestureDraw.Reset();
}
}
float UVRGestureComponent::dtw(FVRGesture seq1, FVRGesture seq2, bool bMirrorGesture, float Scaler)
{
// #TODO: Skip copying the array and reversing it in the future, we only ever use the reversed value.
// So pre-reverse it and keep it stored like that on init. When we do the initial sample we can check off of the first index instead of last then
// Should also be able to get SizeSquared for values and compared to squared thresholds instead of doing the full SQRT calc.
// Getting number of average samples recorded over of a gesture (top down) may be able to achieve a basic % completed check
// to see how far into detecting a gesture we are, this would require ignoring the last position threshold though....
int RowCount = seq1.Samples.Num() + 1;
int ColumnCount = seq2.Samples.Num() + 1;
TArray<float> LookupTable;
LookupTable.AddZeroed(ColumnCount * RowCount);
TArray<int> SlopeI;
SlopeI.AddZeroed(ColumnCount * RowCount);
TArray<int> SlopeJ;
SlopeJ.AddZeroed(ColumnCount * RowCount);
for (int i = 1; i < (ColumnCount * RowCount); i++)
{
LookupTable[i] = MAX_FLT;
}
// Don't need to do this, it is already handled by add zeroed
//tab[0, 0] = 0;
int icol = 0, icolneg = 0;
// Dynamic computation of the DTW matrix.
for (int i = 1; i < RowCount; i++)
{
for (int j = 1; j < ColumnCount; j++)
{
icol = i * ColumnCount;
icolneg = icol - ColumnCount;// (i - 1) * ColumnCount;
if (
LookupTable[icol + (j - 1)] < LookupTable[icolneg + (j - 1)] &&
LookupTable[icol + (j - 1)] < LookupTable[icolneg + j] &&
SlopeI[icol + (j - 1)] < maxSlope)
{
LookupTable[icol + j] = GetGestureDistance(seq1.Samples[i - 1] * Scaler, seq2.Samples[j - 1], bMirrorGesture) + LookupTable[icol + j - 1];
SlopeI[icol + j] = SlopeJ[icol + j - 1] + 1;
SlopeJ[icol + j] = 0;
}
else if (
LookupTable[icolneg + j] < LookupTable[icolneg + j - 1] &&
LookupTable[icolneg + j] < LookupTable[icol + j - 1] &&
SlopeJ[icolneg + j] < maxSlope)
{
LookupTable[icol + j] = GetGestureDistance(seq1.Samples[i - 1] * Scaler, seq2.Samples[j - 1], bMirrorGesture) + LookupTable[icolneg + j];
SlopeI[icol + j] = 0;
SlopeJ[icol + j] = SlopeJ[icolneg + j] + 1;
}
else
{
LookupTable[icol + j] = GetGestureDistance(seq1.Samples[i - 1] * Scaler, seq2.Samples[j - 1], bMirrorGesture) + LookupTable[icolneg + j - 1];
SlopeI[icol + j] = 0;
SlopeJ[icol + j] = 0;
}
}
}
// Find best between seq2 and an ending (postfix) of seq1.
float bestMatch = FLT_MAX;
for (int i = 1; i < seq1.Samples.Num() + 1/* - seq2.Minimum_Gesture_Length*/; i++)
{
if (LookupTable[(i*ColumnCount) + seq2.Samples.Num()] < bestMatch)
bestMatch = LookupTable[(i*ColumnCount) + seq2.Samples.Num()];
}
return bestMatch;
}
void UVRGestureComponent::DrawDebugGesture(UObject* WorldContextObject, FTransform &StartTransform, FVRGesture GestureToDraw, FColor const& Color, bool bPersistentLines, uint8 DepthPriority, float LifeTime, float Thickness)
{
#if ENABLE_DRAW_DEBUG
UWorld* InWorld = GEngine->GetWorldFromContextObject(WorldContextObject, EGetWorldErrorMode::LogAndReturnNull);
if (InWorld != nullptr)
{
// no debug line drawing on dedicated server
if (GEngine->GetNetMode(InWorld) != NM_DedicatedServer && GestureToDraw.Samples.Num() > 1)
{
bool bMirrorGesture = (MirroringHand != EVRGestureMirrorMode::GES_NoMirror && MirroringHand == GestureToDraw.GestureSettings.MirrorMode);
FVector MirrorVector = FVector(1.f, -1.f, 1.f); // Only mirroring on Y axis to flip Left/Right
// this means foreground lines can't be persistent
ULineBatchComponent* const LineBatcher = (InWorld ? ((DepthPriority == SDPG_Foreground) ? InWorld->ForegroundLineBatcher : ((bPersistentLines || (LifeTime > 0.f)) ? InWorld->PersistentLineBatcher : InWorld->LineBatcher)) : NULL);
if (LineBatcher != NULL)
{
float const LineLifeTime = (LifeTime > 0.f) ? LifeTime : LineBatcher->DefaultLifeTime;
TArray<FBatchedLine> Lines;
FBatchedLine Line;
Line.Color = Color;
Line.Thickness = Thickness;
Line.RemainingLifeTime = LineLifeTime;
Line.DepthPriority = DepthPriority;
FVector FirstLoc = bMirrorGesture ? GestureToDraw.Samples[GestureToDraw.Samples.Num() - 1] * MirrorVector : GestureToDraw.Samples[GestureToDraw.Samples.Num() - 1];
for (int i = GestureToDraw.Samples.Num() - 2; i >= 0; --i)
{
Line.Start = bMirrorGesture ? GestureToDraw.Samples[i] * MirrorVector : GestureToDraw.Samples[i];
Line.End = FirstLoc;
FirstLoc = Line.Start;
Line.End = StartTransform.TransformPosition(Line.End);
Line.Start = StartTransform.TransformPosition(Line.Start);
Lines.Add(Line);
}
LineBatcher->DrawLines(Lines);
}
}
}
#endif
}
void UGesturesDatabase::RecalculateGestures(bool bScaleToDatabase)
{
for (int i = 0; i < Gestures.Num(); ++i)
{
Gestures[i].CalculateSizeOfGesture(bScaleToDatabase, TargetGestureScale);
}
}
bool UGesturesDatabase::ImportSplineAsGesture(USplineComponent * HostSplineComponent, FString GestureName, bool bKeepSplineCurves, float SegmentLen, bool bScaleToDatabase)
{
FVRGesture NewGesture;
if (HostSplineComponent->GetNumberOfSplinePoints() < 2)
return false;
NewGesture.Name = GestureName;
FVector FirstPointPos = HostSplineComponent->GetLocationAtSplinePoint(0, ESplineCoordinateSpace::Local);
float LastDistance = 0.f;
float ThisDistance = 0.f;
FVector LastDistanceV;
FVector ThisDistanceV;
FVector DistNormal;
float DistAlongSegment = 0.f;
// Realign to xForward on the gesture, normally splines lay out as X to the right
FTransform Realignment = FTransform(FRotator(0.f, 90.f, 0.f), -FirstPointPos);
// Prefill the first point
NewGesture.Samples.Add(Realignment.TransformPosition(HostSplineComponent->GetLocationAtSplinePoint(HostSplineComponent->GetNumberOfSplinePoints() - 1, ESplineCoordinateSpace::Local)));
// Inserting in reverse order -2 so we start one down
for (int i = HostSplineComponent->GetNumberOfSplinePoints() - 2; i >= 0; --i)
{
if (bKeepSplineCurves)
{
LastDistance = HostSplineComponent->GetDistanceAlongSplineAtSplinePoint(i + 1);
ThisDistance = HostSplineComponent->GetDistanceAlongSplineAtSplinePoint(i);
DistAlongSegment = FMath::Abs(ThisDistance - LastDistance);
}
else
{
LastDistanceV = Realignment.TransformPosition(HostSplineComponent->GetLocationAtSplinePoint(i + 1, ESplineCoordinateSpace::Local));
ThisDistanceV = Realignment.TransformPosition(HostSplineComponent->GetLocationAtSplinePoint(i, ESplineCoordinateSpace::Local));
DistAlongSegment = FVector::Dist(ThisDistanceV, LastDistanceV);
DistNormal = ThisDistanceV - LastDistanceV;
DistNormal.Normalize();
}
float SegmentCount = FMath::FloorToFloat(DistAlongSegment / SegmentLen);
float OverFlow = FMath::Fmod(DistAlongSegment, SegmentLen);
if (SegmentCount < 1)
{
SegmentCount++;
}
float DistPerSegment = (DistAlongSegment / SegmentCount);
for (int j = 0; j < SegmentCount; j++)
{
if (j == SegmentCount - 1 && i > 0)
DistPerSegment += OverFlow;
if (bKeepSplineCurves)
{
LastDistance -= DistPerSegment;
if (j == SegmentCount - 1 && i > 0)
{
LastDistance = ThisDistance;
}
FVector loc = Realignment.TransformPosition(HostSplineComponent->GetLocationAtDistanceAlongSpline(LastDistance, ESplineCoordinateSpace::Local));
if (!loc.IsNearlyZero())
NewGesture.Samples.Add(loc);
}
else
{
LastDistanceV += DistPerSegment * DistNormal;
if (j == SegmentCount - 1 && i > 0)
{
LastDistanceV = ThisDistanceV;
}
if (!LastDistanceV.IsNearlyZero())
NewGesture.Samples.Add(LastDistanceV);
}
}
}
NewGesture.CalculateSizeOfGesture(bScaleToDatabase, this->TargetGestureScale);
Gestures.Add(NewGesture);
return true;
}
void FVRGestureSplineDraw::ClearLastPoint()
{
SplineComponent->RemoveSplinePoint(0, false);
if (SplineMeshes.Num() < NextIndexCleared + 1)
NextIndexCleared = 0;
SplineMeshes[NextIndexCleared]->SetVisibility(false);
NextIndexCleared++;
}
void FVRGestureSplineDraw::Reset()
{
if (SplineComponent != nullptr)
SplineComponent->ClearSplinePoints(true);
for (int i = SplineMeshes.Num() - 1; i >= 0; --i)
{
if (SplineMeshes[i] != nullptr)
SplineMeshes[i]->SetVisibility(false);
else
SplineMeshes.RemoveAt(i);
}
LastIndexSet = 0;
NextIndexCleared = 0;
}
void FVRGestureSplineDraw::Clear()
{
for (int i = 0; i < SplineMeshes.Num(); ++i)
{
if (SplineMeshes[i] != nullptr && !SplineMeshes[i]->IsBeingDestroyed())
{
SplineMeshes[i]->Modify();
SplineMeshes[i]->DestroyComponent();
}
}
SplineMeshes.Empty();
if (SplineComponent != nullptr)
{
SplineComponent->DestroyComponent();
SplineComponent = nullptr;
}
LastIndexSet = 0;
NextIndexCleared = 0;
}
FVRGestureSplineDraw::FVRGestureSplineDraw()
{
SplineComponent = nullptr;
NextIndexCleared = 0;
LastIndexSet = 0;
}
FVRGestureSplineDraw::~FVRGestureSplineDraw()
{
Clear();
}
void UVRGestureComponent::BeginDestroy()
{
Super::BeginDestroy();
RecordingGestureDraw.Clear();
if (TickGestureTimer_Handle.IsValid())
{
if (UWorld* MyWorld = GetWorld())
{
MyWorld->GetTimerManager().ClearTimer(TickGestureTimer_Handle);
}
}
}
void UVRGestureComponent::RecalculateGestureSize(FVRGesture & InputGesture, UGesturesDatabase * GestureDB)
{
if (GestureDB != nullptr)
InputGesture.CalculateSizeOfGesture(true, GestureDB->TargetGestureScale);
else
InputGesture.CalculateSizeOfGesture(false);
}
FVRGesture UVRGestureComponent::EndRecording()
{
if (TickGestureTimer_Handle.IsValid())
{
if (UWorld* MyWorld = GetWorld())
{
MyWorld->GetTimerManager().ClearTimer(TickGestureTimer_Handle);
}
}
this->SetComponentTickEnabled(false);
CurrentState = EVRGestureState::GES_None;
// Reset the recording gesture
RecordingGestureDraw.Reset();
return GestureLog;
}
void UVRGestureComponent::ClearRecording()
{
GestureLog.Samples.Reset(RecordingBufferSize);
}
void UVRGestureComponent::SaveRecording(FVRGesture &Recording, FString RecordingName, bool bScaleRecordingToDatabase)
{
if (GesturesDB)
{
Recording.CalculateSizeOfGesture(bScaleRecordingToDatabase, GesturesDB->TargetGestureScale);
Recording.Name = RecordingName;
GesturesDB->Gestures.Add(Recording);
}
}

View file

@ -0,0 +1,466 @@
#include "VRGlobalSettings.h"
#include UE_INLINE_GENERATED_CPP_BY_NAME(VRGlobalSettings)
#include "Chaos/ChaosConstraintSettings.h"
#include "Grippables/GrippableSkeletalMeshComponent.h"
UVRGlobalSettings::UVRGlobalSettings(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer),
bLerpHybridWithSweepGrips(true),
bOnlyLerpHybridRotation(false),
bHybridWithSweepUseDistanceBasedLerp(true),
HybridWithSweepLerpDuration(0.2f),
bUseGlobalLerpToHand(false),
bSkipLerpToHandIfHeld(false),
MinDistanceForLerp(10.0f),
LerpDuration(0.25f),
MinSpeedForLerp(100.f),
MaxSpeedForLerp(500.f),
LerpInterpolationMode(EVRLerpInterpolationMode::QuatInterp),
bUseCurve(false),
OneEuroMinCutoff(0.1f),
OneEuroCutoffSlope(10.0f),
OneEuroDeltaCutoff(10.0f),
CurrentControllerProfileInUse(NAME_None),
CurrentControllerProfileTransform(FTransform::Identity),
bUseSeperateHandTransforms(false),
CurrentControllerProfileTransformRight(FTransform::Identity)
{
DefaultGrippableCharacterMeshComponentClass = UGrippableSkeletalMeshComponent::StaticClass();
bUseCollisionModificationForCollisionIgnore = false;
CollisionIgnoreSubsystemUpdateRate = 1.f;
bUseChaosTranslationScalers = false;
bSetEngineChaosScalers = false;
LinearDriveStiffnessScale = 1.0f;// Chaos::ConstraintSettings::LinearDriveStiffnessScale();
LinearDriveDampingScale = 1.0f;// Chaos::ConstraintSettings::LinearDriveDampingScale();
AngularDriveStiffnessScale = 0.3f; // 1.5f// Chaos::ConstraintSettings::AngularDriveStiffnessScale();
AngularDriveDampingScale = 0.3f; // 1.5f// Chaos::ConstraintSettings::AngularDriveDampingScale();
// Constraint settings
JointStiffness = 1.0f;// Chaos::ConstraintSettings::JointStiffness();
SoftLinearStiffnessScale = 1.5f;// Chaos::ConstraintSettings::SoftLinearStiffnessScale();
SoftLinearDampingScale = 1.2f;// Chaos::ConstraintSettings::SoftLinearDampingScale();
SoftAngularStiffnessScale = 100000.f;// Chaos::ConstraintSettings::SoftAngularStiffnessScale();
SoftAngularDampingScale = 1000.f;// Chaos::ConstraintSettings::SoftAngularDampingScale();
JointLinearBreakScale = 1.0f; //Chaos::ConstraintSettings::LinearBreakScale();
JointAngularBreakScale = 1.0f; //Chaos::ConstraintSettings::AngularBreakScale();
}
TSubclassOf<class UGrippableSkeletalMeshComponent> UVRGlobalSettings::GetDefaultGrippableCharacterMeshComponentClass()
{
const UVRGlobalSettings* VRSettings = GetDefault<UVRGlobalSettings>();
if (VRSettings)
{
// Using a getter to stay safe from bricking peoples projects if they set it to none somehow
if (VRSettings->DefaultGrippableCharacterMeshComponentClass != nullptr)
{
return VRSettings->DefaultGrippableCharacterMeshComponentClass;
}
}
return UGrippableSkeletalMeshComponent::StaticClass();
}
bool UVRGlobalSettings::IsGlobalLerpEnabled()
{
const UVRGlobalSettings& VRSettings = *GetDefault<UVRGlobalSettings>();
return VRSettings.bUseGlobalLerpToHand;
}
FTransform UVRGlobalSettings::AdjustTransformByControllerProfile(FName OptionalControllerProfileName, const FTransform& SocketTransform, bool bIsRightHand)
{
const UVRGlobalSettings& VRSettings = *GetDefault<UVRGlobalSettings>();
if (OptionalControllerProfileName == NAME_None)
{
if (VRSettings.CurrentControllerProfileInUse != NAME_None)
{
// Use currently loaded transform
return SocketTransform * (((bIsRightHand && VRSettings.bUseSeperateHandTransforms) ? VRSettings.CurrentControllerProfileTransformRight : VRSettings.CurrentControllerProfileTransform));
}
// No override and no default, return base transform back
return SocketTransform;
}
// Had an override, find it if possible and use its transform
const FBPVRControllerProfile* FoundProfile = VRSettings.ControllerProfiles.FindByPredicate([OptionalControllerProfileName](const FBPVRControllerProfile& ArrayItem)
{
return ArrayItem.ControllerName == OptionalControllerProfileName;
});
if (FoundProfile)
{
return SocketTransform * (((bIsRightHand && VRSettings.bUseSeperateHandTransforms) ? FoundProfile->SocketOffsetTransformRightHand : FoundProfile->SocketOffsetTransform));
}
// Couldn't find it, return base transform
return SocketTransform;
}
FTransform UVRGlobalSettings::AdjustTransformByGivenControllerProfile(UPARAM(ref) FBPVRControllerProfile& ControllerProfile, const FTransform& SocketTransform, bool bIsRightHand)
{
// Use currently loaded transform
return SocketTransform * (((bIsRightHand && ControllerProfile.bUseSeperateHandOffsetTransforms) ? ControllerProfile.SocketOffsetTransformRightHand : ControllerProfile.SocketOffsetTransform));
// Couldn't find it, return base transform
return SocketTransform;
}
TArray<FBPVRControllerProfile> UVRGlobalSettings::GetControllerProfiles()
{
const UVRGlobalSettings& VRSettings = *GetDefault<UVRGlobalSettings>();
return VRSettings.ControllerProfiles;
}
void UVRGlobalSettings::OverwriteControllerProfile(UPARAM(ref)FBPVRControllerProfile& OverwritingProfile, bool bSaveOutToConfig)
{
UVRGlobalSettings& VRSettings = *GetMutableDefault<UVRGlobalSettings>();
for (int i = 0; i < VRSettings.ControllerProfiles.Num(); ++i)
{
if (VRSettings.ControllerProfiles[i].ControllerName == OverwritingProfile.ControllerName)
{
VRSettings.ControllerProfiles[i] = OverwritingProfile;
}
}
if (bSaveOutToConfig)
SaveControllerProfiles();
}
void UVRGlobalSettings::AddControllerProfile(UPARAM(ref)FBPVRControllerProfile& NewProfile, bool bSaveOutToConfig)
{
UVRGlobalSettings& VRSettings = *GetMutableDefault<UVRGlobalSettings>();
VRSettings.ControllerProfiles.Add(NewProfile);
if (bSaveOutToConfig)
SaveControllerProfiles();
}
void UVRGlobalSettings::DeleteControllerProfile(FName ControllerProfileName, bool bSaveOutToConfig)
{
UVRGlobalSettings& VRSettings = *GetMutableDefault<UVRGlobalSettings>();
for (int i = VRSettings.ControllerProfiles.Num() - 1; i >= 0; --i)
{
if (VRSettings.ControllerProfiles[i].ControllerName == ControllerProfileName)
{
VRSettings.ControllerProfiles.RemoveAt(i);
}
}
if (bSaveOutToConfig)
SaveControllerProfiles();
}
void UVRGlobalSettings::SaveControllerProfiles()
{
UVRGlobalSettings& VRSettings = *GetMutableDefault<UVRGlobalSettings>();
VRSettings.SaveConfig();
//VRSettings.SaveConfig(CPF_Config, *VRSettings.GetGlobalUserConfigFilename());//VRSettings.GetDefaultConfigFilename());
}
FName UVRGlobalSettings::GetCurrentProfileName(bool& bHadLoadedProfile)
{
const UVRGlobalSettings& VRSettings = *GetDefault<UVRGlobalSettings>();
bHadLoadedProfile = VRSettings.CurrentControllerProfileInUse != NAME_None;
return VRSettings.CurrentControllerProfileInUse;
}
FBPVRControllerProfile UVRGlobalSettings::GetCurrentProfile(bool& bHadLoadedProfile)
{
const UVRGlobalSettings& VRSettings = *GetDefault<UVRGlobalSettings>();
FName ControllerProfileName = VRSettings.CurrentControllerProfileInUse;
const FBPVRControllerProfile* FoundProfile = VRSettings.ControllerProfiles.FindByPredicate([ControllerProfileName](const FBPVRControllerProfile& ArrayItem)
{
return ArrayItem.ControllerName == ControllerProfileName;
});
bHadLoadedProfile = FoundProfile != nullptr;
if (bHadLoadedProfile)
{
return *FoundProfile;
}
else
return FBPVRControllerProfile();
}
bool UVRGlobalSettings::GetControllerProfile(FName ControllerProfileName, FBPVRControllerProfile& OutProfile)
{
const UVRGlobalSettings& VRSettings = *GetDefault<UVRGlobalSettings>();
const FBPVRControllerProfile* FoundProfile = VRSettings.ControllerProfiles.FindByPredicate([ControllerProfileName](const FBPVRControllerProfile& ArrayItem)
{
return ArrayItem.ControllerName == ControllerProfileName;
});
if (FoundProfile)
{
OutProfile = *FoundProfile;
return true;
}
return false;
}
bool UVRGlobalSettings::LoadControllerProfileByName(FName ControllerProfileName, bool bSetAsCurrentProfile)
{
const UVRGlobalSettings& VRSettings = *GetDefault<UVRGlobalSettings>();
const FBPVRControllerProfile* FoundProfile = VRSettings.ControllerProfiles.FindByPredicate([ControllerProfileName](const FBPVRControllerProfile& ArrayItem)
{
return ArrayItem.ControllerName == ControllerProfileName;
});
if (FoundProfile)
{
return LoadControllerProfile(*FoundProfile, bSetAsCurrentProfile);
}
UE_LOG(LogTemp, Warning, TEXT("Could not find controller profile!: %s"), *ControllerProfileName.ToString());
return false;
}
bool UVRGlobalSettings::LoadControllerProfile(const FBPVRControllerProfile& ControllerProfile, bool bSetAsCurrentProfile)
{
/*
UInputSettings* InputSettings = GetMutableDefault<UInputSettings>();
if (false)//InputSettings != nullptr)
{
if (ControllerProfile.ActionOverrides.Num() > 0)
{
// Load button mappings
for (auto& Elem : ControllerProfile.ActionOverrides)
{
FName ActionName = Elem.Key;
FActionMappingDetails Mapping = Elem.Value;
// We allow for 0 mapped actions here in case you want to delete one
if (ActionName == NAME_None)
continue;
const TArray<FInputActionKeyMapping>& ActionMappings = InputSettings->GetActionMappings();
for (int32 ActionIndex = ActionMappings.Num() - 1; ActionIndex >= 0; --ActionIndex)
{
if (ActionMappings[ActionIndex].ActionName == ActionName)
{
InputSettings->RemoveActionMapping(ActionMappings[ActionIndex], false);
// we don't break because the mapping may have been in the array twice
}
}
// Then add the new bindings
for (FInputActionKeyMapping &KeyMapping : Mapping.ActionMappings)
{
// By default the key mappings don't have an action name, add them here
KeyMapping.ActionName = ActionName;
InputSettings->AddActionMapping(KeyMapping, false);
}
}
}
if (ControllerProfile.AxisOverrides.Num() > 0)
{
// Load axis mappings
for (auto& Elem : ControllerProfile.AxisOverrides)
{
FName AxisName = Elem.Key;
FAxisMappingDetails Mapping = Elem.Value;
// We allow for 0 mapped Axis's here in case you want to delete one
if (AxisName == NAME_None)
continue;
const TArray<FInputAxisKeyMapping>& AxisMappings = InputSettings->GetAxisMappings();
// Clear all Axis's that use our Axis name first
for (int32 AxisIndex = AxisMappings.Num() - 1; AxisIndex >= 0; --AxisIndex)
{
if (AxisMappings[AxisIndex].AxisName == AxisName)
{
InputSettings->RemoveAxisMapping(AxisMappings[AxisIndex], false);
// we don't break because the mapping may have been in the array twice
}
}
// Then add the new bindings
for (FInputAxisKeyMapping &KeyMapping : Mapping.AxisMappings)
{
// By default the key mappings don't have an Axis name, add them here
KeyMapping.AxisName = AxisName;
InputSettings->AddAxisMapping(KeyMapping, false);
}
}
}
if (ControllerProfile.ActionOverrides.Num() > 0 || ControllerProfile.AxisOverrides.Num() > 0)
{
// Tell all players to use the new keymappings
InputSettings->ForceRebuildKeymaps();
}
}*/
if (bSetAsCurrentProfile)
{
UVRGlobalSettings* VRSettings = GetMutableDefault<UVRGlobalSettings>();
if (VRSettings)
{
VRSettings->CurrentControllerProfileInUse = ControllerProfile.ControllerName;
VRSettings->CurrentControllerProfileTransform = ControllerProfile.SocketOffsetTransform;
ensure(!VRSettings->CurrentControllerProfileTransform.ContainsNaN());
VRSettings->bUseSeperateHandTransforms = ControllerProfile.bUseSeperateHandOffsetTransforms;
VRSettings->CurrentControllerProfileTransformRight = ControllerProfile.SocketOffsetTransformRightHand;
ensure(!VRSettings->CurrentControllerProfileTransformRight.ContainsNaN());
VRSettings->OnControllerProfileChangedEvent.Broadcast();
}
else
return false;
}
// Not saving key mapping in purpose, app will revert to default on next load and profiles will load custom changes
return true;
}
void UVRGlobalSettings::PostInitProperties()
{
#if WITH_EDITOR
// Not doing this currently, loading defaults is cool, and I may go back to it later when i get
// controller offsets for vive/touch/MR vs each other.
/*if (ControllerProfiles.Num() == 0)
{
ControllerProfiles.Add(FBPVRControllerProfile(TEXT("Vive_Wands")));
ControllerProfiles.Add(FBPVRControllerProfile(TEXT("Oculus_Touch"), ControllerProfileStatics::OculusTouchStaticOffset));
this->SaveConfig(CPF_Config, *this->GetDefaultConfigFilename());
}*/
#endif
SetScalers();
Super::PostInitProperties();
}
#if WITH_EDITOR
void UVRGlobalSettings::PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent)
{
Super::PostEditChangeProperty(PropertyChangedEvent);
FProperty* PropertyThatChanged = PropertyChangedEvent.Property;
if (PropertyThatChanged != nullptr)
{
#if WITH_EDITORONLY_DATA
if (
PropertyThatChanged->GetFName() == GET_MEMBER_NAME_CHECKED(UVRGlobalSettings, bUseChaosTranslationScalers) ||
PropertyThatChanged->GetFName() == GET_MEMBER_NAME_CHECKED(UVRGlobalSettings, bSetEngineChaosScalers) ||
PropertyThatChanged->GetFName() == GET_MEMBER_NAME_CHECKED(UVRGlobalSettings, LinearDriveStiffnessScale) ||
PropertyThatChanged->GetFName() == GET_MEMBER_NAME_CHECKED(UVRGlobalSettings, LinearDriveDampingScale) ||
PropertyThatChanged->GetFName() == GET_MEMBER_NAME_CHECKED(UVRGlobalSettings, AngularDriveStiffnessScale) ||
PropertyThatChanged->GetFName() == GET_MEMBER_NAME_CHECKED(UVRGlobalSettings, AngularDriveDampingScale) ||
PropertyThatChanged->GetFName() == GET_MEMBER_NAME_CHECKED(UVRGlobalSettings, JointStiffness) ||
PropertyThatChanged->GetFName() == GET_MEMBER_NAME_CHECKED(UVRGlobalSettings, SoftLinearStiffnessScale) ||
PropertyThatChanged->GetFName() == GET_MEMBER_NAME_CHECKED(UVRGlobalSettings, SoftLinearDampingScale) ||
PropertyThatChanged->GetFName() == GET_MEMBER_NAME_CHECKED(UVRGlobalSettings, SoftAngularStiffnessScale) ||
PropertyThatChanged->GetFName() == GET_MEMBER_NAME_CHECKED(UVRGlobalSettings, SoftAngularDampingScale) ||
PropertyThatChanged->GetFName() == GET_MEMBER_NAME_CHECKED(UVRGlobalSettings, JointLinearBreakScale) ||
PropertyThatChanged->GetFName() == GET_MEMBER_NAME_CHECKED(UVRGlobalSettings, JointAngularBreakScale)
)
{
SetScalers();
}
#endif
}
}
#endif
void UVRGlobalSettings::SetScalers()
{
auto CVarLinearDriveStiffnessScale = IConsoleManager::Get().FindConsoleVariable(TEXT("p.Chaos.JointConstraint.LinearDriveStiffnessScale"));
auto CVarLinearDriveDampingScale = IConsoleManager::Get().FindConsoleVariable(TEXT("p.Chaos.JointConstraint.LinaearDriveDampingScale"));
auto CVarAngularDriveStiffnessScale = IConsoleManager::Get().FindConsoleVariable(TEXT("p.Chaos.JointConstraint.AngularDriveStiffnessScale"));
auto CVarAngularDriveDampingScale = IConsoleManager::Get().FindConsoleVariable(TEXT("p.Chaos.JointConstraint.AngularDriveDampingScale"));
// Constraint settings
auto CVarJointStiffness = IConsoleManager::Get().FindConsoleVariable(TEXT("p.Chaos.JointConstraint.JointStiffness"));
auto CVarSoftLinearStiffnessScale = IConsoleManager::Get().FindConsoleVariable(TEXT("p.Chaos.JointConstraint.SoftLinearStiffnessScale"));
auto CVarSoftLinearDampingScale = IConsoleManager::Get().FindConsoleVariable(TEXT("p.Chaos.JointConstraint.SoftLinearDampingScale"));
auto CVarSoftAngularStiffnessScale = IConsoleManager::Get().FindConsoleVariable(TEXT("p.Chaos.JointConstraint.SoftAngularStiffnessScale"));
auto CVarSoftAngularDampingScale = IConsoleManager::Get().FindConsoleVariable(TEXT("p.Chaos.JointConstraint.SoftAngularDampingScale"));
auto CVarJointLinearBreakScale = IConsoleManager::Get().FindConsoleVariable(TEXT("p.Chaos.JointConstraint.LinearBreakScale"));
auto CVarJointAngularBreakScale = IConsoleManager::Get().FindConsoleVariable(TEXT("p.Chaos.JointConstraint.AngularBreakScale"));
if (bUseChaosTranslationScalers && bSetEngineChaosScalers)
{
CVarLinearDriveStiffnessScale->Set(LinearDriveStiffnessScale, EConsoleVariableFlags::ECVF_SetByCode);
CVarLinearDriveDampingScale->Set(LinearDriveDampingScale, EConsoleVariableFlags::ECVF_SetByCode);
CVarAngularDriveStiffnessScale->Set(AngularDriveStiffnessScale, EConsoleVariableFlags::ECVF_SetByCode);
CVarAngularDriveDampingScale->Set(AngularDriveDampingScale, EConsoleVariableFlags::ECVF_SetByCode);
// Constraint settings
CVarJointStiffness->Set(JointStiffness, EConsoleVariableFlags::ECVF_SetByCode);
CVarSoftLinearStiffnessScale->Set(SoftLinearStiffnessScale, EConsoleVariableFlags::ECVF_SetByCode);
CVarSoftLinearDampingScale->Set(SoftLinearDampingScale, EConsoleVariableFlags::ECVF_SetByCode);
CVarSoftAngularStiffnessScale->Set(SoftAngularStiffnessScale, EConsoleVariableFlags::ECVF_SetByCode);
CVarSoftAngularDampingScale->Set(SoftAngularDampingScale, EConsoleVariableFlags::ECVF_SetByCode);
CVarJointLinearBreakScale->Set(JointLinearBreakScale, EConsoleVariableFlags::ECVF_SetByCode);
CVarJointAngularBreakScale->Set(JointAngularBreakScale, EConsoleVariableFlags::ECVF_SetByCode);
}
else if (!bSetEngineChaosScalers)
{
CVarLinearDriveStiffnessScale->Set(1.0f, EConsoleVariableFlags::ECVF_SetByCode);
CVarLinearDriveDampingScale->Set(1.0f, EConsoleVariableFlags::ECVF_SetByCode);
CVarAngularDriveStiffnessScale->Set(1.5f, EConsoleVariableFlags::ECVF_SetByCode);
CVarAngularDriveDampingScale->Set(1.5f, EConsoleVariableFlags::ECVF_SetByCode);
// Constraint settings
CVarJointStiffness->Set(1.0f, EConsoleVariableFlags::ECVF_SetByCode);
CVarSoftLinearStiffnessScale->Set(1.5f, EConsoleVariableFlags::ECVF_SetByCode);
CVarSoftLinearDampingScale->Set(1.2f, EConsoleVariableFlags::ECVF_SetByCode);
CVarSoftAngularStiffnessScale->Set(100000.f, EConsoleVariableFlags::ECVF_SetByCode);
CVarSoftAngularDampingScale->Set(1000.f, EConsoleVariableFlags::ECVF_SetByCode);
CVarJointLinearBreakScale->Set(1.0f, EConsoleVariableFlags::ECVF_SetByCode);
CVarJointAngularBreakScale->Set(1.0f, EConsoleVariableFlags::ECVF_SetByCode);
}
}
void UVRGlobalSettings::GetMeleeSurfaceGlobalSettings(TArray<FBPHitSurfaceProperties>& OutMeleeSurfaceSettings)
{
const UVRGlobalSettings& VRSettings = *GetDefault<UVRGlobalSettings>();
OutMeleeSurfaceSettings = VRSettings.MeleeSurfaceSettings;
}
void UVRGlobalSettings::GetVirtualStockGlobalSettings(FBPVirtualStockSettings& OutVirtualStockSettings)
{
const UVRGlobalSettings& VRSettings = *GetDefault<UVRGlobalSettings>();
OutVirtualStockSettings.bUseDistanceBasedStockSnapping = VRSettings.VirtualStockSettings.bUseDistanceBasedStockSnapping;
OutVirtualStockSettings.StockSnapDistance = VRSettings.VirtualStockSettings.StockSnapDistance;
OutVirtualStockSettings.StockSnapOffset = VRSettings.VirtualStockSettings.StockSnapOffset;
OutVirtualStockSettings.bSmoothStockHand = VRSettings.VirtualStockSettings.bSmoothStockHand;
OutVirtualStockSettings.SmoothingValueForStock = VRSettings.VirtualStockSettings.SmoothingValueForStock;
}
void UVRGlobalSettings::SaveVirtualStockGlobalSettings(FBPVirtualStockSettings NewVirtualStockSettings)
{
UVRGlobalSettings& VRSettings = *GetMutableDefault<UVRGlobalSettings>();
VRSettings.VirtualStockSettings = NewVirtualStockSettings;
VRSettings.SaveConfig();
}

View file

@ -0,0 +1,20 @@
// Fill out your copyright notice in the Description page of Project Settings.
#include "VRGripInterface.h"
#include UE_INLINE_GENERATED_CPP_BY_NAME(VRGripInterface)
#include "UObject/ObjectMacros.h"
#include "GripScripts/VRGripScriptBase.h"
#include "UObject/Interface.h"
UVRGripInterface::UVRGripInterface(const class FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{
}
void IVRGripInterface::Native_NotifyThrowGripDelegates(UGripMotionControllerComponent* Controller, bool bGripped, const FBPActorGripInformation& GripInformation, bool bWasSocketed)
{
}

View file

@ -0,0 +1,443 @@
// Copyright 1998-2016 Epic Games, Inc. All Rights Reserved.
#include "VRPathFollowingComponent.h"
#include UE_INLINE_GENERATED_CPP_BY_NAME(VRPathFollowingComponent)
//#include "Runtime/Engine/Private/EnginePrivate.h"
//#if ENGINE_MAJOR_VERSION == 4 && ENGINE_MINOR_VERSION >= 13
#include "Navigation/MetaNavMeshPath.h"
#include "NavLinkCustomInterface.h"
//#endif
// Force to use new movement comp
DEFINE_LOG_CATEGORY(LogPathFollowingVR);
void UVRPathFollowingComponent::SetMovementComponent(UNavMovementComponent* MoveComp)
{
Super::SetMovementComponent(MoveComp);
VRMovementComp = Cast<UVRBaseCharacterMovementComponent>(MovementComp);
if (VRMovementComp)
{
OnRequestFinished.AddUObject(VRMovementComp, &UVRBaseCharacterMovementComponent::OnMoveCompleted);
}
}
void UVRPathFollowingComponent::GetDebugStringTokens(TArray<FString>& Tokens, TArray<EPathFollowingDebugTokens::Type>& Flags) const
{
Tokens.Add(GetStatusDesc());
Flags.Add(EPathFollowingDebugTokens::Description);
if (Status != EPathFollowingStatus::Moving)
{
return;
}
FString& StatusDesc = Tokens[0];
if (Path.IsValid())
{
const int32 NumMoveSegments = (Path.IsValid() && Path->IsValid()) ? Path->GetPathPoints().Num() : -1;
const bool bIsDirect = (Path->CastPath<FAbstractNavigationPath>() != NULL);
const bool bIsCustomLink = CurrentCustomLinkOb.IsValid();
if (!bIsDirect)
{
StatusDesc += FString::Printf(TEXT(" (%d..%d/%d)%s"), MoveSegmentStartIndex + 1, MoveSegmentEndIndex + 1, NumMoveSegments,
bIsCustomLink ? TEXT(" (custom NavLink)") : TEXT(""));
}
else
{
StatusDesc += TEXT(" (direct)");
}
}
else
{
StatusDesc += TEXT(" (invalid path)");
}
// add debug params
float CurrentDot = 0.0f, CurrentDistance = 0.0f, CurrentHeight = 0.0f;
uint8 bFailedDot = 0, bFailedDistance = 0, bFailedHeight = 0;
DebugReachTest(CurrentDot, CurrentDistance, CurrentHeight, bFailedHeight, bFailedDistance, bFailedHeight);
Tokens.Add(TEXT("dot"));
Flags.Add(EPathFollowingDebugTokens::ParamName);
Tokens.Add(FString::Printf(TEXT("%.2f"), CurrentDot));
Flags.Add(bFailedDot ? EPathFollowingDebugTokens::FailedValue : EPathFollowingDebugTokens::PassedValue);
Tokens.Add(TEXT("dist2D"));
Flags.Add(EPathFollowingDebugTokens::ParamName);
Tokens.Add(FString::Printf(TEXT("%.0f"), CurrentDistance));
Flags.Add(bFailedDistance ? EPathFollowingDebugTokens::FailedValue : EPathFollowingDebugTokens::PassedValue);
Tokens.Add(TEXT("distZ"));
Flags.Add(EPathFollowingDebugTokens::ParamName);
Tokens.Add(FString::Printf(TEXT("%.0f"), CurrentHeight));
Flags.Add(bFailedHeight ? EPathFollowingDebugTokens::FailedValue : EPathFollowingDebugTokens::PassedValue);
}
void UVRPathFollowingComponent::PauseMove(FAIRequestID RequestID, EPathFollowingVelocityMode VelocityMode)
{
//UE_VLOG(GetOwner(), LogPathFollowing, Log, TEXT("PauseMove: RequestID(%u)"), RequestID);
if (Status == EPathFollowingStatus::Paused)
{
return;
}
if (RequestID.IsEquivalent(GetCurrentRequestId()))
{
if ((VelocityMode == EPathFollowingVelocityMode::Reset) && MovementComp && HasMovementAuthority())
{
MovementComp->StopMovementKeepPathing();
}
LocationWhenPaused = MovementComp ? (VRMovementComp != nullptr ? VRMovementComp->GetActorFeetLocationVR() : MovementComp->GetActorFeetLocation()) : FVector::ZeroVector;
PathTimeWhenPaused = Path.IsValid() ? Path->GetTimeStamp() : 0.;
Status = EPathFollowingStatus::Paused;
UpdateMoveFocus();
}
}
bool UVRPathFollowingComponent::ShouldCheckPathOnResume() const
{
bool bCheckPath = true;
if (MovementComp != NULL)
{
float AgentRadius = 0.0f, AgentHalfHeight = 0.0f;
MovementComp->GetOwner()->GetSimpleCollisionCylinder(AgentRadius, AgentHalfHeight);
const FVector CurrentLocation = (VRMovementComp != nullptr ? VRMovementComp->GetActorFeetLocation() : MovementComp->GetActorFeetLocation());
const FVector::FReal DeltaMove2DSq = (CurrentLocation - LocationWhenPaused).SizeSquared2D();
const FVector::FReal DeltaZ = FMath::Abs(CurrentLocation.Z - LocationWhenPaused.Z);
if (DeltaMove2DSq < FMath::Square(AgentRadius) && DeltaZ < (AgentHalfHeight * 0.5))
{
bCheckPath = false;
}
}
return bCheckPath;
}
int32 UVRPathFollowingComponent::DetermineStartingPathPoint(const FNavigationPath* ConsideredPath) const
{
int32 PickedPathPoint = INDEX_NONE;
if (ConsideredPath && ConsideredPath->IsValid())
{
// if we already have some info on where we were on previous path
// we can find out if there's a segment on new path we're currently at
if (MoveSegmentStartRef != INVALID_NAVNODEREF &&
MoveSegmentEndRef != INVALID_NAVNODEREF &&
ConsideredPath->GetNavigationDataUsed() != NULL)
{
// iterate every new path node and see if segment match
for (int32 PathPoint = 0; PathPoint < ConsideredPath->GetPathPoints().Num() - 1; ++PathPoint)
{
if (ConsideredPath->GetPathPoints()[PathPoint].NodeRef == MoveSegmentStartRef &&
ConsideredPath->GetPathPoints()[PathPoint + 1].NodeRef == MoveSegmentEndRef)
{
PickedPathPoint = PathPoint;
break;
}
}
}
if (MovementComp && PickedPathPoint == INDEX_NONE)
{
if (ConsideredPath->GetPathPoints().Num() > 2)
{
// check if is closer to first or second path point (don't assume AI's standing)
const FVector CurrentLocation = (VRMovementComp != nullptr ? VRMovementComp->GetActorFeetLocationVR() : MovementComp->GetActorFeetLocation());
const FVector PathPt0 = *ConsideredPath->GetPathPointLocation(0);
const FVector PathPt1 = *ConsideredPath->GetPathPointLocation(1);
// making this test in 2d to avoid situation where agent's Z location not being in "navmesh plane"
// would influence the result
const FVector::FReal SqDistToFirstPoint = (CurrentLocation - PathPt0).SizeSquared2D();
const FVector::FReal SqDistToSecondPoint = (CurrentLocation - PathPt1).SizeSquared2D();
PickedPathPoint = FMath::IsNearlyEqual(SqDistToFirstPoint, SqDistToSecondPoint) ?
((FMath::Abs(CurrentLocation.Z - PathPt0.Z) < FMath::Abs(CurrentLocation.Z - PathPt1.Z)) ? 0 : 1) :
((SqDistToFirstPoint < SqDistToSecondPoint) ? 0 : 1);
}
else
{
// If there are only two point we probably should start from the beginning
PickedPathPoint = 0;
}
}
}
return PickedPathPoint;
}
bool UVRPathFollowingComponent::UpdateBlockDetection()
{
const double GameTime = GetWorld()->GetTimeSeconds();
if (bUseBlockDetection &&
MovementComp &&
GameTime > (LastSampleTime + BlockDetectionInterval) &&
BlockDetectionSampleCount > 0)
{
LastSampleTime = GameTime;
if (LocationSamples.Num() == NextSampleIdx)
{
LocationSamples.AddZeroed(1);
}
LocationSamples[NextSampleIdx] = (VRMovementComp != nullptr ? VRMovementComp->GetActorFeetLocationBased() : MovementComp->GetActorFeetLocationBased());
NextSampleIdx = (NextSampleIdx + 1) % BlockDetectionSampleCount;
return true;
}
return false;
}
void UVRPathFollowingComponent::UpdatePathSegment()
{
#if !UE_BUILD_SHIPPING
DEBUG_bMovingDirectlyToGoal = false;
#endif // !UE_BUILD_SHIPPING
if ((Path.IsValid() == false) || (MovementComp == nullptr))
{
//UE_CVLOG(Path.IsValid() == false, this, LogPathFollowing, Log, TEXT("Aborting move due to not having a valid path object"));
OnPathFinished(EPathFollowingResult::Aborted, FPathFollowingResultFlags::InvalidPath);
return;
}
if (!Path->IsValid())
{
if (!Path->IsWaitingForRepath())
{
//UE_VLOG(this, LogPathFollowing, Log, TEXT("Aborting move due to path being invelid and not waiting for repath"));
OnPathFinished(EPathFollowingResult::Aborted, FPathFollowingResultFlags::InvalidPath);
}
return;
}
FMetaNavMeshPath* MetaNavPath = bIsUsingMetaPath ? Path->CastPath<FMetaNavMeshPath>() : nullptr;
// if agent has control over its movement, check finish conditions
const FVector CurrentLocation = (VRMovementComp != nullptr ? VRMovementComp->GetActorFeetLocationVR() : MovementComp->GetActorFeetLocation());
const bool bCanUpdateState = HasMovementAuthority();
if (bCanUpdateState && Status == EPathFollowingStatus::Moving)
{
const int32 LastSegmentEndIndex = Path->GetPathPoints().Num() - 1;
const bool bFollowingLastSegment = (MoveSegmentEndIndex >= LastSegmentEndIndex);
const bool bLastPathChunk = (MetaNavPath == nullptr || MetaNavPath->IsLastSection());
if (bCollidedWithGoal)
{
// check if collided with goal actor
OnSegmentFinished();
OnPathFinished(EPathFollowingResult::Success, FPathFollowingResultFlags::None);
}
else if (HasReachedDestination(CurrentLocation))
{
// always check for destination, acceptance radius may cause it to pass before reaching last segment
OnSegmentFinished();
OnPathFinished(EPathFollowingResult::Success, FPathFollowingResultFlags::None);
}
else if (bFollowingLastSegment && bMoveToGoalOnLastSegment && bLastPathChunk)
{
// use goal actor for end of last path segment
// UNLESS it's partial path (can't reach goal)
if (DestinationActor.IsValid() && Path->IsPartial() == false)
{
const FVector AgentLocation = DestinationAgent ? DestinationAgent->GetNavAgentLocation() : DestinationActor->GetActorLocation();
// note that the condition below requires GoalLocation to be in world space.
const FVector GoalLocation = FQuatRotationTranslationMatrix(DestinationActor->GetActorQuat(), AgentLocation).TransformPosition(MoveOffset);
CurrentDestination.Set(NULL, GoalLocation);
//UE_VLOG(this, LogPathFollowing, Log, TEXT("Moving directly to move goal rather than following last path segment"));
//UE_VLOG_LOCATION(this, LogPathFollowing, VeryVerbose, GoalLocation, 30, FColor::Green, TEXT("Last-segment-to-actor"));
//UE_VLOG_SEGMENT(this, LogPathFollowing, VeryVerbose, CurrentLocation, GoalLocation, FColor::Green, TEXT_EMPTY);
}
UpdateMoveFocus();
#if !UE_BUILD_SHIPPING
DEBUG_bMovingDirectlyToGoal = true;
#endif // !UE_BUILD_SHIPPING
}
// check if current move segment is finished
else if (HasReachedCurrentTarget(CurrentLocation))
{
OnSegmentFinished();
SetNextMoveSegment();
}
}
if (bCanUpdateState && Status == EPathFollowingStatus::Moving)
{
// check waypoint switch condition in meta paths
if (MetaNavPath && Status == EPathFollowingStatus::Moving)
{
MetaNavPath->ConditionalMoveToNextSection(CurrentLocation, EMetaPathUpdateReason::MoveTick);
}
// gather location samples to detect if moving agent is blocked
const bool bHasNewSample = UpdateBlockDetection();
if (bHasNewSample && IsBlocked())
{
if (Path->GetPathPoints().IsValidIndex(MoveSegmentEndIndex) && Path->GetPathPoints().IsValidIndex(MoveSegmentStartIndex))
{
//LogBlockHelper(GetOwner(), MovementComp, MinAgentRadiusPct, MinAgentHalfHeightPct,
//*Path->GetPathPointLocation(MoveSegmentStartIndex),
//*Path->GetPathPointLocation(MoveSegmentEndIndex));
}
else
{
if ((GetOwner() != NULL) && (MovementComp != NULL))
{
// UE_VLOG(GetOwner(), LogPathFollowing, Verbose, TEXT("Path blocked, but move segment indices are not valid: start %d, end %d of %d"), MoveSegmentStartIndex, MoveSegmentEndIndex, Path->GetPathPoints().Num());
}
}
OnPathFinished(EPathFollowingResult::Blocked, FPathFollowingResultFlags::None);
}
}
}
void UVRPathFollowingComponent::FollowPathSegment(float DeltaTime)
{
if (!Path.IsValid() || MovementComp == nullptr)
{
return;
}
const FVector CurrentLocation = (VRMovementComp != nullptr ? VRMovementComp->GetActorFeetLocationVR() : MovementComp->GetActorFeetLocation());
const FVector CurrentTarget = GetCurrentTargetLocation();
// set to false by default, we will set set this back to true if appropriate
bIsDecelerating = false;
const bool bAccelerationBased = MovementComp->UseAccelerationForPathFollowing();
if (bAccelerationBased)
{
CurrentMoveInput = (CurrentTarget - CurrentLocation).GetSafeNormal();
if (bStopMovementOnFinish && (MoveSegmentStartIndex >= DecelerationSegmentIndex))
{
const FVector PathEnd = Path->GetEndLocation();
const FVector::FReal DistToEndSq = FVector::DistSquared(CurrentLocation, PathEnd);
const bool bShouldDecelerate = DistToEndSq < FMath::Square(CachedBrakingDistance);
if (bShouldDecelerate)
{
bIsDecelerating = true;
const FVector::FReal SpeedPct = FMath::Clamp(FMath::Sqrt(DistToEndSq) / CachedBrakingDistance, 0., 1.);
CurrentMoveInput *= SpeedPct;
}
}
PostProcessMove.ExecuteIfBound(this, CurrentMoveInput);
MovementComp->RequestPathMove(CurrentMoveInput);
}
else
{
FVector MoveVelocity = (CurrentTarget - CurrentLocation) / DeltaTime;
const int32 LastSegmentStartIndex = Path->GetPathPoints().Num() - 2;
const bool bNotFollowingLastSegment = (MoveSegmentStartIndex < LastSegmentStartIndex);
PostProcessMove.ExecuteIfBound(this, MoveVelocity);
MovementComp->RequestDirectMove(MoveVelocity, bNotFollowingLastSegment);
}
}
bool UVRPathFollowingComponent::HasReachedCurrentTarget(const FVector& CurrentLocation) const
{
if (MovementComp == NULL)
{
return false;
}
const FVector CurrentTarget = GetCurrentTargetLocation();
const FVector CurrentDirection = GetCurrentDirection();
// check if moved too far
const FVector ToTarget = (CurrentTarget - (VRMovementComp != nullptr ? VRMovementComp->GetActorFeetLocationVR() : MovementComp->GetActorFeetLocation()));
const FVector::FReal SegmentDot = FVector::DotProduct(ToTarget, CurrentDirection);
if (SegmentDot < 0.)
{
return true;
}
// or standing at target position
// don't use acceptance radius here, it has to be exact for moving near corners (2D test < 5% of agent radius)
const float GoalRadius = 0.0f;
const float GoalHalfHeight = 0.0f;
return HasReachedInternal(CurrentTarget, GoalRadius, GoalHalfHeight, CurrentLocation, CurrentAcceptanceRadius, 0.05f);
}
void UVRPathFollowingComponent::DebugReachTest(float& CurrentDot, float& CurrentDistance, float& CurrentHeight, uint8& bDotFailed, uint8& bDistanceFailed, uint8& bHeightFailed) const
{
if (!Path.IsValid() || MovementComp == NULL)
{
return;
}
const int32 LastSegmentEndIndex = Path->GetPathPoints().Num() - 1;
const bool bFollowingLastSegment = (MoveSegmentEndIndex >= LastSegmentEndIndex);
float GoalRadius = 0.0f;
float GoalHalfHeight = 0.0f;
float RadiusThreshold = 0.0f;
float AgentRadiusPct = 0.05f;
FVector AgentLocation = (VRMovementComp != nullptr ? VRMovementComp->GetActorFeetLocationVR() : MovementComp->GetActorFeetLocation());
FVector GoalLocation = GetCurrentTargetLocation();
RadiusThreshold = CurrentAcceptanceRadius;
if (bFollowingLastSegment)
{
GoalLocation = *Path->GetPathPointLocation(Path->GetPathPoints().Num() - 1);
AgentRadiusPct = MinAgentRadiusPct;
// take goal's current location, unless path is partial or last segment doesn't reach goal actor (used by tethered AI)
if (DestinationActor.IsValid() && !Path->IsPartial() && bMoveToGoalOnLastSegment)
{
if (DestinationAgent)
{
FVector GoalOffset;
const AActor* OwnerActor = GetOwner();
DestinationAgent->GetMoveGoalReachTest(OwnerActor, MoveOffset, GoalOffset, GoalRadius, GoalHalfHeight);
GoalLocation = FQuatRotationTranslationMatrix(DestinationActor->GetActorQuat(), DestinationAgent->GetNavAgentLocation()).TransformPosition(GoalOffset);
}
else
{
GoalLocation = DestinationActor->GetActorLocation();
}
}
}
const FVector ToGoal = (GoalLocation - AgentLocation);
const FVector CurrentDirection = GetCurrentDirection();
CurrentDot = FloatCastChecked<float>(FVector::DotProduct(ToGoal.GetSafeNormal(), CurrentDirection), /* Precision */ 1. / 128.);
bDotFailed = (CurrentDot < 0.0f) ? 1 : 0;
// get cylinder of moving agent
float AgentRadius = 0.0f;
float AgentHalfHeight = 0.0f;
AActor* MovingAgent = MovementComp->GetOwner();
MovingAgent->GetSimpleCollisionCylinder(AgentRadius, AgentHalfHeight);
CurrentDistance = FloatCastChecked<float>(ToGoal.Size2D(), UE::LWC::DefaultFloatPrecision);
const float UseRadius = FMath::Max(RadiusThreshold, GoalRadius + (AgentRadius * AgentRadiusPct));
bDistanceFailed = (CurrentDistance > UseRadius) ? 1 : 0;
CurrentHeight = FloatCastChecked<float>(FMath::Abs(ToGoal.Z), UE::LWC::DefaultFloatPrecision);
const float UseHeight = GoalHalfHeight + (AgentHalfHeight * MinAgentHalfHeightPct);
bHeightFailed = (CurrentHeight > UseHeight) ? 1 : 0;
}

View file

@ -0,0 +1,122 @@
// Copyright 1998-2016 Epic Games, Inc. All Rights Reserved.
#include "VRPlayerController.h"
#include UE_INLINE_GENERATED_CPP_BY_NAME(VRPlayerController)
#include "AI/NavigationSystemBase.h"
#include "VRBaseCharacterMovementComponent.h"
#include "VRPathFollowingComponent.h"
//#include "VRBPDatatypes.h"
#include "Engine/Player.h"
//#include "Runtime/Engine/Private/EnginePrivate.h"
AVRPlayerController::AVRPlayerController(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{
bDisableServerUpdateCamera = true;
}
void AVRPlayerController::SpawnPlayerCameraManager()
{
Super::SpawnPlayerCameraManager();
// Turn off the default FOV and position replication of the camera manager, most functions should be sending values anyway and I am replicating
// the actual camera position myself so this is just wasted bandwidth
if(PlayerCameraManager != NULL && bDisableServerUpdateCamera)
PlayerCameraManager->bUseClientSideCameraUpdates = false;
}
// #TODO 4.20: This was removed
/*void AVRPlayerController::InitNavigationControl(UPathFollowingComponent*& PathFollowingComp)
{
PathFollowingComp = FindComponentByClass<UPathFollowingComponent>();
if (PathFollowingComp == NULL)
{
PathFollowingComp = NewObject<UVRPathFollowingComponent>(this);
PathFollowingComp->RegisterComponentWithWorld(GetWorld());
PathFollowingComp->Initialize();
}
}*/
/*IPathFollowingAgentInterface* AVRPlayerController::GetPathFollowingAgent() const
{
// Moved spawning the path following component into the path finding logic instead
return FNavigationSystem::FindPathFollowingAgentForActor(*this);
}*/
void AVRPlayerController::PlayerTick(float DeltaTime)
{
// #TODO: Should I be only doing this if ticking CMC and CMC is active?
if (AVRBaseCharacter * VRChar = Cast<AVRBaseCharacter>(GetPawn()))
{
// Keep from calling multiple times
UVRBaseCharacterMovementComponent * BaseCMC = Cast<UVRBaseCharacterMovementComponent>(VRChar->GetMovementComponent());
if (!BaseCMC || !BaseCMC->bRunControlRotationInMovementComponent)
return Super::PlayerTick(DeltaTime);
if (!bShortConnectTimeOut)
{
bShortConnectTimeOut = true;
ServerShortTimeout();
}
TickPlayerInput(DeltaTime, DeltaTime == 0.f);
LastRotationInput = RotationInput;
if ((Player != NULL) && (Player->PlayerController == this))
{
// Validate current state
bool bUpdateRotation = false;
if (IsInState(NAME_Playing))
{
if (GetPawn() == NULL)
{
ChangeState(NAME_Inactive);
}
else if (Player && GetPawn() == AcknowledgedPawn && (!BaseCMC || (BaseCMC && !BaseCMC->IsActive())))
{
bUpdateRotation = true;
}
}
if (IsInState(NAME_Inactive))
{
if (GetLocalRole() < ROLE_Authority)
{
SafeServerCheckClientPossession();
}
//bUpdateRotation = !IsFrozen();
}
else if (IsInState(NAME_Spectating))
{
if (GetLocalRole() < ROLE_Authority)
{
SafeServerUpdateSpectatorState();
}
// Keep it when spectating
bUpdateRotation = true;
}
// Update rotation
if (bUpdateRotation)
{
UpdateRotation(DeltaTime);
}
}
}
else
{
// Not our character, forget it
Super::PlayerTick(DeltaTime);
}
}
UVRLocalPlayer::UVRLocalPlayer(const FObjectInitializer & ObjectInitializer)
: Super(ObjectInitializer)
{
}

View file

@ -0,0 +1,72 @@
// Fill out your copyright notice in the Description page of Project Settings.
#include "VRTrackedParentInterface.h"
#include UE_INLINE_GENERATED_CPP_BY_NAME(VRTrackedParentInterface)
#include "UObject/Interface.h"
#include "VRBPDatatypes.h"
UVRTrackedParentInterface::UVRTrackedParentInterface(const class FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{
}
void IVRTrackedParentInterface::Default_SetTrackedParent_Impl(UPrimitiveComponent * NewParentComponent, float WaistRadius, EBPVRWaistTrackingMode WaistTrackingMode, FBPVRWaistTracking_Info & OptionalWaistTrackingParent, USceneComponent * Self)
{
// If had a different original tracked parent
// Moved this to first thing so the pre-res is removed prior to erroring out and clearing this
if (OptionalWaistTrackingParent.IsValid())
{
// Remove the tick Prerequisite
Self->RemoveTickPrerequisiteComponent(OptionalWaistTrackingParent.TrackedDevice);
}
if (!NewParentComponent || !Self)
{
OptionalWaistTrackingParent.Clear();
return;
}
// Make other component tick first if possible, waste of time if in wrong tick group
if (NewParentComponent->PrimaryComponentTick.TickGroup == Self->PrimaryComponentTick.TickGroup)
{
// Make sure the other component isn't depending on this one
NewParentComponent->RemoveTickPrerequisiteComponent(Self);
// Add a tick pre-res for ourselves so that we tick after our tracked parent.
Self->AddTickPrerequisiteComponent(NewParentComponent);
}
OptionalWaistTrackingParent.TrackedDevice = NewParentComponent;
OptionalWaistTrackingParent.RestingRotation = NewParentComponent->GetRelativeRotation();
OptionalWaistTrackingParent.RestingRotation.Yaw = 0.0f;
OptionalWaistTrackingParent.TrackingMode = WaistTrackingMode;
OptionalWaistTrackingParent.WaistRadius = WaistRadius;
}
FTransform IVRTrackedParentInterface::Default_GetWaistOrientationAndPosition(FBPVRWaistTracking_Info & WaistTrackingInfo)
{
if (!WaistTrackingInfo.IsValid())
return FTransform::Identity;
FTransform DeviceTransform = WaistTrackingInfo.TrackedDevice->GetRelativeTransform();
// Rewind by the initial rotation when the new parent was set, this should be where the tracker rests on the person
DeviceTransform.ConcatenateRotation(WaistTrackingInfo.RestingRotation.Quaternion().Inverse());
DeviceTransform.SetScale3D(FVector(1, 1, 1));
// Don't bother if not set
if (WaistTrackingInfo.WaistRadius > 0.0f)
{
DeviceTransform.AddToTranslation(DeviceTransform.GetRotation().RotateVector(FVector(-WaistTrackingInfo.WaistRadius, 0, 0)));
}
// This changes the forward vector to be correct
// I could pre do it by changed the yaw in resting mode to these values, but that had its own problems
// If given an initial forward vector that it should align to I wouldn't have to do this and could auto calculate it.
// But without that I am limited to this.
// #TODO: add optional ForwardVector to initial setup function that auto calculates offset so that the user can pass in HMD forward or something for calibration X+
// Also would be better overall because slightly offset from right angles in yaw wouldn't matter anymore, it would adjust for it.
switch (WaistTrackingInfo.TrackingMode)
{
case EBPVRWaistTrackingMode::VRWaist_Tracked_Front: DeviceTransform.ConcatenateRotation(FRotator(0, 0, 0).Quaternion()); break;
case EBPVRWaistTrackingMode::VRWaist_Tracked_Rear: DeviceTransform.ConcatenateRotation(FRotator(0, -180, 0).Quaternion()); break;
case EBPVRWaistTrackingMode::VRWaist_Tracked_Left: DeviceTransform.ConcatenateRotation(FRotator(0, 90, 0).Quaternion()); break;
case EBPVRWaistTrackingMode::VRWaist_Tracked_Right: DeviceTransform.ConcatenateRotation(FRotator(0, -90, 0).Quaternion()); break;
}
return DeviceTransform;
}

View file

@ -0,0 +1,747 @@
// Copyright 1998-2016 Epic Games, Inc. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "VRBPDatatypes.h"
#include "GameFramework/Character.h"
#include "Components/SkeletalMeshComponent.h"
#include "Engine/ScopedMovementUpdate.h"
#include "GameFramework/CharacterMovementComponent.h"
#include "CharacterMovementCompTypes.generated.h"
class AVRBaseCharacter;
class UVRBaseCharacterMovementComponent;
UENUM(Blueprintable)
enum class EVRMoveAction : uint8
{
VRMOVEACTION_None = 0x00,
VRMOVEACTION_SnapTurn = 0x01,
VRMOVEACTION_Teleport = 0x02,
VRMOVEACTION_StopAllMovement = 0x03,
VRMOVEACTION_SetRotation = 0x04,
VRMOVEACTION_PauseTracking = 0x14,
VRMOVEACTION_SetGravityDirection = 0x15, // Reserved from here up to 0x40
VRMOVEACTION_CUSTOM1 = 0x05,
VRMOVEACTION_CUSTOM2 = 0x06,
VRMOVEACTION_CUSTOM3 = 0x07,
VRMOVEACTION_CUSTOM4 = 0x08,
VRMOVEACTION_CUSTOM5 = 0x09,
VRMOVEACTION_CUSTOM6 = 0x0A,
VRMOVEACTION_CUSTOM7 = 0x0B,
VRMOVEACTION_CUSTOM8 = 0x0C,
VRMOVEACTION_CUSTOM9 = 0x0D,
VRMOVEACTION_CUSTOM10 = 0x0E,
VRMOVEACTION_CUSTOM11 = 0x0F,
VRMOVEACTION_CUSTOM12 = 0x10,
VRMOVEACTION_CUSTOM13 = 0x11,
VRMOVEACTION_CUSTOM14 = 0x12,
VRMOVEACTION_CUSTOM15 = 0x13,
// Up to 0x20 currently allowed for
};
// What to do with the players velocity when specific move actions are called
// Default of none leaves it as is, for people with 0 ramp up time on acelleration
// This likely won't be that useful.
UENUM(Blueprintable)
enum class EVRMoveActionVelocityRetention : uint8
{
// Leaves velocity as is
VRMOVEACTION_Velocity_None = 0x00,
// Clears velocity entirely
VRMOVEACTION_Velocity_Clear = 0x01,
// Rotates the velocity to match new heading
VRMOVEACTION_Velocity_Turn = 0x02
};
UENUM(Blueprintable)
enum class EVRMoveActionDataReq : uint8
{
VRMOVEACTIONDATA_None = 0x00,
VRMOVEACTIONDATA_LOC = 0x01,
VRMOVEACTIONDATA_ROT = 0x02,
VRMOVEACTIONDATA_LOC_AND_ROT = 0x03
};
USTRUCT()
struct VREXPANSIONPLUGIN_API FVRMoveActionContainer
{
GENERATED_USTRUCT_BODY()
public:
UPROPERTY()
EVRMoveAction MoveAction;
UPROPERTY()
EVRMoveActionDataReq MoveActionDataReq;
UPROPERTY()
FVector MoveActionLoc;
UPROPERTY()
FVector MoveActionVel;
UPROPERTY()
FRotator MoveActionRot;
UPROPERTY()
float MoveActionDeltaYaw;
UPROPERTY()
uint8 MoveActionFlags;
UPROPERTY()
TArray<UObject*> MoveActionObjectReferences;
UPROPERTY()
EVRMoveActionVelocityRetention VelRetentionSetting;
FVRMoveActionContainer()
{
Clear();
}
void Clear()
{
MoveAction = EVRMoveAction::VRMOVEACTION_None;
MoveActionDataReq = EVRMoveActionDataReq::VRMOVEACTIONDATA_None;
MoveActionLoc = FVector::ZeroVector;
MoveActionVel = FVector::ZeroVector;
MoveActionRot = FRotator::ZeroRotator;
MoveActionDeltaYaw = 0.0f;
MoveActionFlags = 0;
VelRetentionSetting = EVRMoveActionVelocityRetention::VRMOVEACTION_Velocity_None;
MoveActionObjectReferences.Empty();
}
/** Network serialization */
// Doing a custom NetSerialize here because this is sent via RPCs and should change on every update
bool NetSerialize(FArchive& Ar, class UPackageMap* Map, bool& bOutSuccess)
{
bOutSuccess = true;
Ar.SerializeBits(&MoveAction, 6); // 64 elements, only allowing 1 per frame, they aren't flags
switch (MoveAction)
{
case EVRMoveAction::VRMOVEACTION_None: break;
case EVRMoveAction::VRMOVEACTION_SetRotation:
case EVRMoveAction::VRMOVEACTION_SnapTurn:
{
uint16 Yaw = 0;
uint16 Pitch = 0;
if (Ar.IsSaving())
{
bool bUseLocOnly = MoveActionFlags & 0x04;
Ar.SerializeBits(&bUseLocOnly, 1);
if (!bUseLocOnly)
{
//Yaw = FRotator::CompressAxisToShort(MoveActionRot.Yaw);
//Ar << Yaw;
Ar << MoveActionRot;
}
else
{
Ar << MoveActionLoc;
}
bool bTeleportGrips = MoveActionFlags & 0x01;// MoveActionRot.Roll > 0.0f && MoveActionRot.Roll < 1.5f;
Ar.SerializeBits(&bTeleportGrips, 1);
if (!bTeleportGrips)
{
bool bTeleportCharacter = MoveActionFlags & 0x02;// MoveActionRot.Roll > 1.5f;
Ar.SerializeBits(&bTeleportCharacter, 1);
}
Ar.SerializeBits(&VelRetentionSetting, 2);
if (VelRetentionSetting == EVRMoveActionVelocityRetention::VRMOVEACTION_Velocity_Turn)
{
bOutSuccess &= SerializePackedVector<100, 30>(MoveActionVel, Ar);
}
// Always send this in case we are sitting
Pitch = FRotator::CompressAxisToShort(MoveActionDeltaYaw);
Ar << Pitch;
bool bRotateAroundCapsule = MoveActionFlags & 0x08;
Ar.SerializeBits(&bRotateAroundCapsule, 1);
}
else
{
bool bUseLocOnly = false;
Ar.SerializeBits(&bUseLocOnly, 1);
MoveActionFlags |= (bUseLocOnly << 2);
if (!bUseLocOnly)
{
//Ar << Yaw;
//MoveActionRot.Yaw = FRotator::DecompressAxisFromShort(Yaw);
Ar << MoveActionRot;
}
else
{
Ar << MoveActionLoc;
}
bool bTeleportGrips = false;
Ar.SerializeBits(&bTeleportGrips, 1);
MoveActionFlags |= (uint8)bTeleportGrips; //.Roll = bTeleportGrips ? 1.0f : 0.0f;
if (!bTeleportGrips)
{
bool bTeleportCharacter = false;
Ar.SerializeBits(&bTeleportCharacter, 1);
MoveActionFlags |= ((uint8)bTeleportCharacter << 1);
//MoveActionRot.Roll = 2.0f;
}
Ar.SerializeBits(&VelRetentionSetting, 2);
if (VelRetentionSetting == EVRMoveActionVelocityRetention::VRMOVEACTION_Velocity_Turn)
{
bOutSuccess &= SerializePackedVector<100, 30>(MoveActionVel, Ar);
}
Ar << Pitch;
MoveActionDeltaYaw = FRotator::DecompressAxisFromShort(Pitch);
bool bRotateAroundCapsule = false;
Ar.SerializeBits(&bRotateAroundCapsule, 1);
MoveActionFlags |= (uint8)(bRotateAroundCapsule << 3);
}
//bOutSuccess &= SerializePackedVector<100, 30>(MoveActionLoc, Ar);
}break;
case EVRMoveAction::VRMOVEACTION_Teleport: // Not replicating rot as Control rot does that already
{
uint16 Yaw = 0;
uint16 Pitch = 0;
if (Ar.IsSaving())
{
Yaw = FRotator::CompressAxisToShort(MoveActionRot.Yaw);
Ar << Yaw;
bool bSkipEncroachment = MoveActionFlags & 0x01;// MoveActionRot.Roll > 0.0f;
Ar.SerializeBits(&bSkipEncroachment, 1);
Ar.SerializeBits(&VelRetentionSetting, 2);
if (VelRetentionSetting == EVRMoveActionVelocityRetention::VRMOVEACTION_Velocity_Turn)
{
bOutSuccess &= SerializePackedVector<100, 30>(MoveActionVel, Ar);
//Pitch = FRotator::CompressAxisToShort(MoveActionRot.Pitch);
//Ar << Pitch;
}
}
else
{
Ar << Yaw;
MoveActionRot.Yaw = FRotator::DecompressAxisFromShort(Yaw);
bool bSkipEncroachment = false;
Ar.SerializeBits(&bSkipEncroachment, 1);
MoveActionFlags |= (uint8)bSkipEncroachment;
//MoveActionRot.Roll = bSkipEncroachment ? 1.0f : 0.0f;
Ar.SerializeBits(&VelRetentionSetting, 2);
if (VelRetentionSetting == EVRMoveActionVelocityRetention::VRMOVEACTION_Velocity_Turn)
{
bOutSuccess &= SerializePackedVector<100, 30>(MoveActionVel, Ar);
//Ar << Pitch;
//MoveActionRot.Pitch = FRotator::DecompressAxisFromShort(Pitch);
}
}
bOutSuccess &= SerializePackedVector<100, 30>(MoveActionLoc, Ar);
}break;
case EVRMoveAction::VRMOVEACTION_StopAllMovement:
{}break;
case EVRMoveAction::VRMOVEACTION_SetGravityDirection:
{
bOutSuccess = SerializeFixedVector<1, 16>(MoveActionVel, Ar);
if (Ar.IsSaving())
{
bool bOrientToGravity = MoveActionFlags > 0;
Ar.SerializeBits(&bOrientToGravity, 1);
}
else
{
bool bOrientToGravity = false;
Ar.SerializeBits(&bOrientToGravity, 1);
MoveActionFlags |= (uint8)bOrientToGravity;
}
}break;
case EVRMoveAction::VRMOVEACTION_PauseTracking:
{
Ar.SerializeBits(&MoveActionFlags, 1);
bOutSuccess &= SerializePackedVector<100, 30>(MoveActionLoc, Ar);
uint16 Yaw = 0;
// Loc and rot for capsule should also be sent here
if (Ar.IsSaving())
{
Yaw = FRotator::CompressAxisToShort(MoveActionRot.Yaw);
Ar << Yaw;
}
else
{
Ar << Yaw;
MoveActionRot.Yaw = FRotator::DecompressAxisFromShort(Yaw);
}
}break;
default: // Everything else
{
// Defines how much to replicate - only 4 possible values, 0 - 3 so only send 2 bits
Ar.SerializeBits(&MoveActionDataReq, 2);
if (((uint8)MoveActionDataReq & (uint8)EVRMoveActionDataReq::VRMOVEACTIONDATA_LOC) != 0)
bOutSuccess &= SerializePackedVector<100, 30>(MoveActionLoc, Ar);
if (((uint8)MoveActionDataReq & (uint8)EVRMoveActionDataReq::VRMOVEACTIONDATA_ROT) != 0)
MoveActionRot.SerializeCompressedShort(Ar);
bool bSerializeObjects = MoveActionObjectReferences.Num() > 0;
Ar.SerializeBits(&bSerializeObjects, 1);
if (bSerializeObjects)
{
Ar << MoveActionObjectReferences;
}
bool bSerializeFlags = MoveActionFlags != 0x00;
Ar.SerializeBits(&bSerializeFlags, 1);
if (bSerializeFlags)
{
Ar << MoveActionFlags;
}
}break;
}
return bOutSuccess;
}
};
template<>
struct TStructOpsTypeTraits< FVRMoveActionContainer > : public TStructOpsTypeTraitsBase2<FVRMoveActionContainer>
{
enum
{
WithNetSerializer = true
};
};
USTRUCT()
struct VREXPANSIONPLUGIN_API FVRMoveActionArray
{
GENERATED_USTRUCT_BODY()
public:
UPROPERTY()
TArray<FVRMoveActionContainer> MoveActions;
bool CanCombine() const
{
return !MoveActions.Num();
/*if (!MoveActions.Num())
{
return true;
}
for (const FVRMoveActionContainer& MoveAction : MoveActions)
{
if (MoveAction.MoveAction != EVRMoveAction::VRMOVEACTION_SnapTurn)
return false;
}
return true;*/
}
void Clear()
{
MoveActions.Empty();
}
/** Network serialization */
// Doing a custom NetSerialize here because this is sent via RPCs and should change on every update
bool NetSerialize(FArchive& Ar, class UPackageMap* Map, bool& bOutSuccess)
{
bOutSuccess = true;
uint8 MoveActionCount = (uint8)MoveActions.Num();
bool bHasAMoveAction = MoveActionCount > 0;
Ar.SerializeBits(&bHasAMoveAction, 1);
if (bHasAMoveAction)
{
bool bHasMoreThanOneMoveAction = MoveActionCount > 1;
Ar.SerializeBits(&bHasMoreThanOneMoveAction, 1);
if (Ar.IsSaving())
{
if (bHasMoreThanOneMoveAction)
{
Ar << MoveActionCount;
for (int i = 0; i < MoveActionCount; i++)
{
bOutSuccess &= MoveActions[i].NetSerialize(Ar, Map, bOutSuccess);
}
}
else
{
bOutSuccess &= MoveActions[0].NetSerialize(Ar, Map, bOutSuccess);
}
}
else
{
if (bHasMoreThanOneMoveAction)
{
Ar << MoveActionCount;
}
else
MoveActionCount = 1;
for (int i = 0; i < MoveActionCount; i++)
{
FVRMoveActionContainer MoveAction;
bOutSuccess &= MoveAction.NetSerialize(Ar, Map, bOutSuccess);
MoveActions.Add(MoveAction);
}
}
}
return bOutSuccess;
}
};
template<>
struct TStructOpsTypeTraits< FVRMoveActionArray > : public TStructOpsTypeTraitsBase2<FVRMoveActionArray>
{
enum
{
WithNetSerializer = true
};
};
USTRUCT()
struct VREXPANSIONPLUGIN_API FVRConditionalMoveRep
{
GENERATED_USTRUCT_BODY()
public:
UPROPERTY(Transient)
FVector CustomVRInputVector;
UPROPERTY(Transient)
FVector RequestedVelocity;
UPROPERTY(Transient)
FVRMoveActionArray MoveActionArray;
//FVRMoveActionContainer MoveAction;
FVRConditionalMoveRep()
{
CustomVRInputVector = FVector::ZeroVector;
RequestedVelocity = FVector::ZeroVector;
}
/** Network serialization */
// Doing a custom NetSerialize here because this is sent via RPCs and should change on every update
bool NetSerialize(FArchive& Ar, class UPackageMap* Map, bool& bOutSuccess)
{
bOutSuccess = true;
bool bIsLoading = Ar.IsLoading();
bool bHasVRinput = !CustomVRInputVector.IsZero();
bool bHasRequestedVelocity = !RequestedVelocity.IsZero();
bool bHasMoveAction = MoveActionArray.MoveActions.Num() > 0;//MoveAction.MoveAction != EVRMoveAction::VRMOVEACTION_None;
bool bHasAnyProperties = bHasVRinput || bHasRequestedVelocity || bHasMoveAction;
Ar.SerializeBits(&bHasAnyProperties, 1);
if (bHasAnyProperties)
{
Ar.SerializeBits(&bHasVRinput, 1);
Ar.SerializeBits(&bHasRequestedVelocity, 1);
//Ar.SerializeBits(&bHasMoveAction, 1);
if (bHasVRinput)
{
bOutSuccess &= SerializePackedVector<100, 22/*30*/>(CustomVRInputVector, Ar);
}
else if (bIsLoading)
{
CustomVRInputVector = FVector::ZeroVector;
}
if (bHasRequestedVelocity)
{
bOutSuccess &= SerializePackedVector<100, 22/*30*/>(RequestedVelocity, Ar);
}
else if (bIsLoading)
{
RequestedVelocity = FVector::ZeroVector;
}
//if (bHasMoveAction)
MoveActionArray.NetSerialize(Ar, Map, bOutSuccess);
}
else if (bIsLoading)
{
CustomVRInputVector = FVector::ZeroVector;
RequestedVelocity = FVector::ZeroVector;
MoveActionArray.Clear();
}
return bOutSuccess;
}
};
template<>
struct TStructOpsTypeTraits< FVRConditionalMoveRep > : public TStructOpsTypeTraitsBase2<FVRConditionalMoveRep>
{
enum
{
WithNetSerializer = true
};
};
// #TODO: DELETE THIS
USTRUCT()
struct VREXPANSIONPLUGIN_API FVRConditionalMoveRep2
{
GENERATED_USTRUCT_BODY()
public:
// Moved these here to avoid having to duplicate tons of properties
UPROPERTY(Transient)
UPrimitiveComponent* ClientMovementBase;
UPROPERTY(Transient)
FName ClientBaseBoneName;
UPROPERTY(Transient)
uint16 ClientYaw;
UPROPERTY(Transient)
uint16 ClientPitch;
UPROPERTY(Transient)
uint8 ClientRoll;
FVRConditionalMoveRep2()
{
ClientMovementBase = nullptr;
ClientBaseBoneName = NAME_None;
ClientRoll = 0;
ClientPitch = 0;
ClientYaw = 0;
}
void UnpackAndSetINTRotations(uint32 Rotation32)
{
// Reversed the order of these so it costs less to replicate
ClientYaw = (Rotation32 & 65535);
ClientPitch = (Rotation32 >> 16);
}
/** Network serialization */
// Doing a custom NetSerialize here because this is sent via RPCs and should change on every update
bool NetSerialize(FArchive& Ar, class UPackageMap* Map, bool& bOutSuccess)
{
bOutSuccess = true;
bool bRepRollAndPitch = (ClientRoll != 0 || ClientPitch != 0);
Ar.SerializeBits(&bRepRollAndPitch, 1);
if (bRepRollAndPitch)
{
// Reversed the order of these
uint32 Rotation32 = (((uint32)ClientPitch) << 16) | ((uint32)ClientYaw);
Ar.SerializeIntPacked(Rotation32);
Ar << ClientRoll;
if (Ar.IsLoading())
{
UnpackAndSetINTRotations(Rotation32);
}
}
else
{
uint32 Yaw32 = ClientYaw;
Ar.SerializeIntPacked(Yaw32);
ClientYaw = (uint16)Yaw32;
}
bool bHasMovementBase = MovementBaseUtility::IsDynamicBase(ClientMovementBase);
Ar.SerializeBits(&bHasMovementBase, 1);
if (bHasMovementBase)
{
Ar << ClientMovementBase;
bool bValidName = ClientBaseBoneName != NAME_None;
Ar.SerializeBits(&bValidName, 1);
// This saves 9 bits on average, we almost never have a valid bone name and default rep goes to 9 bits for hardcoded
// total of 6 bits savings as we use 3 extra for our flags in here.
if (bValidName)
{
Ar << ClientBaseBoneName;
}
}
return bOutSuccess;
}
};
template<>
struct TStructOpsTypeTraits< FVRConditionalMoveRep2 > : public TStructOpsTypeTraitsBase2<FVRConditionalMoveRep2>
{
enum
{
WithNetSerializer = true
};
};
/**
* Helper to change mesh bone updates within a scope.
* Example usage:
* {
* FScopedPreventMeshBoneUpdate ScopedNoMeshBoneUpdate(CharacterOwner->GetMesh(), EKinematicBonesUpdateToPhysics::SkipAllBones);
* // Do something to move mesh, bones will not update
* }
* // Movement of mesh at this point will use previous setting.
*/
struct FScopedMeshBoneUpdateOverrideVR
{
FScopedMeshBoneUpdateOverrideVR(USkeletalMeshComponent* Mesh, EKinematicBonesUpdateToPhysics::Type OverrideSetting);
~FScopedMeshBoneUpdateOverrideVR();
private:
USkeletalMeshComponent* MeshRef;
EKinematicBonesUpdateToPhysics::Type SavedUpdateSetting;
};
class VREXPANSIONPLUGIN_API FSavedMove_VRBaseCharacter : public FSavedMove_Character
{
public:
EVRConjoinedMovementModes VRReplicatedMovementMode;
FVector VRCapsuleLocation;
FVector LFDiff;
FRotator VRCapsuleRotation;
float CapsuleHeight;
FVRConditionalMoveRep ConditionalValues;
void Clear();
virtual void SetInitialPosition(ACharacter* C);
virtual void PrepMoveFor(ACharacter* Character) override;
virtual void CombineWith(const FSavedMove_Character* OldMove, ACharacter* InCharacter, APlayerController* PC, const FVector& OldStartLocation) override;
/** Set the properties describing the final position, etc. of the moved pawn. */
virtual void PostUpdate(ACharacter* C, EPostUpdateMode PostUpdateMode) override;
FSavedMove_VRBaseCharacter();
virtual uint8 GetCompressedFlags() const override;
virtual bool CanCombineWith(const FSavedMovePtr& NewMove, ACharacter* Character, float MaxDelta) const override;
virtual bool IsImportantMove(const FSavedMovePtr& LastAckedMove) const override;
};
// Using this fixes the problem where the character capsule isn't reset after a scoped movement update revert (pretty much just in StepUp operations)
class VREXPANSIONPLUGIN_API FVRCharacterScopedMovementUpdate : public FScopedMovementUpdate
{
public:
FVRCharacterScopedMovementUpdate(USceneComponent* Component, EScopedUpdate::Type ScopeBehavior = EScopedUpdate::DeferredUpdates, bool bRequireOverlapsEventFlagToQueueOverlaps = true);
FTransform InitialVRTransform;
/** Revert movement to the initial location of the Component at the start of the scoped update. Also clears pending overlaps and sets bHasMoved to false. */
void RevertMove();
};
/** Shared pointer for easy memory management of FSavedMove_Character, for accumulating and replaying network moves. */
//typedef TSharedPtr<class FSavedMove_Character> FSavedMovePtr;
struct VREXPANSIONPLUGIN_API FVRCharacterNetworkMoveData : public FCharacterNetworkMoveData
{
public:
FVector_NetQuantize100 VRCapsuleLocation;
FVector/*_NetQuantize100*/ LFDiff;
float CapsuleHeight;
uint16 VRCapsuleRotation;
EVRConjoinedMovementModes ReplicatedMovementMode;
FVRConditionalMoveRep ConditionalMoveReps;
FVRCharacterNetworkMoveData();
virtual ~FVRCharacterNetworkMoveData();
virtual void ClientFillNetworkMoveData(const FSavedMove_Character& ClientMove, ENetworkMoveType MoveType) override;
virtual bool Serialize(UCharacterMovementComponent& CharacterMovement, FArchive& Ar, UPackageMap* PackageMap, ENetworkMoveType MoveType) override;
};
struct VREXPANSIONPLUGIN_API FVRCharacterNetworkMoveDataContainer : public FCharacterNetworkMoveDataContainer
{
public:
/**
* Default constructor. Sets data storage (NewMoveData, PendingMoveData, OldMoveData) to point to default data members. Override those pointers to instead point to custom data if you want to use derived classes.
*/
FVRCharacterNetworkMoveDataContainer() : FCharacterNetworkMoveDataContainer()
{
NewMoveData = &VRBaseDefaultMoveData[0];
PendingMoveData = &VRBaseDefaultMoveData[1];
OldMoveData = &VRBaseDefaultMoveData[2];
}
virtual ~FVRCharacterNetworkMoveDataContainer()
{
}
/**
* Passes through calls to ClientFillNetworkMoveData on each FCharacterNetworkMoveData matching the client moves. Note that ClientNewMove will never be null, but others may be.
*/
//virtual void ClientFillNetworkMoveData(const FSavedMove_Character* ClientNewMove, const FSavedMove_Character* ClientPendingMove, const FSavedMove_Character* ClientOldMove);
/**
* Serialize movement data. Passes Serialize calls to each FCharacterNetworkMoveData as applicable, based on bHasPendingMove and bHasOldMove.
*/
//virtual bool Serialize(UCharacterMovementComponent& CharacterMovement, FArchive& Ar, UPackageMap* PackageMap);
protected:
FVRCharacterNetworkMoveData VRBaseDefaultMoveData[3];
};
struct VREXPANSIONPLUGIN_API FVRCharacterMoveResponseDataContainer : public FCharacterMoveResponseDataContainer
{
public:
FVRCharacterMoveResponseDataContainer() : FCharacterMoveResponseDataContainer()
{
}
virtual ~FVRCharacterMoveResponseDataContainer()
{
}
/**
* Copy the FClientAdjustment and set a few flags relevant to that data.
*/
virtual void ServerFillResponseData(const UCharacterMovementComponent& CharacterMovement, const FClientAdjustment& PendingAdjustment) override;
//bool bHasRotation; // By default ClientAdjustment.NewRot is not serialized. Set this to true after base ServerFillResponseData if you want Rotation to be serialized.
};

View file

@ -0,0 +1,48 @@
#pragma once
#include "CoreMinimal.h"
#include "VRGripScriptBase.h"
#include "GS_Default.generated.h"
/**
* The default grip transform logic for the motion controllers
*/
UCLASS(NotBlueprintable, ClassGroup = (VRExpansionPlugin))
class VREXPANSIONPLUGIN_API UGS_Default : public UVRGripScriptBase
{
GENERATED_BODY()
public:
UGS_Default(const FObjectInitializer& ObjectInitializer);
//virtual void BeginPlay_Implementation() override;
virtual bool GetWorldTransform_Implementation(UGripMotionControllerComponent * GrippingController, float DeltaTime, FTransform & WorldTransform, const FTransform &ParentTransform, FBPActorGripInformation &Grip, AActor * actor, UPrimitiveComponent * root, bool bRootHasInterface, bool bActorHasInterface, bool bIsForTeleport) override;
virtual void GetAnyScaling(FVector& Scaler, FBPActorGripInformation& Grip, FVector& frontLoc, FVector& frontLocOrig, ESecondaryGripType SecondaryType, FTransform& SecondaryTransform);
virtual void ApplySmoothingAndLerp(FBPActorGripInformation& Grip, FVector& frontLoc, FVector& frontLocOrig, float DeltaTime);
virtual void CalculateSecondaryLocation(FVector & frontLoc, const FVector& BasePoint, FBPActorGripInformation& Grip, UGripMotionControllerComponent * GrippingController);
};
// An extended default grip script that adds less common grip features that were moved out of the default implementation
UCLASS(BlueprintType, ClassGroup = (VRExpansionPlugin), hideCategories = TickSettings)
class VREXPANSIONPLUGIN_API UGS_ExtendedDefault : public UGS_Default
{
GENERATED_BODY()
public:
// Whether clamp the grip scaling in scaling grips
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "SecondaryGripSettings")
bool bLimitGripScaling;
// Minimum size to allow scaling in double grip to reach
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "SecondaryGripSettings", meta = (editcondition = "bLimitGripScaling"))
FVector_NetQuantize100 MinimumGripScaling;
// Maximum size to allow scaling in double grip to reach
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "SecondaryGripSettings", meta = (editcondition = "bLimitGripScaling"))
FVector_NetQuantize100 MaximumGripScaling;
virtual void GetAnyScaling(FVector& Scaler, FBPActorGripInformation& Grip, FVector& frontLoc, FVector& frontLocOrig, ESecondaryGripType SecondaryType, FTransform& SecondaryTransform) override;
};

View file

@ -0,0 +1,318 @@
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
//#include "Engine/Engine.h"
#include "VRGripScriptBase.h"
#include "GripScripts/GS_Default.h"
#include "GS_GunTools.generated.h"
class UGripMotionControllerComponent;
// Event thrown when we enter into virtual stock mode
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FVRVirtualStockModeChangedSignature, bool, IsVirtualStockEngaged);
// Global settings for this player
USTRUCT(BlueprintType, Category = "GunSettings")
struct VREXPANSIONPLUGIN_API FBPVirtualStockSettings
{
GENERATED_BODY()
public:
// *Global Value* Should we auto snap to the virtual stock by a set distance
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "VirtualStock")
bool bUseDistanceBasedStockSnapping;
// *Global Value* The distance before snapping to the stock / unsnapping
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "VirtualStock")
float StockSnapDistance;
// *Global Value* The distance from the edge of the stock snap distance where it will be at 100% influence
// Prior to this threshold being hit it will lerp from standard hold to the virtual stock version.
// A value of 0.0f will leave it always off
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "VirtualStock", meta = (ClampMin = "0.00", UIMin = "0.00"))
float StockSnapLerpThreshold;
// Current lerp value of the stock from zero influence to full influence
UPROPERTY(BlueprintReadOnly, Category = "VirtualStock")
float StockLerpValue;
// *Global Value* An offset to apply to the HMD location to be considered the neck / mount pivot
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "VirtualStock")
FVector_NetQuantize100 StockSnapOffset;
// *Global Value* If we want to have the stock location adjust to follow the primary hands Z value
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "VirtualStock")
bool bAdjustZOfStockToPrimaryHand;
// *Global Value* Whether we should lerp the location of the rearmost (stock side) hand, mostly used for snipers.
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "VirtualStock|Smoothing")
bool bSmoothStockHand;
// *Global Value* How much influence the virtual stock smoothing should have, 0.0f is zero smoothing, 1.0f is full smoothing, you should test with full smoothing to get the amount you
// want and then set the smoothing value up until it feels right between the fully smoothed and unsmoothed values.
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "VirtualStock|Smoothing", meta = (editcondition = "bSmoothStockHand", ClampMin = "0.00", UIMin = "0.00", ClampMax = "1.00", UIMax = "1.00"))
float SmoothingValueForStock;
// Used to smooth filter the virtual stocks primary hand location
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "GunSettings|VirtualStock|Smoothing")
FBPEuroLowPassFilterTrans StockHandSmoothing;
// Draw debug elements showing the virtual stock location and angles to interacting components
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "GunSettings|VirtualStock|Debug")
bool bDebugDrawVirtualStock;
void CopyFrom(FBPVirtualStockSettings & B)
{
bUseDistanceBasedStockSnapping = B.bUseDistanceBasedStockSnapping;
StockSnapDistance = B.StockSnapDistance;
StockSnapLerpThreshold = B.StockSnapLerpThreshold;
StockSnapOffset = B.StockSnapOffset;
bAdjustZOfStockToPrimaryHand = B.bAdjustZOfStockToPrimaryHand;
bSmoothStockHand = B.bSmoothStockHand;
SmoothingValueForStock = B.SmoothingValueForStock;
StockHandSmoothing = B.StockHandSmoothing;
}
FBPVirtualStockSettings()
{
StockSnapOffset = FVector(0.f, 0.f, 0.f);
bAdjustZOfStockToPrimaryHand = true;
StockSnapDistance = 35.f;
StockSnapLerpThreshold = 20.0f;
StockLerpValue = 0.0f;
bUseDistanceBasedStockSnapping = true;
SmoothingValueForStock = 0.0f;
bSmoothStockHand = false;
// Speed up the lerp on fast movements for this
StockHandSmoothing.DeltaCutoff = 20.0f;
StockHandSmoothing.MinCutoff = 5.0f;
bDebugDrawVirtualStock = false;
}
};
USTRUCT(BlueprintType, Category = "VRExpansionLibrary")
struct VREXPANSIONPLUGIN_API FGunTools_AdvSecondarySettings
{
GENERATED_BODY()
public:
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "AdvSecondarySettings")
bool bUseAdvancedSecondarySettings;
// Scaler used for handling the smoothing amount, 0.0f is zero smoothing, 1.0f is full smoothing, you should test with full smoothing to get the amount you
// want and then set the smoothing value up until it feels right between the fully smoothed and unsmoothed values.
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "AdvSecondarySettings|Smoothing", meta = (editcondition = "bUseAdvancedSecondarySettings", ClampMin = "0.00", UIMin = "0.00", ClampMax = "1.00", UIMax = "1.00"))
float SecondaryGripScaler;
// If true we will constantly be lerping with the grip scaler instead of only sometimes.
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "AdvSecondarySettings|Smoothing", meta = (editcondition = "bUseAdvancedSecondarySettings"))
bool bUseConstantGripScaler;
// If true will override custom settings for the smoothing values with the global settings in VRSettings
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "AdvSecondarySettings|Smoothing", meta = (editcondition = "bUseAdvancedSecondarySettings"))
bool bUseGlobalSmoothingSettings;
// Used to smooth filter the secondary influence
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "AdvSecondarySettings|Smoothing")
FBPEuroLowPassFilter SecondarySmoothing;
// Whether to scale the secondary hand influence off of distance from grip point
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "AdvSecondarySettings|DistanceInfluence", meta = (editcondition = "bUseAdvancedSecondarySettings"))
bool bUseSecondaryGripDistanceInfluence;
// If true, will use the GripInfluenceDeadZone as a constant value instead of calculating the distance and lerping, lets you define a static influence amount.
//UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "SecondaryGripSettings", meta = (editcondition = "bUseSecondaryGripDistanceInfluence"))
// bool bUseGripInfluenceDeadZoneAsConstant;
// Distance from grip point in local space where there is 100% influence
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "AdvSecondarySettings|DistanceInfluence", meta = (editcondition = "bUseSecondaryGripDistanceInfluence", ClampMin = "0.00", UIMin = "0.00", ClampMax = "256.00", UIMax = "256.00"))
float GripInfluenceDeadZone;
// Distance from grip point in local space before all influence is lost on the secondary grip (1.0f - 0.0f influence over this range)
// this comes into effect outside of the deadzone
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "AdvSecondarySettings|DistanceInfluence", meta = (editcondition = "bUseSecondaryGripDistanceInfluence", ClampMin = "1.00", UIMin = "1.00", ClampMax = "256.00", UIMax = "256.00"))
float GripInfluenceDistanceToZero;
FGunTools_AdvSecondarySettings()
{
bUseAdvancedSecondarySettings = false;
SecondaryGripScaler = 0.0f;
bUseGlobalSmoothingSettings = true;
bUseSecondaryGripDistanceInfluence = false;
//bUseGripInfluenceDeadZoneAsConstant(false),
GripInfluenceDeadZone = 50.0f;
GripInfluenceDistanceToZero = 100.0f;
bUseConstantGripScaler = false;
}
};
// A grip script that adds useful fire-arm related features to grips
// Just adding it to the grippable object provides the features without removing standard
// Grip features.
UCLASS(NotBlueprintable, ClassGroup = (VRExpansionPlugin), hideCategories = TickSettings)
class VREXPANSIONPLUGIN_API UGS_GunTools : public UGS_Default
{
GENERATED_BODY()
public:
UGS_GunTools(const FObjectInitializer& ObjectInitializer);
virtual void OnGrip_Implementation(UGripMotionControllerComponent * GrippingController, const FBPActorGripInformation & GripInformation) override;
virtual void OnSecondaryGrip_Implementation(UGripMotionControllerComponent * Controller, USceneComponent * SecondaryGripComponent, const FBPActorGripInformation & GripInformation) override;
virtual void OnBeginPlay_Implementation(UObject* CallingOwner) override;
virtual void HandlePrePhysicsHandle(UGripMotionControllerComponent* GrippingController, const FBPActorGripInformation &GripInfo, FBPActorPhysicsHandleInformation* HandleInfo, FTransform& KinPose) override;
//virtual void HandlePostPhysicsHandle(UGripMotionControllerComponent* GrippingController, FBPActorPhysicsHandleInformation* HandleInfo) override;
// The name of the component that is used to orient the weapon along its primary axis
// If it does not exist then the weapon is assumed to be X+ facing.
// Also used to perform some calculations, make sure it is parented to the gripped object (root component for actors),
// and that the X+ vector of the orientation component is facing the forward direction of the weapon (gun tip for guns, ect).
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Weapon Settings")
FName WeaponRootOrientationComponent;
FTransform OrientationComponentRelativeFacing;
FQuat StoredRootOffset;
// (default false) If true will run through the entire simulation that the owning client uses for the gun. If false, does a lighter and more performant approximation.
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "GunSettings")
bool bUseHighQualityRemoteSimulation;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "GunSettings")
FGunTools_AdvSecondarySettings AdvSecondarySettings;
// Offset to apply to the pivot (good for centering pivot into the palm ect).
// For this to apply to the physical center of mass as well an OrientationComponent needs to be defined
// So that we have a valid directional vector to work off of, otherwise the pivot will be in component space and you
// will have a harder time aligning it if the weapon is off axis (still works, just less intuitive).
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Pivot")
FVector_NetQuantize100 PivotOffset;
UFUNCTION(BlueprintCallable, Category = "VirtualStock")
void SetVirtualStockComponent(USceneComponent * NewStockComponent)
{
VirtualStockComponent = NewStockComponent;
}
UFUNCTION(BlueprintCallable, Category = "VirtualStock")
void SetVirtualStockEnabled(bool bAllowVirtualStock)
{
if (!bUseVirtualStock && bAllowVirtualStock)
ResetStockVariables();
bUseVirtualStock = bAllowVirtualStock;
}
void ResetStockVariables()
{
VirtualStockSettings.StockHandSmoothing.ResetSmoothingFilter();
}
void GetVirtualStockTarget(UGripMotionControllerComponent * GrippingController);
// Call to use an object
UPROPERTY(BlueprintAssignable, Category = "VirtualStock")
FVRVirtualStockModeChangedSignature OnVirtualStockModeChanged;
// Overrides the pivot location to be at this component instead
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "VirtualStock")
bool bUseVirtualStock;
FTransform MountWorldTransform;
bool bIsMounted;
FTransform RelativeTransOnSecondaryRelease;
TObjectPtr<USceneComponent> CameraComponent;
// Overrides the default behavior of using the HMD location for the stock and uses this component instead
UPROPERTY(BlueprintReadWrite, Category = "VirtualStock")
TObjectPtr<USceneComponent> VirtualStockComponent;
// Loads the global virtual stock settings on grip (only if locally controlled, you need to manually replicate and store the global settings
// In the character if networked).
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "VirtualStock")
bool bUseGlobalVirtualStockSettings;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "VirtualStock", meta = (editcondition = "!bUseGlobalVirtualStockSettings"))
FBPVirtualStockSettings VirtualStockSettings;
// If this gun has recoil
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Recoil")
bool bHasRecoil;
// If true then the recoil will be added as a physical force instead of logical blend
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Recoil")
bool bApplyRecoilAsPhysicalForce;
// Maximum recoil addition
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Recoil", meta = (editcondition = "bHasRecoil"))
FVector_NetQuantize100 MaxRecoilTranslation;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Recoil", meta = (editcondition = "bHasRecoil"))
FVector_NetQuantize100 MaxRecoilRotation;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Recoil", meta = (editcondition = "bHasRecoil"))
FVector_NetQuantize100 MaxRecoilScale;
// Recoil decay rate, how fast it decays back to baseline
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Recoil", meta = (editcondition = "bHasRecoil"))
float DecayRate;
// Recoil lerp rate, how long it takes to lerp to the target recoil amount (0.0f would be instant)
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Recoil", meta = (editcondition = "bHasRecoil"))
float LerpRate;
// Stores the current amount of recoil
FTransform BackEndRecoilStorage;
// Stores the target amount of recoil
FTransform BackEndRecoilTarget;
bool bHasActiveRecoil;
// Adds a recoil instance to the gun tools, the option location is for if using the physical recoil mode
// Physical recoil is in world space and positional only, logical recoil is in relative space to the mesh itself and uses all
// of the transforms properties.
UFUNCTION(BlueprintCallable, Category = "Recoil")
void AddRecoilInstance(const FTransform & RecoilAddition, FVector Optional_Location = FVector::ZeroVector);
UFUNCTION(BlueprintCallable, Category = "Recoil")
void ResetRecoil();
virtual bool GetWorldTransform_Implementation(UGripMotionControllerComponent * GrippingController, float DeltaTime, FTransform & WorldTransform, const FTransform &ParentTransform, FBPActorGripInformation &Grip, AActor * actor, UPrimitiveComponent * root, bool bRootHasInterface, bool bActorHasInterface, bool bIsForTeleport) override;
// Applies the two hand modifier, broke this out into a function so that we can handle late updates
static void ApplyTwoHandModifier(FTransform & OriginalTransform)
{
}
// Returns the smoothed value now
inline FVector GunTools_ApplySmoothingAndLerp(FBPActorGripInformation & Grip, FVector &frontLoc, FVector & frontLocOrig, float DeltaTime, bool bSkipHighQualitySimulations)
{
FVector SmoothedValue = frontLoc;
if (Grip.SecondaryGripInfo.GripLerpState == EGripLerpState::StartLerp) // Lerp into the new grip to smooth the transition
{
if (!bSkipHighQualitySimulations && AdvSecondarySettings.SecondaryGripScaler < 1.0f)
{
SmoothedValue = AdvSecondarySettings.SecondarySmoothing.RunFilterSmoothing(frontLoc, DeltaTime);
frontLoc = FMath::Lerp(frontLoc, SmoothedValue, AdvSecondarySettings.SecondaryGripScaler);
}
//Default_ApplySmoothingAndLerp(Grip, frontLoc, frontLocOrig, DeltaTime);
}
else if (!bSkipHighQualitySimulations && AdvSecondarySettings.bUseAdvancedSecondarySettings && AdvSecondarySettings.bUseConstantGripScaler) // If there is a frame by frame lerp
{
SmoothedValue = AdvSecondarySettings.SecondarySmoothing.RunFilterSmoothing(frontLoc, DeltaTime);
frontLoc = FMath::Lerp(frontLoc, SmoothedValue, AdvSecondarySettings.SecondaryGripScaler);
}
return SmoothedValue;
}
};

View file

@ -0,0 +1,114 @@
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "VRGripScriptBase.h"
#include "GS_InteractibleSettings.generated.h"
class UGripMotionControllerComponent;
USTRUCT(BlueprintType, Category = "VRExpansionLibrary")
struct VREXPANSIONPLUGIN_API FBPGS_InteractionSettings
{
GENERATED_BODY()
public:
bool bHasValidBaseTransform; // So we don't have to equals the transform
FTransform BaseTransform;
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Settings")
uint32 bLimitsInLocalSpace : 1;
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Settings")
uint32 bGetInitialPositionsOnBeginPlay : 1;
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Settings|Linear")
uint32 bLimitX : 1;
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Settings|Linear")
uint32 bLimitY : 1;
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Settings|Linear")
uint32 bLimitZ : 1;
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Settings|Angular")
uint32 bLimitPitch : 1;
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Settings|Angular")
uint32 bLimitYaw : 1;
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Settings|Angular")
uint32 bLimitRoll : 1;
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Settings|Angular")
uint32 bIgnoreHandRotation : 1;
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Settings|Linear", meta = (editcondition = "!bGetInitialPositionsOnBeginPlay"))
FVector_NetQuantize100 InitialLinearTranslation;
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Settings|Linear")
FVector_NetQuantize100 MinLinearTranslation;
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Settings|Linear")
FVector_NetQuantize100 MaxLinearTranslation;
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Settings|Angular", meta = (editcondition = "!bGetInitialPositionsOnBeginPlay"))
FRotator InitialAngularTranslation;
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Settings|Angular")
FRotator MinAngularTranslation;
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Settings|Angular")
FRotator MaxAngularTranslation;
FBPGS_InteractionSettings() :
bLimitsInLocalSpace(true),
bGetInitialPositionsOnBeginPlay(true),
bLimitX(false),
bLimitY(false),
bLimitZ(false),
bLimitPitch(false),
bLimitYaw(false),
bLimitRoll(false),
bIgnoreHandRotation(false),
InitialLinearTranslation(FVector::ZeroVector),
MinLinearTranslation(FVector::ZeroVector),
MaxLinearTranslation(FVector::ZeroVector),
InitialAngularTranslation(FRotator::ZeroRotator),
MinAngularTranslation(FRotator::ZeroRotator),
MaxAngularTranslation(FRotator::ZeroRotator)
{
BaseTransform = FTransform::Identity;
bHasValidBaseTransform = false;
}
};
// A Grip script that overrides the default grip behavior and adds custom clamping logic instead,
UCLASS(NotBlueprintable, ClassGroup = (VRExpansionPlugin), hideCategories = TickSettings)
class VREXPANSIONPLUGIN_API UGS_InteractibleSettings : public UVRGripScriptBase
{
GENERATED_BODY()
public:
UGS_InteractibleSettings(const FObjectInitializer& ObjectInitializer);
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "InteractionSettings")
FBPGS_InteractionSettings InteractionSettings;
virtual void OnBeginPlay_Implementation(UObject * CallingOwner) override;
virtual bool GetWorldTransform_Implementation(UGripMotionControllerComponent * GrippingController, float DeltaTime, FTransform & WorldTransform, const FTransform &ParentTransform, FBPActorGripInformation &Grip, AActor * actor, UPrimitiveComponent * root, bool bRootHasInterface, bool bActorHasInterface, bool bIsForTeleport) override;
virtual void OnGrip_Implementation(UGripMotionControllerComponent * GrippingController, const FBPActorGripInformation & GripInformation) override;
virtual void OnGripRelease_Implementation(UGripMotionControllerComponent * ReleasingController, const FBPActorGripInformation & GripInformation, bool bWasSocketed = false) override;
// Flags the the interaction settings so that it will regenerate removing the hand rotation.
// Use this if you just changed the relative hand transform.
UFUNCTION(BlueprintCallable, Category = "InteractionSettings")
void RemoveHandRotation()
{
// Flag the base transform to be re-applied
InteractionSettings.bHasValidBaseTransform = false;
}
void RemoveRelativeRotation(UGripMotionControllerComponent* GrippingController, const FBPActorGripInformation& GripInformation);
};

View file

@ -0,0 +1,69 @@
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "VRGripScriptBase.h"
#include "VRBPDatatypes.h"
#include "Curves/CurveFloat.h"
#include "GS_LerpToHand.generated.h"
DECLARE_DYNAMIC_MULTICAST_DELEGATE(FVRLerpToHandFinishedSignature);
// A grip script that causes new grips to lerp to the hand (from their current position to where they are supposed to sit).
// It turns off when the lerp is complete.
UCLASS(NotBlueprintable, ClassGroup = (VRExpansionPlugin), hideCategories = TickSettings)
class VREXPANSIONPLUGIN_API UGS_LerpToHand : public UVRGripScriptBase
{
GENERATED_BODY()
public:
UGS_LerpToHand(const FObjectInitializer& ObjectInitializer);
float CurrentLerpTime;
float LerpSpeed;
// If the initial grip distance is closer than this value then the lerping will not be performed.
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LerpSettings")
float MinDistanceForLerp;
// How many seconds the lerp should take
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LerpSettings")
float LerpDuration;
// The minimum speed (in UU per second) that that the lerp should have across the initial grip distance
// Will speed the LerpSpeed up to try and maintain this initial speed if required
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LerpSettings")
float MinSpeedForLerp;
// The maximum speed (in UU per second) that the lerp should have across the initial grip distance
// Will slow the LerpSpeed down to try and maintain this initial speed if required
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LerpSettings")
float MaxSpeedForLerp;
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LerpSettings")
EVRLerpInterpolationMode LerpInterpolationMode;
UPROPERTY(BlueprintAssignable, Category = "LerpEvents")
FVRLerpToHandFinishedSignature OnLerpToHandBegin;
UPROPERTY(BlueprintAssignable, Category = "LerpEvents")
FVRLerpToHandFinishedSignature OnLerpToHandFinished;
// Whether to use a curve map to map the lerp to
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LerpCurve")
bool bUseCurve;
// The curve to follow when using a curve map, only uses from 0.0 - 1.0 of the curve timeline and maps it across the entire duration
UPROPERTY(Category = "LerpCurve", EditAnywhere, meta = (editcondition = "bUseCurve"))
FRuntimeFloatCurve OptionalCurveToFollow;
FTransform OnGripTransform;
uint8 TargetGrip;
//virtual void BeginPlay_Implementation() override;
virtual bool GetWorldTransform_Implementation(UGripMotionControllerComponent * OwningController, float DeltaTime, FTransform & WorldTransform, const FTransform &ParentTransform, FBPActorGripInformation &Grip, AActor * actor, UPrimitiveComponent * root, bool bRootHasInterface, bool bActorHasInterface, bool bIsForTeleport) override;
virtual void OnGrip_Implementation(UGripMotionControllerComponent * GrippingController, const FBPActorGripInformation & GripInformation) override;
virtual void OnGripRelease_Implementation(UGripMotionControllerComponent * ReleasingController, const FBPActorGripInformation & GripInformation, bool bWasSocketed) override;
};

View file

@ -0,0 +1,319 @@
#pragma once
#include "CoreMinimal.h"
#include "Engine/Engine.h"
#include "VRGripScriptBase.h"
#include "GripScripts/GS_Default.h"
#include "GS_Melee.generated.h"
// The type of melee hit zone we are
UENUM(BlueprintType)
enum class EVRMeleeZoneType : uint8
{
// This zone is only valid for stabs
VRPMELLE_ZONETYPE_Stab UMETA(DisplayName = "Stab"),
// This zone is only valid for hits
VRPMELLE_ZONETYPE_Hit UMETA(DisplayName = "Hit"),
// This zone is valid for both stabs and hits
VRPMELLE_ZONETYPE_StabAndHit UMETA(DisplayName = "StabAndHit")
};
// The type of COM selection to use
UENUM(BlueprintType)
enum class EVRMeleeComType : uint8
{
// Does not set COM
VRPMELEECOM_Normal UMETA(DisplayName = "Normal"),
// Sets COM to between hands
VRPMELEECOM_BetweenHands UMETA(DisplayName = "BetweenHands"),
// Uses the primary hand as com location
VRPMELEECOM_PrimaryHand UMETA(DisplayName = "PrimaryHand")
};
// The type of primary hand selection to use
UENUM(BlueprintType)
enum class EVRMeleePrimaryHandType : uint8
{
// Uses the rearmost hand as the primary hand
VRPHAND_Rear UMETA(DisplayName = "Rear"),
// Uses the foremost hand as the primary hand
VRPHAND_Front UMETA(DisplayName = "Front"),
// Uses the first slotted hand as the primary hand
// If neither are slotted then its first come first serve and both hannds load the secondary settings
VRPHAND_Slotted UMETA(DisplayName = "Slotted")
};
// A Lodge component data struct
USTRUCT(BlueprintType, Category = "Lodging")
struct VREXPANSIONPLUGIN_API FBPHitSurfaceProperties
{
GENERATED_BODY()
public:
// Does this surface type allow penetration
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Surface Property")
bool bSurfaceAllowsPenetration;
// Scaler to damage applied from hitting this surface with blunt damage
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Surface Property")
float BluntDamageScaler;
// Scaler to damage applied from hitting this surface with sharp damage
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Surface Property")
float SharpDamageScaler;
// Alters the stab velocity to let you make it harder or easier to stab this surface
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Surface Property")
float StabVelocityScaler;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Surface Property")
TEnumAsByte<EPhysicalSurface> SurfaceType;
FBPHitSurfaceProperties()
{
// Default to true on this
bSurfaceAllowsPenetration = true;
BluntDamageScaler = 1.f;
SharpDamageScaler = 1.f;
StabVelocityScaler = 1.f;
SurfaceType = EPhysicalSurface::SurfaceType_Default;
}
};
// A Lodge component data struct
USTRUCT(BlueprintType, Category = "Lodging")
struct VREXPANSIONPLUGIN_API FBPLodgeComponentInfo
{
GENERATED_BODY()
public:
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "LodgeComponentInfo")
FName ComponentName;
// Type of collision zone we are
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "LodgeComponentInfo")
EVRMeleeZoneType ZoneType;
// If true than we will calculate hit impulse off of its total value and not just off of it axially aligned to the forward of this body
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "LodgeComponentInfo")
bool bIgnoreForwardVectorForHitImpulse;
// For end users to provide a base damage per zone if they want
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "LodgeComponentInfo")
float DamageScaler;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "LodgeComponentInfo")
float PenetrationDepth;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "LodgeComponentInfo")
bool bAllowPenetrationInReverseAsWell;
// This is the impulse velocity (along forward axis of component) required to throw an OnPenetrated event from a PenetrationNotifierComponent
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Weapon Settings")
float PenetrationVelocity;
// This is the impulse velocity required to throw an OnHit event from a PenetrationNotifierComponent (If a stab didn't take place)
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Weapon Settings")
float MinimumHitVelocity;
// The acceptable range of the dot product of the forward vector and the impact normal to define a valid facing
// Subtracted from the 1.0f forward facing value
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Weapon Settings")
float AcceptableForwardProductRange;
// The acceptable range of the dot product of the forward vector and the impact normal to define a valid facing
// Subtracted from the 1.0f forward facing value
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Weapon Settings")
float AcceptableForwardProductRangeForHits;
FBPLodgeComponentInfo()
{
ComponentName = NAME_None;
ZoneType = EVRMeleeZoneType::VRPMELLE_ZONETYPE_StabAndHit;
bIgnoreForwardVectorForHitImpulse = false;
DamageScaler = 0.f;
PenetrationDepth = 100.f;
bAllowPenetrationInReverseAsWell = false;
PenetrationVelocity = 8000.f;
MinimumHitVelocity = 1000.f;
AcceptableForwardProductRange = 0.1f;
AcceptableForwardProductRangeForHits = 0.1f;
TargetComponent = nullptr;
}
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "LodgeComponentInfo")
TObjectPtr<UPrimitiveComponent> TargetComponent;
FORCEINLINE bool operator==(const FName& Other) const
{
return (ComponentName == Other);
}
};
// Event thrown when we the melee weapon becomes lodged
DECLARE_DYNAMIC_MULTICAST_DELEGATE_SevenParams(FVROnMeleeShouldLodgeSignature, FBPLodgeComponentInfo, LogComponent, AActor *, OtherActor, UPrimitiveComponent *, OtherComp, ECollisionChannel, OtherCompCollisionChannel, FBPHitSurfaceProperties, HitSurfaceProperties, FVector, NormalImpulse, const FHitResult&, Hit);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_SevenParams(FVROnMeleeOnHit, FBPLodgeComponentInfo, LogComponent, AActor*, OtherActor, UPrimitiveComponent*, OtherComp, ECollisionChannel, OtherCompCollisionChannel, FBPHitSurfaceProperties, HitSurfaceProperties, FVector, NormalImpulse, const FHitResult&, Hit);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_FourParams(FVROnMeleeInvalidHitSignature, AActor*, OtherActor, UPrimitiveComponent*, OtherComp, FVector, NormalImpulse, const FHitResult&, Hit);
/**
* A Melee grip script that hands multi hand interactions and penetration notifications*
* The per surface damage and penetration options have been moved to the project settings unless the per script override is set
*/
UCLASS(NotBlueprintable, ClassGroup = (VRExpansionPlugin), hideCategories = TickSettings)
class VREXPANSIONPLUGIN_API UGS_Melee : public UGS_Default
{
GENERATED_BODY()
public:
UGS_Melee(const FObjectInitializer& ObjectInitializer);
UFUNCTION()
virtual void OnLodgeHitCallback(AActor* SelfActor, AActor* OtherActor, FVector NormalImpulse, const FHitResult& Hit);
UFUNCTION(BlueprintCallable, Category = "Weapon Settings")
void SetIsLodged(bool IsLodged, UPrimitiveComponent * LodgeComponent)
{
bIsLodged = IsLodged;
LodgedComponent = LodgeComponent;
}
bool bIsLodged;
TWeakObjectPtr<UPrimitiveComponent> LodgedComponent;
//virtual void Tick(float DeltaTime) override;
// Thrown if we should lodge into a hit object
UPROPERTY(BlueprintAssignable, Category = "Melee|Lodging")
FVROnMeleeShouldLodgeSignature OnShouldLodgeInObject;
// Thrown if we hit something we can damage
UPROPERTY(BlueprintAssignable, Category = "Melee|Hit")
FVROnMeleeOnHit OnMeleeHit;
// Fired when a hit is invalid (hit something that isn't flagged for damage or stabbing or was below the damage or stab threshold)
UPROPERTY(BlueprintAssignable, Category = "Melee|Hit")
FVROnMeleeInvalidHitSignature OnMeleeInvalidHit;
// Always tick for penetration
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Melee|Lodging")
bool bAlwaysTickPenetration;
// Only penetrate with two hands on the weapon
// Mostly for very large weapons
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Melee|Lodging")
bool bOnlyPenetrateWithTwoHands;
// A list of surface types that allow penetration and their properties
// If empty then the script will use the global settings, if filled with anything then it will override the global settings
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Melee|Lodging")
TArray<FBPHitSurfaceProperties> OverrideMeleeSurfaceSettings;
// FVector RollingVelocityAverage;
//FVector RollingAngVelocityAverage;
// The name of the component that is used to orient the weapon along its primary axis
// If it does not exist then the weapon is assumed to be X+ facing.
// Also used to perform some calculations, make sure it is parented to the gripped object (root component for actors).
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Weapon Settings")
FName WeaponRootOrientationComponent;
FTransform OrientationComponentRelativeFacing;
// UpdateHand location on the shaft in the X axis
// If primary hand is false then it will do the secondary hand
// World location is of the pivot generally, I have it passing in so people can snap
// LocDifference returns the relative distance of the change in position (or zero if there was none).
UFUNCTION(BlueprintCallable, Category = "Weapon Settings")
void UpdateHandPosition(FBPGripPair HandPair, FVector HandWorldPosition, FVector & LocDifference);
// UpdateHand location and rotation on the shaft in the X axis
// If primary hand is false then it will do the secondary hand
// World location is of the pivot generally, I have it passing in so people can snap
// LocDifference returns the relative distance of the change in position (or zero if there was none).
UFUNCTION(BlueprintCallable, Category = "Weapon Settings")
void UpdateHandPositionAndRotation(FBPGripPair HandPair, FTransform HandWorldTransform, FVector& LocDifference, float& RotDifference, bool bUpdateLocation = true, bool bUpdateRotation = true);
// This is a built list of components that act as penetration notifiers, they will have their OnHit bound too and we will handle penetration logic
// off of it.
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Weapon Settings")
TArray<FBPLodgeComponentInfo> PenetrationNotifierComponents;
bool bCheckLodge;
bool bIsHeld;
FVector LastRelativePos;
FVector RelativeBetweenGripsCenterPos;
// When true, will auto set the primary and secondary hands by the WeaponRootOrientationComponents X Axis distance.
// Smallest value along the X Axis will be considered the primary hand.
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Weapon Settings")
bool bAutoSetPrimaryAndSecondaryHands;
// If we couldn't decide on a true valid primary hand then this will be false and we will load secondary settings for both
bool bHasValidPrimaryHand;
UFUNCTION(BlueprintCallable, Category = "Weapon Settings")
void SetPrimaryAndSecondaryHands(FBPGripPair & PrimaryGrip, FBPGripPair & SecondaryGrip);
// Which method of primary hand to select
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Weapon Settings")
EVRMeleePrimaryHandType PrimaryHandSelectionType;
UPROPERTY(BlueprintReadOnly, Category = "Weapon Settings")
FBPGripPair PrimaryHand;
UPROPERTY(BlueprintReadOnly, Category = "Weapon Settings")
FBPGripPair SecondaryHand;
// If true we will use the primary hands grip settings when we only have one hand gripping instead of the objects VRGripInterfaces settings
UPROPERTY(BlueprintReadOnly, Category = "Weapon Settings")
bool bUsePrimaryHandSettingsWithOneHand;
// To select the type of com setting to use
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Weapon Settings")
EVRMeleeComType COMType;
FTransform ObjectRelativeGripCenter;
void SetComBetweenHands(UGripMotionControllerComponent* GrippingController, FBPActorPhysicsHandleInformation * HandleInfo);
// Grip settings to use on the primary hand when multiple grips are active
// Falls back to the standard grip settings when only one grip is active
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Weapon Settings")
FBPAdvancedPhysicsHandleSettings PrimaryHandPhysicsSettings;
// Grip settings to use on the secondary hand when multiple grips are active
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Weapon Settings")
FBPAdvancedPhysicsHandleSettings SecondaryHandPhysicsSettings;
void UpdateDualHandInfo();
virtual void HandlePostPhysicsHandle(UGripMotionControllerComponent* GrippingController, FBPActorPhysicsHandleInformation* HandleInfo) override;
virtual void HandlePrePhysicsHandle(UGripMotionControllerComponent* GrippingController, const FBPActorGripInformation &GripInfo, FBPActorPhysicsHandleInformation* HandleInfo, FTransform& KinPose) override;
virtual void OnBeginPlay_Implementation(UObject* CallingOwner) override;
virtual void OnEndPlay_Implementation(const EEndPlayReason::Type EndPlayReason) override;
virtual void OnSecondaryGrip_Implementation(UGripMotionControllerComponent* Controller, USceneComponent* SecondaryGripComponent, const FBPActorGripInformation& GripInformation) override;
virtual void OnGrip_Implementation(UGripMotionControllerComponent* GrippingController, const FBPActorGripInformation& GripInformation) override;
virtual void OnGripRelease_Implementation(UGripMotionControllerComponent* ReleasingController, const FBPActorGripInformation& GripInformation, bool bWasSocketed = false) override;
virtual bool Wants_DenyTeleport_Implementation(UGripMotionControllerComponent* Controller) override;
//virtual void BeginPlay_Implementation() override;
virtual bool GetWorldTransform_Implementation(UGripMotionControllerComponent * GrippingController, float DeltaTime, FTransform & WorldTransform, const FTransform &ParentTransform, FBPActorGripInformation &Grip, AActor * actor, UPrimitiveComponent * root, bool bRootHasInterface, bool bActorHasInterface, bool bIsForTeleport) override;
};

View file

@ -0,0 +1,38 @@
#pragma once
#include "CoreMinimal.h"
//#include "Engine/Engine.h"
#include "VRGripScriptBase.h"
#include "GameFramework/WorldSettings.h"
#include "GripScripts/GS_Default.h"
#include "GS_Physics.generated.h"
/**
* A pure physics multi hand interaction grip script, expects that bAllowMultiGrips is set on the parent object*
*/
UCLASS(NotBlueprintable, ClassGroup = (VRExpansionPlugin), hideCategories = TickSettings)
class VREXPANSIONPLUGIN_API UGS_Physics : public UGS_Default
{
GENERATED_BODY()
public:
UGS_Physics(const FObjectInitializer& ObjectInitializer);
// Grip settings to use when a single hand is gripping, overrides interface defaults
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Physics Settings")
FBPAdvancedPhysicsHandleSettings SingleHandPhysicsSettings;
// Grip settings to use when multiple hands are gripping
// Overrides interface defaults
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Physics Settings")
FBPAdvancedPhysicsHandleSettings MultiHandPhysicsSettings;
void UpdateDualHandInfo(UGripMotionControllerComponent* GrippingController = nullptr, bool bRecreate = true);
virtual void HandlePostPhysicsHandle(UGripMotionControllerComponent* GrippingController, FBPActorPhysicsHandleInformation* HandleInfo) override;
//virtual void HandlePrePhysicsHandle(UGripMotionControllerComponent* GrippingController, const FBPActorGripInformation &GripInfo, FBPActorPhysicsHandleInformation* HandleInfo, FTransform& KinPose) override;
virtual void OnGrip_Implementation(UGripMotionControllerComponent* GrippingController, const FBPActorGripInformation& GripInformation) override;
virtual void OnGripRelease_Implementation(UGripMotionControllerComponent* ReleasingController, const FBPActorGripInformation& GripInformation, bool bWasSocketed = false) override;
};

View file

@ -0,0 +1,273 @@
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
//#include "Engine/Engine.h"
#include "UObject/Object.h"
#include "VRBPDatatypes.h"
#include "Tickable.h"
#include "VRGripScriptBase.generated.h"
class UGripMotionControllerComponent;
class UVRGripInterface;
class UPrimitiveComponent;
class AActor;
UENUM(Blueprintable)
enum class EGSTransformOverrideType : uint8
{
/** Does not alter the world transform */
None,
/* Overrides the world transform */
OverridesWorldTransform,
/* Modifies the world transform*/
ModifiesWorldTransform
};
UCLASS(NotBlueprintable, BlueprintType, EditInlineNew, DefaultToInstanced, Abstract, ClassGroup = (VRExpansionPlugin), HideCategories = DefaultSettings)
class VREXPANSIONPLUGIN_API UVRGripScriptBase : public UObject, public FTickableGameObject
{
GENERATED_BODY()
public:
UVRGripScriptBase(const FObjectInitializer& ObjectInitializer);
// Gets the first grip script of the specified type in this object, do NOT call this on tick, save out and store the reference given
UFUNCTION(BlueprintCallable, Category = "VRGripScript|Functions", meta = (WorldContext = "WorldContextObject", bIgnoreSelf = "true", DisplayName = "GetGripScriptByClass", ExpandEnumAsExecs = "Result"))
static UVRGripScriptBase* GetGripScriptByClass(UObject* WorldContextObject, TSubclassOf<UVRGripScriptBase> GripScriptClass, EBPVRResultSwitch& Result);
bool IsSupportedForNetworking() const override
{
return true;
//return bRequiresReplicationSupport || Super::IsSupportedForNetworking();
}
// I don't need to do this, there should be no dynamic script spawning and they are all name stable by default
// Returns if the script is currently active and should be used
bool IsScriptActive();
// Is currently active helper variable, returned from IsScriptActive()
UPROPERTY(BlueprintReadWrite, EditDefaultsOnly, Category = "GSSettings")
bool bIsActive;
// Returns if the script is going to modify the world transform of the grip
EGSTransformOverrideType GetWorldTransformOverrideType();
// Whether this script overrides or modifies the world transform
UPROPERTY(BlueprintReadWrite, EditDefaultsOnly, Category = "GSSettings")
EGSTransformOverrideType WorldTransformOverrideType;
// Returns if the script wants auto drop to be ignored
FORCEINLINE bool Wants_DenyAutoDrop()
{
return bDenyAutoDrop;
}
// Returns if we want to deny auto dropping
UPROPERTY(BlueprintReadWrite, EditDefaultsOnly, Category = "GSSettings")
bool bDenyAutoDrop;
// Returns if the script wants to force a drop
FORCEINLINE bool Wants_ToForceDrop()
{
return bForceDrop;
}
// Returns if we want to force a drop
UPROPERTY(BlueprintReadWrite, Category = "GSSettings")
bool bForceDrop;
// Flags the grip to be dropped as soon as possible
UFUNCTION(BlueprintCallable, Category = "VRGripScript")
void ForceGripToDrop()
{
bForceDrop = true;
}
// Returns if the script wants to deny late updates
FORCEINLINE bool Wants_DenyLateUpdates()
{
return bDenyLateUpdates;
}
// Returns if we want to inject changes prior to the physics handle
UPROPERTY(BlueprintReadWrite, EditDefaultsOnly, Category = "GSSettings")
bool bDenyLateUpdates;
// Returns if the script wants auto drop to be ignored
FORCEINLINE bool InjectPrePhysicsHandle()
{
return bInjectPrePhysicsHandle;
}
// Returns if we want to inject changes prior to the physics handle
UPROPERTY(BlueprintReadWrite, EditDefaultsOnly, Category = "GSSettings")
bool bInjectPrePhysicsHandle;
virtual void HandlePrePhysicsHandle(UGripMotionControllerComponent* GrippingController, const FBPActorGripInformation &GripInfo, FBPActorPhysicsHandleInformation * HandleInfo, FTransform & KinPose);
// Returns if we want to inject changes after the physics handle
FORCEINLINE bool InjectPostPhysicsHandle()
{
return bInjectPostPhysicsHandle;
}
// Returns if we want to inject changes after the physics handle
UPROPERTY(BlueprintReadWrite, EditDefaultsOnly, Category = "GSSettings")
bool bInjectPostPhysicsHandle;
virtual void HandlePostPhysicsHandle(UGripMotionControllerComponent* GrippingController, FBPActorPhysicsHandleInformation * HandleInfo);
// Returns if the script is currently active and should be used
UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "VRGripScript")
bool Wants_DenyTeleport(UGripMotionControllerComponent * Controller);
virtual bool Wants_DenyTeleport_Implementation(UGripMotionControllerComponent* Controller);
virtual void GetLifetimeReplicatedProps(TArray< class FLifetimeProperty > & OutLifetimeProps) const override;
// doesn't currently compile in editor builds, not sure why the linker is screwing up there but works elsewhere
//virtual void PreReplication(IRepChangedPropertyTracker & ChangedPropertyTracker);
virtual bool CallRemoteFunction(UFunction * Function, void * Parms, FOutParmRec * OutParms, FFrame * Stack) override;
virtual int32 GetFunctionCallspace(UFunction * Function, FFrame * Stack) override;
// FTickableGameObject functions
// If true then this scrip can tick when bAllowticking is true
UPROPERTY(BlueprintReadWrite, EditDefaultsOnly, Category = "TickSettings")
bool bCanEverTick;
// If true and we bCanEverTick, then will fire off the tick function
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "TickSettings")
bool bAllowTicking;
// Set whether the grip script can tick or not
UFUNCTION(BlueprintCallable, Category = "TickSettings")
void SetTickEnabled(bool bTickEnabled);
/**
* Function called every frame on this GripScript. Override this function to implement custom logic to be executed every frame.
* Only executes if bCanEverTick is true and bAllowTicking is true
*
* @param DeltaTime - The time since the last tick.
*/
virtual void Tick(float DeltaTime) override;
virtual bool IsTickable() const override;
virtual UWorld* GetTickableGameObjectWorld() const override;
virtual bool IsTickableInEditor() const;
virtual bool IsTickableWhenPaused() const override;
virtual ETickableTickType GetTickableTickType() const;
virtual TStatId GetStatId() const override;
virtual UWorld* GetWorld() const override;
// End tickable object information
// Returns the expected grip transform (relative * controller + addition)
UFUNCTION(BlueprintPure, Category = "VRGripScript")
FTransform GetGripTransform(const FBPActorGripInformation &Grip, const FTransform & ParentTransform);
// Returns the current world transform of the owning object (or root comp of if it is an actor)
UFUNCTION(BlueprintPure, Category = "VRGripScript")
FTransform GetParentTransform(bool bGetWorldTransform = true, FName BoneName = NAME_None);
// Returns the scene component of the parent, either being the parent itself or the root comp of it.
// Nullptr if there is no valid scene component
UFUNCTION(BlueprintCallable, Category = "VRGripScript")
USceneComponent* GetParentSceneComp();
// Returns the root body instance of the parent
FBodyInstance * GetParentBodyInstance(FName OptionalBoneName = NAME_None);
// Returns the parent component or actor to this
UFUNCTION(BlueprintPure, Category = "VRGripScript")
UObject * GetParent();
// Returns the owning actor
UFUNCTION(BlueprintPure, Category = "VRGripScript")
AActor * GetOwner();
// If the owning actor has authority on this connection
UFUNCTION(BlueprintPure, Category = "VRGripScript")
bool HasAuthority();
// If the owning actor is on the server on this connection
UFUNCTION(BlueprintPure, Category = "VRGripScript")
bool IsServer();
void EndPlay(const EEndPlayReason::Type EndPlayReason);
// Not all scripts will require this function, specific ones that use things like Lever logic however will. Best to call it.
// Grippables will automatically call this, however if you manually spawn a grip script during play or you make your own
// Interfaced grip object and give it grippables, YOU will be required to call this event on them.
UFUNCTION(BlueprintNativeEvent, Category = "VRGripScript")
void OnEndPlay(const EEndPlayReason::Type EndPlayReason);
virtual void OnEndPlay_Implementation(const EEndPlayReason::Type EndPlayReason);
void BeginPlay(UObject * CallingOwner);
bool bAlreadyNotifiedPlay = false;
virtual void PostInitProperties() override;
// Not all scripts will require this function, specific ones that use things like Lever logic however will. Best to call it.
// Grippables will automatically call this, however if you manually spawn a grip script during play or you make your own
// Interfaced grip object and give it grippables, YOU will be required to call this event on them.
UFUNCTION(BlueprintNativeEvent, Category = "VRGripScript")
void OnBeginPlay(UObject * CallingOwner);
virtual void OnBeginPlay_Implementation(UObject * CallingOwner);
// Overrides or Modifies the world transform with this grip script
UFUNCTION(BlueprintNativeEvent, Category = "VRGripScript")
bool GetWorldTransform(UGripMotionControllerComponent * GrippingController, float DeltaTime, UPARAM(ref) FTransform & WorldTransform, const FTransform &ParentTransform, UPARAM(ref) FBPActorGripInformation &Grip, AActor * actor, UPrimitiveComponent * root, bool bRootHasInterface, bool bActorHasInterface, bool bIsForTeleport);
virtual bool GetWorldTransform_Implementation(UGripMotionControllerComponent * OwningController, float DeltaTime, FTransform & WorldTransform, const FTransform &ParentTransform, FBPActorGripInformation &Grip, AActor * actor, UPrimitiveComponent * root, bool bRootHasInterface, bool bActorHasInterface, bool bIsForTeleport);
// Event triggered on the interfaced object when gripped
UFUNCTION(BlueprintNativeEvent, Category = "VRGripScript")
void OnGrip(UGripMotionControllerComponent * GrippingController, const FBPActorGripInformation & GripInformation);
virtual void OnGrip_Implementation(UGripMotionControllerComponent * GrippingController, const FBPActorGripInformation & GripInformation);
// Event triggered on the interfaced object when grip is released
UFUNCTION(BlueprintNativeEvent, Category = "VRGripScript")
void OnGripRelease(UGripMotionControllerComponent * ReleasingController, const FBPActorGripInformation & GripInformation, bool bWasSocketed = false);
virtual void OnGripRelease_Implementation(UGripMotionControllerComponent * ReleasingController, const FBPActorGripInformation & GripInformation, bool bWasSocketed = false);
// Event triggered on the interfaced object when secondary gripped
UFUNCTION(BlueprintNativeEvent, Category = "VRGripInterface")
void OnSecondaryGrip(UGripMotionControllerComponent * Controller, USceneComponent * SecondaryGripComponent, const FBPActorGripInformation & GripInformation);
virtual void OnSecondaryGrip_Implementation(UGripMotionControllerComponent * Controller, USceneComponent * SecondaryGripComponent, const FBPActorGripInformation & GripInformation);
// Event triggered on the interfaced object when secondary grip is released
UFUNCTION(BlueprintNativeEvent, Category = "VRGripInterface")
void OnSecondaryGripRelease(UGripMotionControllerComponent * Controller, USceneComponent * ReleasingSecondaryGripComponent, const FBPActorGripInformation & GripInformation);
virtual void OnSecondaryGripRelease_Implementation(UGripMotionControllerComponent * Controller, USceneComponent * ReleasingSecondaryGripComponent, const FBPActorGripInformation & GripInformation);
virtual bool CallCorrect_GetWorldTransform(UGripMotionControllerComponent * OwningController, float DeltaTime, FTransform & WorldTransform, const FTransform &ParentTransform, FBPActorGripInformation &Grip, AActor * actor, UPrimitiveComponent * root, bool bRootHasInterface, bool bActorHasInterface, bool bIsForTeleport)
{
return GetWorldTransform_Implementation(OwningController, DeltaTime, WorldTransform, ParentTransform, Grip, actor, root, bRootHasInterface, bActorHasInterface, bIsForTeleport);
}
};
UCLASS(Blueprintable, Abstract, ClassGroup = (VRExpansionPlugin), ShowCategories = DefaultSettings)
class VREXPANSIONPLUGIN_API UVRGripScriptBaseBP : public UVRGripScriptBase
{
GENERATED_BODY()
public:
virtual bool CallCorrect_GetWorldTransform(UGripMotionControllerComponent * OwningController, float DeltaTime, FTransform & WorldTransform, const FTransform &ParentTransform, FBPActorGripInformation &Grip, AActor * actor, UPrimitiveComponent * root, bool bRootHasInterface, bool bActorHasInterface, bool bIsForTeleport) override
{
return GetWorldTransform(OwningController, DeltaTime, WorldTransform, ParentTransform, Grip, actor, root, bRootHasInterface, bActorHasInterface, bIsForTeleport);
}
virtual void Tick(float DeltaTime) override;
/** Event called every frame if ticking is enabled */
UFUNCTION(BlueprintImplementableEvent, meta = (DisplayName = "Tick"))
void ReceiveTick(float DeltaSeconds);
};

View file

@ -0,0 +1,273 @@
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "VRBPDatatypes.h"
#include "VRGripInterface.h"
//#include "Engine/Engine.h"
#include "GameplayTagContainer.h"
#include "GameplayTagAssetInterface.h"
#include "Engine/ActorChannel.h"
#include "Grippables/GrippableDataTypes.h"
#include "Grippables/GrippablePhysicsReplication.h"
#include "GrippableActor.generated.h"
class UGripMotionControllerComponent;
class UVRGripScriptBase;
/**
*
*/
UCLASS(Blueprintable, meta = (BlueprintSpawnableComponent), ClassGroup = (VRExpansionPlugin))
class VREXPANSIONPLUGIN_API AGrippableActor : public AActor, public IVRGripInterface, public IGameplayTagAssetInterface
{
GENERATED_BODY()
public:
AGrippableActor(const FObjectInitializer& ObjectInitializer);
~AGrippableActor();
virtual void BeginPlay() override;
virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override;
UPROPERTY(Replicated, ReplicatedUsing = OnRep_AttachmentReplication)
FRepAttachmentWithWeld AttachmentWeldReplication;
virtual void GatherCurrentMovement() override;
UPROPERTY(EditAnywhere, Replicated, BlueprintReadOnly, Instanced, Category = "VRGripInterface")
TArray<TObjectPtr<UVRGripScriptBase>> GripLogicScripts;
// If true then the grip script array will be considered for replication, if false then it will not
// This is an optimization for when you have a lot of grip scripts in use, you can toggle this off in cases
// where the object will never have a replicating script
UPROPERTY(EditAnywhere, Replicated, BlueprintReadWrite, Category = "VRGripInterface")
bool bReplicateGripScripts;
bool ReplicateSubobjects(UActorChannel* Channel, class FOutBunch *Bunch, FReplicationFlags *RepFlags) override;
virtual void GetSubobjectsWithStableNamesForNetworking(TArray<UObject*>& ObjList) override;
// Sets the Deny Gripping variable on the FBPInterfaceSettings struct
UFUNCTION(BlueprintCallable, Category = "VRGripInterface")
void SetDenyGripping(bool bDenyGripping);
// Sets the grip priority on the FBPInterfaceSettings struct
UFUNCTION(BlueprintCallable, Category = "VRGripInterface")
void SetGripPriority(int NewGripPriority);
// Called when a object is gripped
UPROPERTY(BlueprintAssignable, Category = "Grip Events")
FVROnGripSignature OnGripped;
// Called when a object is dropped
UPROPERTY(BlueprintAssignable, Category = "Grip Events")
FVROnDropSignature OnDropped;
// Called when an object we hold is secondary gripped
UPROPERTY(BlueprintAssignable, Category = "Grip Events")
FVROnGripSignature OnSecondaryGripAdded;
// Called when an object we hold is secondary dropped
UPROPERTY(BlueprintAssignable, Category = "Grip Events")
FVROnGripSignature OnSecondaryGripRemoved;
// ------------------------------------------------
// Client Auth Throwing Data and functions
// ------------------------------------------------
UPROPERTY(EditAnywhere, Replicated, BlueprintReadWrite, Category = "Replication")
FVRClientAuthReplicationData ClientAuthReplicationData;
// Add this to client side physics replication (until coming to rest or timeout period is hit)
UFUNCTION(BlueprintCallable, Category = "Networking")
bool AddToClientReplicationBucket();
// Remove this from client side physics replication
UFUNCTION(BlueprintCallable, Category = "Networking")
bool RemoveFromClientReplicationBucket();
UFUNCTION()
bool PollReplicationEvent();
UFUNCTION(Category = "Networking")
void CeaseReplicationBlocking();
// Notify the server that we are no longer trying to run the throwing auth
UFUNCTION(Reliable, Server, WithValidation, Category = "Networking")
void Server_EndClientAuthReplication();
// Notify the server about a new movement rep
UFUNCTION(UnReliable, Server, WithValidation, Category = "Networking")
void Server_GetClientAuthReplication(const FRepMovementVR & newMovement);
// Returns if this object is currently client auth throwing
UFUNCTION(BlueprintPure, Category = "Networking")
FORCEINLINE bool IsCurrentlyClientAuthThrowing()
{
return ClientAuthReplicationData.bIsCurrentlyClientAuth;
}
// End client auth throwing data and functions //
// ------------------------------------------------
// Gameplay tag interface
// ------------------------------------------------
/** Overridden to return requirements tags */
virtual void GetOwnedGameplayTags(FGameplayTagContainer& TagContainer) const override
{
TagContainer = GameplayTags;
}
/** Tags that are set on this object */
UPROPERTY(EditAnywhere, Replicated, BlueprintReadWrite, Category = "GameplayTags")
FGameplayTagContainer GameplayTags;
// End Gameplay Tag Interface
virtual void PreReplication(IRepChangedPropertyTracker & ChangedPropertyTracker) override;
// Skips the attachment replication if we are locally owned and our grip settings say that we are a client authed grip.
UPROPERTY(EditAnywhere, Replicated, BlueprintReadWrite, Category = "Replication")
bool bAllowIgnoringAttachOnOwner;
// Should we skip attachment replication (vr settings say we are a client auth grip and our owner is locally controlled)
inline bool ShouldWeSkipAttachmentReplication(bool bConsiderHeld = true) const
{
if ((bConsiderHeld && !VRGripInterfaceSettings.bWasHeld) || GetNetMode() < ENetMode::NM_Client)
return false;
if (VRGripInterfaceSettings.MovementReplicationType == EGripMovementReplicationSettings::ClientSide_Authoritive ||
VRGripInterfaceSettings.MovementReplicationType == EGripMovementReplicationSettings::ClientSide_Authoritive_NoRep)
{
return HasLocalNetOwner();
}
else
return false;
}
// Handle fixing some bugs and issues with ReplicateMovement being off
virtual void OnRep_AttachmentReplication() override;
virtual void OnRep_ReplicateMovement() override;
virtual void OnRep_ReplicatedMovement() override;
virtual void PostNetReceivePhysicState() override;
// Debug printing of when the object is replication destroyed
/*virtual void OnSubobjectDestroyFromReplication(UObject *Subobject) override
{
Super::OnSubobjectDestroyFromReplication(Subobject);
GEngine->AddOnScreenDebugMessage(-1, 15.f, FColor::Red, FString::Printf(TEXT("Killed Object On Actor: x: %s"), *Subobject->GetName()));
}*/
// This isn't called very many places but it does come up
virtual void MarkComponentsAsGarbage(bool bModify) override;
/** Called right before being marked for destruction due to network replication */
// Clean up our objects so that they aren't sitting around for GC
virtual void PreDestroyFromReplication() override;
// On Destroy clean up our objects
virtual void BeginDestroy() override;
UPROPERTY(EditAnywhere, Replicated, BlueprintReadWrite, Category = "VRGripInterface")
bool bRepGripSettingsAndGameplayTags;
UPROPERTY(EditAnywhere, Replicated, BlueprintReadWrite, Category = "VRGripInterface")
FBPInterfaceProperties VRGripInterfaceSettings;
// Set up as deny instead of allow so that default allows for gripping
// The GripInitiator is not guaranteed to be valid, check it for validity
virtual bool DenyGripping_Implementation(UGripMotionControllerComponent * GripInitiator = nullptr) override;
// How an interfaced object behaves when teleporting
virtual EGripInterfaceTeleportBehavior TeleportBehavior_Implementation() override;
// Should this object simulate on drop
virtual bool SimulateOnDrop_Implementation() override;
// Grip type to use
virtual EGripCollisionType GetPrimaryGripType_Implementation(bool bIsSlot) override;
// Secondary grip type
virtual ESecondaryGripType SecondaryGripType_Implementation() override;
// Define which movement repliation setting to use
virtual EGripMovementReplicationSettings GripMovementReplicationType_Implementation() override;
// Define the late update setting
virtual EGripLateUpdateSettings GripLateUpdateSetting_Implementation() override;
// What grip stiffness and damping to use if using a physics constraint
virtual void GetGripStiffnessAndDamping_Implementation(float& GripStiffnessOut, float& GripDampingOut) override;
// Get the advanced physics settings for this grip
virtual FBPAdvGripSettings AdvancedGripSettings_Implementation() override;
// What distance to break a grip at (only relevent with physics enabled grips
virtual float GripBreakDistance_Implementation() override;
// Get closest primary slot in range
virtual void ClosestGripSlotInRange_Implementation(FVector WorldLocation, bool bSecondarySlot, bool& bHadSlotInRange, FTransform& SlotWorldTransform, FName& SlotName, UGripMotionControllerComponent* CallingController = nullptr, FName OverridePrefix = NAME_None) override;
// Check if an object allows multiple grips at one time
virtual bool AllowsMultipleGrips_Implementation() override;
// Returns if the object is held and if so, which controllers are holding it
virtual void IsHeld_Implementation(TArray<FBPGripPair>& HoldingControllers, bool& bIsHeld) override;
// Sets is held, used by the plugin
virtual void SetHeld_Implementation(UGripMotionControllerComponent* HoldingController, uint8 GripID, bool bIsHeld) override;
// Interface function used to throw the delegates that is invisible to blueprints so that it can't be overridden
virtual void Native_NotifyThrowGripDelegates(UGripMotionControllerComponent* Controller, bool bGripped, const FBPActorGripInformation& GripInformation, bool bWasSocketed = false) override;
// Returns if the object wants to be socketed
virtual bool RequestsSocketing_Implementation(USceneComponent*& ParentToSocketTo, FName& OptionalSocketName, FTransform_NetQuantize& RelativeTransform) override;
// Get grip scripts
virtual bool GetGripScripts_Implementation(TArray<UVRGripScriptBase*>& ArrayReference) override;
// Events //
// Event triggered each tick on the interfaced object when gripped, can be used for custom movement or grip based logic
virtual void TickGrip_Implementation(UGripMotionControllerComponent* GrippingController, const FBPActorGripInformation& GripInformation, float DeltaTime) override;
// Event triggered on the interfaced object when gripped
virtual void OnGrip_Implementation(UGripMotionControllerComponent* GrippingController, const FBPActorGripInformation& GripInformation) override;
// Event triggered on the interfaced object when grip is released
virtual void OnGripRelease_Implementation(UGripMotionControllerComponent* ReleasingController, const FBPActorGripInformation& GripInformation, bool bWasSocketed = false) override;
// Event triggered on the interfaced object when child component is gripped
virtual void OnChildGrip_Implementation(UGripMotionControllerComponent* GrippingController, const FBPActorGripInformation& GripInformation) override;
// Event triggered on the interfaced object when child component is released
virtual void OnChildGripRelease_Implementation(UGripMotionControllerComponent* ReleasingController, const FBPActorGripInformation& GripInformation, bool bWasSocketed = false) override;
// Event triggered on the interfaced object when secondary gripped
virtual void OnSecondaryGrip_Implementation(UGripMotionControllerComponent* GripOwningController, USceneComponent* SecondaryGripComponent, const FBPActorGripInformation& GripInformation) override;
// Event triggered on the interfaced object when secondary grip is released
virtual void OnSecondaryGripRelease_Implementation(UGripMotionControllerComponent* GripOwningController, USceneComponent* ReleasingSecondaryGripComponent, const FBPActorGripInformation& GripInformation) override;
// Interaction Functions
// Call to use an object
virtual void OnUsed_Implementation() override;
// Call to stop using an object
virtual void OnEndUsed_Implementation() override;
// Call to use an object
virtual void OnSecondaryUsed_Implementation() override;
// Call to stop using an object
virtual void OnEndSecondaryUsed_Implementation() override;
// Call to send an action event to the object
virtual void OnInput_Implementation(FKey Key, EInputEvent KeyEvent) override;
};

View file

@ -0,0 +1,202 @@
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "VRBPDatatypes.h"
#include "VRGripInterface.h"
#include "GameplayTagContainer.h"
#include "Components/BoxComponent.h"
#include "GameplayTagAssetInterface.h"
#include "Engine/ActorChannel.h"
#include "GrippableBoxComponent.generated.h"
class UVRGripScriptBase;
class UGripMotionControllerComponent;
/**
*
*/
UCLASS(Blueprintable, meta = (BlueprintSpawnableComponent), ClassGroup = (VRExpansionPlugin))
class VREXPANSIONPLUGIN_API UGrippableBoxComponent : public UBoxComponent, public IVRGripInterface, public IGameplayTagAssetInterface
{
GENERATED_BODY()
public:
UGrippableBoxComponent(const FObjectInitializer& ObjectInitializer);
~UGrippableBoxComponent();
virtual void BeginPlay() override;
virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override;
UPROPERTY(EditAnywhere, Replicated, BlueprintReadOnly, Instanced, Category = "VRGripInterface")
TArray<TObjectPtr<UVRGripScriptBase>> GripLogicScripts;
// If true then the grip script array will be considered for replication, if false then it will not
// This is an optimization for when you have a lot of grip scripts in use, you can toggle this off in cases
// where the object will never have a replicating script
UPROPERTY(EditAnywhere, Replicated, BlueprintReadWrite, Category = "VRGripInterface")
bool bReplicateGripScripts;
bool ReplicateSubobjects(UActorChannel* Channel, class FOutBunch *Bunch, FReplicationFlags *RepFlags) override;
// Sets the Deny Gripping variable on the FBPInterfaceSettings struct
UFUNCTION(BlueprintCallable, Category = "VRGripInterface")
void SetDenyGripping(bool bDenyGripping);
// Sets the grip priority on the FBPInterfaceSettings struct
UFUNCTION(BlueprintCallable, Category = "VRGripInterface")
void SetGripPriority(int NewGripPriority);
// Called when a object is gripped
UPROPERTY(BlueprintAssignable, Category = "Grip Events")
FVROnGripSignature OnGripped;
// Called when a object is dropped
UPROPERTY(BlueprintAssignable, Category = "Grip Events")
FVROnDropSignature OnDropped;
// Called when an object we hold is secondary gripped
UPROPERTY(BlueprintAssignable, Category = "Grip Events")
FVROnGripSignature OnSecondaryGripAdded;
// Called when an object we hold is secondary dropped
UPROPERTY(BlueprintAssignable, Category = "Grip Events")
FVROnGripSignature OnSecondaryGripRemoved;
// ------------------------------------------------
// Gameplay tag interface
// ------------------------------------------------
/** Overridden to return requirements tags */
virtual void GetOwnedGameplayTags(FGameplayTagContainer& TagContainer) const override
{
TagContainer = GameplayTags;
}
/** Tags that are set on this object */
UPROPERTY(EditAnywhere, Replicated, BlueprintReadWrite, Category = "GameplayTags")
FGameplayTagContainer GameplayTags;
// End Gameplay Tag Interface
virtual void PreReplication(IRepChangedPropertyTracker & ChangedPropertyTracker) override;
/** Called right before being marked for destruction due to network replication */
// Clean up our objects so that they aren't sitting around for GC
virtual void PreDestroyFromReplication() override;
virtual void GetSubobjectsWithStableNamesForNetworking(TArray<UObject*> &ObjList) override;
// This one is for components to clean up
virtual void OnComponentDestroyed(bool bDestroyingHierarchy) override;
// Requires bReplicates to be true for the component
UPROPERTY(EditAnywhere, Replicated, BlueprintReadWrite, Category = "VRGripInterface|Replication")
bool bRepGripSettingsAndGameplayTags;
// Overrides the default of : true and allows for controlling it like in an actor, should be default of off normally with grippable components
UPROPERTY(EditAnywhere, Replicated, BlueprintReadWrite, Category = "VRGripInterface|Replication")
bool bReplicateMovement;
bool bOriginalReplicatesMovement;
UPROPERTY(EditAnywhere, Replicated, BlueprintReadWrite, Category = "VRGripInterface")
FBPInterfaceProperties VRGripInterfaceSettings;
// Set up as deny instead of allow so that default allows for gripping
// The GripInitiator is not guaranteed to be valid, check it for validity
virtual bool DenyGripping_Implementation(UGripMotionControllerComponent * GripInitiator = nullptr) override;
// How an interfaced object behaves when teleporting
virtual EGripInterfaceTeleportBehavior TeleportBehavior_Implementation() override;
// Should this object simulate on drop
virtual bool SimulateOnDrop_Implementation() override;
// Grip type to use
virtual EGripCollisionType GetPrimaryGripType_Implementation(bool bIsSlot) override;
// Secondary grip type
virtual ESecondaryGripType SecondaryGripType_Implementation() override;
// Define which movement repliation setting to use
virtual EGripMovementReplicationSettings GripMovementReplicationType_Implementation() override;
// Define the late update setting
virtual EGripLateUpdateSettings GripLateUpdateSetting_Implementation() override;
// What grip stiffness and damping to use if using a physics constraint
virtual void GetGripStiffnessAndDamping_Implementation(float& GripStiffnessOut, float& GripDampingOut) override;
// Get the advanced physics settings for this grip
virtual FBPAdvGripSettings AdvancedGripSettings_Implementation() override;
// What distance to break a grip at (only relevent with physics enabled grips
virtual float GripBreakDistance_Implementation() override;
// Get closest primary slot in range
virtual void ClosestGripSlotInRange_Implementation(FVector WorldLocation, bool bSecondarySlot, bool& bHadSlotInRange, FTransform& SlotWorldTransform, FName& SlotName, UGripMotionControllerComponent* CallingController = nullptr, FName OverridePrefix = NAME_None) override;
// Check if an object allows multiple grips at one time
virtual bool AllowsMultipleGrips_Implementation() override;
// Returns if the object is held and if so, which controllers are holding it
virtual void IsHeld_Implementation(TArray<FBPGripPair>& HoldingControllers, bool& bIsHeld) override;
// Sets is held, used by the plugin
virtual void SetHeld_Implementation(UGripMotionControllerComponent* HoldingController, uint8 GripID, bool bIsHeld) override;
// Interface function used to throw the delegates that is invisible to blueprints so that it can't be overridden
virtual void Native_NotifyThrowGripDelegates(UGripMotionControllerComponent* Controller, bool bGripped, const FBPActorGripInformation& GripInformation, bool bWasSocketed = false) override;
// Returns if the object wants to be socketed
virtual bool RequestsSocketing_Implementation(USceneComponent*& ParentToSocketTo, FName& OptionalSocketName, FTransform_NetQuantize& RelativeTransform) override;
// Get grip scripts
virtual bool GetGripScripts_Implementation(TArray<UVRGripScriptBase*>& ArrayReference) override;
// Events //
// Event triggered each tick on the interfaced object when gripped, can be used for custom movement or grip based logic
virtual void TickGrip_Implementation(UGripMotionControllerComponent* GrippingController, const FBPActorGripInformation& GripInformation, float DeltaTime) override;
// Event triggered on the interfaced object when gripped
virtual void OnGrip_Implementation(UGripMotionControllerComponent* GrippingController, const FBPActorGripInformation& GripInformation) override;
// Event triggered on the interfaced object when grip is released
virtual void OnGripRelease_Implementation(UGripMotionControllerComponent* ReleasingController, const FBPActorGripInformation& GripInformation, bool bWasSocketed = false) override;
// Event triggered on the interfaced object when child component is gripped
virtual void OnChildGrip_Implementation(UGripMotionControllerComponent* GrippingController, const FBPActorGripInformation& GripInformation) override;
// Event triggered on the interfaced object when child component is released
virtual void OnChildGripRelease_Implementation(UGripMotionControllerComponent* ReleasingController, const FBPActorGripInformation& GripInformation, bool bWasSocketed = false) override;
// Event triggered on the interfaced object when secondary gripped
virtual void OnSecondaryGrip_Implementation(UGripMotionControllerComponent* GripOwningController, USceneComponent* SecondaryGripComponent, const FBPActorGripInformation& GripInformation) override;
// Event triggered on the interfaced object when secondary grip is released
virtual void OnSecondaryGripRelease_Implementation(UGripMotionControllerComponent* GripOwningController, USceneComponent* ReleasingSecondaryGripComponent, const FBPActorGripInformation& GripInformation) override;
// Interaction Functions
// Call to use an object
virtual void OnUsed_Implementation() override;
// Call to stop using an object
virtual void OnEndUsed_Implementation() override;
// Call to use an object
virtual void OnSecondaryUsed_Implementation() override;
// Call to stop using an object
virtual void OnEndSecondaryUsed_Implementation() override;
// Call to send an action event to the object
virtual void OnInput_Implementation(FKey Key, EInputEvent KeyEvent) override;
};

View file

@ -0,0 +1,199 @@
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "VRBPDatatypes.h"
#include "VRGripInterface.h"
#include "GameplayTagContainer.h"
#include "GameplayTagAssetInterface.h"
#include "Components/CapsuleComponent.h"
#include "Engine/ActorChannel.h"
#include "GrippableCapsuleComponent.generated.h"
class UVRGripScriptBase;
class UGripMotionControllerComponent;
/**
*
*/
UCLASS(Blueprintable, meta = (BlueprintSpawnableComponent, ChildCanTick), ClassGroup = (VRExpansionPlugin))
class VREXPANSIONPLUGIN_API UGrippableCapsuleComponent : public UCapsuleComponent, public IVRGripInterface, public IGameplayTagAssetInterface
{
GENERATED_BODY()
public:
UGrippableCapsuleComponent(const FObjectInitializer& ObjectInitializer);
~UGrippableCapsuleComponent();
virtual void BeginPlay() override;
virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override;
UPROPERTY(EditAnywhere, Replicated, BlueprintReadOnly, Instanced, Category = "VRGripInterface")
TArray<TObjectPtr<UVRGripScriptBase>> GripLogicScripts;
// If true then the grip script array will be considered for replication, if false then it will not
// This is an optimization for when you have a lot of grip scripts in use, you can toggle this off in cases
// where the object will never have a replicating script
UPROPERTY(EditAnywhere, Replicated, BlueprintReadWrite, Category = "VRGripInterface")
bool bReplicateGripScripts;
bool ReplicateSubobjects(UActorChannel* Channel, class FOutBunch *Bunch, FReplicationFlags *RepFlags) override;
// Sets the Deny Gripping variable on the FBPInterfaceSettings struct
UFUNCTION(BlueprintCallable, Category = "VRGripInterface")
void SetDenyGripping(bool bDenyGripping);
// Sets the grip priority on the FBPInterfaceSettings struct
UFUNCTION(BlueprintCallable, Category = "VRGripInterface")
void SetGripPriority(int NewGripPriority);
// Called when a object is gripped
UPROPERTY(BlueprintAssignable, Category = "Grip Events")
FVROnGripSignature OnGripped;
// Called when a object is dropped
UPROPERTY(BlueprintAssignable, Category = "Grip Events")
FVROnDropSignature OnDropped;
// Called when an object we hold is secondary gripped
UPROPERTY(BlueprintAssignable, Category = "Grip Events")
FVROnGripSignature OnSecondaryGripAdded;
// Called when an object we hold is secondary dropped
UPROPERTY(BlueprintAssignable, Category = "Grip Events")
FVROnGripSignature OnSecondaryGripRemoved;
// Gameplay tag interface
// ------------------------------------------------
/** Overridden to return requirements tags */
virtual void GetOwnedGameplayTags(FGameplayTagContainer& TagContainer) const override
{
TagContainer = GameplayTags;
}
/** Tags that are set on this object */
UPROPERTY(EditAnywhere, Replicated, BlueprintReadWrite, Category = "GameplayTags")
FGameplayTagContainer GameplayTags;
// End Gameplay Tag Interface
virtual void PreReplication(IRepChangedPropertyTracker & ChangedPropertyTracker) override;
/** Called right before being marked for destruction due to network replication */
// Clean up our objects so that they aren't sitting around for GC
virtual void PreDestroyFromReplication() override;
virtual void GetSubobjectsWithStableNamesForNetworking(TArray<UObject*> &ObjList) override;
// This one is for components to clean up
virtual void OnComponentDestroyed(bool bDestroyingHierarchy) override;
// Requires bReplicates to be true for the component
UPROPERTY(EditAnywhere, Replicated, BlueprintReadWrite, Category = "VRGripInterface|Replication")
bool bRepGripSettingsAndGameplayTags;
// Overrides the default of : true and allows for controlling it like in an actor, should be default of off normally with grippable components
UPROPERTY(EditAnywhere, Replicated, BlueprintReadWrite, Category = "VRGripInterface|Replication")
bool bReplicateMovement;
bool bOriginalReplicatesMovement;
UPROPERTY(EditAnywhere, Replicated, BlueprintReadWrite, Category = "VRGripInterface")
FBPInterfaceProperties VRGripInterfaceSettings;
// Set up as deny instead of allow so that default allows for gripping
// The GripInitiator is not guaranteed to be valid, check it for validity
virtual bool DenyGripping_Implementation(UGripMotionControllerComponent * GripInitiator = nullptr) override;
// How an interfaced object behaves when teleporting
virtual EGripInterfaceTeleportBehavior TeleportBehavior_Implementation() override;
// Should this object simulate on drop
virtual bool SimulateOnDrop_Implementation() override;
// Grip type to use
virtual EGripCollisionType GetPrimaryGripType_Implementation(bool bIsSlot) override;
// Secondary grip type
virtual ESecondaryGripType SecondaryGripType_Implementation() override;
// Define which movement repliation setting to use
virtual EGripMovementReplicationSettings GripMovementReplicationType_Implementation() override;
// Define the late update setting
virtual EGripLateUpdateSettings GripLateUpdateSetting_Implementation() override;
// What grip stiffness and damping to use if using a physics constraint
virtual void GetGripStiffnessAndDamping_Implementation(float& GripStiffnessOut, float& GripDampingOut) override;
// Get the advanced physics settings for this grip
virtual FBPAdvGripSettings AdvancedGripSettings_Implementation() override;
// What distance to break a grip at (only relevent with physics enabled grips
virtual float GripBreakDistance_Implementation() override;
// Get closest primary slot in range
virtual void ClosestGripSlotInRange_Implementation(FVector WorldLocation, bool bSecondarySlot, bool& bHadSlotInRange, FTransform& SlotWorldTransform, FName& SlotName, UGripMotionControllerComponent* CallingController = nullptr, FName OverridePrefix = NAME_None) override;
// Check if an object allows multiple grips at one time
virtual bool AllowsMultipleGrips_Implementation() override;
// Returns if the object is held and if so, which controllers are holding it
virtual void IsHeld_Implementation(TArray<FBPGripPair>& HoldingControllers, bool& bIsHeld) override;
// Sets is held, used by the plugin
virtual void SetHeld_Implementation(UGripMotionControllerComponent* HoldingController, uint8 GripID, bool bIsHeld) override;
// Interface function used to throw the delegates that is invisible to blueprints so that it can't be overridden
virtual void Native_NotifyThrowGripDelegates(UGripMotionControllerComponent* Controller, bool bGripped, const FBPActorGripInformation& GripInformation, bool bWasSocketed = false) override;
// Returns if the object wants to be socketed
virtual bool RequestsSocketing_Implementation(USceneComponent*& ParentToSocketTo, FName& OptionalSocketName, FTransform_NetQuantize& RelativeTransform) override;
// Get grip scripts
virtual bool GetGripScripts_Implementation(TArray<UVRGripScriptBase*>& ArrayReference) override;
// Events //
// Event triggered each tick on the interfaced object when gripped, can be used for custom movement or grip based logic
virtual void TickGrip_Implementation(UGripMotionControllerComponent* GrippingController, const FBPActorGripInformation& GripInformation, float DeltaTime) override;
// Event triggered on the interfaced object when gripped
virtual void OnGrip_Implementation(UGripMotionControllerComponent* GrippingController, const FBPActorGripInformation& GripInformation) override;
// Event triggered on the interfaced object when grip is released
virtual void OnGripRelease_Implementation(UGripMotionControllerComponent* ReleasingController, const FBPActorGripInformation& GripInformation, bool bWasSocketed = false) override;
// Event triggered on the interfaced object when child component is gripped
virtual void OnChildGrip_Implementation(UGripMotionControllerComponent* GrippingController, const FBPActorGripInformation& GripInformation) override;
// Event triggered on the interfaced object when child component is released
virtual void OnChildGripRelease_Implementation(UGripMotionControllerComponent* ReleasingController, const FBPActorGripInformation& GripInformation, bool bWasSocketed = false) override;
// Event triggered on the interfaced object when secondary gripped
virtual void OnSecondaryGrip_Implementation(UGripMotionControllerComponent* GripOwningController, USceneComponent* SecondaryGripComponent, const FBPActorGripInformation& GripInformation) override;
// Event triggered on the interfaced object when secondary grip is released
virtual void OnSecondaryGripRelease_Implementation(UGripMotionControllerComponent* GripOwningController, USceneComponent* ReleasingSecondaryGripComponent, const FBPActorGripInformation& GripInformation) override;
// Interaction Functions
// Call to use an object
virtual void OnUsed_Implementation() override;
// Call to stop using an object
virtual void OnEndUsed_Implementation() override;
// Call to use an object
virtual void OnSecondaryUsed_Implementation() override;
// Call to stop using an object
virtual void OnEndSecondaryUsed_Implementation() override;
// Call to send an action event to the object
virtual void OnInput_Implementation(FKey Key, EInputEvent KeyEvent) override;
};

View file

@ -0,0 +1,28 @@
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Character.h"
#include "GrippableCharacter.generated.h"
class UGrippableSkeletalMeshComponent;
UCLASS()
class VREXPANSIONPLUGIN_API AGrippableCharacter : public ACharacter
{
GENERATED_BODY()
public:
AGrippableCharacter(const FObjectInitializer& ObjectInitializer = FObjectInitializer::Get());
// A reference to the grippable character that can be used instead of casting the root, BP doesn't like the class override.
UPROPERTY(Category = GrippableCharacter, VisibleAnywhere, Transient, BlueprintReadOnly, meta = (AllowPrivateAccess = "true"))
TObjectPtr<UGrippableSkeletalMeshComponent> GrippableMeshReference;
// A Custom bone to use on the character mesh as the originator for the perception systems sight sense
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "AI")
FName ViewOriginationSocket;
virtual void GetActorEyesViewPoint(FVector& Location, FRotator& Rotation) const override;
};

View file

@ -0,0 +1,33 @@
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
//#include "Engine/EngineTypes.h"
#include "Engine/ReplicatedState.h"
#include "GrippableDataTypes.generated.h"
// A version of the attachment structure that include welding data
USTRUCT()
struct VREXPANSIONPLUGIN_API FRepAttachmentWithWeld : public FRepAttachment
{
public:
GENERATED_BODY()
// Add in the is welded property
UPROPERTY()
bool bIsWelded;
bool NetSerialize(FArchive& Ar, class UPackageMap* Map, bool& bOutSuccess);
FRepAttachmentWithWeld();
};
template<>
struct TStructOpsTypeTraits< FRepAttachmentWithWeld > : public TStructOpsTypeTraitsBase2<FRepAttachmentWithWeld>
{
enum
{
WithNetSerializer = true//,
//WithNetSharedSerialization = true,
};
};

View file

@ -0,0 +1,168 @@
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "Physics/PhysicsInterfaceUtils.h"
#include "PhysicsReplication.h"
#include "Engine/ReplicatedState.h"
#include "PhysicsReplicationInterface.h"
#include "Physics/PhysicsInterfaceDeclares.h"
#include "PhysicsProxy/SingleParticlePhysicsProxyFwd.h"
#include "Chaos/Particles.h"
#include "Chaos/PhysicsObject.h"
#include "Chaos/SimCallbackObject.h"
#include "GrippablePhysicsReplication.generated.h"
//#include "GrippablePhysicsReplication.generated.h"
//DECLARE_DYNAMIC_MULTICAST_DELEGATE(FVRPhysicsReplicationDelegate, void, Return);
/*static TAutoConsoleVariable<int32> CVarEnableCustomVRPhysicsReplication(
TEXT("vr.VRExpansion.EnableCustomVRPhysicsReplication"),
0,
TEXT("Enable valves input controller that overrides legacy input.\n")
TEXT(" 0: use the engines default input mapping (default), will also be default if vr.SteamVR.EnableVRInput is enabled\n")
TEXT(" 1: use the valve input controller. You will have to define input bindings for the controllers you want to support."),
ECVF_ReadOnly);*/
//#if PHYSICS_INTERFACE_PHYSX
//struct FAsyncPhysicsRepCallbackDataVR;
//class FPhysicsReplicationAsyncCallbackVR;
class FPhysicsReplicationAsyncVR : public Chaos::TSimCallbackObject<
FPhysicsReplicationAsyncInput,
Chaos::FSimCallbackNoOutput,
Chaos::ESimCallbackOptions::Presimulate>
{
virtual FName GetFNameForStatId() const override;
virtual void OnPreSimulate_Internal() override;
virtual void ApplyTargetStatesAsync(const float DeltaSeconds, const FPhysicsRepErrorCorrectionData& ErrorCorrection, const TArray<FPhysicsRepAsyncInputData>& TargetStates);
// Replication functions
virtual void DefaultReplication_DEPRECATED(Chaos::FRigidBodyHandle_Internal* Handle, const FPhysicsRepAsyncInputData& State, const float DeltaSeconds, const FPhysicsRepErrorCorrectionData& ErrorCorrection);
virtual bool DefaultReplication(Chaos::FPBDRigidParticleHandle* Handle, FReplicatedPhysicsTargetAsync& Target, const float DeltaSeconds);
virtual bool PredictiveInterpolation(Chaos::FPBDRigidParticleHandle* Handle, FReplicatedPhysicsTargetAsync& Target, const float DeltaSeconds);
virtual bool ResimulationReplication(Chaos::FPBDRigidParticleHandle* Handle, FReplicatedPhysicsTargetAsync& Target, const float DeltaSeconds);
private:
float LatencyOneWay;
FRigidBodyErrorCorrection ErrorCorrectionDefault;
TMap<Chaos::FPhysicsObject*, FReplicatedPhysicsTargetAsync> ObjectToTarget;
private:
void UpdateAsyncTarget(const FPhysicsRepAsyncInputData& Input);
void UpdateRewindDataTarget(const FPhysicsRepAsyncInputData& Input);
public:
void Setup(FRigidBodyErrorCorrection ErrorCorrection)
{
ErrorCorrectionDefault = ErrorCorrection;
}
};
class FPhysicsReplicationVR : public FPhysicsReplication
{
public:
FPhysScene* PhysSceneVR;
FPhysicsReplicationVR(FPhysScene* PhysScene);
~FPhysicsReplicationVR();
static bool IsInitialized();
virtual void OnTick(float DeltaSeconds, TMap<TWeakObjectPtr<UPrimitiveComponent>, FReplicatedPhysicsTarget>& ComponentsToTargets) override;
virtual bool ApplyRigidBodyState(float DeltaSeconds, FBodyInstance* BI, FReplicatedPhysicsTarget& PhysicsTarget, const FRigidBodyErrorCorrection& ErrorCorrection, const float PingSecondsOneWay, int32 LocalFrame, int32 NumPredictedFrames) override;
virtual bool ApplyRigidBodyState(float DeltaSeconds, FBodyInstance* BI, FReplicatedPhysicsTarget& PhysicsTarget, const FRigidBodyErrorCorrection& ErrorCorrection, const float PingSecondsOneWay, bool* bDidHardSnap = nullptr) override;
virtual void SetReplicatedTarget(UPrimitiveComponent* Component, FName BoneName, const FRigidBodyState& ReplicatedTarget, int32 ServerFrame) override;
void SetReplicatedTargetVR(Chaos::FPhysicsObject* PhysicsObject, const FRigidBodyState& ReplicatedTarget, int32 ServerFrame, EPhysicsReplicationMode ReplicationMode);
TArray<FReplicatedPhysicsTarget> ReplicatedTargetsQueueVR;
FPhysicsReplicationAsyncVR* PhysicsReplicationAsyncVR;
FPhysicsReplicationAsyncInput* AsyncInputVR; //async data being written into before we push into callback
void PrepareAsyncData_ExternalVR(const FRigidBodyErrorCorrection& ErrorCorrection); //prepare async data for writing. Call on external thread (i.e. game thread)
};
class IPhysicsReplicationFactoryVR : public IPhysicsReplicationFactory
{
public:
virtual TUniquePtr<IPhysicsReplication> CreatePhysicsReplication(FPhysScene* OwningPhysScene) override
{
return TUniquePtr<IPhysicsReplication>(new FPhysicsReplicationVR(OwningPhysScene));
}
/*virtual FPhysicsReplication* Create(FPhysScene* OwningPhysScene)
{
return new FPhysicsReplicationVR(OwningPhysScene);
}
virtual void Destroy(FPhysicsReplication* PhysicsReplication)
{
if (PhysicsReplication)
delete PhysicsReplication;
}*/
};
//#endif
USTRUCT()
struct VREXPANSIONPLUGIN_API FRepMovementVR : public FRepMovement
{
GENERATED_USTRUCT_BODY()
public:
FRepMovementVR();
FRepMovementVR(FRepMovement& other);
void CopyTo(FRepMovement& other) const;
bool NetSerialize(FArchive& Ar, class UPackageMap* Map, bool& bOutSuccess);
bool GatherActorsMovement(AActor* OwningActor);
};
template<>
struct TStructOpsTypeTraits<FRepMovementVR> : public TStructOpsTypeTraitsBase2<FRepMovementVR>
{
enum
{
WithNetSerializer = true,
WithNetSharedSerialization = true,
};
};
USTRUCT(BlueprintType)
struct VREXPANSIONPLUGIN_API FVRClientAuthReplicationData
{
GENERATED_BODY()
public:
// If True and we are using a client auth grip type then we will replicate our throws on release
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "VRReplication")
bool bUseClientAuthThrowing;
// Rate that we will be sending throwing events to the server, not replicated, only serialized
UPROPERTY(EditAnywhere, NotReplicated, BlueprintReadOnly, Category = "VRReplication", meta = (ClampMin = "0", UIMin = "0", ClampMax = "100", UIMax = "100"))
int32 UpdateRate;
FTimerHandle ResetReplicationHandle;
FTransform LastActorTransform;
float TimeAtInitialThrow;
bool bIsCurrentlyClientAuth;
FVRClientAuthReplicationData() :
bUseClientAuthThrowing(false),
UpdateRate(30),
LastActorTransform(FTransform::Identity),
TimeAtInitialThrow(0.0f),
bIsCurrentlyClientAuth(false)
{
}
};

View file

@ -0,0 +1,299 @@
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
//#include "Engine/Engine.h"
#include "VRBPDatatypes.h"
#include "VRGripInterface.h"
#include "Animation/SkeletalMeshActor.h"
#include "Components/SkeletalMeshComponent.h"
#include "GameplayTagContainer.h"
#include "GameplayTagAssetInterface.h"
#include "Engine/ActorChannel.h"
#include "Grippables/GrippableDataTypes.h"
#include "Grippables/GrippablePhysicsReplication.h"
#include "GrippableSkeletalMeshActor.generated.h"
class UGripMotionControllerComponent;
class UVRGripScriptBase;
/**
* A component specifically for being able to turn off movement replication in the component at will
* Has the upside of also being a blueprintable base since UE4 doesn't allow that with std ones
*/
UCLASS(Blueprintable, meta = (BlueprintSpawnableComponent, ChildCanTick), ClassGroup = (VRExpansionPlugin))
class VREXPANSIONPLUGIN_API UOptionalRepSkeletalMeshComponent : public USkeletalMeshComponent
{
GENERATED_BODY()
public:
UOptionalRepSkeletalMeshComponent(const FObjectInitializer& ObjectInitializer);
public:
// Overrides the default of : true and allows for controlling it like in an actor, should be default of off normally with grippable components
UPROPERTY(EditAnywhere, Replicated, BlueprintReadWrite, Category = "Component Replication")
bool bReplicateMovement;
virtual void PreReplication(IRepChangedPropertyTracker& ChangedPropertyTracker) override;
// Weld Fix until epic fixes it #TODO: check back in on this and remove and the asset include when epic fixes it
virtual void GetWeldedBodies(TArray<FBodyInstance*>& OutWeldedBodies, TArray<FName>& OutLabels, bool bIncludingAutoWeld) override;
virtual FBodyInstance* GetBodyInstance(FName BoneName = NAME_None, bool bGetWelded = true, int32 Index = INDEX_NONE) const override;
};
/**
*
*/
UCLASS(Blueprintable, meta = (BlueprintSpawnableComponent, ChildCanTick), ClassGroup = (VRExpansionPlugin))
class VREXPANSIONPLUGIN_API AGrippableSkeletalMeshActor : public ASkeletalMeshActor, public IVRGripInterface, public IGameplayTagAssetInterface
{
GENERATED_BODY()
public:
AGrippableSkeletalMeshActor(const FObjectInitializer& ObjectInitializer);
~AGrippableSkeletalMeshActor();
virtual void BeginPlay() override;
virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override;
UPROPERTY(Replicated, ReplicatedUsing = OnRep_AttachmentReplication)
FRepAttachmentWithWeld AttachmentWeldReplication;
virtual void GatherCurrentMovement() override;
UPROPERTY(EditAnywhere, Replicated, BlueprintReadOnly, Instanced, Category = "VRGripInterface")
TArray<TObjectPtr<UVRGripScriptBase>> GripLogicScripts;
// If true then the grip script array will be considered for replication, if false then it will not
// This is an optimization for when you have a lot of grip scripts in use, you can toggle this off in cases
// where the object will never have a replicating script
UPROPERTY(EditAnywhere, Replicated, BlueprintReadWrite, Category = "VRGripInterface")
bool bReplicateGripScripts;
bool ReplicateSubobjects(UActorChannel* Channel, class FOutBunch* Bunch, FReplicationFlags* RepFlags) override;
virtual void GetSubobjectsWithStableNamesForNetworking(TArray<UObject*>& ObjList) override;
// Sets the Deny Gripping variable on the FBPInterfaceSettings struct
UFUNCTION(BlueprintCallable, Category = "VRGripInterface")
void SetDenyGripping(bool bDenyGripping);
// Sets the grip priority on the FBPInterfaceSettings struct
UFUNCTION(BlueprintCallable, Category = "VRGripInterface")
void SetGripPriority(int NewGripPriority);
// Called when a object is gripped
UPROPERTY(BlueprintAssignable, Category = "Grip Events")
FVROnGripSignature OnGripped;
// Called when a object is dropped
UPROPERTY(BlueprintAssignable, Category = "Grip Events")
FVROnDropSignature OnDropped;
// Called when an object we hold is secondary gripped
UPROPERTY(BlueprintAssignable, Category = "Grip Events")
FVROnGripSignature OnSecondaryGripAdded;
// Called when an object we hold is secondary dropped
UPROPERTY(BlueprintAssignable, Category = "Grip Events")
FVROnGripSignature OnSecondaryGripRemoved;
// ------------------------------------------------
// Client Auth Throwing Data and functions
// ------------------------------------------------
UPROPERTY(EditAnywhere, Replicated, BlueprintReadWrite, Category = "Replication")
FVRClientAuthReplicationData ClientAuthReplicationData;
// Add this to client side physics replication (until coming to rest or timeout period is hit)
UFUNCTION(BlueprintCallable, Category = "Networking")
bool AddToClientReplicationBucket();
// Remove this from client side physics replication
UFUNCTION(BlueprintCallable, Category = "Networking")
bool RemoveFromClientReplicationBucket();
UFUNCTION()
bool PollReplicationEvent();
UFUNCTION(Category = "Networking")
void CeaseReplicationBlocking();
// Notify the server that we are no longer trying to run the throwing auth
UFUNCTION(Reliable, Server, WithValidation, Category = "Networking")
void Server_EndClientAuthReplication();
// Notify the server about a new movement rep
UFUNCTION(UnReliable, Server, WithValidation, Category = "Networking")
void Server_GetClientAuthReplication(const FRepMovementVR& newMovement);
// Returns if this object is currently client auth throwing
UFUNCTION(BlueprintPure, Category = "Networking")
FORCEINLINE bool IsCurrentlyClientAuthThrowing()
{
return ClientAuthReplicationData.bIsCurrentlyClientAuth;
}
// End client auth throwing data and functions //
// ------------------------------------------------
// Gameplay tag interface
// ------------------------------------------------
/** Overridden to return requirements tags */
virtual void GetOwnedGameplayTags(FGameplayTagContainer& TagContainer) const override
{
TagContainer = GameplayTags;
}
/** Tags that are set on this object */
UPROPERTY(EditAnywhere, Replicated, BlueprintReadWrite, Category = "GameplayTags")
FGameplayTagContainer GameplayTags;
// End Gameplay Tag Interface
virtual void PreReplication(IRepChangedPropertyTracker& ChangedPropertyTracker) override;
// Skips the attachment replication if we are locally owned and our grip settings say that we are a client authed grip.
UPROPERTY(EditAnywhere, Replicated, BlueprintReadWrite, Category = "Replication")
bool bAllowIgnoringAttachOnOwner;
// Should we skip attachment replication (vr settings say we are a client auth grip and our owner is locally controlled)
inline bool ShouldWeSkipAttachmentReplication(bool bConsiderHeld = true) const
{
if ((bConsiderHeld && !VRGripInterfaceSettings.bWasHeld) || GetNetMode() < ENetMode::NM_Client)
return false;
if (VRGripInterfaceSettings.MovementReplicationType == EGripMovementReplicationSettings::ClientSide_Authoritive ||
VRGripInterfaceSettings.MovementReplicationType == EGripMovementReplicationSettings::ClientSide_Authoritive_NoRep)
{
return HasLocalNetOwner();
}
else
return false;
}
// Fix bugs with replication and bReplicateMovement
virtual void OnRep_AttachmentReplication() override;
virtual void OnRep_ReplicateMovement() override;
virtual void OnRep_ReplicatedMovement() override;
virtual void PostNetReceivePhysicState() override;
// Debug printing of when the object is replication destroyed
/*virtual void OnSubobjectDestroyFromReplication(UObject *Subobject) override
{
Super::OnSubobjectDestroyFromReplication(Subobject);
GEngine->AddOnScreenDebugMessage(-1, 15.f, FColor::Red, FString::Printf(TEXT("Killed Object On Actor: x: %s"), *Subobject->GetName()));
}*/
// This isn't called very many places but it does come up
virtual void MarkComponentsAsGarbage(bool bModify) override;
/** Called right before being marked for destruction due to network replication */
// Clean up our objects so that they aren't sitting around for GC
virtual void PreDestroyFromReplication() override;
// On Destroy clean up our objects
virtual void BeginDestroy() override;
UPROPERTY(EditAnywhere, Replicated, BlueprintReadWrite, Category = "VRGripInterface")
bool bRepGripSettingsAndGameplayTags;
UPROPERTY(EditAnywhere, Replicated, BlueprintReadWrite, Category = "VRGripInterface")
FBPInterfaceProperties VRGripInterfaceSettings;
// Set up as deny instead of allow so that default allows for gripping
// The GripInitiator is not guaranteed to be valid, check it for validity
virtual bool DenyGripping_Implementation(UGripMotionControllerComponent * GripInitiator = nullptr) override;
// How an interfaced object behaves when teleporting
virtual EGripInterfaceTeleportBehavior TeleportBehavior_Implementation() override;
// Should this object simulate on drop
virtual bool SimulateOnDrop_Implementation() override;
// Grip type to use
virtual EGripCollisionType GetPrimaryGripType_Implementation(bool bIsSlot) override;
// Secondary grip type
virtual ESecondaryGripType SecondaryGripType_Implementation() override;
// Define which movement repliation setting to use
virtual EGripMovementReplicationSettings GripMovementReplicationType_Implementation() override;
// Define the late update setting
virtual EGripLateUpdateSettings GripLateUpdateSetting_Implementation() override;
// What grip stiffness and damping to use if using a physics constraint
virtual void GetGripStiffnessAndDamping_Implementation(float& GripStiffnessOut, float& GripDampingOut) override;
// Get the advanced physics settings for this grip
virtual FBPAdvGripSettings AdvancedGripSettings_Implementation() override;
// What distance to break a grip at (only relevent with physics enabled grips
virtual float GripBreakDistance_Implementation() override;
// Get closest primary slot in range
virtual void ClosestGripSlotInRange_Implementation(FVector WorldLocation, bool bSecondarySlot, bool& bHadSlotInRange, FTransform& SlotWorldTransform, FName& SlotName, UGripMotionControllerComponent* CallingController = nullptr, FName OverridePrefix = NAME_None) override;
// Check if an object allows multiple grips at one time
virtual bool AllowsMultipleGrips_Implementation() override;
// Returns if the object is held and if so, which controllers are holding it
virtual void IsHeld_Implementation(TArray<FBPGripPair>& HoldingControllers, bool& bIsHeld) override;
// Sets is held, used by the plugin
virtual void SetHeld_Implementation(UGripMotionControllerComponent* HoldingController, uint8 GripID, bool bIsHeld) override;
// Interface function used to throw the delegates that is invisible to blueprints so that it can't be overridden
virtual void Native_NotifyThrowGripDelegates(UGripMotionControllerComponent* Controller, bool bGripped, const FBPActorGripInformation& GripInformation, bool bWasSocketed = false) override;
// Returns if the object wants to be socketed
virtual bool RequestsSocketing_Implementation(USceneComponent*& ParentToSocketTo, FName& OptionalSocketName, FTransform_NetQuantize& RelativeTransform) override;
// Get grip scripts
virtual bool GetGripScripts_Implementation(TArray<UVRGripScriptBase*>& ArrayReference) override;
// Events //
// Event triggered each tick on the interfaced object when gripped, can be used for custom movement or grip based logic
virtual void TickGrip_Implementation(UGripMotionControllerComponent* GrippingController, const FBPActorGripInformation& GripInformation, float DeltaTime) override;
// Event triggered on the interfaced object when gripped
virtual void OnGrip_Implementation(UGripMotionControllerComponent* GrippingController, const FBPActorGripInformation& GripInformation) override;
// Event triggered on the interfaced object when grip is released
virtual void OnGripRelease_Implementation(UGripMotionControllerComponent* ReleasingController, const FBPActorGripInformation& GripInformation, bool bWasSocketed = false) override;
// Event triggered on the interfaced object when child component is gripped
virtual void OnChildGrip_Implementation(UGripMotionControllerComponent* GrippingController, const FBPActorGripInformation& GripInformation) override;
// Event triggered on the interfaced object when child component is released
virtual void OnChildGripRelease_Implementation(UGripMotionControllerComponent* ReleasingController, const FBPActorGripInformation& GripInformation, bool bWasSocketed = false) override;
// Event triggered on the interfaced object when secondary gripped
virtual void OnSecondaryGrip_Implementation(UGripMotionControllerComponent* GripOwningController, USceneComponent* SecondaryGripComponent, const FBPActorGripInformation& GripInformation) override;
// Event triggered on the interfaced object when secondary grip is released
virtual void OnSecondaryGripRelease_Implementation(UGripMotionControllerComponent* GripOwningController, USceneComponent* ReleasingSecondaryGripComponent, const FBPActorGripInformation& GripInformation) override;
// Interaction Functions
// Call to use an object
virtual void OnUsed_Implementation() override;
// Call to stop using an object
virtual void OnEndUsed_Implementation() override;
// Call to use an object
virtual void OnSecondaryUsed_Implementation() override;
// Call to stop using an object
virtual void OnEndSecondaryUsed_Implementation() override;
// Call to send an action event to the object
virtual void OnInput_Implementation(FKey Key, EInputEvent KeyEvent) override;
};

View file

@ -0,0 +1,205 @@
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "VRBPDatatypes.h"
#include "VRGripInterface.h"
#include "GameplayTagContainer.h"
#include "GameplayTagAssetInterface.h"
#include "Components/SkeletalMeshComponent.h"
#include "Engine/ActorChannel.h"
#include "GrippableSkeletalMeshComponent.generated.h"
class UVRGripScriptBase;
class UGripMotionControllerComponent;
/**
*
*/
UCLASS(Blueprintable, meta = (BlueprintSpawnableComponent, ChildCanTick), ClassGroup = (VRExpansionPlugin))
class VREXPANSIONPLUGIN_API UGrippableSkeletalMeshComponent : public USkeletalMeshComponent, public IVRGripInterface, public IGameplayTagAssetInterface
{
GENERATED_BODY()
public:
UGrippableSkeletalMeshComponent(const FObjectInitializer& ObjectInitializer);
~UGrippableSkeletalMeshComponent();
virtual void BeginPlay() override;
virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override;
// Weld Fix until epic fixes it #TODO: check back in on this and remove and the asset include when epic fixes it
virtual void GetWeldedBodies(TArray<FBodyInstance*>& OutWeldedBodies, TArray<FName>& OutLabels, bool bIncludingAutoWeld) override;
virtual FBodyInstance* GetBodyInstance(FName BoneName = NAME_None, bool bGetWelded = true, int32 Index = INDEX_NONE) const override;
UPROPERTY(EditAnywhere, Replicated, BlueprintReadOnly, Instanced, Category = "VRGripInterface")
TArray<TObjectPtr<UVRGripScriptBase>> GripLogicScripts;
// If true then the grip script array will be considered for replication, if false then it will not
// This is an optimization for when you have a lot of grip scripts in use, you can toggle this off in cases
// where the object will never have a replicating script
UPROPERTY(EditAnywhere, Replicated, BlueprintReadWrite, Category = "VRGripInterface")
bool bReplicateGripScripts;
bool ReplicateSubobjects(UActorChannel* Channel, class FOutBunch *Bunch, FReplicationFlags *RepFlags) override;
// Sets the Deny Gripping variable on the FBPInterfaceSettings struct
UFUNCTION(BlueprintCallable, Category = "VRGripInterface")
void SetDenyGripping(bool bDenyGripping);
// Sets the grip priority on the FBPInterfaceSettings struct
UFUNCTION(BlueprintCallable, Category = "VRGripInterface")
void SetGripPriority(int NewGripPriority);
// Called when a object is gripped
UPROPERTY(BlueprintAssignable, Category = "Grip Events")
FVROnGripSignature OnGripped;
// Called when a object is dropped
UPROPERTY(BlueprintAssignable, Category = "Grip Events")
FVROnDropSignature OnDropped;
// Called when an object we hold is secondary gripped
UPROPERTY(BlueprintAssignable, Category = "Grip Events")
FVROnGripSignature OnSecondaryGripAdded;
// Called when an object we hold is secondary dropped
UPROPERTY(BlueprintAssignable, Category = "Grip Events")
FVROnGripSignature OnSecondaryGripRemoved;
// ------------------------------------------------
// Gameplay tag interface
// ------------------------------------------------
/** Overridden to return requirements tags */
virtual void GetOwnedGameplayTags(FGameplayTagContainer& TagContainer) const override
{
TagContainer = GameplayTags;
}
/** Tags that are set on this object */
UPROPERTY(EditAnywhere, Replicated, BlueprintReadWrite, Category = "GameplayTags")
FGameplayTagContainer GameplayTags;
// End Gameplay Tag Interface
virtual void PreReplication(IRepChangedPropertyTracker & ChangedPropertyTracker) override;
/** Called right before being marked for destruction due to network replication */
// Clean up our objects so that they aren't sitting around for GC
virtual void PreDestroyFromReplication() override;
virtual void GetSubobjectsWithStableNamesForNetworking(TArray<UObject*> &ObjList) override;
// This one is for components to clean up
virtual void OnComponentDestroyed(bool bDestroyingHierarchy) override;
// Requires bReplicates to be true for the component
UPROPERTY(EditAnywhere, Replicated, BlueprintReadWrite, Category = "VRGripInterface|Replication")
bool bRepGripSettingsAndGameplayTags;
// Overrides the default of : true and allows for controlling it like in an actor, should be default of off normally with grippable components
UPROPERTY(EditAnywhere, Replicated, BlueprintReadWrite, Category = "VRGripInterface|Replication")
bool bReplicateMovement;
bool bOriginalReplicatesMovement;
UPROPERTY(EditAnywhere, Replicated, BlueprintReadWrite, Category = "VRGripInterface")
FBPInterfaceProperties VRGripInterfaceSettings;
// Set up as deny instead of allow so that default allows for gripping
// The GripInitiator is not guaranteed to be valid, check it for validity
virtual bool DenyGripping_Implementation(UGripMotionControllerComponent * GripInitiator = nullptr) override;
// How an interfaced object behaves when teleporting
virtual EGripInterfaceTeleportBehavior TeleportBehavior_Implementation() override;
// Should this object simulate on drop
virtual bool SimulateOnDrop_Implementation() override;
// Grip type to use
virtual EGripCollisionType GetPrimaryGripType_Implementation(bool bIsSlot) override;
// Secondary grip type
virtual ESecondaryGripType SecondaryGripType_Implementation() override;
// Define which movement repliation setting to use
virtual EGripMovementReplicationSettings GripMovementReplicationType_Implementation() override;
// Define the late update setting
virtual EGripLateUpdateSettings GripLateUpdateSetting_Implementation() override;
// What grip stiffness and damping to use if using a physics constraint
virtual void GetGripStiffnessAndDamping_Implementation(float& GripStiffnessOut, float& GripDampingOut) override;
// Get the advanced physics settings for this grip
virtual FBPAdvGripSettings AdvancedGripSettings_Implementation() override;
// What distance to break a grip at (only relevent with physics enabled grips
virtual float GripBreakDistance_Implementation() override;
// Get closest primary slot in range
virtual void ClosestGripSlotInRange_Implementation(FVector WorldLocation, bool bSecondarySlot, bool& bHadSlotInRange, FTransform& SlotWorldTransform, FName& SlotName, UGripMotionControllerComponent* CallingController = nullptr, FName OverridePrefix = NAME_None) override;
// Check if an object allows multiple grips at one time
virtual bool AllowsMultipleGrips_Implementation() override;
// Returns if the object is held and if so, which controllers are holding it
virtual void IsHeld_Implementation(TArray<FBPGripPair>& HoldingControllers, bool& bIsHeld) override;
// Sets is held, used by the plugin
virtual void SetHeld_Implementation(UGripMotionControllerComponent* HoldingController, uint8 GripID, bool bIsHeld) override;
// Interface function used to throw the delegates that is invisible to blueprints so that it can't be overridden
virtual void Native_NotifyThrowGripDelegates(UGripMotionControllerComponent* Controller, bool bGripped, const FBPActorGripInformation& GripInformation, bool bWasSocketed = false) override;
// Returns if the object wants to be socketed
virtual bool RequestsSocketing_Implementation(USceneComponent*& ParentToSocketTo, FName& OptionalSocketName, FTransform_NetQuantize& RelativeTransform) override;
// Get grip scripts
virtual bool GetGripScripts_Implementation(TArray<UVRGripScriptBase*>& ArrayReference) override;
// Events //
// Event triggered each tick on the interfaced object when gripped, can be used for custom movement or grip based logic
virtual void TickGrip_Implementation(UGripMotionControllerComponent* GrippingController, const FBPActorGripInformation& GripInformation, float DeltaTime) override;
// Event triggered on the interfaced object when gripped
virtual void OnGrip_Implementation(UGripMotionControllerComponent* GrippingController, const FBPActorGripInformation& GripInformation) override;
// Event triggered on the interfaced object when grip is released
virtual void OnGripRelease_Implementation(UGripMotionControllerComponent* ReleasingController, const FBPActorGripInformation& GripInformation, bool bWasSocketed = false) override;
// Event triggered on the interfaced object when child component is gripped
virtual void OnChildGrip_Implementation(UGripMotionControllerComponent* GrippingController, const FBPActorGripInformation& GripInformation) override;
// Event triggered on the interfaced object when child component is released
virtual void OnChildGripRelease_Implementation(UGripMotionControllerComponent* ReleasingController, const FBPActorGripInformation& GripInformation, bool bWasSocketed = false) override;
// Event triggered on the interfaced object when secondary gripped
virtual void OnSecondaryGrip_Implementation(UGripMotionControllerComponent* GripOwningController, USceneComponent* SecondaryGripComponent, const FBPActorGripInformation& GripInformation) override;
// Event triggered on the interfaced object when secondary grip is released
virtual void OnSecondaryGripRelease_Implementation(UGripMotionControllerComponent* GripOwningController, USceneComponent* ReleasingSecondaryGripComponent, const FBPActorGripInformation& GripInformation) override;
// Interaction Functions
// Call to use an object
virtual void OnUsed_Implementation() override;
// Call to stop using an object
virtual void OnEndUsed_Implementation() override;
// Call to use an object
virtual void OnSecondaryUsed_Implementation() override;
// Call to stop using an object
virtual void OnEndSecondaryUsed_Implementation() override;
// Call to send an action event to the object
virtual void OnInput_Implementation(FKey Key, EInputEvent KeyEvent) override;
};

View file

@ -0,0 +1,201 @@
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "VRBPDatatypes.h"
#include "VRGripInterface.h"
#include "GameplayTagContainer.h"
#include "GameplayTagAssetInterface.h"
#include "Components/SphereComponent.h"
#include "Engine/ActorChannel.h"
#include "GrippableSphereComponent.generated.h"
class UVRGripScriptBase;
class UGripMotionControllerComponent;
/**
*
*/
UCLASS(Blueprintable, meta = (BlueprintSpawnableComponent, ChildCanTick), ClassGroup = (VRExpansionPlugin))
class VREXPANSIONPLUGIN_API UGrippableSphereComponent : public USphereComponent, public IVRGripInterface, public IGameplayTagAssetInterface
{
GENERATED_BODY()
public:
UGrippableSphereComponent(const FObjectInitializer& ObjectInitializer);
~UGrippableSphereComponent();
virtual void BeginPlay() override;
virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override;
UPROPERTY(EditAnywhere, Replicated, BlueprintReadOnly, Instanced, Category = "VRGripInterface")
TArray<TObjectPtr<UVRGripScriptBase>> GripLogicScripts;
// If true then the grip script array will be considered for replication, if false then it will not
// This is an optimization for when you have a lot of grip scripts in use, you can toggle this off in cases
// where the object will never have a replicating script
UPROPERTY(EditAnywhere, Replicated, BlueprintReadWrite, Category = "VRGripInterface")
bool bReplicateGripScripts;
bool ReplicateSubobjects(UActorChannel* Channel, class FOutBunch *Bunch, FReplicationFlags *RepFlags) override;
// Sets the Deny Gripping variable on the FBPInterfaceSettings struct
UFUNCTION(BlueprintCallable, Category = "VRGripInterface")
void SetDenyGripping(bool bDenyGripping);
// Sets the grip priority on the FBPInterfaceSettings struct
UFUNCTION(BlueprintCallable, Category = "VRGripInterface")
void SetGripPriority(int NewGripPriority);
// Called when a object is gripped
UPROPERTY(BlueprintAssignable, Category = "Grip Events")
FVROnGripSignature OnGripped;
// Called when a object is dropped
UPROPERTY(BlueprintAssignable, Category = "Grip Events")
FVROnDropSignature OnDropped;
// Called when an object we hold is secondary gripped
UPROPERTY(BlueprintAssignable, Category = "Grip Events")
FVROnGripSignature OnSecondaryGripAdded;
// Called when an object we hold is secondary dropped
UPROPERTY(BlueprintAssignable, Category = "Grip Events")
FVROnGripSignature OnSecondaryGripRemoved;
// ------------------------------------------------
// Gameplay tag interface
// ------------------------------------------------
/** Overridden to return requirements tags */
virtual void GetOwnedGameplayTags(FGameplayTagContainer& TagContainer) const override
{
TagContainer = GameplayTags;
}
/** Tags that are set on this object */
UPROPERTY(EditAnywhere, Replicated, BlueprintReadWrite, Category = "GameplayTags")
FGameplayTagContainer GameplayTags;
// End Gameplay Tag Interface
virtual void PreReplication(IRepChangedPropertyTracker & ChangedPropertyTracker) override;
/** Called right before being marked for destruction due to network replication */
// Clean up our objects so that they aren't sitting around for GC
virtual void PreDestroyFromReplication() override;
virtual void GetSubobjectsWithStableNamesForNetworking(TArray<UObject*> &ObjList) override;
// This one is for components to clean up
virtual void OnComponentDestroyed(bool bDestroyingHierarchy) override;
// Requires bReplicates to be true for the component
UPROPERTY(EditAnywhere, Replicated, BlueprintReadWrite, Category = "VRGripInterface|Replication")
bool bRepGripSettingsAndGameplayTags;
// Overrides the default of : true and allows for controlling it like in an actor, should be default of off normally with grippable components
UPROPERTY(EditAnywhere, Replicated, BlueprintReadWrite, Category = "VRGripInterface|Replication")
bool bReplicateMovement;
bool bOriginalReplicatesMovement;
UPROPERTY(EditAnywhere, Replicated, BlueprintReadWrite, Category = "VRGripInterface")
FBPInterfaceProperties VRGripInterfaceSettings;
// Set up as deny instead of allow so that default allows for gripping
// The GripInitiator is not guaranteed to be valid, check it for validity
virtual bool DenyGripping_Implementation(UGripMotionControllerComponent * GripInitiator = nullptr) override;
// How an interfaced object behaves when teleporting
virtual EGripInterfaceTeleportBehavior TeleportBehavior_Implementation() override;
// Should this object simulate on drop
virtual bool SimulateOnDrop_Implementation() override;
// Grip type to use
virtual EGripCollisionType GetPrimaryGripType_Implementation(bool bIsSlot) override;
// Secondary grip type
virtual ESecondaryGripType SecondaryGripType_Implementation() override;
// Define which movement repliation setting to use
virtual EGripMovementReplicationSettings GripMovementReplicationType_Implementation() override;
// Define the late update setting
virtual EGripLateUpdateSettings GripLateUpdateSetting_Implementation() override;
// What grip stiffness and damping to use if using a physics constraint
virtual void GetGripStiffnessAndDamping_Implementation(float& GripStiffnessOut, float& GripDampingOut) override;
// Get the advanced physics settings for this grip
virtual FBPAdvGripSettings AdvancedGripSettings_Implementation() override;
// What distance to break a grip at (only relevent with physics enabled grips
virtual float GripBreakDistance_Implementation() override;
// Get closest primary slot in range
virtual void ClosestGripSlotInRange_Implementation(FVector WorldLocation, bool bSecondarySlot, bool& bHadSlotInRange, FTransform& SlotWorldTransform, FName& SlotName, UGripMotionControllerComponent* CallingController = nullptr, FName OverridePrefix = NAME_None) override;
// Check if an object allows multiple grips at one time
virtual bool AllowsMultipleGrips_Implementation() override;
// Returns if the object is held and if so, which controllers are holding it
virtual void IsHeld_Implementation(TArray<FBPGripPair>& HoldingControllers, bool& bIsHeld) override;
// Sets is held, used by the plugin
virtual void SetHeld_Implementation(UGripMotionControllerComponent* HoldingController, uint8 GripID, bool bIsHeld) override;
// Interface function used to throw the delegates that is invisible to blueprints so that it can't be overridden
virtual void Native_NotifyThrowGripDelegates(UGripMotionControllerComponent* Controller, bool bGripped, const FBPActorGripInformation& GripInformation, bool bWasSocketed = false) override;
// Returns if the object wants to be socketed
virtual bool RequestsSocketing_Implementation(USceneComponent*& ParentToSocketTo, FName& OptionalSocketName, FTransform_NetQuantize& RelativeTransform) override;
// Get grip scripts
virtual bool GetGripScripts_Implementation(TArray<UVRGripScriptBase*>& ArrayReference) override;
// Events //
// Event triggered each tick on the interfaced object when gripped, can be used for custom movement or grip based logic
virtual void TickGrip_Implementation(UGripMotionControllerComponent* GrippingController, const FBPActorGripInformation& GripInformation, float DeltaTime) override;
// Event triggered on the interfaced object when gripped
virtual void OnGrip_Implementation(UGripMotionControllerComponent* GrippingController, const FBPActorGripInformation& GripInformation) override;
// Event triggered on the interfaced object when grip is released
virtual void OnGripRelease_Implementation(UGripMotionControllerComponent* ReleasingController, const FBPActorGripInformation& GripInformation, bool bWasSocketed = false) override;
// Event triggered on the interfaced object when child component is gripped
virtual void OnChildGrip_Implementation(UGripMotionControllerComponent* GrippingController, const FBPActorGripInformation& GripInformation) override;
// Event triggered on the interfaced object when child component is released
virtual void OnChildGripRelease_Implementation(UGripMotionControllerComponent* ReleasingController, const FBPActorGripInformation& GripInformation, bool bWasSocketed = false) override;
// Event triggered on the interfaced object when secondary gripped
virtual void OnSecondaryGrip_Implementation(UGripMotionControllerComponent* GripOwningController, USceneComponent* SecondaryGripComponent, const FBPActorGripInformation& GripInformation) override;
// Event triggered on the interfaced object when secondary grip is released
virtual void OnSecondaryGripRelease_Implementation(UGripMotionControllerComponent* GripOwningController, USceneComponent* ReleasingSecondaryGripComponent, const FBPActorGripInformation& GripInformation) override;
// Interaction Functions
// Call to use an object
virtual void OnUsed_Implementation() override;
// Call to stop using an object
virtual void OnEndUsed_Implementation() override;
// Call to use an object
virtual void OnSecondaryUsed_Implementation() override;
// Call to stop using an object
virtual void OnEndSecondaryUsed_Implementation() override;
// Call to send an action event to the object
virtual void OnInput_Implementation(FKey Key, EInputEvent KeyEvent) override;
};

View file

@ -0,0 +1,298 @@
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
//#include "Engine/Engine.h"
#include "Engine/StaticMeshActor.h"
#include "Components/StaticMeshComponent.h"
#include "VRBPDatatypes.h"
#include "VRGripInterface.h"
#include "GameplayTagContainer.h"
#include "GameplayTagAssetInterface.h"
#include "Engine/ActorChannel.h"
#include "Grippables/GrippableDataTypes.h"
#include "Grippables/GrippablePhysicsReplication.h"
#include "GrippableStaticMeshActor.generated.h"
class UGripMotionControllerComponent;
class UVRGripScriptBase;
/**
* A component specifically for being able to turn off movement replication in the component at will
* Has the upside of also being a blueprintable base since UE4 doesn't allow that with std ones
*/
UCLASS(Blueprintable, meta = (BlueprintSpawnableComponent,ChildCanTick), ClassGroup = (VRExpansionPlugin))
class VREXPANSIONPLUGIN_API UOptionalRepStaticMeshComponent : public UStaticMeshComponent
{
GENERATED_BODY()
public:
UOptionalRepStaticMeshComponent(const FObjectInitializer& ObjectInitializer);
// Overrides the default of : true and allows for controlling it like in an actor, should be default of off normally with grippable components
UPROPERTY(EditAnywhere, Replicated, BlueprintReadWrite, Category = "Component Replication")
bool bReplicateMovement;
virtual void PreReplication(IRepChangedPropertyTracker & ChangedPropertyTracker) override;
};
/**
*
*/
UCLASS(Blueprintable, meta = (BlueprintSpawnableComponent, ChildCanTick), ClassGroup = (VRExpansionPlugin))
class VREXPANSIONPLUGIN_API AGrippableStaticMeshActor : public AStaticMeshActor, public IVRGripInterface, public IGameplayTagAssetInterface
{
GENERATED_BODY()
public:
AGrippableStaticMeshActor(const FObjectInitializer& ObjectInitializer);
~AGrippableStaticMeshActor();
virtual void BeginPlay() override;
virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override;
UPROPERTY(Replicated, ReplicatedUsing = OnRep_AttachmentReplication)
FRepAttachmentWithWeld AttachmentWeldReplication;
virtual void GatherCurrentMovement() override;
UPROPERTY(EditAnywhere, Replicated, BlueprintReadOnly, Instanced, Category = "VRGripInterface")
TArray<TObjectPtr<UVRGripScriptBase>> GripLogicScripts;
// If true then the grip script array will be considered for replication, if false then it will not
// This is an optimization for when you have a lot of grip scripts in use, you can toggle this off in cases
// where the object will never have a replicating script
UPROPERTY(EditAnywhere, Replicated, BlueprintReadWrite, Category = "VRGripInterface")
bool bReplicateGripScripts;
bool ReplicateSubobjects(UActorChannel* Channel, class FOutBunch *Bunch, FReplicationFlags *RepFlags) override;
virtual void GetSubobjectsWithStableNamesForNetworking(TArray<UObject*>& ObjList) override;
// Sets the Deny Gripping variable on the FBPInterfaceSettings struct
UFUNCTION(BlueprintCallable, Category = "VRGripInterface")
void SetDenyGripping(bool bDenyGripping);
// Sets the grip priority on the FBPInterfaceSettings struct
UFUNCTION(BlueprintCallable, Category = "VRGripInterface")
void SetGripPriority(int NewGripPriority);
// Called when a object is gripped
UPROPERTY(BlueprintAssignable, Category = "Grip Events")
FVROnGripSignature OnGripped;
// Called when a object is dropped
UPROPERTY(BlueprintAssignable, Category = "Grip Events")
FVROnDropSignature OnDropped;
// Called when an object we hold is secondary gripped
UPROPERTY(BlueprintAssignable, Category = "Grip Events")
FVROnGripSignature OnSecondaryGripAdded;
// Called when an object we hold is secondary dropped
UPROPERTY(BlueprintAssignable, Category = "Grip Events")
FVROnGripSignature OnSecondaryGripRemoved;
// ------------------------------------------------
// Client Auth Throwing Data and functions
// ------------------------------------------------
UPROPERTY(EditAnywhere, Replicated, BlueprintReadWrite, Category = "Replication")
FVRClientAuthReplicationData ClientAuthReplicationData;
// Add this to client side physics replication (until coming to rest or timeout period is hit)
UFUNCTION(BlueprintCallable, Category = "Networking")
bool AddToClientReplicationBucket();
// Remove this from client side physics replication
UFUNCTION(BlueprintCallable, Category = "Networking")
bool RemoveFromClientReplicationBucket();
// From IVRReplicationInterface
UFUNCTION()
bool PollReplicationEvent();
UFUNCTION(Category = "Networking")
void CeaseReplicationBlocking();
// Notify the server that we are no longer trying to run the throwing auth
UFUNCTION(Reliable, Server, WithValidation, Category = "Networking")
void Server_EndClientAuthReplication();
// Notify the server about a new movement rep
UFUNCTION(UnReliable, Server, WithValidation, Category = "Networking")
void Server_GetClientAuthReplication(const FRepMovementVR & newMovement);
// Returns if this object is currently client auth throwing
UFUNCTION(BlueprintPure, Category = "Networking")
FORCEINLINE bool IsCurrentlyClientAuthThrowing()
{
return ClientAuthReplicationData.bIsCurrentlyClientAuth;
}
// End client auth throwing data and functions //
// ------------------------------------------------
// Gameplay tag interface
// ------------------------------------------------
/** Overridden to return requirements tags */
virtual void GetOwnedGameplayTags(FGameplayTagContainer& TagContainer) const override
{
TagContainer = GameplayTags;
}
/** Tags that are set on this object */
UPROPERTY(EditAnywhere, Replicated, BlueprintReadWrite, Category = "GameplayTags")
FGameplayTagContainer GameplayTags;
// End Gameplay Tag Interface
virtual void PreReplication(IRepChangedPropertyTracker & ChangedPropertyTracker) override;
// Skips the attachment replication if we are locally owned and our grip settings say that we are a client authed grip.
UPROPERTY(EditAnywhere, Replicated, BlueprintReadWrite, Category = "Replication")
bool bAllowIgnoringAttachOnOwner;
// Should we skip attachment replication (vr settings say we are a client auth grip and our owner is locally controlled)
inline bool ShouldWeSkipAttachmentReplication(bool bConsiderHeld = true) const
{
if((bConsiderHeld && !VRGripInterfaceSettings.bWasHeld) || GetNetMode() < ENetMode::NM_Client)
return false;
if (VRGripInterfaceSettings.MovementReplicationType == EGripMovementReplicationSettings::ClientSide_Authoritive ||
VRGripInterfaceSettings.MovementReplicationType == EGripMovementReplicationSettings::ClientSide_Authoritive_NoRep)
{
return HasLocalNetOwner();
}
else
return false;
}
// Fix bugs with replication and bReplicateMovement
virtual void OnRep_AttachmentReplication() override;
virtual void OnRep_ReplicateMovement() override;
virtual void OnRep_ReplicatedMovement() override;
virtual void PostNetReceivePhysicState() override;
// Debug printing of when the object is replication destroyed
/*virtual void OnSubobjectDestroyFromReplication(UObject *Subobject) override
{
Super::OnSubobjectDestroyFromReplication(Subobject);
GEngine->AddOnScreenDebugMessage(-1, 15.f, FColor::Red, FString::Printf(TEXT("Killed Object On Actor: x: %s"), *Subobject->GetName()));
}*/
// This isn't called very many places but it does come up
virtual void MarkComponentsAsGarbage(bool bModify) override;
/** Called right before being marked for destruction due to network replication */
// Clean up our objects so that they aren't sitting around for GC
virtual void PreDestroyFromReplication() override;
// On Destroy clean up our objects
virtual void BeginDestroy() override;
UPROPERTY(EditAnywhere, Replicated, BlueprintReadWrite, Category = "VRGripInterface")
bool bRepGripSettingsAndGameplayTags;
UPROPERTY(EditAnywhere, Replicated, BlueprintReadWrite, Category = "VRGripInterface")
FBPInterfaceProperties VRGripInterfaceSettings;
// Set up as deny instead of allow so that default allows for gripping
// The GripInitiator is not guaranteed to be valid, check it for validity
virtual bool DenyGripping_Implementation(UGripMotionControllerComponent * GripInitiator = nullptr) override;
// How an interfaced object behaves when teleporting
virtual EGripInterfaceTeleportBehavior TeleportBehavior_Implementation() override;
// Should this object simulate on drop
virtual bool SimulateOnDrop_Implementation() override;
// Grip type to use
virtual EGripCollisionType GetPrimaryGripType_Implementation(bool bIsSlot) override;
// Secondary grip type
virtual ESecondaryGripType SecondaryGripType_Implementation() override;
// Define which movement repliation setting to use
virtual EGripMovementReplicationSettings GripMovementReplicationType_Implementation() override;
// Define the late update setting
virtual EGripLateUpdateSettings GripLateUpdateSetting_Implementation() override;
// What grip stiffness and damping to use if using a physics constraint
virtual void GetGripStiffnessAndDamping_Implementation(float& GripStiffnessOut, float& GripDampingOut) override;
// Get the advanced physics settings for this grip
virtual FBPAdvGripSettings AdvancedGripSettings_Implementation() override;
// What distance to break a grip at (only relevent with physics enabled grips
virtual float GripBreakDistance_Implementation() override;
// Get closest primary slot in range
virtual void ClosestGripSlotInRange_Implementation(FVector WorldLocation, bool bSecondarySlot, bool& bHadSlotInRange, FTransform& SlotWorldTransform, FName& SlotName, UGripMotionControllerComponent* CallingController = nullptr, FName OverridePrefix = NAME_None) override;
// Check if an object allows multiple grips at one time
virtual bool AllowsMultipleGrips_Implementation() override;
// Returns if the object is held and if so, which controllers are holding it
virtual void IsHeld_Implementation(TArray<FBPGripPair>& HoldingControllers, bool& bIsHeld) override;
// Sets is held, used by the plugin
virtual void SetHeld_Implementation(UGripMotionControllerComponent* HoldingController, uint8 GripID, bool bIsHeld) override;
// Interface function used to throw the delegates that is invisible to blueprints so that it can't be overridden
virtual void Native_NotifyThrowGripDelegates(UGripMotionControllerComponent* Controller, bool bGripped, const FBPActorGripInformation& GripInformation, bool bWasSocketed = false) override;
// Returns if the object wants to be socketed
virtual bool RequestsSocketing_Implementation(USceneComponent*& ParentToSocketTo, FName& OptionalSocketName, FTransform_NetQuantize& RelativeTransform) override;
// Get grip scripts
virtual bool GetGripScripts_Implementation(TArray<UVRGripScriptBase*>& ArrayReference) override;
// Events //
// Event triggered each tick on the interfaced object when gripped, can be used for custom movement or grip based logic
virtual void TickGrip_Implementation(UGripMotionControllerComponent* GrippingController, const FBPActorGripInformation& GripInformation, float DeltaTime) override;
// Event triggered on the interfaced object when gripped
virtual void OnGrip_Implementation(UGripMotionControllerComponent* GrippingController, const FBPActorGripInformation& GripInformation) override;
// Event triggered on the interfaced object when grip is released
virtual void OnGripRelease_Implementation(UGripMotionControllerComponent* ReleasingController, const FBPActorGripInformation& GripInformation, bool bWasSocketed = false) override;
// Event triggered on the interfaced object when child component is gripped
virtual void OnChildGrip_Implementation(UGripMotionControllerComponent* GrippingController, const FBPActorGripInformation& GripInformation) override;
// Event triggered on the interfaced object when child component is released
virtual void OnChildGripRelease_Implementation(UGripMotionControllerComponent* ReleasingController, const FBPActorGripInformation& GripInformation, bool bWasSocketed = false) override;
// Event triggered on the interfaced object when secondary gripped
virtual void OnSecondaryGrip_Implementation(UGripMotionControllerComponent* GripOwningController, USceneComponent* SecondaryGripComponent, const FBPActorGripInformation& GripInformation) override;
// Event triggered on the interfaced object when secondary grip is released
virtual void OnSecondaryGripRelease_Implementation(UGripMotionControllerComponent* GripOwningController, USceneComponent* ReleasingSecondaryGripComponent, const FBPActorGripInformation& GripInformation) override;
// Interaction Functions
// Call to use an object
virtual void OnUsed_Implementation() override;
// Call to stop using an object
virtual void OnEndUsed_Implementation() override;
// Call to use an object
virtual void OnSecondaryUsed_Implementation() override;
// Call to stop using an object
virtual void OnEndSecondaryUsed_Implementation() override;
// Call to send an action event to the object
virtual void OnInput_Implementation(FKey Key, EInputEvent KeyEvent) override;
};

View file

@ -0,0 +1,200 @@
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "VRBPDatatypes.h"
#include "VRGripInterface.h"
#include "GameplayTagContainer.h"
#include "GameplayTagAssetInterface.h"
#include "Components/StaticMeshComponent.h"
#include "Engine/ActorChannel.h"
#include "GrippableStaticMeshComponent.generated.h"
class UVRGripScriptBase;
class UGripMotionControllerComponent;
/**
*
*/
UCLASS(Blueprintable, meta = (BlueprintSpawnableComponent,ChildCanTick), ClassGroup = (VRExpansionPlugin))
class VREXPANSIONPLUGIN_API UGrippableStaticMeshComponent : public UStaticMeshComponent, public IVRGripInterface, public IGameplayTagAssetInterface
{
GENERATED_BODY()
public:
UGrippableStaticMeshComponent(const FObjectInitializer& ObjectInitializer);
~UGrippableStaticMeshComponent();
virtual void BeginPlay() override;
virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override;
// ------------------------------------------------
// Gameplay tag interface
// ------------------------------------------------
/** Overridden to return requirements tags */
UPROPERTY(EditAnywhere, Replicated, BlueprintReadOnly, Instanced, Category = "VRGripInterface")
TArray<TObjectPtr<UVRGripScriptBase>> GripLogicScripts;
// If true then the grip script array will be considered for replication, if false then it will not
// This is an optimization for when you have a lot of grip scripts in use, you can toggle this off in cases
// where the object will never have a replicating script
UPROPERTY(EditAnywhere, Replicated, BlueprintReadWrite, Category = "VRGripInterface")
bool bReplicateGripScripts;
bool ReplicateSubobjects(UActorChannel* Channel, class FOutBunch *Bunch, FReplicationFlags *RepFlags) override;
// Sets the Deny Gripping variable on the FBPInterfaceSettings struct
UFUNCTION(BlueprintCallable, Category = "VRGripInterface")
void SetDenyGripping(bool bDenyGripping);
// Sets the grip priority on the FBPInterfaceSettings struct
UFUNCTION(BlueprintCallable, Category = "VRGripInterface")
void SetGripPriority(int NewGripPriority);
// Called when a object is gripped
UPROPERTY(BlueprintAssignable, Category = "Grip Events")
FVROnGripSignature OnGripped;
// Called when a object is dropped
UPROPERTY(BlueprintAssignable, Category = "Grip Events")
FVROnDropSignature OnDropped;
// Called when an object we hold is secondary gripped
UPROPERTY(BlueprintAssignable, Category = "Grip Events")
FVROnGripSignature OnSecondaryGripAdded;
// Called when an object we hold is secondary dropped
UPROPERTY(BlueprintAssignable, Category = "Grip Events")
FVROnGripSignature OnSecondaryGripRemoved;
virtual void GetOwnedGameplayTags(FGameplayTagContainer& TagContainer) const override
{
TagContainer = GameplayTags;
}
/** Tags that are set on this object */
UPROPERTY(EditAnywhere, Replicated, BlueprintReadWrite, Category = "GameplayTags")
FGameplayTagContainer GameplayTags;
// End Gameplay Tag Interface
virtual void PreReplication(IRepChangedPropertyTracker & ChangedPropertyTracker) override;
/** Called right before being marked for destruction due to network replication */
// Clean up our objects so that they aren't sitting around for GC
virtual void PreDestroyFromReplication() override;
virtual void GetSubobjectsWithStableNamesForNetworking(TArray<UObject*> &ObjList) override;
// This one is for components to clean up
virtual void OnComponentDestroyed(bool bDestroyingHierarchy) override;
// Requires bReplicates to be true for the component
UPROPERTY(EditAnywhere, Replicated, BlueprintReadWrite, Category = "VRGripInterface|Replication")
bool bRepGripSettingsAndGameplayTags;
// Overrides the default of : true and allows for controlling it like in an actor, should be default of off normally with grippable components
UPROPERTY(EditAnywhere, Replicated, BlueprintReadWrite, Category = "VRGripInterface|Replication")
bool bReplicateMovement;
bool bOriginalReplicatesMovement;
UPROPERTY(EditAnywhere, Replicated, BlueprintReadWrite, Category = "VRGripInterface")
FBPInterfaceProperties VRGripInterfaceSettings;
// Set up as deny instead of allow so that default allows for gripping
// The GripInitiator is not guaranteed to be valid, check it for validity
virtual bool DenyGripping_Implementation(UGripMotionControllerComponent * GripInitiator = nullptr) override;
// How an interfaced object behaves when teleporting
virtual EGripInterfaceTeleportBehavior TeleportBehavior_Implementation() override;
// Should this object simulate on drop
virtual bool SimulateOnDrop_Implementation() override;
// Grip type to use
virtual EGripCollisionType GetPrimaryGripType_Implementation(bool bIsSlot) override;
// Secondary grip type
virtual ESecondaryGripType SecondaryGripType_Implementation() override;
// Define which movement repliation setting to use
virtual EGripMovementReplicationSettings GripMovementReplicationType_Implementation() override;
// Define the late update setting
virtual EGripLateUpdateSettings GripLateUpdateSetting_Implementation() override;
// What grip stiffness and damping to use if using a physics constraint
virtual void GetGripStiffnessAndDamping_Implementation(float& GripStiffnessOut, float& GripDampingOut) override;
// Get the advanced physics settings for this grip
virtual FBPAdvGripSettings AdvancedGripSettings_Implementation() override;
// What distance to break a grip at (only relevent with physics enabled grips
virtual float GripBreakDistance_Implementation() override;
// Get closest primary slot in range
virtual void ClosestGripSlotInRange_Implementation(FVector WorldLocation, bool bSecondarySlot, bool& bHadSlotInRange, FTransform& SlotWorldTransform, FName& SlotName, UGripMotionControllerComponent* CallingController = nullptr, FName OverridePrefix = NAME_None) override;
// Check if an object allows multiple grips at one time
virtual bool AllowsMultipleGrips_Implementation() override;
// Returns if the object is held and if so, which controllers are holding it
virtual void IsHeld_Implementation(TArray<FBPGripPair>& HoldingControllers, bool& bIsHeld) override;
// Sets is held, used by the plugin
virtual void SetHeld_Implementation(UGripMotionControllerComponent* HoldingController, uint8 GripID, bool bIsHeld) override;
// Interface function used to throw the delegates that is invisible to blueprints so that it can't be overridden
virtual void Native_NotifyThrowGripDelegates(UGripMotionControllerComponent* Controller, bool bGripped, const FBPActorGripInformation& GripInformation, bool bWasSocketed = false) override;
// Returns if the object wants to be socketed
virtual bool RequestsSocketing_Implementation(USceneComponent*& ParentToSocketTo, FName& OptionalSocketName, FTransform_NetQuantize& RelativeTransform) override;
// Get grip scripts
virtual bool GetGripScripts_Implementation(TArray<UVRGripScriptBase*>& ArrayReference) override;
// Events //
// Event triggered each tick on the interfaced object when gripped, can be used for custom movement or grip based logic
virtual void TickGrip_Implementation(UGripMotionControllerComponent* GrippingController, const FBPActorGripInformation& GripInformation, float DeltaTime) override;
// Event triggered on the interfaced object when gripped
virtual void OnGrip_Implementation(UGripMotionControllerComponent* GrippingController, const FBPActorGripInformation& GripInformation) override;
// Event triggered on the interfaced object when grip is released
virtual void OnGripRelease_Implementation(UGripMotionControllerComponent* ReleasingController, const FBPActorGripInformation& GripInformation, bool bWasSocketed = false) override;
// Event triggered on the interfaced object when child component is gripped
virtual void OnChildGrip_Implementation(UGripMotionControllerComponent* GrippingController, const FBPActorGripInformation& GripInformation) override;
// Event triggered on the interfaced object when child component is released
virtual void OnChildGripRelease_Implementation(UGripMotionControllerComponent* ReleasingController, const FBPActorGripInformation& GripInformation, bool bWasSocketed = false) override;
// Event triggered on the interfaced object when secondary gripped
virtual void OnSecondaryGrip_Implementation(UGripMotionControllerComponent* GripOwningController, USceneComponent* SecondaryGripComponent, const FBPActorGripInformation& GripInformation) override;
// Event triggered on the interfaced object when secondary grip is released
virtual void OnSecondaryGripRelease_Implementation(UGripMotionControllerComponent* GripOwningController, USceneComponent* ReleasingSecondaryGripComponent, const FBPActorGripInformation& GripInformation) override;
// Interaction Functions
// Call to use an object
virtual void OnUsed_Implementation() override;
// Call to stop using an object
virtual void OnEndUsed_Implementation() override;
// Call to use an object
virtual void OnSecondaryUsed_Implementation() override;
// Call to stop using an object
virtual void OnEndSecondaryUsed_Implementation() override;
// Call to send an action event to the object
virtual void OnInput_Implementation(FKey Key, EInputEvent KeyEvent) override;
};

View file

@ -0,0 +1,447 @@
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "GameplayTagContainer.h"
#include "GameplayTagAssetInterface.h"
#include "Components/SceneComponent.h"
#include "Animation/AnimInstance.h"
#include "Misc/Guid.h"
#include "HandSocketComponent.generated.h"
class USkeletalMeshComponent;
class UPoseableMeshComponent;
class USkeletalMesh;
class UGripMotionControllerComponent;
class UAnimSequence;
struct FPoseSnapshot;
DECLARE_LOG_CATEGORY_EXTERN(LogVRHandSocketComponent, Log, All);
// Custom serialization version for the hand socket component
struct VREXPANSIONPLUGIN_API FVRHandSocketCustomVersion
{
enum Type
{
// Before any version changes were made in the plugin
BeforeCustomVersionWasAdded = 0,
// Added a set state tracker to handle in editor construction edge cases
HandSocketStoringSetState = 1,
// -----<new versions can be added above this line>-------------------------------------------------
VersionPlusOne,
LatestVersion = VersionPlusOne - 1
};
// The GUID for this custom version number
const static FGuid GUID;
private:
FVRHandSocketCustomVersion() {}
};
UENUM(BlueprintType)
namespace EVRAxis
{
enum Type
{
X,
Y,
Z
};
}
/**
* A base class for custom hand socket objects
* Not directly blueprint spawnable as you are supposed to subclass this to add on top your own custom data
*/
USTRUCT(BlueprintType, Category = "VRExpansionLibrary")
struct VREXPANSIONPLUGIN_API FBPVRHandPoseBonePair
{
GENERATED_BODY()
public:
// Distance to offset to get center of waist from tracked parent location
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Settings")
FName BoneName;
// Initial "Resting" location of the tracker parent, assumed to be the calibration zero
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Settings")
FQuat DeltaPose;
FBoneReference ReferenceToConstruct;
FBPVRHandPoseBonePair()
{
BoneName = NAME_None;
DeltaPose = FQuat::Identity;
}
FORCEINLINE bool operator==(const FName& Other) const
{
return (BoneName == Other);
}
};
UCLASS(Blueprintable, ClassGroup = (VRExpansionPlugin), hideCategories = ("Component Tick", Events, Physics, Lod, "Asset User Data", Collision))
class VREXPANSIONPLUGIN_API UHandSocketComponent : public USceneComponent, public IGameplayTagAssetInterface
{
GENERATED_BODY()
public:
UHandSocketComponent(const FObjectInitializer& ObjectInitializer);
~UHandSocketComponent();
//static get socket compoonent
//Axis to mirror on for this socket
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Hand Socket Data|Mirroring|Advanced")
TEnumAsByte<EVRAxis::Type> MirrorAxis;
// Axis to flip on when mirroring this socket
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Hand Socket Data|Mirroring|Advanced")
TEnumAsByte<EVRAxis::Type> FlipAxis;
// Relative placement of the hand to this socket
UPROPERTY(EditAnywhere, BlueprintReadWrite, /*DuplicateTransient,*/ Category = "Hand Socket Data")
FTransform HandRelativePlacement;
// Target Slot Prefix
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Hand Socket Data")
FName SlotPrefix;
// If true the hand meshes relative transform will be de-coupled from the hand socket
UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = "Hand Socket Data")
bool bDecoupleMeshPlacement;
// If true we should only be used to snap mesh to us, not for the actual socket transform
// Will act like free gripping but the mesh will snap into position
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Hand Socket Data")
bool bOnlySnapMesh;
// If true the end user should only pull the hand pose, not its transform from this component
// This is up to the end user to make use of as its part of the query steps.
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Hand Socket Data")
bool bOnlyUseHandPose;
// If true we will not create the mesh relative transform using the attach socket we are attached too
// Useful in cases where you aren't doing per bone gripping but want the socket to follow a bone that is animating
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Hand Socket Data")
bool bIgnoreAttachBone;
// If true then this socket is left hand dominant and will flip for the right hand instead
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Hand Socket Data")
bool bLeftHandDominant;
// If true we will mirror ourselves automatically for the off hand
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Hand Socket Data|Mirroring", meta = (DisplayName = "Flip For Off Hand"))
bool bFlipForLeftHand;
// If true, when we mirror the hand socket it will only mirror rotation, not position
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Hand Socket Data|Mirroring", meta = (editcondition = "bFlipForLeftHand"))
bool bOnlyFlipRotation;
// If true then this hand socket will always be considered "in range" and checked against others for lowest distance
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Hand Socket Data")
bool bAlwaysInRange;
// If true and there are multiple hand socket components in range with this setting
// Then the default behavior will compare closest rotation on them all to pick one
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Hand Socket Data")
bool bMatchRotation;
// If true then the hand socket will not be considered for search operations
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Hand Socket Data")
bool bDisabled;
// Snap distance to use if you want to override the defaults.
// Will be ignored if == 0.0f or bAlwaysInRange is true
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Hand Socket Data")
float OverrideDistance;
// If true we are expected to have a list of custom deltas for bones to overlay onto our base pose
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Hand Animation")
bool bUseCustomPoseDeltas;
// Custom rotations that are added on top of an animations bone rotation to make a final transform
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Hand Animation")
TArray<FBPVRHandPoseBonePair> CustomPoseDeltas;
// Primary hand animation, for both hands if they share animations, right hand if they don't
// If using a custom pose delta this is expected to be the base pose
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Hand Animation")
TObjectPtr<UAnimSequence> HandTargetAnimation;
// Scale to apply when mirroring the hand, adjust to visualize your off hand correctly
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Hand Socket Data")
FVector MirroredScale;
#if WITH_EDITORONLY_DATA
// If true we will attempt to only show editing widgets for bones matching the _l or _r postfixes
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Hand Animation")
bool bFilterBonesByPostfix;
// The postfix to filter by
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Hand Animation")
FString FilterPostfix;
// An array of bones to skip when looking to edit deltas, can help clean up the interaction if you have extra bones in the heirarchy
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Hand Animation")
TArray<FName> BonesToSkip;
FTransform GetBoneTransformAtTime(UAnimSequence* MyAnimSequence, /*float AnimTime,*/ int BoneIdx, FName BoneName, bool bUseRawDataOnly);
#endif
// Returns the base target animation of the hand (if there is one)
UFUNCTION(BlueprintCallable, Category = "Hand Socket Data")
UAnimSequence* GetTargetAnimation();
/**
* Returns the target animation of the hand blended with the delta rotations if there are any
* @param PoseSnapShot - Snapshot generated by this function
* @param TargetMesh - Targetmesh to check the skeleton of
* @param bSkipRootBone - If true we will skip the root bone (IE: Hand_r) and only apply the children poses (Full body)
* @param bFlipHand - If true we will mirror the pose, this is primarily to apply to a left hand from a right
*/
UFUNCTION(BlueprintCallable, Category = "Hand Socket Data")
bool GetBlendedPoseSnapShot(FPoseSnapshot& PoseSnapShot, USkeletalMeshComponent* TargetMesh = nullptr, bool bSkipRootBone = false, bool bFlipHand = false);
/**
* Converts an animation sequence into a pose snapshot
* @param InAnimationSequence - Sequence to convert to a pose snapshot
* @param OutPoseSnapShot - Snapshot returned by this function
* @param TargetMesh - Targetmesh to check the skeleton of
* @param bSkipRootBone - If true we will skip the root bone (IE: Hand_r) and only apply the children poses (Full body)
* @param bFlipHand - If true we will mirror the pose, this is primarily to apply to a left hand from a right
*/
UFUNCTION(BlueprintCallable, Category = "Hand Socket Data", meta = (bIgnoreSelf = "true"))
static bool GetAnimationSequenceAsPoseSnapShot(UAnimSequence * InAnimationSequence, FPoseSnapshot& OutPoseSnapShot, USkeletalMeshComponent* TargetMesh = nullptr, bool bSkipRootBone = false, bool bFlipHand = false);
// Returns the target relative transform of the hand
//UFUNCTION(BlueprintCallable, Category = "Hand Socket Data")
FTransform GetHandRelativePlacement();
inline void MirrorHandTransform(FTransform& ReturnTrans, FTransform& relTrans)
{
if (bOnlyFlipRotation)
{
ReturnTrans.SetTranslation(ReturnTrans.GetTranslation() - relTrans.GetTranslation());
ReturnTrans.Mirror(GetAsEAxis(MirrorAxis), GetCrossAxis());
ReturnTrans.SetTranslation(ReturnTrans.GetTranslation() + relTrans.GetTranslation());
}
else
{
ReturnTrans.Mirror(GetAsEAxis(MirrorAxis), GetCrossAxis());
}
}
inline TEnumAsByte<EAxis::Type> GetAsEAxis(TEnumAsByte<EVRAxis::Type> InAxis)
{
switch (InAxis)
{
case EVRAxis::X:
{
return EAxis::X;
}break;
case EVRAxis::Y:
{
return EAxis::Y;
}break;
case EVRAxis::Z:
{
return EAxis::Z;
}break;
}
return EAxis::X;
}
inline FVector GetMirrorVector()
{
switch (MirrorAxis)
{
case EVRAxis::Y:
{
return FVector::RightVector;
}break;
case EVRAxis::Z:
{
return FVector::UpVector;
}break;
case EVRAxis::X:
default:
{
return FVector::ForwardVector;
}break;
}
}
inline FVector GetFlipVector()
{
switch (FlipAxis)
{
case EVRAxis::Y:
{
return FVector::RightVector;
}break;
case EVRAxis::Z:
{
return FVector::UpVector;
}break;
case EVRAxis::X:
default:
{
return FVector::ForwardVector;
}break;
}
}
inline TEnumAsByte<EAxis::Type> GetCrossAxis()
{
// Checking against the sign now to avoid possible mobile precision issues
FVector SignVec = MirroredScale.GetSignVector();
if (SignVec.X < 0)
{
return EAxis::X;
}
else if (SignVec.Z < 0)
{
return EAxis::Z;
}
else if (SignVec.Y < 0)
{
return EAxis::Y;
}
return GetAsEAxis(FlipAxis);
/*if (FlipAxis == EVRAxis::Y)
{
return EAxis::Z;
}
else if (FlipAxis == EVRAxis::Z)
{
return EAxis::X;
}
else if (FlipAxis == EVRAxis::X)
{
return EAxis::Y;
}*/
//return EAxis::None;
}
// Returns the target relative transform of the hand to the gripped object
// If you want the transform mirrored you need to pass in which hand is requesting the information
// If UseParentScale is true then we will scale the value by the parent scale (generally only for when not using absolute hand scale)
// If UseMirrorScale is true then we will mirror the scale on the hand by the hand sockets mirror scale when appropriate (not for fully body!)
// if UseMirrorScale is false than the resulting transform will not have mirroring scale added so you may have to break the transform.
UFUNCTION(BlueprintCallable, Category = "Hand Socket Data")
FTransform GetMeshRelativeTransform(bool bIsRightHand, bool bUseParentScale = false, bool bUseMirrorScale = false);
// Returns the defined hand socket component (if it exists, you need to valid check the return!
// If it is a valid return you can then cast to your projects base socket class and handle whatever logic you want
UFUNCTION(BlueprintCallable, Category = "Hand Socket Data")
static UHandSocketComponent* GetHandSocketComponentFromObject(UObject* ObjectToCheck, FName SocketName);
virtual FTransform GetHandSocketTransform(UGripMotionControllerComponent* QueryController, bool bIgnoreOnlySnapMesh = false);
#if WITH_EDITOR
virtual void PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) override;
#endif
#if WITH_EDITORONLY_DATA
static void AddReferencedObjects(UObject* InThis, FReferenceCollector& Collector);
virtual void OnComponentDestroyed(bool bDestroyingHierarchy) override;
void PoseVisualizationToAnimation(bool bForceRefresh = false);
bool bTickedPose;
UPROPERTY()
bool bDecoupled;
#endif
virtual void Serialize(FArchive& Ar) override;
virtual void OnRegister() override;
virtual void PreReplication(IRepChangedPropertyTracker& ChangedPropertyTracker) override;
// ------------------------------------------------
// Gameplay tag interface
// ------------------------------------------------
/** Overridden to return requirements tags */
virtual void GetOwnedGameplayTags(FGameplayTagContainer& TagContainer) const override
{
TagContainer = GameplayTags;
}
/** Tags that are set on this object */
UPROPERTY(EditAnywhere, Replicated, BlueprintReadWrite, Category = "GameplayTags")
FGameplayTagContainer GameplayTags;
// End Gameplay Tag Interface
// Requires bReplicates to be true for the component
UPROPERTY(EditAnywhere, Replicated, BlueprintReadWrite, Category = "VRGripInterface|Replication")
bool bRepGameplayTags;
// Overrides the default of : true and allows for controlling it like in an actor, should be default of off normally with grippable components
UPROPERTY(EditAnywhere, Replicated, BlueprintReadWrite, Category = "VRGripInterface|Replication")
bool bReplicateMovement;
/** mesh component to indicate hand placement */
#if WITH_EDITORONLY_DATA
UPROPERTY()
TObjectPtr<UPoseableMeshComponent> HandVisualizerComponent;
UPROPERTY(EditAnywhere, BlueprintReadOnly, Transient, Category = "Hand Visualization")
TObjectPtr<USkeletalMesh> VisualizationMesh;
// If we should show the visualization mesh
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Hand Visualization")
bool bShowVisualizationMesh;
// Show the visualization mirrored
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Hand Visualization")
bool bMirrorVisualizationMesh;
// If we should show the grip range of this socket (shows text if always in range)
// If override distance is zero then it attempts to infer the value from the parent architecture
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Hand Visualization")
bool bShowRangeVisualization;
void PositionVisualizationMesh();
void HideVisualizationMesh();
#endif
#if WITH_EDITORONLY_DATA
// Material to apply to the hand
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Hand Visualization")
TObjectPtr<UMaterial> HandPreviewMaterial;
#endif
};
UCLASS(transient, Blueprintable, hideCategories = AnimInstance, BlueprintType)
class VREXPANSIONPLUGIN_API UHandSocketAnimInstance : public UAnimInstance
{
GENERATED_BODY()
public:
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, transient, Category = "Socket Data")
TObjectPtr<UHandSocketComponent> OwningSocket;
virtual void NativeInitializeAnimation() override
{
Super::NativeInitializeAnimation();
OwningSocket = Cast<UHandSocketComponent>(GetOwningComponent()->GetAttachParent());
}
};

View file

@ -0,0 +1,189 @@
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "Components/StaticMeshComponent.h"
#include "VRInteractibleFunctionLibrary.h"
#include "VRButtonComponent.generated.h"
/**
*
*/
// VR Button Types
UENUM(Blueprintable)
enum class EVRButtonType : uint8
{
Btn_Press,
Btn_Toggle_Return,
Btn_Toggle_Stay
};
// VR Button SyncOptions
UENUM(Blueprintable)
enum class EVRStateChangeAuthorityType : uint8
{
/* Button state can be changed on all connections */
CanChangeState_All,
/* Button state can be changed only on the server */
CanChangeState_Server,
/* Button state can be changed only on the owner of the interacting primitive */
CanChangeState_Owner
};
/** Delegate for notification when the button state changes. */
DECLARE_DYNAMIC_MULTICAST_DELEGATE_ThreeParams(FVRButtonStateChangedSignature, bool, ButtonState, AActor *, InteractingActor, UPrimitiveComponent *, InteractingComponent);
/** Delegate for notification when the begins a new interaction. */
DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FVRButtonStartedInteractionSignature, AActor *, InteractingActor, UPrimitiveComponent *, InteractingComponent);
UCLASS(Blueprintable, meta = (BlueprintSpawnableComponent), ClassGroup = (VRExpansionPlugin))
class VREXPANSIONPLUGIN_API UVRButtonComponent : public UStaticMeshComponent
{
GENERATED_BODY()
public:
UVRButtonComponent(const FObjectInitializer& ObjectInitializer);
~UVRButtonComponent();
UFUNCTION()
void OnOverlapBegin(UPrimitiveComponent* OverlappedComp, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult);
UFUNCTION()
void OnOverlapEnd(UPrimitiveComponent* OverlappedComp, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex);
virtual void TickComponent(float DeltaTime, enum ELevelTick TickType, FActorComponentTickFunction *ThisTickFunction) override;
virtual void BeginPlay() override;
UFUNCTION(BlueprintPure, Category = "VRButtonComponent")
bool IsButtonInUse();
// Should be called after the button is moved post begin play
UFUNCTION(BlueprintCallable, Category = "VRButtonComponent")
void ResetInitialButtonLocation();
// Sets the button state outside of interaction, bSnapIntoPosition is for Toggle_Stay mode, it will lerp into the new position if this is false
UFUNCTION(BlueprintCallable, Category = "VRButtonComponent")
void SetButtonState(bool bNewButtonState, bool bCallButtonChangedEvent = true, bool bSnapIntoPosition = false);
// Resets the button to its resting location (mostly for Toggle_Stay)
UFUNCTION(BlueprintCallable, Category = "VRButtonComponent")
void SetButtonToRestingPosition(bool bLerpToPosition = false);
// On the button state changing, keep in mind that InteractingActor can be invalid if manually setting the state
UPROPERTY(BlueprintAssignable, Category = "VRButtonComponent")
FVRButtonStateChangedSignature OnButtonStateChanged;
// On the button state changing, keep in mind that InteractingActor can be invalid if manually setting the state
UFUNCTION(BlueprintImplementableEvent, meta = (DisplayName = "Button State Changed"))
void ReceiveButtonStateChanged(bool bCurButtonState, AActor * LastInteractingActor, UPrimitiveComponent * InteractingComponent);
// On Button beginning interaction (may spam a bit depending on if overlap is jittering)
UPROPERTY(BlueprintAssignable, Category = "VRButtonComponent")
FVRButtonStartedInteractionSignature OnButtonBeginInteraction;
// On Button ending interaction (may spam a bit depending on if overlap is jittering)
UPROPERTY(BlueprintAssignable, Category = "VRButtonComponent")
FVRButtonStartedInteractionSignature OnButtonEndInteraction;
// On Button beginning interaction (may spam a bit depending on if overlap is jittering)
UFUNCTION(BlueprintImplementableEvent, meta = (DisplayName = "Button Started Interaction"))
void ReceiveButtonBeginInteraction(AActor * InteractingActor, UPrimitiveComponent * InteractingComponent);
// On Button ending interaction (may spam a bit depending on if overlap is jittering)
UFUNCTION(BlueprintImplementableEvent, meta = (DisplayName = "Button Ended Interaction"))
void ReceiveButtonEndInteraction(AActor * LastInteractingActor, UPrimitiveComponent * LastInteractingComponent);
// On the button state changing, keep in mind that InteractingActor can be invalid if manually setting the state
UPROPERTY(BlueprintReadOnly, Category = "VRButtonComponent")
TObjectPtr<UPrimitiveComponent> LocalInteractingComponent;
UPROPERTY(BlueprintReadOnly, Category = "VRButtonComponent")
TObjectPtr<AActor> LocalLastInteractingActor;
UPROPERTY(BlueprintReadOnly, Category = "VRButtonComponent")
TObjectPtr<UPrimitiveComponent> LocalLastInteractingComponent;
// Whether the button is enabled or not (can be interacted with)
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "VRButtonComponent")
bool bIsEnabled;
// Current state of the button, writable to set initial value
UPROPERTY(EditAnywhere,BlueprintReadWrite, Replicated, Category = "VRButtonComponent")
bool bButtonState;
// Who is allowed to change the button state
UPROPERTY(EditAnywhere, BlueprintReadWrite, Replicated, Category = "VRButtonComponent|Replication")
EVRStateChangeAuthorityType StateChangeAuthorityType;
// Speed that the button de-presses when no longer interacted with
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "VRButtonComponent")
float DepressSpeed;
// Distance that the button depresses
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "VRButtonComponent")
float DepressDistance;
// Type of button this is
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "VRButtonComponent")
EVRButtonType ButtonType;
// Negative on this axis is the depress direction
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "VRButtonComponent")
EVRInteractibleAxis ButtonAxis;
// Depth at which the button engages (switches)
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "VRButtonComponent")
float ButtonEngageDepth;
// Minimum time before the button can be switched again
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "VRButtonComponent")
float MinTimeBetweenEngaging;
// Skips filtering overlaps on the button and lets you manage it yourself, this is the alternative to overriding IsValidOverlap
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "VRButtonComponent")
bool bSkipOverlapFiltering;
UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "VRButtonComponent")
bool IsValidOverlap(UPrimitiveComponent * OverlapComponent);
// Sets the Last interacting actor variable
void SetLastInteractingActor();
virtual FVector GetTargetRelativeLocation();
// Overrides the default of : true and allows for controlling it like in an actor, should be default of off normally with grippable components
UPROPERTY(EditAnywhere, Replicated, BlueprintReadWrite, Category = "VRGripInterface|Replication")
bool bReplicateMovement;
virtual void PreReplication(IRepChangedPropertyTracker & ChangedPropertyTracker) override;
// Resetting the initial transform here so that it comes in prior to BeginPlay and save loading.
virtual void OnRegister() override;
// Now replicating this so that it works correctly over the network
UPROPERTY(BlueprintReadOnly, ReplicatedUsing = OnRep_InitialRelativeTransform, Category = "VRButtonComponent")
FTransform_NetQuantize InitialRelativeTransform;
UFUNCTION()
virtual void OnRep_InitialRelativeTransform()
{
SetButtonToRestingPosition();
}
protected:
// Control variables
FVector InitialLocation;
bool bToggledThisTouch;
FVector InitialComponentLoc;
float LastToggleTime;
float GetAxisValue(FVector CheckLocation);
FVector SetAxisValue(float SetValue);
};

View file

@ -0,0 +1,307 @@
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "GameplayTagContainer.h"
#include "GameplayTagAssetInterface.h"
#include "VRGripInterface.h"
#include "Components/StaticMeshComponent.h"
#include "Interactibles/VRInteractibleFunctionLibrary.h"
#include "VRDialComponent.generated.h"
class UGripMotionControllerComponent;
/** Delegate for notification when the lever state changes. */
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FVRDialStateChangedSignature, float, DialMilestoneAngle);
DECLARE_DYNAMIC_MULTICAST_DELEGATE(FVRDialFinishedLerpingSignature);
UCLASS(Blueprintable, meta = (BlueprintSpawnableComponent), ClassGroup = (VRExpansionPlugin))
class VREXPANSIONPLUGIN_API UVRDialComponent : public UStaticMeshComponent, public IVRGripInterface, public IGameplayTagAssetInterface
{
GENERATED_BODY()
public:
UVRDialComponent(const FObjectInitializer& ObjectInitializer);
~UVRDialComponent();
// Call to use an object
UPROPERTY(BlueprintAssignable, Category = "VRDialComponent")
FVRDialStateChangedSignature OnDialHitSnapAngle;
UFUNCTION(BlueprintImplementableEvent, meta = (DisplayName = "Dial Hit Snap Angle"))
void ReceiveDialHitSnapAngle(float DialMilestoneAngle);
// If true the dial will lerp back to zero on release
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "VRDialComponent|Lerping")
bool bLerpBackOnRelease;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "VRDialComponent|Lerping")
bool bSendDialEventsDuringLerp;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "VRDialComponent|Lerping")
float DialReturnSpeed;
UPROPERTY(BlueprintReadOnly, Category = "VRDialComponent|Lerping")
bool bIsLerping;
UPROPERTY(BlueprintAssignable, Category = "VRDialComponent|Lerping")
FVRDialFinishedLerpingSignature OnDialFinishedLerping;
UFUNCTION(BlueprintImplementableEvent, meta = (DisplayName = "Dial Finished Lerping"))
void ReceiveDialFinishedLerping();
UPROPERTY(BlueprintReadOnly, Category = "VRDialComponent")
float CurrentDialAngle;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "VRDialComponent")//, meta = (ClampMin = "0.0", ClampMax = "360.0", UIMin = "0.0", UIMax = "360.0"))
float ClockwiseMaximumDialAngle;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "VRDialComponent")//, meta = (ClampMin = "0.0", ClampMax = "360.0", UIMin = "0.0", UIMax = "360.0"))
float CClockwiseMaximumDialAngle;
// If true then the dial can "roll over" past 360/0 degrees in a direction
// Allowing unlimited dial angle values
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "VRDialComponent")
bool bUseRollover;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "VRDialComponent")
bool bDialUsesAngleSnap;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "VRDialComponent")
bool bDialUseSnapAngleList;
// Optional list of snap angles for the dial
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "VRDialComponent", meta = (editcondition = "bDialUseSnapAngleList"))
TArray<float> DialSnapAngleList;
// Angle that the dial snaps to on release and when within the threshold distance
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "VRDialComponent", meta = (editcondition = "!bDialUseSnapAngleList", ClampMin = "0.0", ClampMax = "180.0", UIMin = "0.0", UIMax = "180.0"))
float SnapAngleIncrement;
// Threshold distance that when within the dial will stay snapped to its closest snap increment
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "VRDialComponent", meta = (editcondition = "!bDialUseSnapAngleList", ClampMin = "0.0", ClampMax = "180.0", UIMin = "0.0", UIMax = "180.0"))
float SnapAngleThreshold;
// Scales rotational input to speed up or slow down the rotation
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "VRDialComponent", meta = (ClampMin = "0.0", ClampMax = "180.0", UIMin = "0.0", UIMax = "180.0"))
float RotationScaler;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "VRDialComponent")
EVRInteractibleAxis DialRotationAxis;
// If true then the dial will directly sample the hands rotation instead of using its movement around it.
// This is good for roll specific dials but is fairly bad elsewhere.
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "VRDialComponent")
bool bDialUseDirectHandRotation;
float LastGripRot;
float InitialGripRot;
float InitialRotBackEnd;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "VRDialComponent", meta = (editcondition = "bDialUseDirectHandRotation"))
EVRInteractibleAxis InteractorRotationAxis;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "GripSettings")
float PrimarySlotRange;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "GripSettings")
float SecondarySlotRange;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "GripSettings")
int GripPriority;
// Sets the grip priority
UFUNCTION(BlueprintCallable, Category = "GripSettings")
void SetGripPriority(int NewGripPriority);
// Resetting the initial transform here so that it comes in prior to BeginPlay and save loading.
virtual void OnRegister() override;
// Now replicating this so that it works correctly over the network
UPROPERTY(BlueprintReadOnly, ReplicatedUsing = OnRep_InitialRelativeTransform, Category = "VRDialComponent")
FTransform_NetQuantize InitialRelativeTransform;
UFUNCTION()
virtual void OnRep_InitialRelativeTransform()
{
CalculateDialProgress();
}
void CalculateDialProgress();
//FTransform InitialRelativeTransform;
FVector InitialInteractorLocation;
FVector InitialDropLocation;
float CurRotBackEnd;
FRotator LastRotation;
float LastSnapAngle;
// Should be called after the dial is moved post begin play
UFUNCTION(BlueprintCallable, Category = "VRDialComponent")
void ResetInitialDialLocation();
// Can be called to recalculate the dial angle after you move it if you want different values
UFUNCTION(BlueprintCallable, Category = "VRDialComponent")
void AddDialAngle(float DialAngleDelta, bool bCallEvents = false, bool bSkipSettingRot = false);
// Directly sets the dial angle, still obeys maximum limits and snapping though
UFUNCTION(BlueprintCallable, Category = "VRDialComponent")
void SetDialAngle(float DialAngle, bool bCallEvents = false);
// ------------------------------------------------
// Gameplay tag interface
// ------------------------------------------------
/** Overridden to return requirements tags */
virtual void GetOwnedGameplayTags(FGameplayTagContainer& TagContainer) const override
{
TagContainer = GameplayTags;
}
/** Tags that are set on this object */
UPROPERTY(EditAnywhere, Replicated, BlueprintReadWrite, Category = "GameplayTags")
FGameplayTagContainer GameplayTags;
// End Gameplay Tag Interface
virtual void PreReplication(IRepChangedPropertyTracker & ChangedPropertyTracker) override;
// Requires bReplicates to be true for the component
UPROPERTY(EditAnywhere, Replicated, BlueprintReadWrite, Category = "VRGripInterface")
bool bRepGameplayTags;
// Overrides the default of : true and allows for controlling it like in an actor, should be default of off normally with grippable components
UPROPERTY(EditAnywhere, Replicated, BlueprintReadWrite, Category = "VRGripInterface|Replication")
bool bReplicateMovement;
virtual void TickComponent(float DeltaTime, enum ELevelTick TickType, FActorComponentTickFunction *ThisTickFunction) override;
virtual void BeginPlay() override;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "VRGripInterface")
EGripMovementReplicationSettings MovementReplicationSetting;
UPROPERTY(BlueprintReadOnly, Category = "VRGripInterface", meta = (ScriptName = "IsCurrentlyHeld"))
bool bIsHeld; // Set on grip notify, not net serializing
UPROPERTY(BlueprintReadOnly, Category = "VRGripInterface")
FBPGripPair HoldingGrip; // Set on grip notify, not net serializing
bool bOriginalReplicatesMovement;
// Called when a object is gripped
UPROPERTY(BlueprintAssignable, Category = "Grip Events")
FVROnGripSignature OnGripped;
// Called when a object is dropped
UPROPERTY(BlueprintAssignable, Category = "Grip Events")
FVROnDropSignature OnDropped;
// Distance before the object will break out of the hand, 0.0f == never will
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "VRGripInterface")
float BreakDistance;
// Should we deny gripping on this object
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "VRGripInterface", meta = (ScriptName = "IsDenyGripping"))
bool bDenyGripping;
// Grip interface setup
// Set up as deny instead of allow so that default allows for gripping
// The GripInitiator is not guaranteed to be valid, check it for validity
bool DenyGripping_Implementation(UGripMotionControllerComponent * GripInitiator = nullptr) override;
// How an interfaced object behaves when teleporting
EGripInterfaceTeleportBehavior TeleportBehavior_Implementation() override;
// Should this object simulate on drop
bool SimulateOnDrop_Implementation() override;
// Grip type to use
EGripCollisionType GetPrimaryGripType_Implementation(bool bIsSlot) override;
// Secondary grip type
ESecondaryGripType SecondaryGripType_Implementation() override;
// Define which movement repliation setting to use
EGripMovementReplicationSettings GripMovementReplicationType_Implementation() override;
// Define the late update setting
EGripLateUpdateSettings GripLateUpdateSetting_Implementation() override;
// What grip stiffness and damping to use if using a physics constraint
void GetGripStiffnessAndDamping_Implementation(float &GripStiffnessOut, float &GripDampingOut) override;
// Get the advanced physics settings for this grip
FBPAdvGripSettings AdvancedGripSettings_Implementation() override;
// What distance to break a grip at (only relevent with physics enabled grips
float GripBreakDistance_Implementation() override;
// Get grip slot in range
void ClosestGripSlotInRange_Implementation(FVector WorldLocation, bool bSecondarySlot, bool & bHadSlotInRange, FTransform & SlotWorldTransform, FName & SlotName, UGripMotionControllerComponent * CallingController = nullptr, FName OverridePrefix = NAME_None) override;
// Check if an object allows multiple grips at one time
bool AllowsMultipleGrips_Implementation() override;
// Returns if the object is held and if so, which controllers are holding it
void IsHeld_Implementation(TArray<FBPGripPair>& CurHoldingControllers, bool & bCurIsHeld) override;
// Interface function used to throw the delegates that is invisible to blueprints so that it can't be overridden
virtual void Native_NotifyThrowGripDelegates(UGripMotionControllerComponent* Controller, bool bGripped, const FBPActorGripInformation& GripInformation, bool bWasSocketed = false) override;
// Sets is held, used by the plugin
void SetHeld_Implementation(UGripMotionControllerComponent * NewHoldingController, uint8 GripID, bool bNewIsHeld) override;
// Returns if the object wants to be socketed
bool RequestsSocketing_Implementation(USceneComponent *& ParentToSocketTo, FName & OptionalSocketName, FTransform_NetQuantize & RelativeTransform) override;
// Get grip scripts
bool GetGripScripts_Implementation(TArray<UVRGripScriptBase*> & ArrayReference) override;
// Events //
// Event triggered each tick on the interfaced object when gripped, can be used for custom movement or grip based logic
void TickGrip_Implementation(UGripMotionControllerComponent * GrippingController, const FBPActorGripInformation & GripInformation, float DeltaTime) override;
// Event triggered on the interfaced object when gripped
void OnGrip_Implementation(UGripMotionControllerComponent * GrippingController, const FBPActorGripInformation & GripInformation) override;
// Event triggered on the interfaced object when grip is released
void OnGripRelease_Implementation(UGripMotionControllerComponent * ReleasingController, const FBPActorGripInformation & GripInformation, bool bWasSocketed = false) override;
// Event triggered on the interfaced object when child component is gripped
void OnChildGrip_Implementation(UGripMotionControllerComponent * GrippingController, const FBPActorGripInformation & GripInformation) override;
// Event triggered on the interfaced object when child component is released
void OnChildGripRelease_Implementation(UGripMotionControllerComponent * ReleasingController, const FBPActorGripInformation & GripInformation, bool bWasSocketed = false) override;
// Event triggered on the interfaced object when secondary gripped
void OnSecondaryGrip_Implementation(UGripMotionControllerComponent * GripOwningController, USceneComponent * SecondaryGripComponent, const FBPActorGripInformation & GripInformation) override;
// Event triggered on the interfaced object when secondary grip is released
void OnSecondaryGripRelease_Implementation(UGripMotionControllerComponent * GripOwningController, USceneComponent * ReleasingSecondaryGripComponent, const FBPActorGripInformation & GripInformation) override;
// Interaction Functions
// Call to use an object
void OnUsed_Implementation() override;
// Call to stop using an object
void OnEndUsed_Implementation() override;
// Call to use an object
void OnSecondaryUsed_Implementation() override;
// Call to stop using an object
void OnEndSecondaryUsed_Implementation() override;
// Call to send an action event to the object
void OnInput_Implementation(FKey Key, EInputEvent KeyEvent) override;
protected:
};

View file

@ -0,0 +1,303 @@
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "VRBPDatatypes.h"
//#include "UObject/ObjectMacros.h"
//#include "Engine/EngineTypes.h"
//#include "UObject/ScriptInterface.h"
#include "Kismet/BlueprintFunctionLibrary.h"
#include "Components/SceneComponent.h"
#include "GameplayTagContainer.h"
#include "VRInteractibleFunctionLibrary.generated.h"
//General Advanced Sessions Log
DECLARE_LOG_CATEGORY_EXTERN(VRInteractibleFunctionLibraryLog, Log, All);
// Declares our interactible axis's
UENUM(Blueprintable)
enum class EVRInteractibleAxis : uint8
{
Axis_X,
Axis_Y,
Axis_Z
};
// A data structure to hold important interactible data
// Should be init'd in Beginplay with BeginPlayInit as well as OnGrip with OnGripInit.
// Works in "static space", it records the original relative transform of the interactible on begin play
// so that calculations on the actual component can be done based off of it.
USTRUCT(BlueprintType, Category = "VRExpansionLibrary")
struct VREXPANSIONPLUGIN_API FBPVRInteractibleBaseData
{
GENERATED_BODY()
public:
// Our initial relative transform to our parent "static space" - Set in BeginPlayInit
UPROPERTY(BlueprintReadOnly, VisibleAnywhere, Category = "InteractibleData")
FTransform InitialRelativeTransform;
// Initial location in "static space" of the interactor on grip - Set in OnGripInit
UPROPERTY(BlueprintReadOnly, VisibleAnywhere, Category = "InteractibleData")
FVector InitialInteractorLocation;
// Initial location of the interactible in the "static space" - Set in OnGripInit
UPROPERTY(BlueprintReadOnly, VisibleAnywhere, Category = "InteractibleData")
FVector InitialGripLoc;
// Initial location on the interactible of the grip, used for drop calculations - Set in OnGripInit
UPROPERTY(BlueprintReadOnly, VisibleAnywhere, Category = "InteractibleData")
FVector InitialDropLocation;
// The initial transform in relative space of the grip to us - Set in OnGripInit
UPROPERTY(BlueprintReadOnly, VisibleAnywhere, Category = "InteractibleData")
FTransform ReversedRelativeTransform;
FBPVRInteractibleBaseData()
{
InitialInteractorLocation = FVector::ZeroVector;
InitialGripLoc = FVector::ZeroVector;
InitialDropLocation = FVector::ZeroVector;
}
};
UCLASS()
class VREXPANSIONPLUGIN_API UVRInteractibleFunctionLibrary : public UBlueprintFunctionLibrary
{
GENERATED_BODY()
public:
static float GetAtan2Angle(EVRInteractibleAxis AxisToCalc, FVector CurInteractorLocation, float OptionalInitialRotation = 0.0f)
{
switch (AxisToCalc)
{
case EVRInteractibleAxis::Axis_X:
{
return FMath::RadiansToDegrees(FMath::Atan2(CurInteractorLocation.Y, CurInteractorLocation.Z)) - OptionalInitialRotation;
}break;
case EVRInteractibleAxis::Axis_Y:
{
return FMath::RadiansToDegrees(FMath::Atan2(CurInteractorLocation.Z, CurInteractorLocation.X)) - OptionalInitialRotation;
}break;
case EVRInteractibleAxis::Axis_Z:
{
return FMath::RadiansToDegrees(FMath::Atan2(CurInteractorLocation.Y, CurInteractorLocation.X)) - OptionalInitialRotation;
}break;
default:
{}break;
}
return 0.0f;
}
static float GetDeltaAngleFromTransforms(EVRInteractibleAxis RotAxis, FTransform & InitialRelativeTransform, FTransform &CurrentRelativeTransform)
{
return GetDeltaAngle(RotAxis, (CurrentRelativeTransform.GetRelativeTransform(InitialRelativeTransform).GetRotation()).GetNormalized());
}
static float GetDeltaAngle(EVRInteractibleAxis RotAxis, FQuat DeltaQuat)
{
FVector Axis;
float Angle;
DeltaQuat.ToAxisAndAngle(Axis, Angle);
if (RotAxis == EVRInteractibleAxis::Axis_Z)
return FRotator::NormalizeAxis(FMath::RadiansToDegrees(Angle)) * (FMath::Sign(GetAxisValue(RotAxis, Axis)));
else
return FRotator::NormalizeAxis(FMath::RadiansToDegrees(Angle)) * (-FMath::Sign(GetAxisValue(RotAxis, Axis)));
}
static float GetAxisValue(EVRInteractibleAxis RotAxis, FRotator CheckRotation)
{
switch (RotAxis)
{
case EVRInteractibleAxis::Axis_X:
return CheckRotation.Roll; break;
case EVRInteractibleAxis::Axis_Y:
return CheckRotation.Pitch; break;
case EVRInteractibleAxis::Axis_Z:
return CheckRotation.Yaw; break;
default:return 0.0f; break;
}
}
static float GetAxisValue(EVRInteractibleAxis RotAxis, FVector CheckAxis)
{
switch (RotAxis)
{
case EVRInteractibleAxis::Axis_X:
return CheckAxis.X; break;
case EVRInteractibleAxis::Axis_Y:
return CheckAxis.Y; break;
case EVRInteractibleAxis::Axis_Z:
return CheckAxis.Z; break;
default:return 0.0f; break;
}
}
static FVector SetAxisValueVec(EVRInteractibleAxis RotAxis, float SetValue)
{
FVector vec = FVector::ZeroVector;
switch (RotAxis)
{
case EVRInteractibleAxis::Axis_X:
vec.X = SetValue; break;
case EVRInteractibleAxis::Axis_Y:
vec.Y = SetValue; break;
case EVRInteractibleAxis::Axis_Z:
vec.Z = SetValue; break;
default:break;
}
return vec;
}
static FRotator SetAxisValueRot(EVRInteractibleAxis RotAxis, float SetValue)
{
FRotator vec = FRotator::ZeroRotator;
switch (RotAxis)
{
case EVRInteractibleAxis::Axis_X:
vec.Roll = SetValue; break;
case EVRInteractibleAxis::Axis_Y:
vec.Pitch = SetValue; break;
case EVRInteractibleAxis::Axis_Z:
vec.Yaw = SetValue; break;
default:break;
}
return vec;
}
static FRotator SetAxisValueRot(EVRInteractibleAxis RotAxis, float SetValue, FRotator Var)
{
FRotator vec = Var;
switch (RotAxis)
{
case EVRInteractibleAxis::Axis_X:
vec.Roll = SetValue; break;
case EVRInteractibleAxis::Axis_Y:
vec.Pitch = SetValue; break;
case EVRInteractibleAxis::Axis_Z:
vec.Yaw = SetValue; break;
default:break;
}
return vec;
}
// Get current parent transform
UFUNCTION(BlueprintPure, Category = "VRInteractibleFunctions", meta = (bIgnoreSelf = "true"))
static FTransform Interactible_GetCurrentParentTransform(USceneComponent * SceneComponentToCheck)
{
if (SceneComponentToCheck)
{
if (USceneComponent * AttachParent = SceneComponentToCheck->GetAttachParent())
{
return AttachParent->GetComponentTransform();
}
}
return FTransform::Identity;
}
// Get current relative transform (original transform we were at on grip for the current parent transform)
UFUNCTION(BlueprintPure, Category = "VRInteractibleFunctions", meta = (bIgnoreSelf = "true"))
static FTransform Interactible_GetCurrentRelativeTransform(USceneComponent * SceneComponentToCheck, UPARAM(ref)FBPVRInteractibleBaseData & BaseData)
{
FTransform ParentTransform = FTransform::Identity;
if (SceneComponentToCheck)
{
if (USceneComponent * AttachParent = SceneComponentToCheck->GetAttachParent())
{
// during grip there is no parent so we do this, might as well do it anyway for lerping as well
ParentTransform = AttachParent->GetComponentTransform();
}
}
return BaseData.InitialRelativeTransform * ParentTransform;
}
// Inits the initial relative transform of an interactible on begin play
UFUNCTION(BlueprintCallable, Category = "VRInteractibleFunctions", meta = (bIgnoreSelf = "true"))
static void Interactible_BeginPlayInit(USceneComponent * InteractibleComp, UPARAM(ref) FBPVRInteractibleBaseData & BaseDataToInit)
{
if (!InteractibleComp)
return;
BaseDataToInit.InitialRelativeTransform = InteractibleComp->GetRelativeTransform();
}
// Inits the calculated values of a VR Interactible Base Data Structure on a grip event
UFUNCTION(BlueprintCallable, Category = "VRInteractibleFunctions", meta = (bIgnoreSelf = "true"))
static void Interactible_OnGripInit(USceneComponent * InteractibleComp, UPARAM(ref) FBPActorGripInformation& GripInformation, UPARAM(ref) FBPVRInteractibleBaseData & BaseDataToInit)
{
if (!InteractibleComp)
return;
BaseDataToInit.ReversedRelativeTransform = FTransform(GripInformation.RelativeTransform.ToInverseMatrixWithScale());
BaseDataToInit.InitialDropLocation = BaseDataToInit.ReversedRelativeTransform.GetTranslation(); // Technically a duplicate, but will be more clear
FTransform RelativeToGripTransform = BaseDataToInit.ReversedRelativeTransform * InteractibleComp->GetComponentTransform();
FTransform CurrentRelativeTransform = BaseDataToInit.InitialRelativeTransform * UVRInteractibleFunctionLibrary::Interactible_GetCurrentParentTransform(InteractibleComp);
BaseDataToInit.InitialInteractorLocation = CurrentRelativeTransform.InverseTransformPosition(RelativeToGripTransform.GetTranslation());
BaseDataToInit.InitialGripLoc = BaseDataToInit.InitialRelativeTransform.InverseTransformPosition(InteractibleComp->GetRelativeLocation());
}
// Returns (in degrees) the angle around the axis of a location
// Expects the CurInteractorLocation to be in relative space already
UFUNCTION(BlueprintPure, Category = "VRInteractibleFunctions", meta = (bIgnoreSelf = "true"))
static float Interactible_GetAngleAroundAxis(EVRInteractibleAxis AxisToCalc, FVector CurInteractorLocation)
{
float ReturnAxis = 0.0f;
switch (AxisToCalc)
{
case EVRInteractibleAxis::Axis_X:
{
ReturnAxis = FMath::RadiansToDegrees(FMath::Atan2(CurInteractorLocation.Y, CurInteractorLocation.Z));
}break;
case EVRInteractibleAxis::Axis_Y:
{
ReturnAxis = FMath::RadiansToDegrees(FMath::Atan2(CurInteractorLocation.Z, CurInteractorLocation.X));
}break;
case EVRInteractibleAxis::Axis_Z:
{
ReturnAxis = FMath::RadiansToDegrees(FMath::Atan2(CurInteractorLocation.Y, CurInteractorLocation.X));
}break;
default:
{}break;
}
return ReturnAxis;
}
// Returns (in degrees) the delta rotation from the initial angle at grip to the current interactor angle around the axis
// Expects CurInteractorLocation to be in relative space already
// You can add this to an initial rotation and clamp the result to rotate over time based on hand position
UFUNCTION(BlueprintPure, Category = "VRInteractibleFunctions", meta = (bIgnoreSelf = "true"))
static float Interactible_GetAngleAroundAxisDelta(EVRInteractibleAxis AxisToCalc, FVector CurInteractorLocation, float InitialAngle)
{
return FRotator::NormalizeAxis(Interactible_GetAngleAroundAxis(AxisToCalc, CurInteractorLocation) - InitialAngle);
}
// Returns a value that is snapped to the given settings, taking into account the threshold and increment
UFUNCTION(BlueprintPure, Category = "VRInteractibleFunctions", meta = (bIgnoreSelf = "true"))
static float Interactible_GetThresholdSnappedValue(float ValueToSnap, float SnapIncrement, float SnapThreshold)
{
if (SnapIncrement > 0.f && FMath::Fmod(ValueToSnap, SnapIncrement) <= FMath::Min(SnapIncrement, SnapThreshold))
{
return FMath::GridSnap(ValueToSnap, SnapIncrement);
}
return ValueToSnap;
}
};

View file

@ -0,0 +1,409 @@
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "GameplayTagContainer.h"
#include "GameplayTagAssetInterface.h"
#include "VRGripInterface.h"
#include "Interactibles/VRInteractibleFunctionLibrary.h"
#include "Components/StaticMeshComponent.h"
#include "VRLeverComponent.generated.h"
class UGripMotionControllerComponent;
UENUM(Blueprintable)
enum class EVRInteractibleLeverAxis : uint8
{
/* Rotates only towards the X Axis */
Axis_X,
/* Rotates only towards the Y Axis */
Axis_Y,
/* Rotates only towards the Z Axis */
Axis_Z,
/* Rotates freely on the XY Axis' */
Axis_XY,
/* Acts like a flight stick, with AllCurrentLeverAngles being the positive / negative of the current full angle (yaw based on initial grip delta) */
FlightStick_XY,
};
UENUM(Blueprintable)
enum class EVRInteractibleLeverEventType : uint8
{
LeverPositive,
LeverNegative
};
UENUM(Blueprintable)
enum class EVRInteractibleLeverReturnType : uint8
{
/** Stays in place on drop */
Stay,
/** Returns to zero on drop (lerps) */
ReturnToZero,
/** Lerps to closest max (only works with X/Y/Z axis levers) */
LerpToMax,
/** Lerps to closest max if over the toggle threshold (only works with X/Y/Z axis levers) */
LerpToMaxIfOverThreshold,
/** Retains momentum on release (only works with X/Y/Z axis levers) */
RetainMomentum
};
/** Delegate for notification when the lever state changes. */
DECLARE_DYNAMIC_MULTICAST_DELEGATE_FourParams(FVRLeverStateChangedSignature, bool, LeverStatus, EVRInteractibleLeverEventType, LeverStatusType, float, LeverAngleAtTime, float, FullLeverAngleAtTime);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FVRLeverFinishedLerpingSignature, float, FinalAngle);
/**
* A Lever component, can act like a lever, door, wheel, joystick.
* If set to replicates it will replicate its values to the clients.
*/
UCLASS(Blueprintable, meta = (BlueprintSpawnableComponent), ClassGroup = (VRExpansionPlugin))
class VREXPANSIONPLUGIN_API UVRLeverComponent : public UStaticMeshComponent, public IVRGripInterface, public IGameplayTagAssetInterface
{
GENERATED_BODY()
public:
UVRLeverComponent(const FObjectInitializer& ObjectInitializer);
~UVRLeverComponent();
// Call to use an object
UPROPERTY(BlueprintAssignable, Category = "VRLeverComponent")
FVRLeverStateChangedSignature OnLeverStateChanged;
UFUNCTION(BlueprintImplementableEvent, meta = (DisplayName = "Lever State Changed"))
void ReceiveLeverStateChanged(bool LeverStatus, EVRInteractibleLeverEventType LeverStatusType, float LeverAngleAtTime, float FullLeverAngleAttime);
UPROPERTY(BlueprintAssignable, Category = "VRLeverComponent")
FVRLeverFinishedLerpingSignature OnLeverFinishedLerping;
UFUNCTION(BlueprintImplementableEvent, meta = (DisplayName = "Lever Finished Lerping"))
void ReceiveLeverFinishedLerping(float LeverFinalAngle);
// Primary axis angle only
UPROPERTY(BlueprintReadOnly, Category = "VRLeverComponent")
float CurrentLeverAngle;
// Writes out all current angles to this rotator, useful mostly for XY and Flight stick modes
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "VRLeverComponent")
FRotator AllCurrentLeverAngles;
// Bearing Direction, for X/Y is their signed direction, for XY mode it is an actual 2D directional vector
UPROPERTY(BlueprintReadOnly, Category = "VRLeverComponent")
FVector CurrentLeverForwardVector;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "VRLeverComponent")
bool bUngripAtTargetRotation;
// Rotation axis to use, XY is combined X and Y, only LerpToZero and PositiveLimits work with this mode
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "VRLeverComponent")
EVRInteractibleLeverAxis LeverRotationAxis;
// The percentage of the angle at witch the lever will toggle
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "VRLeverComponent", meta = (ClampMin = "0.01", ClampMax = "1.0", UIMin = "0.01", UIMax = "1.0"))
float LeverTogglePercentage;
// The max angle of the lever in the positive direction
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "VRLeverComponent", meta = (ClampMin = "0.0", ClampMax = "179.9", UIMin = "0.0", UIMax = "180.0"))
float LeverLimitPositive;
// The max angle of the lever in the negative direction
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "VRLeverComponent", meta = (ClampMin = "0.0", ClampMax = "179.9", UIMin = "0.0", UIMax = "180.0"))
float LeverLimitNegative;
// The max angle of the flightsticks yaw in either direction off of 0
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "VRLeverComponent|Flight Stick Settings", meta = (ClampMin = "0.0", ClampMax = "179.9", UIMin = "0.0", UIMax = "180.0"))
float FlightStickYawLimit;
// If true then this lever is locked in place until unlocked again
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "VRLeverComponent")
bool bIsLocked;
// If true then this lever will auto drop even when locked
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "VRLeverComponent")
bool bAutoDropWhenLocked;
// Sets if the lever is locked or not
UFUNCTION(BlueprintCallable, Category = "GripSettings")
void SetIsLocked(bool bNewLockedState);
// Checks and applies auto drop if we need too
bool CheckAutoDrop(UGripMotionControllerComponent* GrippingController, const FBPActorGripInformation& GripInformation);
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "VRLeverComponent")
EVRInteractibleLeverReturnType LeverReturnTypeWhenReleased;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "VRLeverComponent")
bool bSendLeverEventsDuringLerp;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "VRLeverComponent")
float LeverReturnSpeed;
// If true then we will blend the values of the XY axis' by the AngleThreshold, lowering Pitch/Yaw influence based on how far from leaning into the axis that the lever is
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "VRLeverComponent|Flight Stick Settings")
bool bBlendAxisValuesByAngleThreshold;
// The angle threshold to blend around, default of 90.0 blends 0.0 to 1.0 smoothly across entire sweep
// A value of 45 would blend it to 0 halfway rotated to the other axis, while 180 would still leave half the influence when fully rotated out of facing
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "VRLeverComponent|Flight Stick Settings", meta = (ClampMin = "1.0", ClampMax = "360.0", UIMin = "1.0", UIMax = "360.0"))
float AngleThreshold;
// Number of frames to average momentum across for the release momentum (avoids quick waggles)
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "VRLeverComponent|Momentum Settings", meta = (ClampMin = "0", ClampMax = "12", UIMin = "0", UIMax = "12"))
int FramesToAverage;
// Units in degrees per second to slow a momentum lerp down
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "VRLeverComponent|Momentum Settings", meta = (ClampMin = "0.0", ClampMax = "180", UIMin = "0.0", UIMax = "180.0"))
float LeverMomentumFriction;
// % of elasticity on reaching the end 0 - 1.0
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "VRLeverComponent|Momentum Settings", meta = (ClampMin = "0.0", ClampMax = "1.0", UIMin = "0.0", UIMax = "1.0"))
float LeverRestitution;
// Maximum momentum of the lever in degrees per second
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "VRLeverComponent|Momentum Settings", meta = (ClampMin = "0.0", UIMin = "0.0"))
float MaxLeverMomentum;
UPROPERTY(BlueprintReadOnly, Category = "VRLeverComponent")
bool bIsLerping;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "GripSettings")
float PrimarySlotRange;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "GripSettings")
float SecondarySlotRange;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "GripSettings")
int GripPriority;
// Sets the grip priority
UFUNCTION(BlueprintCallable, Category = "GripSettings")
void SetGripPriority(int NewGripPriority);
// Full precision current angle
float FullCurrentAngle;
float LastDeltaAngle;
// Resetting the initial transform here so that it comes in prior to BeginPlay and save loading.
virtual void OnRegister() override;
// Now replicating this so that it works correctly over the network
UPROPERTY(BlueprintReadOnly, ReplicatedUsing = OnRep_InitialRelativeTransform, Category = "VRLeverComponent")
FTransform_NetQuantize InitialRelativeTransform;
UFUNCTION()
virtual void OnRep_InitialRelativeTransform()
{
ReCalculateCurrentAngle();
}
// Flight stick variables
FTransform InteractorOffsetTransform;
FVector InitialInteractorLocation;
FVector InitialInteractorDropLocation;
float InitialGripRot;
float RotAtGrab;
FQuat qRotAtGrab;
bool bLeverState;
bool bIsInFirstTick;
// For momentum retention
float MomentumAtDrop;
float LastLeverAngle;
float CalcAngle(EVRInteractibleLeverAxis AxisToCalc, FVector CurInteractorLocation, bool bSkipLimits = false);
void LerpAxis(float CurrentAngle, float DeltaTime);
void CalculateCurrentAngle(FTransform & CurrentTransform);
// ------------------------------------------------
// Gameplay tag interface
// ------------------------------------------------
/** Overridden to return requirements tags */
virtual void GetOwnedGameplayTags(FGameplayTagContainer& TagContainer) const override
{
TagContainer = GameplayTags;
}
/** Tags that are set on this object */
UPROPERTY(EditAnywhere, Replicated, BlueprintReadWrite, Category = "GameplayTags")
FGameplayTagContainer GameplayTags;
// End Gameplay Tag Interface
virtual void PreReplication(IRepChangedPropertyTracker & ChangedPropertyTracker) override;
// Requires bReplicates to be true for the component
UPROPERTY(EditAnywhere, Replicated, BlueprintReadWrite, Category = "VRGripInterface")
bool bRepGameplayTags;
// Overrides the default of : true and allows for controlling it like in an actor, should be default of off normally with grippable components
UPROPERTY(EditAnywhere, Replicated, BlueprintReadWrite, Category = "VRGripInterface|Replication")
bool bReplicateMovement;
virtual void TickComponent(float DeltaTime, enum ELevelTick TickType, FActorComponentTickFunction *ThisTickFunction) override;
virtual void BeginPlay() override;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "VRGripInterface")
EGripMovementReplicationSettings MovementReplicationSetting;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "VRGripInterface")
float Stiffness;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "VRGripInterface")
float Damping;
// Distance before the object will break out of the hand, 0.0f == never will
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "VRGripInterface")
float BreakDistance;
// Should we deny gripping on this object
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "VRGripInterface", meta = (ScriptName = "IsDenyGripping"))
bool bDenyGripping;
UPROPERTY(BlueprintReadOnly, Category = "VRGripInterface", meta = (ScriptName = "IsCurrentlyHeld"))
bool bIsHeld; // Set on grip notify, not net serializing
UPROPERTY(BlueprintReadOnly, Category = "VRGripInterface")
FBPGripPair HoldingGrip; // Set on grip notify, not net serializing
bool bOriginalReplicatesMovement;
TWeakObjectPtr<USceneComponent> ParentComponent;
// Should be called after the lever is moved post begin play
UFUNCTION(BlueprintCallable, Category = "VRLeverComponent")
void ResetInitialLeverLocation(bool bAllowThrowingEvents = true);
/**
* Sets the angle of the lever forcefully
* @param NewAngle The new angle to use, for single axis levers the sign of the angle will be used
* @param DualAxisForwardVector Only used with dual axis levers, you need to define the forward axis for the angle to apply too
*/
UFUNCTION(BlueprintCallable, Category = "VRLeverComponent")
void SetLeverAngle(float NewAngle, FVector DualAxisForwardVector, bool bAllowThrowingEvents = true);
// ReCalculates the current angle, sets it on the back end, and returns it
// If allow throwing events is true then it will trigger the callbacks for state changes as well
UFUNCTION(BlueprintCallable, Category = "VRLeverComponent")
float ReCalculateCurrentAngle(bool bAllowThrowingEvents = true);
void ProccessCurrentState(bool bWasLerping = false, bool bThrowEvents = true, bool bCheckAutoDrop = true);
virtual void OnUnregister() override;
// Called when a object is gripped
UPROPERTY(BlueprintAssignable, Category = "Grip Events")
FVROnGripSignature OnGripped;
// Called when a object is dropped
UPROPERTY(BlueprintAssignable, Category = "Grip Events")
FVROnDropSignature OnDropped;
// Grip interface setup
// Set up as deny instead of allow so that default allows for gripping
// The GripInitiator is not guaranteed to be valid, check it for validity
bool DenyGripping_Implementation(UGripMotionControllerComponent * GripInitiator = nullptr) override;
// How an interfaced object behaves when teleporting
EGripInterfaceTeleportBehavior TeleportBehavior_Implementation() override;
// Should this object simulate on drop
bool SimulateOnDrop_Implementation() override;
// Grip type to use
EGripCollisionType GetPrimaryGripType_Implementation(bool bIsSlot) override;
// Secondary grip type
ESecondaryGripType SecondaryGripType_Implementation() override;
// Define which movement repliation setting to use
EGripMovementReplicationSettings GripMovementReplicationType_Implementation() override;
// Define the late update setting
EGripLateUpdateSettings GripLateUpdateSetting_Implementation() override;
// What grip stiffness and damping to use if using a physics constraint
void GetGripStiffnessAndDamping_Implementation(float& GripStiffnessOut, float& GripDampingOut) override;
// Get the advanced physics settings for this grip
FBPAdvGripSettings AdvancedGripSettings_Implementation() override;
// What distance to break a grip at (only relevent with physics enabled grips
float GripBreakDistance_Implementation() override;
// Get grip slot in range
void ClosestGripSlotInRange_Implementation(FVector WorldLocation, bool bSecondarySlot, bool& bHadSlotInRange, FTransform& SlotWorldTransform, FName& SlotName, UGripMotionControllerComponent* CallingController = nullptr, FName OverridePrefix = NAME_None) override;
// Check if an object allows multiple grips at one time
bool AllowsMultipleGrips_Implementation() override;
// Returns if the object is held and if so, which controllers are holding it
void IsHeld_Implementation(TArray<FBPGripPair>& CurHoldingControllers, bool& bCurIsHeld) override;
// Interface function used to throw the delegates that is invisible to blueprints so that it can't be overridden
virtual void Native_NotifyThrowGripDelegates(UGripMotionControllerComponent* Controller, bool bGripped, const FBPActorGripInformation& GripInformation, bool bWasSocketed = false) override;
// Sets is held, used by the plugin
void SetHeld_Implementation(UGripMotionControllerComponent* NewHoldingController, uint8 GripID, bool bNewIsHeld) override;
// Returns if the object wants to be socketed
bool RequestsSocketing_Implementation(USceneComponent*& ParentToSocketTo, FName& OptionalSocketName, FTransform_NetQuantize& RelativeTransform) override;
// Get grip scripts
bool GetGripScripts_Implementation(TArray<UVRGripScriptBase*>& ArrayReference) override;
// Events //
// Event triggered each tick on the interfaced object when gripped, can be used for custom movement or grip based logic
void TickGrip_Implementation(UGripMotionControllerComponent* GrippingController, const FBPActorGripInformation& GripInformation, float DeltaTime) override;
// Event triggered on the interfaced object when gripped
void OnGrip_Implementation(UGripMotionControllerComponent* GrippingController, const FBPActorGripInformation& GripInformation) override;
// Event triggered on the interfaced object when grip is released
void OnGripRelease_Implementation(UGripMotionControllerComponent* ReleasingController, const FBPActorGripInformation& GripInformation, bool bWasSocketed = false) override;
// Event triggered on the interfaced object when child component is gripped
void OnChildGrip_Implementation(UGripMotionControllerComponent* GrippingController, const FBPActorGripInformation& GripInformation) override;
// Event triggered on the interfaced object when child component is released
void OnChildGripRelease_Implementation(UGripMotionControllerComponent* ReleasingController, const FBPActorGripInformation& GripInformation, bool bWasSocketed = false) override;
// Event triggered on the interfaced object when secondary gripped
void OnSecondaryGrip_Implementation(UGripMotionControllerComponent* GripOwningController, USceneComponent* SecondaryGripComponent, const FBPActorGripInformation& GripInformation) override;
// Event triggered on the interfaced object when secondary grip is released
void OnSecondaryGripRelease_Implementation(UGripMotionControllerComponent* GripOwningController, USceneComponent* ReleasingSecondaryGripComponent, const FBPActorGripInformation& GripInformation) override;
// Interaction Functions
// Call to use an object
void OnUsed_Implementation() override;
// Call to stop using an object
void OnEndUsed_Implementation() override;
// Call to use an object
void OnSecondaryUsed_Implementation() override;
// Call to stop using an object
void OnEndSecondaryUsed_Implementation() override;
// Call to send an action event to the object
void OnInput_Implementation(FKey Key, EInputEvent KeyEvent) override;
protected:
};

View file

@ -0,0 +1,254 @@
#pragma once
#include "CoreMinimal.h"
#include "GameplayTagContainer.h"
#include "GameplayTagAssetInterface.h"
#include "Components/StaticMeshComponent.h"
#include "Interactibles/VRInteractibleFunctionLibrary.h"
#include "VRGripInterface.h"
#include "VRMountComponent.generated.h"
class UGripMotionControllerComponent;
UENUM(Blueprintable)
enum class EVRInteractibleMountAxis : uint8
{
/** Limit Rotation to Yaw and Roll */
Axis_XZ
};
// A mounted lever/interactible implementation - Created by SpaceHarry - Merged into the plugin 01/29/2018
UCLASS(Blueprintable, meta = (BlueprintSpawnableComponent), ClassGroup = (VRExpansionPlugin))
class VREXPANSIONPLUGIN_API UVRMountComponent : public UStaticMeshComponent, public IVRGripInterface, public IGameplayTagAssetInterface
{
GENERATED_BODY()
public:
UVRMountComponent(const FObjectInitializer& ObjectInitializer);
~UVRMountComponent();
// Rotation axis to use, XY is combined X and Y, only LerpToZero and PositiveLimits work with this mode
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "VRMountComponent")
EVRInteractibleMountAxis MountRotationAxis;
// Resetting the initial transform here so that it comes in prior to BeginPlay and save loading.
virtual void OnRegister() override;
FTransform InitialRelativeTransform;
FVector InitialInteractorLocation;
FVector InitialInteractorDropLocation;
float InitialGripRot;
FQuat qRotAtGrab;
// If the mount is swirling around a 90 degree pitch increase this number by 0.1 steps.
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "VRMountComponent")
float FlipingZone;
// If the mount feels lagging behind in yaw at some point after 90 degree pitch increase this number by 0.1 steps
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "VRMountComponent")
float FlipReajustYawSpeed;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "GripSettings")
int GripPriority;
// Sets the grip priority
UFUNCTION(BlueprintCallable, Category = "GripSettings")
void SetGripPriority(int NewGripPriority);
bool GrippedOnBack;
bool bIsInsideFrontFlipingZone;
bool bIsInsideBackFlipZone;
FVector CurInterpGripLoc;
float TwistDiff;
FVector InitialGripToForwardVec;
FVector InitialForwardVector;
FVector EntryUpXYNeg;
FVector EntryUpVec;
FVector EntryRightVec;
bool bFirstEntryToHalfFlipZone;
bool bLerpingOutOfFlipZone;
bool bIsFlipped;
FPlane FlipPlane;
FPlane ForwardPullPlane;
FVector LastPointOnForwardPlane;
FVector CurPointOnForwardPlane;
float LerpOutAlpha;
// ------------------------------------------------
// Gameplay tag interface
// ------------------------------------------------
/** Overridden to return requirements tags */
virtual void GetOwnedGameplayTags(FGameplayTagContainer& TagContainer) const override
{
TagContainer = GameplayTags;
}
/** Tags that are set on this object */
UPROPERTY(EditAnywhere, Replicated, BlueprintReadWrite, Category = "GameplayTags")
FGameplayTagContainer GameplayTags;
// End Gameplay Tag Interface
virtual void PreReplication(IRepChangedPropertyTracker & ChangedPropertyTracker) override;
// Requires bReplicates to be true for the component
UPROPERTY(EditAnywhere, Replicated, BlueprintReadWrite, Category = "VRGripInterface")
bool bRepGameplayTags;
// Overrides the default of : true and allows for controlling it like in an actor, should be default of off normally with grippable components
UPROPERTY(EditAnywhere, Replicated, BlueprintReadWrite, Category = "VRGripInterface|Replication")
bool bReplicateMovement;
virtual void TickComponent(float DeltaTime, enum ELevelTick TickType, FActorComponentTickFunction *ThisTickFunction) override;
virtual void BeginPlay() override;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "VRGripInterface")
EGripMovementReplicationSettings MovementReplicationSetting;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "VRGripInterface")
float Stiffness;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "VRGripInterface")
float Damping;
// Distance before the object will break out of the hand, 0.0f == never will
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "VRGripInterface")
float BreakDistance;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "GripSettings")
float PrimarySlotRange;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "GripSettings")
float SecondarySlotRange;
// Should we deny gripping on this object
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "VRGripInterface", meta = (ScriptName = "IsDenyGripping"))
bool bDenyGripping;
UPROPERTY(BlueprintReadOnly, Category = "VRGripInterface", meta = (ScriptName = "IsCurrentlyHeld"))
bool bIsHeld; // Set on grip notify, not net serializing
UPROPERTY(BlueprintReadOnly, Category = "VRGripInterface")
FBPGripPair HoldingGrip; // Set on grip notify, not net serializing
bool bOriginalReplicatesMovement;
// Should be called after the Mount is moved post begin play
UFUNCTION(BlueprintCallable, Category = "VRMountComponent")
void ResetInitialMountLocation()
{
// Get our initial relative transform to our parent (or not if un-parented).
InitialRelativeTransform = this->GetRelativeTransform();
}
virtual void OnUnregister() override;;
// Grip interface setup
// Set up as deny instead of allow so that default allows for gripping
// The GripInitiator is not guaranteed to be valid, check it for validity
bool DenyGripping_Implementation(UGripMotionControllerComponent * GripInitiator = nullptr) override;
// How an interfaced object behaves when teleporting
EGripInterfaceTeleportBehavior TeleportBehavior_Implementation() override;
// Should this object simulate on drop
bool SimulateOnDrop_Implementation() override;
// Grip type to use
EGripCollisionType GetPrimaryGripType_Implementation(bool bIsSlot) override;
// Secondary grip type
ESecondaryGripType SecondaryGripType_Implementation() override;
// Define which movement repliation setting to use
EGripMovementReplicationSettings GripMovementReplicationType_Implementation() override;
// Define the late update setting
EGripLateUpdateSettings GripLateUpdateSetting_Implementation() override;
// What grip stiffness and damping to use if using a physics constraint
void GetGripStiffnessAndDamping_Implementation(float& GripStiffnessOut, float& GripDampingOut) override;
// Get the advanced physics settings for this grip
FBPAdvGripSettings AdvancedGripSettings_Implementation() override;
// What distance to break a grip at (only relevent with physics enabled grips
float GripBreakDistance_Implementation() override;
// Get grip slot in range
void ClosestGripSlotInRange_Implementation(FVector WorldLocation, bool bSecondarySlot, bool& bHadSlotInRange, FTransform& SlotWorldTransform, FName& SlotName, UGripMotionControllerComponent* CallingController = nullptr, FName OverridePrefix = NAME_None) override;
// Check if an object allows multiple grips at one time
bool AllowsMultipleGrips_Implementation() override;
// Returns if the object is held and if so, which controllers are holding it
void IsHeld_Implementation(TArray<FBPGripPair>& CurHoldingControllers, bool& bCurIsHeld) override;
// Sets is held, used by the plugin
void SetHeld_Implementation(UGripMotionControllerComponent* NewHoldingController, uint8 GripID, bool bNewIsHeld) override;
// Returns if the object wants to be socketed
bool RequestsSocketing_Implementation(USceneComponent*& ParentToSocketTo, FName& OptionalSocketName, FTransform_NetQuantize& RelativeTransform) override;
// Get grip scripts
bool GetGripScripts_Implementation(TArray<UVRGripScriptBase*>& ArrayReference) override;
// Events //
// Event triggered each tick on the interfaced object when gripped, can be used for custom movement or grip based logic
void TickGrip_Implementation(UGripMotionControllerComponent* GrippingController, const FBPActorGripInformation& GripInformation, float DeltaTime) override;
// Event triggered on the interfaced object when gripped
void OnGrip_Implementation(UGripMotionControllerComponent* GrippingController, const FBPActorGripInformation& GripInformation) override;
// Event triggered on the interfaced object when grip is released
void OnGripRelease_Implementation(UGripMotionControllerComponent* ReleasingController, const FBPActorGripInformation& GripInformation, bool bWasSocketed = false) override;
// Event triggered on the interfaced object when child component is gripped
void OnChildGrip_Implementation(UGripMotionControllerComponent* GrippingController, const FBPActorGripInformation& GripInformation) override;
// Event triggered on the interfaced object when child component is released
void OnChildGripRelease_Implementation(UGripMotionControllerComponent* ReleasingController, const FBPActorGripInformation& GripInformation, bool bWasSocketed = false) override;
// Event triggered on the interfaced object when secondary gripped
void OnSecondaryGrip_Implementation(UGripMotionControllerComponent* GripOwningController, USceneComponent* SecondaryGripComponent, const FBPActorGripInformation& GripInformation) override;
// Event triggered on the interfaced object when secondary grip is released
void OnSecondaryGripRelease_Implementation(UGripMotionControllerComponent* GripOwningController, USceneComponent* ReleasingSecondaryGripComponent, const FBPActorGripInformation& GripInformation) override;
// Interaction Functions
// Call to use an object
void OnUsed_Implementation() override;
// Call to stop using an object
void OnEndUsed_Implementation() override;
// Call to use an object
void OnSecondaryUsed_Implementation() override;
// Call to stop using an object
void OnEndSecondaryUsed_Implementation() override;
// Call to send an action event to the object
void OnInput_Implementation(FKey Key, EInputEvent KeyEvent) override;
protected:
};

View file

@ -0,0 +1,406 @@
// Fill out your copyright notice in the Description page of Project Settings.
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "GameplayTagContainer.h"
#include "VRGripInterface.h"
#include "GameplayTagAssetInterface.h"
#include "Interactibles/VRInteractibleFunctionLibrary.h"
#include "Components/StaticMeshComponent.h"
#include "VRSliderComponent.generated.h"
class UGripMotionControllerComponent;
class USplineComponent;
UENUM(Blueprintable)
enum class EVRInteractibleSliderLerpType : uint8
{
Lerp_None,
Lerp_Interp,
Lerp_InterpConstantTo
};
UENUM(Blueprintable)
enum class EVRInteractibleSliderDropBehavior : uint8
{
/** Stays in place on drop */
Stay,
/** Retains momentum on release*/
RetainMomentum
};
/** Delegate for notification when the slider state changes. */
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FVRSliderHitPointSignature, float, SliderProgressPoint);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FVRSliderFinishedLerpingSignature, float, FinalProgress);
/**
* A slider component, can act like a scroll bar, or gun bolt, or spline following component
*/
UCLASS(Blueprintable, meta = (BlueprintSpawnableComponent), ClassGroup = (VRExpansionPlugin))
class VREXPANSIONPLUGIN_API UVRSliderComponent : public UStaticMeshComponent, public IVRGripInterface, public IGameplayTagAssetInterface
{
GENERATED_BODY()
public:
UVRSliderComponent(const FObjectInitializer& ObjectInitializer);
~UVRSliderComponent();
// Call to use an object
UPROPERTY(BlueprintAssignable, Category = "VRSliderComponent")
FVRSliderHitPointSignature OnSliderHitPoint;
UFUNCTION(BlueprintImplementableEvent, meta = (DisplayName = "Slider State Changed"))
void ReceiveSliderHitPoint(float SliderProgressPoint);
UPROPERTY(BlueprintAssignable, Category = "VRSliderComponent")
FVRSliderFinishedLerpingSignature OnSliderFinishedLerping;
UFUNCTION(BlueprintImplementableEvent, meta = (DisplayName = "Slider Finished Lerping"))
void ReceiveSliderFinishedLerping(float FinalProgress);
// If true then this slider will only update in its tick event instead of normally using the controllers update event
// Keep in mind that you then must adjust the tick group in order to make sure it happens after the gripping controller
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "VRSliderComponent")
bool bUpdateInTick;
bool bPassThrough;
float LastSliderProgressState;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "VRSliderComponent")
FVector MaxSlideDistance;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "VRSliderComponent")
FVector MinSlideDistance;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "VRSliderComponent")
EVRInteractibleSliderDropBehavior SliderBehaviorWhenReleased;
// Number of frames to average momentum across for the release momentum (avoids quick waggles)
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "VRSliderComponent|Momentum Settings", meta = (ClampMin = "0", ClampMax = "12", UIMin = "0", UIMax = "12"))
int FramesToAverage;
// Units in % of total length per second to slow a momentum lerp down
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "VRSliderComponent|Momentum Settings", meta = (ClampMin = "0.0", ClampMax = "10.0", UIMin = "0.0", UIMax = "10.0"))
float SliderMomentumFriction;
// % of elasticity on reaching the end 0 - 1.0
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "VRSliderComponent|Momentum Settings", meta = (ClampMin = "0.0", ClampMax = "1.0", UIMin = "0.0", UIMax = "1.0"))
float SliderRestitution;
// Maximum momentum of the slider in units of the total distance per second (0.0 - 1.0)
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "VRSliderComponent|Momentum Settings", meta = (ClampMin = "0.0", UIMin = "0.0"))
float MaxSliderMomentum;
UPROPERTY(BlueprintReadOnly, Category = "VRSliderComponent")
bool bIsLerping;
// For momentum retention
FVector MomentumAtDrop;
FVector LastSliderProgress;
float SplineMomentumAtDrop;
float SplineLastSliderProgress;
// Gets filled in with the current slider location progress
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "VRSliderComponent")
float CurrentSliderProgress;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "VRSliderComponent")
bool bSlideDistanceIsInParentSpace;
// If true then this slider is locked in place until unlocked again
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "VRSliderComponent")
bool bIsLocked;
// If true then this slider will auto drop even when locked
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "VRSliderComponent")
bool bAutoDropWhenLocked;
// Sets if the slider is locked or not
UFUNCTION(BlueprintCallable, Category = "GripSettings")
void SetIsLocked(bool bNewLockedState);
// Uses the legacy slider logic that doesn't ABS the min and max values
// Retains compatibility with some older projects
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "VRSliderComponent")
bool bUseLegacyLogic;
// How far away from an event state before the slider allows throwing the same state again, default of 1.0 means it takes a full toggle
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "VRSliderComponent", meta = (ClampMin = "0.0", ClampMax = "1.0", UIMin = "0.0", UIMax = "1.0"))
float EventThrowThreshold;
bool bHitEventThreshold;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "GripSettings")
float PrimarySlotRange;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "GripSettings")
float SecondarySlotRange;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "GripSettings")
int GripPriority;
// Sets the grip priority
UFUNCTION(BlueprintCallable, Category = "GripSettings")
void SetGripPriority(int NewGripPriority);
// Set this to assign a spline component to the slider
UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Replicated/*Using = OnRep_SplineComponentToFollow*/, Category = "VRSliderComponent")
TObjectPtr<USplineComponent> SplineComponentToFollow;
/*UFUNCTION()
virtual void OnRep_SplineComponentToFollow()
{
CalculateSliderProgress();
}*/
// Where the slider should follow the rotation and scale of the spline as well
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "VRSliderComponent")
bool bFollowSplineRotationAndScale;
// Does not allow the slider to skip past nodes on the spline, it requires it to progress from node to node
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "VRSliderComponent")
bool bEnforceSplineLinearity;
float LastInputKey;
float LerpedKey;
// Type of lerp to use when following a spline
// For lerping I would suggest using ConstantTo in general as it will be the smoothest.
// Normal Interp will change speed based on distance, that may also have its uses.
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "VRSliderComponent")
EVRInteractibleSliderLerpType SplineLerpType;
// Lerp Value for the spline, when in InterpMode it is the speed of interpolation
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "VRSliderComponent", meta = (ClampMin = "0", UIMin = "0"))
float SplineLerpValue;
// Uses snap increments to move between, not compatible with retain momentum.
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "VRSliderComponent")
bool bSliderUsesSnapPoints;
// Portion of the slider that the slider snaps to on release and when within the threshold distance
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "VRSliderComponent", meta = (editcondition = "bSliderUsesSnapPoints", ClampMin = "0.0", ClampMax = "1.0", UIMin = "0.0", UIMax = "1.0"))
float SnapIncrement;
// Threshold distance that when within the slider will stay snapped to its current snap increment
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "VRSliderComponent", meta = (editcondition = "bSliderUsesSnapPoints", ClampMin = "0.0", ClampMax = "1.0", UIMin = "0.0", UIMax = "1.0"))
float SnapThreshold;
// If true then the slider progress will keep incrementing between snap points if outside of the threshold
// However events will not be thrown
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "VRSliderComponent", meta = (editcondition = "bSliderUsesSnapPoints") )
bool bIncrementProgressBetweenSnapPoints;
// Resetting the initial transform here so that it comes in prior to BeginPlay and save loading.
virtual void OnRegister() override;
// Now replicating this so that it works correctly over the network
UPROPERTY(BlueprintReadOnly, ReplicatedUsing = OnRep_InitialRelativeTransform, Category = "VRSliderComponent")
FTransform_NetQuantize InitialRelativeTransform;
UFUNCTION()
virtual void OnRep_InitialRelativeTransform()
{
CalculateSliderProgress();
}
FVector InitialInteractorLocation;
FVector InitialGripLoc;
FVector InitialDropLocation;
// Checks if we should throw some events
void CheckSliderProgress();
/*
Credit to:
https://github.com/Seurabimn
Who had this function in an engine pull request:
https://github.com/EpicGames/UnrealEngine/pull/6646
*/
float GetDistanceAlongSplineAtSplineInputKey(float InKey) const;
// Calculates the current slider progress
UFUNCTION(BlueprintCallable, Category = "VRSliderComponent")
float CalculateSliderProgress();
// Forcefully sets the slider progress to the defined value
UFUNCTION(BlueprintCallable, Category = "VRSliderComponent")
void SetSliderProgress(float NewSliderProgress);
// Should be called after the slider is moved post begin play
UFUNCTION(BlueprintCallable, Category = "VRSliderComponent")
void ResetInitialSliderLocation();
// Sets the spline component to follow, if empty, just reinitializes the transform and removes the follow component
UFUNCTION(BlueprintCallable, Category = "VRSliderComponent")
void SetSplineComponentToFollow(USplineComponent * SplineToFollow);
void ResetToParentSplineLocation();
void GetLerpedKey(float &ClosestKey, float DeltaTime);
float GetCurrentSliderProgress(FVector CurLocation, bool bUseKeyInstead = false, float CurKey = 0.f);
FVector ClampSlideVector(FVector ValueToClamp);
// ------------------------------------------------
// Gameplay tag interface
// ------------------------------------------------
/** Overridden to return requirements tags */
virtual void GetOwnedGameplayTags(FGameplayTagContainer& TagContainer) const override
{
TagContainer = GameplayTags;
}
/** Tags that are set on this object */
UPROPERTY(EditAnywhere, Replicated, BlueprintReadWrite, Category = "GameplayTags")
FGameplayTagContainer GameplayTags;
// End Gameplay Tag Interface
virtual void PreReplication(IRepChangedPropertyTracker & ChangedPropertyTracker) override;
// Requires bReplicates to be true for the component
UPROPERTY(EditAnywhere, Replicated, BlueprintReadWrite, Category = "VRGripInterface")
bool bRepGameplayTags;
// Overrides the default of : true and allows for controlling it like in an actor, should be default of off normally with grippable components
UPROPERTY(EditAnywhere, Replicated, BlueprintReadWrite, Category = "VRGripInterface|Replication")
bool bReplicateMovement;
virtual void TickComponent(float DeltaTime, enum ELevelTick TickType, FActorComponentTickFunction *ThisTickFunction) override;
virtual void BeginPlay() override;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "VRGripInterface")
EGripMovementReplicationSettings MovementReplicationSetting;
// Distance before the object will break out of the hand, 0.0f == never will
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "VRGripInterface")
float BreakDistance;
// Checks and applies auto drop if we need too
bool CheckAutoDrop(UGripMotionControllerComponent* GrippingController, const FBPActorGripInformation& GripInformation);
// Should we deny gripping on this object
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "VRGripInterface", meta = (ScriptName = "IsDenyGripping"))
bool bDenyGripping;
UPROPERTY(BlueprintReadOnly, Category = "VRGripInterface", meta = (ScriptName = "IsCurrentlyHeld"))
bool bIsHeld; // Set on grip notify, not net serializing
UPROPERTY(BlueprintReadOnly, Category = "VRGripInterface")
FBPGripPair HoldingGrip; // Set on grip notify, not net serializing
bool bOriginalReplicatesMovement;
// Called when a object is gripped
UPROPERTY(BlueprintAssignable, Category = "Grip Events")
FVROnGripSignature OnGripped;
// Called when a object is dropped
UPROPERTY(BlueprintAssignable, Category = "Grip Events")
FVROnDropSignature OnDropped;
// Grip interface setup
// Set up as deny instead of allow so that default allows for gripping
// The GripInitiator is not guaranteed to be valid, check it for validity
bool DenyGripping_Implementation(UGripMotionControllerComponent * GripInitiator = nullptr) override;
// How an interfaced object behaves when teleporting
EGripInterfaceTeleportBehavior TeleportBehavior_Implementation() override;
// Should this object simulate on drop
bool SimulateOnDrop_Implementation() override;
// Grip type to use
EGripCollisionType GetPrimaryGripType_Implementation(bool bIsSlot) override;
// Secondary grip type
ESecondaryGripType SecondaryGripType_Implementation() override;
// Define which movement repliation setting to use
EGripMovementReplicationSettings GripMovementReplicationType_Implementation() override;
// Define the late update setting
EGripLateUpdateSettings GripLateUpdateSetting_Implementation() override;
// What grip stiffness and damping to use if using a physics constraint
void GetGripStiffnessAndDamping_Implementation(float& GripStiffnessOut, float& GripDampingOut) override;
// Get the advanced physics settings for this grip
FBPAdvGripSettings AdvancedGripSettings_Implementation() override;
// What distance to break a grip at (only relevent with physics enabled grips
float GripBreakDistance_Implementation() override;
// Get grip slot in range
void ClosestGripSlotInRange_Implementation(FVector WorldLocation, bool bSecondarySlot, bool& bHadSlotInRange, FTransform& SlotWorldTransform, FName& SlotName, UGripMotionControllerComponent* CallingController = nullptr, FName OverridePrefix = NAME_None) override;
// Check if an object allows multiple grips at one time
bool AllowsMultipleGrips_Implementation() override;
// Returns if the object is held and if so, which controllers are holding it
void IsHeld_Implementation(TArray<FBPGripPair>& CurHoldingControllers, bool& bCurIsHeld) override;
// Interface function used to throw the delegates that is invisible to blueprints so that it can't be overridden
virtual void Native_NotifyThrowGripDelegates(UGripMotionControllerComponent* Controller, bool bGripped, const FBPActorGripInformation& GripInformation, bool bWasSocketed = false) override;
// Sets is held, used by the plugin
void SetHeld_Implementation(UGripMotionControllerComponent* NewHoldingController, uint8 GripID, bool bNewIsHeld) override;
// Returns if the object wants to be socketed
bool RequestsSocketing_Implementation(USceneComponent*& ParentToSocketTo, FName& OptionalSocketName, FTransform_NetQuantize& RelativeTransform) override;
// Get grip scripts
bool GetGripScripts_Implementation(TArray<UVRGripScriptBase*>& ArrayReference) override;
// Events //
// Event triggered each tick on the interfaced object when gripped, can be used for custom movement or grip based logic
void TickGrip_Implementation(UGripMotionControllerComponent* GrippingController, const FBPActorGripInformation& GripInformation, float DeltaTime) override;
// Event triggered on the interfaced object when gripped
void OnGrip_Implementation(UGripMotionControllerComponent* GrippingController, const FBPActorGripInformation& GripInformation) override;
// Event triggered on the interfaced object when grip is released
void OnGripRelease_Implementation(UGripMotionControllerComponent* ReleasingController, const FBPActorGripInformation& GripInformation, bool bWasSocketed = false) override;
// Event triggered on the interfaced object when child component is gripped
void OnChildGrip_Implementation(UGripMotionControllerComponent* GrippingController, const FBPActorGripInformation& GripInformation) override;
// Event triggered on the interfaced object when child component is released
void OnChildGripRelease_Implementation(UGripMotionControllerComponent* ReleasingController, const FBPActorGripInformation& GripInformation, bool bWasSocketed = false) override;
// Event triggered on the interfaced object when secondary gripped
void OnSecondaryGrip_Implementation(UGripMotionControllerComponent* GripOwningController, USceneComponent* SecondaryGripComponent, const FBPActorGripInformation& GripInformation) override;
// Event triggered on the interfaced object when secondary grip is released
void OnSecondaryGripRelease_Implementation(UGripMotionControllerComponent* GripOwningController, USceneComponent* ReleasingSecondaryGripComponent, const FBPActorGripInformation& GripInformation) override;
// Interaction Functions
// Call to use an object
void OnUsed_Implementation() override;
// Call to stop using an object
void OnEndUsed_Implementation() override;
// Call to use an object
void OnSecondaryUsed_Implementation() override;
// Call to stop using an object
void OnEndSecondaryUsed_Implementation() override;
// Call to send an action event to the object
void OnInput_Implementation(FKey Key, EInputEvent KeyEvent) override;
protected:
};

View file

@ -0,0 +1,169 @@
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "Subsystems/WorldSubsystem.h"
#include "Tickable.h"
#include "BucketUpdateSubsystem.generated.h"
//#include "GrippablePhysicsReplication.generated.h"
//DECLARE_DYNAMIC_MULTICAST_DELEGATE(FVRPhysicsReplicationDelegate, void, Return);
DECLARE_DELEGATE_RetVal(bool, FBucketUpdateTickSignature);
DECLARE_DYNAMIC_DELEGATE(FDynamicBucketUpdateTickSignature);
USTRUCT()
struct VREXPANSIONPLUGIN_API FUpdateBucketDrop
{
GENERATED_BODY()
public:
FBucketUpdateTickSignature NativeCallback;
FDynamicBucketUpdateTickSignature DynamicCallback;
FName FunctionName;
bool ExecuteBoundCallback();
bool IsBoundToObjectFunction(UObject * Obj, FName & FuncName);
bool IsBoundToObjectDelegate(FDynamicBucketUpdateTickSignature & DynEvent);
bool IsBoundToObject(UObject * Obj);
FUpdateBucketDrop();
FUpdateBucketDrop(FDynamicBucketUpdateTickSignature & DynCallback);
FUpdateBucketDrop(UObject * Obj, FName FuncName);
};
USTRUCT()
struct VREXPANSIONPLUGIN_API FUpdateBucket
{
GENERATED_BODY()
public:
float nUpdateRate;
float nUpdateCount;
TArray<FUpdateBucketDrop> Callbacks;
bool Update(float DeltaTime);
FUpdateBucket() {}
FUpdateBucket(uint32 UpdateHTZ) :
nUpdateRate(1.0f / UpdateHTZ),
nUpdateCount(0.0f)
{
}
};
USTRUCT()
struct VREXPANSIONPLUGIN_API FUpdateBucketContainer
{
GENERATED_BODY()
public:
bool bNeedsUpdate;
TMap<uint32, FUpdateBucket> ReplicationBuckets;
void UpdateBuckets(float DeltaTime);
bool AddBucketObject(uint32 UpdateHTZ, UObject* InObject, FName FunctionName);
bool AddBucketObject(uint32 UpdateHTZ, FDynamicBucketUpdateTickSignature &Delegate);
/*
template<typename classType>
bool AddReplicatingObject(uint32 UpdateHTZ, classType* InObject, void(classType::* _Func)())
{
}
*/
bool RemoveBucketObject(UObject * ObjectToRemove, FName FunctionName);
bool RemoveBucketObject(FDynamicBucketUpdateTickSignature &DynEvent);
bool RemoveObjectFromAllBuckets(UObject * ObjectToRemove);
bool IsObjectInBucket(UObject * ObjectToRemove);
bool IsObjectFunctionInBucket(UObject * ObjectToRemove, FName FunctionName);
bool IsObjectDelegateInBucket(FDynamicBucketUpdateTickSignature &DynEvent);
FUpdateBucketContainer()
{
bNeedsUpdate = false;
};
};
UCLASS()
class VREXPANSIONPLUGIN_API UBucketUpdateSubsystem : public UTickableWorldSubsystem
{
GENERATED_BODY()
public:
UBucketUpdateSubsystem() :
Super()
{
}
virtual bool DoesSupportWorldType(EWorldType::Type WorldType) const override
{
return WorldType == EWorldType::Game || WorldType == EWorldType::PIE;
// Not allowing for editor type as this is a replication subsystem
}
//UPROPERTY()
FUpdateBucketContainer BucketContainer;
// Adds an object to an update bucket with the set HTZ, calls the passed in UFUNCTION name
// If one of the bucket contains an entry with the function already then the existing one is removed and the new one is added
bool AddObjectToBucket(int32 UpdateHTZ, UObject* InObject, FName FunctionName);
// Adds an object to an update bucket with the set HTZ, calls the passed in UFUNCTION name
// If one of the bucket contains an entry with the function already then the existing one is removed and the new one is added
UFUNCTION(BlueprintCallable, meta = (DisplayName = "Add Object to Bucket Updates", ScriptName = "AddObjectToBucket"), Category = "BucketUpdateSubsystem")
bool K2_AddObjectToBucket(int32 UpdateHTZ = 100, UObject* InObject = nullptr, FName FunctionName = NAME_None);
UFUNCTION(BlueprintCallable, meta = (DisplayName = "Add Object to Bucket Updates by Event", ScriptName = "AddBucketObjectEvent"), Category = "BucketUpdateSubsystem")
bool K2_AddObjectEventToBucket(UPARAM(DisplayName = "Event") FDynamicBucketUpdateTickSignature Delegate, int32 UpdateHTZ = 100);
// Remove the entry in the bucket updates with the passed in UFUNCTION name
UFUNCTION(BlueprintCallable, meta = (DisplayName = "Remove Object From Bucket Updates By Function", ScriptName = "RemoveObjectFromBucketByFunction"), Category = "BucketUpdateSubsystem")
bool RemoveObjectFromBucketByFunctionName(UObject* InObject = nullptr, FName FunctionName = NAME_None);
// Remove the entry in the bucket updates with the passed in UFUNCTION name
UFUNCTION(BlueprintCallable, meta = (DisplayName = "Remove Object From Bucket Updates By Event", ScriptName = "RemoveObjectFromBucketByEvent"), Category = "BucketUpdateSubsystem")
bool RemoveObjectFromBucketByEvent(UPARAM(DisplayName = "Event") FDynamicBucketUpdateTickSignature Delegate);
// Removes ALL entries in the bucket update system with the specified object
UFUNCTION(BlueprintCallable, meta = (DisplayName = "Remove Object From All Bucket Updates", ScriptName = "RemoveObjectFromAllBuckets"), Category = "BucketUpdateSubsystem")
bool RemoveObjectFromAllBuckets(UObject* InObject = nullptr);
// Returns if an update bucket contains an entry with the passed in function
UFUNCTION(BlueprintCallable, meta = (DisplayName = "Is Object In Bucket", ScriptName = "IsObjectInBucket"), Category = "BucketUpdateSubsystem")
bool IsObjectFunctionInBucket(UObject* InObject = nullptr, FName FunctionName = NAME_None);
// Returns if an update bucket contains an entry with the passed in function
UFUNCTION(BlueprintPure, Category = "BucketUpdateSubsystem")
bool IsActive();
// FTickableGameObject functions
/**
* Function called every frame on this GripScript. Override this function to implement custom logic to be executed every frame.
* Only executes if bCanEverTick is true and bAllowTicking is true
*
* @param DeltaTime - The time since the last tick.
*/
virtual void Tick(float DeltaTime) override;
virtual bool IsTickable() const override;
virtual UWorld* GetTickableGameObjectWorld() const override;
virtual bool IsTickableInEditor() const;
virtual bool IsTickableWhenPaused() const override;
virtual ETickableTickType GetTickableTickType() const;
virtual TStatId GetStatId() const override;
// End tickable object information
};

View file

@ -0,0 +1,255 @@
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "Subsystems/WorldSubsystem.h"
#include "TimerManager.h"
#include "Components/PrimitiveComponent.h"
//#include "Grippables/GrippablePhysicsReplication.h"
// For async physics scene callback modifications
// Some of these can be moved out to the cpp
#include "Chaos/SimCallbackObject.h"
#include "Chaos/SimCallbackInput.h"
#include "Chaos/ParticleHandle.h"
//#include "Chaos/ContactModification.h"
//#include "PBDRigidsSolver.h"
#include "CollisionIgnoreSubsystem.generated.h"
//#include "GrippablePhysicsReplication.generated.h"
DECLARE_LOG_CATEGORY_EXTERN(VRE_CollisionIgnoreLog, Log, All);
USTRUCT()
struct FChaosParticlePair
{
GENERATED_BODY()
Chaos::TPBDRigidParticleHandle<Chaos::FReal, 3>* ParticleHandle0;
Chaos::TPBDRigidParticleHandle<Chaos::FReal, 3>* ParticleHandle1;
FChaosParticlePair()
{
ParticleHandle0 = nullptr;
ParticleHandle1 = nullptr;
}
FChaosParticlePair(Chaos::TPBDRigidParticleHandle<Chaos::FReal, 3>* pH1, Chaos::TPBDRigidParticleHandle<Chaos::FReal, 3>* pH2)
{
ParticleHandle0 = pH1;
ParticleHandle1 = pH2;
}
FORCEINLINE bool operator==(const FChaosParticlePair& Other) const
{
return(
(ParticleHandle0 == Other.ParticleHandle0 || ParticleHandle0 == Other.ParticleHandle1) &&
(ParticleHandle1 == Other.ParticleHandle1 || ParticleHandle1 == Other.ParticleHandle0)
);
}
};
/*
* All input is const, non-const data goes in output. 'AsyncSimState' points to non-const sim state.
*/
struct FSimCallbackInputVR : public Chaos::FSimCallbackInput
{
virtual ~FSimCallbackInputVR() {}
void Reset()
{
ParticlePairs.Empty();
}
TArray<FChaosParticlePair> ParticlePairs;
bool bIsInitialized;
};
struct FSimCallbackNoOutputVR : public Chaos::FSimCallbackOutput
{
void Reset() {}
};
class FCollisionIgnoreSubsystemAsyncCallback : public Chaos::TSimCallbackObject<FSimCallbackInputVR, FSimCallbackNoOutputVR, Chaos::ESimCallbackOptions::ContactModification>
{
private:
virtual void OnPreSimulate_Internal() override
{
// Copy paired bodies here?
// Actually use input data to input our paired bodies and copy over here
}
/**
* Called once per simulation step. Allows user to modify contacts
*
* NOTE: you must explicitly request contact modification when registering the callback for this to be called
*/
virtual void OnContactModification_Internal(Chaos::FCollisionContactModifier& Modifier) override;
};
USTRUCT()
struct FCollisionPrimPair
{
GENERATED_BODY()
public:
UPROPERTY()
TObjectPtr<UPrimitiveComponent> Prim1;
UPROPERTY()
TObjectPtr<UPrimitiveComponent> Prim2;
FCollisionPrimPair()
{
Prim1 = nullptr;
Prim2 = nullptr;
}
FORCEINLINE bool operator==(const FCollisionPrimPair& Other) const
{
/*if (!Prim1.IsValid() || !Prim2.IsValid())
return false;
if (!Other.Prim1.IsValid() || !Other.Prim2.IsValid())
return false;*/
return(
(Prim1.Get() == Other.Prim1.Get() || Prim1.Get() == Other.Prim2.Get()) &&
(Prim2.Get() == Other.Prim1.Get() || Prim2.Get() == Other.Prim2.Get())
);
}
friend uint32 GetTypeHash(const FCollisionPrimPair& InKey)
{
return GetTypeHash(InKey.Prim1) ^ GetTypeHash(InKey.Prim2);
}
};
USTRUCT()
struct FCollisionIgnorePair
{
GENERATED_BODY()
public:
///FCollisionPrimPair PrimitivePair;
FPhysicsActorHandle Actor1;
UPROPERTY()
FName BoneName1;
FPhysicsActorHandle Actor2;
UPROPERTY()
FName BoneName2;
// Flip our elements to retain a default ordering in an array
void FlipElements()
{
FPhysicsActorHandle tH = Actor1;
Actor1 = Actor2;
Actor2 = tH;
FName tN = BoneName1;
BoneName1 = BoneName2;
BoneName2 = tN;
}
FORCEINLINE bool operator==(const FCollisionIgnorePair& Other) const
{
return (
(BoneName1 == Other.BoneName1 || BoneName1 == Other.BoneName2) &&
(BoneName2 == Other.BoneName2 || BoneName2 == Other.BoneName1)
);
}
FORCEINLINE bool operator==(const FName& Other) const
{
return (BoneName1 == Other || BoneName2 == Other);
}
};
USTRUCT()
struct FCollisionIgnorePairArray
{
GENERATED_BODY()
public:
UPROPERTY()
TArray<FCollisionIgnorePair> PairArray;
};
UCLASS()
class VREXPANSIONPLUGIN_API UCollisionIgnoreSubsystem : public UWorldSubsystem
{
GENERATED_BODY()
public:
UCollisionIgnoreSubsystem() :
Super()
{
ContactModifierCallback = nullptr;
}
FCollisionIgnoreSubsystemAsyncCallback* ContactModifierCallback;
void ConstructInput();
virtual bool DoesSupportWorldType(EWorldType::Type WorldType) const override
{
return WorldType == EWorldType::Game || WorldType == EWorldType::PIE;
// Not allowing for editor type as this is a replication subsystem
}
/** Implement this for initialization of instances of the system */
virtual void Initialize(FSubsystemCollectionBase& Collection) override
{
Super::Initialize(Collection);
}
virtual void Deinitialize() override
{
Super::Deinitialize();
if (UpdateHandle.IsValid())
{
GetWorld()->GetTimerManager().ClearTimer(UpdateHandle);
}
}
UPROPERTY()
TMap<FCollisionPrimPair, FCollisionIgnorePairArray> CollisionTrackedPairs;
//TArray<FCollisionIgnorePair> CollisionTrackedPairs;
UPROPERTY()
TMap<FCollisionPrimPair, FCollisionIgnorePairArray> RemovedPairs;
//TArray<FCollisionIgnorePair> RemovedPairs;
//
void UpdateTimer(bool bChangesWereMade);
UFUNCTION(Category = "Collision")
void CheckActiveFilters();
// #TODO implement this, though it should be rare
void InitiateIgnore();
void SetComponentCollisionIgnoreState(bool bIterateChildren1, bool bIterateChildren2, UPrimitiveComponent* Prim1, FName OptionalBoneName1, UPrimitiveComponent* Prim2, FName OptionalBoneName2, bool bIgnoreCollision, bool bCheckFilters = false);
void RemoveComponentCollisionIgnoreState(UPrimitiveComponent* Prim1);
bool IsComponentIgnoringCollision(UPrimitiveComponent* Prim1);
bool AreComponentsIgnoringCollisions(UPrimitiveComponent* Prim1, UPrimitiveComponent* Prim2);
bool HasCollisionIgnorePairs();
private:
FTimerHandle UpdateHandle;
};

View file

@ -0,0 +1,132 @@
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "GripMotionControllerComponent.h"
//#include "Engine/Engine.h"
#include "Animation/SkeletalMeshActor.h"
#include "Components/SkeletalMeshComponent.h"
#include "Components/SphereComponent.h"
#include "Engine/ActorChannel.h"
#include "OptionalRepSkeletalMeshActor.generated.h"
// Temp comp to avoid some engine issues, exists only until a bug fix happens
UCLASS(Blueprintable, meta = (BlueprintSpawnableComponent, ChildCanTick), ClassGroup = (VRExpansionPlugin))
class VREXPANSIONPLUGIN_API UNoRepSphereComponent : public USphereComponent
{
GENERATED_BODY()
public:
UNoRepSphereComponent(const FObjectInitializer& ObjectInitializer);
UPROPERTY(EditAnywhere, Replicated, BlueprintReadWrite, Category = "Component Replication")
bool bReplicateMovement;
virtual void PreReplication(IRepChangedPropertyTracker& ChangedPropertyTracker) override;
};
/**
* A component specifically for being able to turn off movement replication in the component at will
* Has the upside of also being a blueprintable base since UE4 doesn't allow that with std ones
*/
USTRUCT()
struct FSkeletalMeshComponentEndPhysicsTickFunctionVR : public FTickFunction
{
GENERATED_USTRUCT_BODY()
UInversePhysicsSkeletalMeshComponent* TargetVR;
virtual void ExecuteTick(float DeltaTime, enum ELevelTick TickType, ENamedThreads::Type CurrentThread, const FGraphEventRef& MyCompletionGraphEvent) override;
virtual FString DiagnosticMessage() override;
virtual FName DiagnosticContext(bool bDetailed) override;
};
template<>
struct TStructOpsTypeTraits<FSkeletalMeshComponentEndPhysicsTickFunctionVR> : public TStructOpsTypeTraitsBase2<FSkeletalMeshComponentEndPhysicsTickFunctionVR>
{
enum
{
WithCopy = false
};
};
// A base skeletal mesh component that has been added to temp correct an engine bug with inversed scale and physics
UCLASS(Blueprintable, meta = (ChildCanTick, BlueprintSpawnableComponent), ClassGroup = (VRExpansionPlugin))
class VREXPANSIONPLUGIN_API UInversePhysicsSkeletalMeshComponent : public USkeletalMeshComponent
{
GENERATED_BODY()
public:
UInversePhysicsSkeletalMeshComponent(const FObjectInitializer& ObjectInitializer);
public:
// Overrides the default of : true and allows for controlling it like in an actor, should be default of off normally with grippable components
UPROPERTY(EditAnywhere, Replicated, BlueprintReadWrite, Category = "Component Replication")
bool bReplicateMovement;
// This is all overrides to fix the skeletal mesh inverse simulation bug
// WILL BE REMOVED LATER when the engine is fixed
FSkeletalMeshComponentEndPhysicsTickFunctionVR EndPhysicsTickFunctionVR;
friend struct FSkeletalMeshComponentEndPhysicsTickFunctionVR;
void EndPhysicsTickComponentVR(FSkeletalMeshComponentEndPhysicsTickFunctionVR& ThisTickFunction);
void BlendInPhysicsInternalVR(FTickFunction& ThisTickFunction);
void FinalizeAnimationUpdateVR();
// Weld Fix until epic fixes it #TODO: check back in on this and remove and the asset include when epic fixes it
virtual void GetWeldedBodies(TArray<FBodyInstance*>& OutWeldedBodies, TArray<FName>& OutLabels, bool bIncludingAutoWeld) override;
virtual FBodyInstance* GetBodyInstance(FName BoneName = NAME_None, bool bGetWelded = true, int32 Index = INDEX_NONE) const override;
virtual FBoxSphereBounds CalcBounds(const FTransform& LocalToWorld) const override
{
// Get rid of inverse issues
FTransform newLocalToWorld = LocalToWorld;
newLocalToWorld.SetScale3D(newLocalToWorld.GetScale3D().GetAbs());
return Super::CalcBounds(newLocalToWorld);
}
UFUNCTION(BlueprintPure, Category = "VRExpansionFunctions")
FBoxSphereBounds GetLocalBounds() const
{
return this->GetCachedLocalBounds();
}
void PerformBlendPhysicsBonesVR(const TArray<FBoneIndexType>& InRequiredBones, TArray<FTransform>& InOutComponentSpaceTransforms, TArray<FTransform>& InOutBoneSpaceTransforms);
virtual void RegisterEndPhysicsTick(bool bRegister) override;
virtual void TickComponent(float DeltaTime, enum ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) override;
// END INVERSED MESH FIX
virtual void PreReplication(IRepChangedPropertyTracker & ChangedPropertyTracker) override;
};
/**
*
* A class specifically for turning off default physics replication with a skeletal mesh and fixing an
* engine bug with inversed scale on skeletal meshes. Generally used for the physical hand setup.
*/
UCLASS(Blueprintable, meta = (BlueprintSpawnableComponent, ChildCanTick), ClassGroup = (VRExpansionPlugin))
class VREXPANSIONPLUGIN_API AOptionalRepGrippableSkeletalMeshActor : public ASkeletalMeshActor
{
GENERATED_BODY()
public:
AOptionalRepGrippableSkeletalMeshActor(const FObjectInitializer& ObjectInitializer);
// Skips the attachment replication
UPROPERTY(EditAnywhere, Replicated, BlueprintReadWrite, Category = "Replication")
bool bIgnoreAttachmentReplication;
// Skips the physics replication
UPROPERTY(EditAnywhere, Replicated, BlueprintReadWrite, Category = "Replication")
bool bIgnorePhysicsReplication;
// Fix bugs with replication and bReplicateMovement
virtual void OnRep_ReplicateMovement() override;
virtual void PostNetReceivePhysicState() override;
};

View file

@ -0,0 +1,398 @@
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "UObject/ObjectMacros.h"
#include "VRBaseCharacter.h"
#include "GenericTeamAgentInterface.h"
#include "Perception/AISense.h"
#include "Perception/AISense_Sight.h"
#include "Perception/AISenseConfig.h"
#include "WorldCollision.h"
#include "Misc/MTAccessDetector.h"
#include "VRAIPerceptionOverrides.generated.h"
class IAISightTargetInterface;
class UAISense_Sight_VR;
class FGameplayDebuggerCategory;
class UAIPerceptionComponent;
DECLARE_LOG_CATEGORY_EXTERN(LogAIPerceptionVR, Warning, All);
UCLASS(meta = (DisplayName = "AI Sight VR config"))
class VREXPANSIONPLUGIN_API UAISenseConfig_Sight_VR : public UAISenseConfig
{
GENERATED_UCLASS_BODY()
public:
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Sense", NoClear, config)
TSubclassOf<UAISense_Sight_VR> Implementation;
/** Maximum sight distance to notice a target. */
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Sense", config, meta = (UIMin = 0.0, ClampMin = 0.0))
float SightRadius;
/** Maximum sight distance to see target that has been already seen. */
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Sense", config, meta = (UIMin = 0.0, ClampMin = 0.0))
float LoseSightRadius;
/** How far to the side AI can see, in degrees. Use SetPeripheralVisionAngle to change the value at runtime.
* The value represents the angle measured in relation to the forward vector, not the whole range. */
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Sense", config, meta = (UIMin = 0.0, ClampMin = 0.0, UIMax = 180.0, ClampMax = 180.0, DisplayName = "PeripheralVisionHalfAngleDegrees"))
float PeripheralVisionAngleDegrees;
/** */
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Sense", config)
FAISenseAffiliationFilter DetectionByAffiliation;
/** If not an InvalidRange (which is the default), we will always be able to see the target that has already been seen if they are within this range of their last seen location. */
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Sense", config)
float AutoSuccessRangeFromLastSeenLocation;
/** Point of view move back distance for cone calculation. In conjunction with near clipping distance, this will act as a close by awareness and peripheral vision. */
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Sense", config, meta = (UIMin = 0.0, ClampMin = 0.0))
float PointOfViewBackwardOffset;
/** Near clipping distance, to be used with point of view backward offset. Will act as a close by awareness and peripheral vision */
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Sense", config, meta = (UIMin = 0.0, ClampMin = 0.0))
float NearClippingRadius;
virtual TSubclassOf<UAISense> GetSenseImplementation() const override;
#if WITH_EDITOR
virtual void PostEditChangeChainProperty(FPropertyChangedChainEvent& PropertyChangedEvent) override;
#endif // WITH_EDITOR
#if WITH_GAMEPLAY_DEBUGGER
virtual void DescribeSelfToGameplayDebugger(const UAIPerceptionComponent* PerceptionComponent, FGameplayDebuggerCategory* DebuggerCategory) const;
#endif // WITH_GAMEPLAY_DEBUGGER
};
namespace ESightPerceptionEventNameVR
{
enum Type
{
Undefined,
GainedSight,
LostSight
};
}
USTRUCT()
struct VREXPANSIONPLUGIN_API FAISightEventVR
{
GENERATED_USTRUCT_BODY()
typedef UAISense_Sight_VR FSenseClass;
float Age;
ESightPerceptionEventNameVR::Type EventType;
UPROPERTY()
TObjectPtr<AActor> SeenActor;
UPROPERTY()
TObjectPtr<AActor> Observer;
FAISightEventVR() : SeenActor(nullptr), Observer(nullptr) {}
FAISightEventVR(AActor* InSeenActor, AActor* InObserver, ESightPerceptionEventNameVR::Type InEventType)
: Age(0.f), EventType(InEventType), SeenActor(InSeenActor), Observer(InObserver)
{
}
};
struct FAISightTargetVR
{
typedef uint32 FTargetId;
static const FTargetId InvalidTargetId;
TWeakObjectPtr<AActor> Target;
IAISightTargetInterface* SightTargetInterface;
FGenericTeamId TeamId;
FTargetId TargetId;
FAISightTargetVR(AActor* InTarget = NULL, FGenericTeamId InTeamId = FGenericTeamId::NoTeam);
FORCEINLINE FVector GetLocationSimple() const
{
// Changed this up to support my VR Characters
const AVRBaseCharacter * VRChar = Cast<const AVRBaseCharacter>(Target);
return Target.IsValid() ? (VRChar != nullptr ? VRChar->GetVRLocation_Inline() : Target->GetActorLocation()) : FVector::ZeroVector;
}
FORCEINLINE const AActor* GetTargetActor() const { return Target.Get(); }
};
struct FAISightQueryVR
{
FPerceptionListenerID ObserverId;
FAISightTargetVR::FTargetId TargetId;
float Score;
float Importance;
FVector LastSeenLocation;
/** User data that can be used inside the IAISightTargetInterface::CanBeSeenFrom method to store a persistence state */
mutable int32 UserData;
union
{
/**
* We can share the memory for these values because they aren't used at the same time :
* - The FrameInfo is used when the query is queued for an update at a later frame. It stores the last time the
* query was processed so that we can prioritize it accordingly against the other queries
* - The TraceInfo is used when the query has requested a asynchronous trace and is waiting for the result.
* The engine guarantees that we'll get the info at the next frame, but since we can have multiple queries that
* are pending at the same time, we need to store some information to identify them when receiving the result callback
*/
struct
{
uint64 bLastResult : 1;
uint64 LastProcessedFrameNumber : 63;
} FrameInfo;
/**
* The 'FrameNumber' value can increase indefinitely while the 'Index' represents the number of queries that were
* already requested during this frame. So it shouldn't reach high values in the allocated 32 bits.
* Thanks to that we can reliable only use 31 bits for this value and thus have space to keep the bLastResult value
*/
struct
{
uint32 bLastResult : 1;
uint32 Index : 31;
uint32 FrameNumber;
} TraceInfo;
};
FAISightQueryVR(FPerceptionListenerID ListenerId = FPerceptionListenerID::InvalidID(), FAISightTargetVR::FTargetId Target = FAISightTargetVR::InvalidTargetId)
: ObserverId(ListenerId), TargetId(Target), Score(0), Importance(0), LastSeenLocation(FAISystem::InvalidLocation), UserData(0)
{
FrameInfo.bLastResult = false;
FrameInfo.LastProcessedFrameNumber = GFrameCounter;
}
/**
* Note: This should only be called on queries that are queued up for later processing (in SightQueriesOutOfRange or SightQueriesOutOfRange)
*/
float GetAge() const
{
return (float)(GFrameCounter - FrameInfo.LastProcessedFrameNumber);
}
/**
* Note: This should only be called on queries that are queued up for later processing (in SightQueriesOutOfRange or SightQueriesOutOfRange)
*/
void RecalcScore()
{
Score = GetAge() + Importance;
}
void OnProcessed()
{
FrameInfo.LastProcessedFrameNumber = GFrameCounter;
}
void ForgetPreviousResult()
{
LastSeenLocation = FAISystem::InvalidLocation;
SetLastResult(false);
}
bool GetLastResult() const
{
return FrameInfo.bLastResult;
}
void SetLastResult(const bool bValue)
{
FrameInfo.bLastResult = bValue;
}
/**
* Note: This only be called for pending queries because it will erase the LastProcessedFrameNumber value
*/
void SetTraceInfo(const FTraceHandle& TraceHandle)
{
check((TraceHandle._Data.Index & (static_cast<uint32>(1) << 31)) == 0);
TraceInfo.Index = TraceHandle._Data.Index;
TraceInfo.FrameNumber = TraceHandle._Data.FrameNumber;
}
class FSortPredicate
{
public:
FSortPredicate()
{}
bool operator()(const FAISightQueryVR& A, const FAISightQueryVR& B) const
{
return A.Score > B.Score;
}
};
};
struct FAISightQueryIDVR
{
FPerceptionListenerID ObserverId;
FAISightTargetVR::FTargetId TargetId;
FAISightQueryIDVR(FPerceptionListenerID ListenerId = FPerceptionListenerID::InvalidID(), FAISightTargetVR::FTargetId Target = FAISightTargetVR::InvalidTargetId)
: ObserverId(ListenerId), TargetId(Target)
{
}
FAISightQueryIDVR(const FAISightQueryVR& Query)
: ObserverId(Query.ObserverId), TargetId(Query.TargetId)
{
}
};
DECLARE_DELEGATE_FiveParams(FOnPendingVisibilityQueryProcessedDelegateVR, const FAISightQueryID&, const bool, const float, const FVector&, const TOptional<int32>&);
UCLASS(ClassGroup = AI, config = Game)
class VREXPANSIONPLUGIN_API UAISense_Sight_VR : public UAISense
{
GENERATED_UCLASS_BODY()
public:
struct FDigestedSightProperties
{
float PeripheralVisionAngleCos;
float SightRadiusSq;
float AutoSuccessRangeSqFromLastSeenLocation;
float LoseSightRadiusSq;
float PointOfViewBackwardOffset;
float NearClippingRadiusSq;
uint8 AffiliationFlags;
FDigestedSightProperties();
FDigestedSightProperties(const UAISenseConfig_Sight_VR& SenseConfig);
};
typedef TMap<FAISightTargetVR::FTargetId, FAISightTargetVR> FTargetsContainer;
FTargetsContainer ObservedTargets;
TMap<FPerceptionListenerID, FDigestedSightProperties> DigestedProperties;
/** The SightQueries are a n^2 problem and to reduce the sort time, they are now split between in range and out of range */
/** Since the out of range queries only age as the distance component of the score is always 0, there is few need to sort them */
/** In the majority of the cases most of the queries are out of range, so the sort time is greatly reduced as we only sort the in range queries */
int32 NextOutOfRangeIndex = 0;
bool bSightQueriesOutOfRangeDirty = true;
TArray<FAISightQueryVR> SightQueriesOutOfRange;
TArray<FAISightQueryVR> SightQueriesInRange;
TArray<FAISightQueryVR> SightQueriesPending;
protected:
UPROPERTY(EditDefaultsOnly, Category = "AI Perception", config)
int32 MaxTracesPerTick;
/** Maximum number of asynchronous traces that can be requested in a single update call*/
UPROPERTY(EditDefaultsOnly, Category = "AI Perception", config)
int32 MaxAsyncTracesPerTick;
UPROPERTY(EditDefaultsOnly, Category = "AI Perception", config)
int32 MinQueriesPerTimeSliceCheck;
UPROPERTY(EditDefaultsOnly, Category = "AI Perception", config)
double MaxTimeSlicePerTick;
UPROPERTY(EditDefaultsOnly, Category = "AI Perception", config)
float HighImportanceQueryDistanceThreshold;
float HighImportanceDistanceSquare;
UPROPERTY(EditDefaultsOnly, Category = "AI Perception", config)
float MaxQueryImportance;
UPROPERTY(EditDefaultsOnly, Category = "AI Perception", config)
float SightLimitQueryImportance;
/** Defines the amount of async trace queries to prevent based on the number of pending queries at the start of an update.
* 1 means that the async trace budget is slashed by the pending queries count
* 0 means that the async trace budget is not impacted by the pending queries
*/
UPROPERTY(EditDefaultsOnly, Category = "AI Perception", config)
float PendingQueriesBudgetReductionRatio;
/** Defines if we are allowed to use asynchronous trace queries when there is no IAISightTargetInterface for a Target */
UPROPERTY(EditDefaultsOnly, Category = "AI Perception", config)
bool bUseAsynchronousTraceForDefaultSightQueries;
ECollisionChannel DefaultSightCollisionChannel;
FOnPendingVisibilityQueryProcessedDelegateVR OnPendingCanBeSeenQueryProcessedDelegate;
FTraceDelegate OnPendingTraceQueryProcessedDelegate;
UE_MT_DECLARE_RW_ACCESS_DETECTOR(QueriesListAccessDetector);
public:
virtual void PostInitProperties() override;
void RegisterEvent(const FAISightEventVR& Event);
virtual void RegisterSource(AActor& SourceActors) override;
virtual void UnregisterSource(AActor& SourceActor) override;
virtual void OnListenerForgetsActor(const FPerceptionListener& Listener, AActor& ActorToForget) override;
virtual void OnListenerForgetsAll(const FPerceptionListener& Listener) override;
#if WITH_GAMEPLAY_DEBUGGER_MENU
virtual void DescribeSelfToGameplayDebugger(const UAIPerceptionSystem& PerceptionSystem, FGameplayDebuggerCategory& DebuggerCategory) const override;
#endif // WITH_GAMEPLAY_DEBUGGER_MENU
protected:
virtual float Update() override;
UAISense_Sight::EVisibilityResult ComputeVisibility(UWorld* World, FAISightQueryVR& SightQuery, FPerceptionListener& Listener, const AActor* ListenerActor, FAISightTargetVR& Target, AActor* TargetActor, const FDigestedSightProperties& PropDigest, float& OutStimulusStrength, FVector& OutSeenLocation, int32& OutNumberOfLoSChecksPerformed, int32& OutNumberOfAsyncLosCheckRequested) const;
virtual bool ShouldAutomaticallySeeTarget(const FDigestedSightProperties& PropDigest, FAISightQueryVR* SightQuery, FPerceptionListener& Listener, AActor* TargetActor, float& OutStimulusStrength) const;
UE_DEPRECATED(5.3, "Please use the UpdateQueryVisibilityStatus version which takes an Actor& instead.")
void UpdateQueryVisibilityStatus(FAISightQueryVR& SightQuery, FPerceptionListener& Listener, const bool bIsVisible, const FVector& SeenLocation, const float StimulusStrength, AActor* TargetActor, const FVector& TargetLocation) const;
void UpdateQueryVisibilityStatus(FAISightQueryVR& SightQuery, FPerceptionListener& Listener, const bool bIsVisible, const FVector& SeenLocation, const float StimulusStrength, AActor& TargetActor, const FVector& TargetLocation) const;
void OnPendingCanBeSeenQueryProcessed(const FAISightQueryID& QueryID, const bool bIsVisible, const float StimulusStrength, const FVector& SeenLocation, const TOptional<int32>& UserData);
void OnPendingTraceQueryProcessed(const FTraceHandle& TraceHandle, FTraceDatum& TraceDatum);
void OnPendingQueryProcessed(const int32 SightQueryIndex, const bool bIsVisible, const float StimulusStrength, const FVector& SeenLocation, const TOptional<int32>& UserData, const TOptional<AActor*> InTargetActor = NullOpt);
void OnNewListenerImpl(const FPerceptionListener& NewListener);
void OnListenerUpdateImpl(const FPerceptionListener& UpdatedListener);
void OnListenerRemovedImpl(const FPerceptionListener& RemovedListener);
virtual void OnListenerConfigUpdated(const FPerceptionListener& UpdatedListener) override;
void GenerateQueriesForListener(const FPerceptionListener& Listener, const FDigestedSightProperties& PropertyDigest, const TFunction<void(FAISightQueryVR&)>& OnAddedFunc = nullptr);
void RemoveAllQueriesByListener(const FPerceptionListener& Listener, const TFunction<void(const FAISightQueryVR&)>& OnRemoveFunc = nullptr);
void RemoveAllQueriesToTarget(const FAISightTargetVR::FTargetId& TargetId, const TFunction<void(const FAISightQueryVR&)>& OnRemoveFunc = nullptr);
/** returns information whether new LoS queries have been added */
bool RegisterTarget(AActor& TargetActor, const TFunction<void(FAISightQueryVR&)>& OnAddedFunc = nullptr);
float CalcQueryImportance(const FPerceptionListener& Listener, const FVector& TargetLocation, const float SightRadiusSq) const;
bool RegisterNewQuery(const FPerceptionListener& Listener, const IGenericTeamAgentInterface* ListenersTeamAgent, const AActor& TargetActor, const FAISightTargetVR::FTargetId& TargetId, const FVector& TargetLocation, const FDigestedSightProperties& PropDigest, const TFunction<void(FAISightQueryVR&)>& OnAddedFunc);
// Deprecated methods
public:
UE_DEPRECATED(4.25, "Not needed anymore done automatically at the beginning of each update.")
FORCEINLINE void SortQueries() {}
enum FQueriesOperationPostProcess
{
DontSort,
Sort
};
};

View file

@ -0,0 +1,106 @@
#pragma once
#include "PhysicsEngine/PhysicalAnimationComponent.h"
#include "CoreMinimal.h"
//#include "UObject/ObjectMacros.h"
#include "Components/ActorComponent.h"
#include "EngineDefines.h"
#if ENABLE_DRAW_DEBUG
#include "Chaos/DebugDrawQueue.h"
#endif
#include "VREPhysicalAnimationComponent.generated.h"
struct FReferenceSkeleton;
USTRUCT()
struct VREXPANSIONPLUGIN_API FWeldedBoneDriverData
{
GENERATED_BODY()
public:
FTransform RelativeTransform;
FName BoneName;
//FPhysicsShapeHandle ShapeHandle;
FTransform LastLocal;
FWeldedBoneDriverData() :
RelativeTransform(FTransform::Identity),
BoneName(NAME_None)
{
}
/*FORCEINLINE bool operator==(const FPhysicsShapeHandle& Other) const
{
return (ShapeHandle == Other);
}*/
FORCEINLINE bool operator==(const FName& Other) const
{
return (BoneName == Other);
}
};
UCLASS(meta = (BlueprintSpawnableComponent), ClassGroup = Physics)
class VREXPANSIONPLUGIN_API UVREPhysicalAnimationComponent : public UPhysicalAnimationComponent
{
GENERATED_UCLASS_BODY()
public:
/** Is the welded bone driver paused */
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = WeldedBoneDriver)
bool bIsPaused;
/** IF true then we will auto adjust the sleep settings of the body so that it won't rest during welded bone driving */
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = WeldedBoneDriver)
bool bAutoSetPhysicsSleepSensitivity;
/** The threshold multiplier to set on the body */
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = WeldedBoneDriver)
float SleepThresholdMultiplier;
/** The Base bone to use as the bone driver root */
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = WeldedBoneDriver)
TArray<FName> BaseWeldedBoneDriverNames;
UPROPERTY()
TArray<FWeldedBoneDriverData> BoneDriverMap;
// Call to setup the welded body driver, initializes all mappings and caches shape contexts
// Requires that SetSkeletalMesh be called first
UFUNCTION(BlueprintCallable, Category = PhysicalAnimation)
void SetupWeldedBoneDriver(TArray<FName> BaseBoneNames);
// Refreshes the welded bone driver, use this in cases where the body may have changed (such as welding to another body or switching physics)
UFUNCTION(BlueprintCallable, Category = PhysicalAnimation)
void RefreshWeldedBoneDriver();
// Sets the welded bone driver to be paused, you can also stop the component from ticking but that will also stop any physical animations going on
UFUNCTION(BlueprintCallable, Category = PhysicalAnimation)
void SetWeldedBoneDriverPaused(bool bPaused);
UFUNCTION(BlueprintPure, Category = PhysicalAnimation)
bool IsWeldedBoneDriverPaused();
void SetupWeldedBoneDriver_Implementation(bool bReInit = false);
//void OnWeldedMassUpdated(FBodyInstance* BodyInstance);
void UpdateWeldedBoneDriver(float DeltaTime);
/** If true then we will debug draw the mesh collision post shape alterations */
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = WeldedBoneDriver)
bool bDebugDrawCollision = false;
#if ENABLE_DRAW_DEBUG
void DebugDrawMesh(const TArray<Chaos::FLatentDrawCommand>& DrawCommands);
#endif
FTransform GetWorldSpaceRefBoneTransform(FReferenceSkeleton& RefSkel, int32 BoneIndex, int32 ParentBoneIndex);
FTransform GetRefPoseBoneRelativeTransform(USkeletalMeshComponent* SkeleMesh, FName BoneName, FName ParentBoneName);
//FCalculateCustomPhysics OnCalculateCustomPhysics;
//void CustomPhysics(float DeltaTime, FBodyInstance* BodyInstance);
virtual void TickComponent(float DeltaTime, enum ELevelTick TickType, FActorComponentTickFunction *ThisTickFunction) override;
};

View file

@ -0,0 +1,191 @@
#pragma once
#include "CoreMinimal.h"
#include "UObject/ObjectMacros.h"
#include "PhysicsEngine/PhysicsConstraintComponent.h"
// Delete this eventually when the physics interface is fixed
#include "Chaos/ParticleHandle.h"
#include "Chaos/KinematicGeometryParticles.h"
#include "Chaos/PBDJointConstraintTypes.h"
#include "Chaos/PBDJointConstraintData.h"
#include "Chaos/Sphere.h"
#include "PhysicsProxy/SingleParticlePhysicsProxy.h"
#include "VREPhysicsConstraintComponent.generated.h"
/**
* A custom constraint component subclass that exposes additional missing functionality from the default one
*/
UCLASS(ClassGroup = Physics, meta = (BlueprintSpawnableComponent), HideCategories = (Activation, "Components|Activation", Physics, Mobility), ShowCategories = ("Physics|Components|PhysicsConstraint", "VRE Constraint Settings"))
class VREXPANSIONPLUGIN_API UVREPhysicsConstraintComponent : public UPhysicsConstraintComponent
{
GENERATED_BODY()
public:
UFUNCTION(BlueprintCallable, Category = "VRE Physics Constraint Component")
void SetConstraintToForceBased(bool bUseForceConstraint)
{
if (!ConstraintInstance.ConstraintHandle.IsValid())
return;
if (ConstraintInstance.ConstraintHandle->IsType(Chaos::EConstraintType::JointConstraintType))
{
if (Chaos::FJointConstraint* Constraint = static_cast<Chaos::FJointConstraint*>(ConstraintInstance.ConstraintHandle.Constraint))
{
Constraint->SetLinearDriveForceMode(bUseForceConstraint ? Chaos::EJointForceMode::Force : Chaos::EJointForceMode::Acceleration);
Constraint->SetAngularDriveForceMode(bUseForceConstraint ? Chaos::EJointForceMode::Force : Chaos::EJointForceMode::Acceleration);
}
}
//#endif
}
UFUNCTION(BlueprintCallable, Category = "VRE Physics Constraint Component")
void GetConstraintReferenceFrame(EConstraintFrame::Type Frame, FTransform& RefFrame)
{
RefFrame = ConstraintInstance.GetRefFrame(Frame);
}
UFUNCTION(BlueprintCallable, Category = "VRE Physics Constraint Component")
FTransform GetLocalPose(EConstraintFrame::Type ConstraintFrame)
{
if (ConstraintInstance.IsValidConstraintInstance())
{
if (ConstraintFrame == EConstraintFrame::Frame1)
{
return FTransform(ConstraintInstance.PriAxis1, ConstraintInstance.SecAxis1, ConstraintInstance.PriAxis1 ^ ConstraintInstance.SecAxis1, ConstraintInstance.Pos1);
}
else
{
return FTransform(ConstraintInstance.PriAxis2, ConstraintInstance.SecAxis2, ConstraintInstance.PriAxis2 ^ ConstraintInstance.SecAxis2, ConstraintInstance.Pos2);
}
}
return FTransform::Identity;
}
UFUNCTION(BlueprintCallable, Category = "VRE Physics Constraint Component")
void GetGlobalPose(EConstraintFrame::Type ConstraintFrame, FTransform& GlobalPose)
{
if (ConstraintInstance.IsValidConstraintInstance())
{
GlobalPose = FPhysicsInterface::GetGlobalPose(ConstraintInstance.ConstraintHandle, ConstraintFrame);
}
else
GlobalPose = FTransform::Identity;
}
// Gets the current linear distance in world space on the joint in +/- from the initial reference frame
UFUNCTION(BlueprintPure, Category = "VRE Physics Constraint Component")
FVector GetCurrentLinearDistance(EConstraintFrame::Type FrameOfReference)
{
EConstraintFrame::Type Frame2 = FrameOfReference;
EConstraintFrame::Type Frame1 = (FrameOfReference == EConstraintFrame::Frame1) ? EConstraintFrame::Frame2 : EConstraintFrame::Frame1;
FTransform Frame1Trans = this->GetBodyTransform(Frame1);
FTransform Frame2Trans = this->GetBodyTransform(Frame2);
FTransform LocalPose = GetLocalPose(Frame1);
FTransform LocalPose2 = GetLocalPose(Frame2);
Frame1Trans.SetScale3D(FVector(1.f));
Frame1Trans = LocalPose * Frame1Trans;
FVector OffsetLoc = Frame1Trans.GetRotation().UnrotateVector(Frame1Trans.GetLocation() - Frame2Trans.GetLocation());
FVector OffsetLoc2 = LocalPose2.GetRotation().UnrotateVector(LocalPose2.GetLocation());
FVector FinalVec = OffsetLoc2 - OffsetLoc;
return FinalVec;
}
// Gets the angular offset on the constraint
UFUNCTION(BlueprintPure, Category = "VRE Physics Constraint Component")
FRotator GetAngularOffset()
{
return ConstraintInstance.AngularRotationOffset;
}
// Sets the angular offset on the constraint and re-initializes it
UFUNCTION(BlueprintCallable, Category="VRE Physics Constraint Component")
void SetAngularOffset(FRotator NewAngularOffset)
{
// If the constraint is broken then there is no reason to do everything below
// Just early out of it.
if (!ConstraintInstance.IsValidConstraintInstance() || ConstraintInstance.IsBroken())
{
ConstraintInstance.AngularRotationOffset = NewAngularOffset;
return;
}
// I could remove a full step if I calc delta in Frame2 local and then apply to the new
// Values. However I am keeping it like this for now, would require an extra inverse / relative calc, this may not even be slower
FVector RefPos = ConstraintInstance.Pos2;
const float RefScale = FMath::Max(GetConstraintScale(), 0.01f);
if (GetBodyInstance(EConstraintFrame::Frame2))
{
RefPos *= RefScale;
}
FQuat AngRotOffset = ConstraintInstance.AngularRotationOffset.Quaternion();
FQuat newAngRotOffset = NewAngularOffset.Quaternion();
FTransform A2Transform = GetBodyTransform(EConstraintFrame::Frame2);
A2Transform.RemoveScaling();
FTransform CurrentLocalFrame(ConstraintInstance.PriAxis2, ConstraintInstance.SecAxis2, ConstraintInstance.PriAxis2 ^ ConstraintInstance.SecAxis2, ConstraintInstance.Pos2);
FTransform WorldLocalFrame = (CurrentLocalFrame * A2Transform);
FVector WPri21 = GetComponentTransform().TransformVectorNoScale(AngRotOffset.GetForwardVector());
FVector WOrth21 = GetComponentTransform().TransformVectorNoScale(AngRotOffset.GetRightVector());
FTransform OriginalRotOffset(WPri21, WOrth21, WPri21 ^ WOrth21, FVector::ZeroVector);
FQuat DeltaRot = WorldLocalFrame.GetRotation() * OriginalRotOffset.GetRotation().Inverse();
DeltaRot.Normalize();
FVector WPri2 = GetComponentTransform().TransformVectorNoScale(newAngRotOffset.GetForwardVector());
FVector WOrth2 = GetComponentTransform().TransformVectorNoScale(newAngRotOffset.GetRightVector());
WPri2 = DeltaRot.RotateVector(WPri2);
WOrth2 = DeltaRot.RotateVector(WOrth2);
ConstraintInstance.PriAxis2 = A2Transform.InverseTransformVectorNoScale(WPri2);
ConstraintInstance.SecAxis2 = A2Transform.InverseTransformVectorNoScale(WOrth2);
ConstraintInstance.AngularRotationOffset = NewAngularOffset;
FPhysicsInterface::ExecuteOnUnbrokenConstraintReadWrite(ConstraintInstance.ConstraintHandle, [&](const FPhysicsConstraintHandle& InUnbrokenConstraint)
{
FTransform URefTransform = FTransform(ConstraintInstance.PriAxis2, ConstraintInstance.SecAxis2, ConstraintInstance.PriAxis2 ^ ConstraintInstance.SecAxis2, RefPos);
FPhysicsInterface::SetLocalPose(InUnbrokenConstraint, URefTransform, EConstraintFrame::Frame2);
});
return;
}
UFUNCTION(BlueprintPure, Category = "VRE Physics Constraint Component")
void GetLinearLimits(float& LinearLimit)
{
LinearLimit = ConstraintInstance.GetLinearLimit();
}
UFUNCTION(BlueprintPure, Category = "VRE Physics Constraint Component")
void GetAngularLimits(float &Swing1Limit, float &Swing2Limit, float &TwistLimit)
{
Swing1Limit = ConstraintInstance.GetAngularSwing1Limit();
Swing2Limit = ConstraintInstance.GetAngularSwing2Limit();
TwistLimit = ConstraintInstance.GetAngularTwistLimit();
}
//UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "VRE Constraint Settings")
//bool bSetAndMaintainCOMOnFrame2;
//UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "VRE Constraint Settings")
// bool bUseForceConstraint;
};

View file

@ -0,0 +1,451 @@
// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "Blueprint/UserWidget.h"
#include "GameFramework/Info.h"
#include "Components/WidgetComponent.h"
#include "ISpectatorScreenController.h"
#include "Templates/NonNullPointer.h"
//#include "CompositingElement.h"
#include "VRFullScreenUserWidget.generated.h"
class FSceneViewport;
class FWidgetRenderer;
class FVRWidgetPostProcessHitTester;
class SConstraintCanvas;
class SVirtualWindow;
class SViewport;
class SWidget;
class ULevel;
class UMaterialInstanceDynamic;
class UMaterialInterface;
class UPostProcessComponent;
class UTextureRenderTarget2D;
class UWorld;
#if WITH_EDITOR
class SLevelViewport;
#endif
UENUM(BlueprintType)
enum class EVRWidgetDisplayType : uint8
{
/** Do not display. */
Inactive,
/** Display on a game viewport. */
Viewport,
/** Display as a post process. */
PostProcess,
/** Render to a texture and send to composure. */
// Composure,
};
USTRUCT()
struct FVRFullScreenUserWidget_Viewport
{
GENERATED_BODY()
public:
//FVRFullScreenUserWidget_Viewport();
bool Display(UWorld* World, UUserWidget* Widget, TAttribute<float> InDPIScale);
void Hide(UWorld* World);
//void Tick(UWorld* World, float DeltaSeconds);
#if WITH_EDITOR
/**
* The viewport to use for displaying.
* Defaults to GetFirstActiveLevelViewport().
*/
TWeakPtr<FSceneViewport> EditorTargetViewport;
#endif
private:
/** Constraint widget that contains the widget we want to display. */
TWeakPtr<SConstraintCanvas> FullScreenCanvasWidget;
#if WITH_EDITOR
/** Level viewport the widget was added to. */
TWeakPtr<SLevelViewport> OverlayWidgetLevelViewport;
#endif
};
USTRUCT()
struct FVRFullScreenUserWidget_PostProcess
{
GENERATED_BODY()
FVRFullScreenUserWidget_PostProcess();
void SetCustomPostProcessSettingsSource(TWeakObjectPtr<UObject> InCustomPostProcessSettingsSource);
bool Display(UWorld* World, UUserWidget* Widget, bool bInRenderToTextureOnly, TAttribute<float> InDPIScale);
void Hide(UWorld* World);
void Tick(UWorld* World, float DeltaSeconds);
TSharedPtr<SVirtualWindow> VREXPANSIONPLUGIN_API GetSlateWindow() const;
private:
//bool CreatePostProcessComponent(UWorld* World);
//void ReleasePostProcessComponent();
bool CreateRenderer(UWorld* World, UUserWidget* Widget, TAttribute<float> InDPIScale);
void ReleaseRenderer();
void TickRenderer(UWorld* World, float DeltaSeconds);
FIntPoint CalculateWidgetDrawSize(UWorld* World);
bool IsTextureSizeValid(FIntPoint Size) const;
void RegisterHitTesterWithViewport(UWorld* World);
void UnRegisterHitTesterWithViewport();
TSharedPtr<SViewport> GetViewport(UWorld* World) const;
float GetDPIScaleForPostProcessHitTester(TWeakObjectPtr<UWorld> World) const;
FPostProcessSettings* GetPostProcessSettings() const;
public:
/**
* Post process material used to display the widget.
* SlateUI [Texture]
* TintColorAndOpacity [Vector]
* OpacityFromTexture [Scalar]
*/
//UPROPERTY(EditAnywhere, Category = PostProcess)
//UMaterialInterface* PostProcessMaterial;
/** Tint color and opacity for this component. */
//UPROPERTY(EditAnywhere, Category = PostProcess)
//FLinearColor PostProcessTintColorAndOpacity;
/** Sets the amount of opacity from the widget's UI texture to use when rendering the translucent or masked UI to the viewport (0.0-1.0). */
//UPROPERTY(EditAnywhere, Category = PostProcess, meta = (ClampMin = 0.0f, ClampMax = 1.0f))
//float PostProcessOpacityFromTexture;
/** The size of the rendered widget. */
UPROPERTY(EditAnywhere, Category = PostProcess, meta=(InlineEditConditionToggle))
bool bWidgetDrawSize;
/** The size of the rendered widget. */
UPROPERTY(EditAnywhere, Category = PostProcess, meta=(EditCondition= bWidgetDrawSize))
FIntPoint WidgetDrawSize;
/** Is the virtual window created to host the widget focusable? */
UPROPERTY(EditAnywhere, AdvancedDisplay, Category = PostProcess)
bool bWindowFocusable;
/** The visibility of the virtual window created to host the widget. */
UPROPERTY(EditAnywhere, AdvancedDisplay, Category = PostProcess)
EWindowVisibility WindowVisibility;
/** Register with the viewport for hardware input from the mouse and keyboard. It can and will steal focus from the viewport. */
UPROPERTY(EditAnywhere, AdvancedDisplay, Category= PostProcess)
bool bReceiveHardwareInput;
/** The background color of the render target */
UPROPERTY(EditAnywhere, AdvancedDisplay, Category = PostProcess)
FLinearColor RenderTargetBackgroundColor;
/** The blend mode for the widget. */
UPROPERTY(EditAnywhere, AdvancedDisplay, Category = PostProcess)
EWidgetBlendMode RenderTargetBlendMode;
/** List of composure layers that are expecting to use the WidgetRenderTarget. */
//UPROPERTY(EditAnywhere, Category= PostProcess)
//TArray<ACompositingElement*> ComposureLayerTargets;
/** The target to which the user widget is rendered. */
UPROPERTY(Transient)
TObjectPtr<UTextureRenderTarget2D> WidgetRenderTarget;
/** Only render to the UTextureRenderTarget2D - do not output to the final viewport. Unless DrawtoVRPreview is active */
UPROPERTY(EditAnywhere, AdvancedDisplay, Category = PostProcess)
bool bRenderToTextureOnly;
/** If we should automatically try to draw and manage this to the VR Preview */
UPROPERTY(EditAnywhere, AdvancedDisplay, Category = PostProcess)
bool bDrawToVRPreview;
// VR Spectator mode to use when active
UPROPERTY(EditAnywhere, Category = "User Interface")
ESpectatorScreenMode VRDisplayType;
// VR Spectator mode to use when not active
UPROPERTY(EditAnywhere, Category = "User Interface")
ESpectatorScreenMode PostVRDisplayType;
#if WITH_EDITOR
/**
* The viewport to use for displaying.
*
* Defaults to GetFirstActiveLevelViewport().
*/
TWeakPtr<FSceneViewport> EditorTargetViewport;
#endif
private:
/** Post process component used to add the material to the post process chain. */
//UPROPERTY(Transient)
//UPostProcessComponent* PostProcessComponent;
/** The dynamic instance of the material that the render target is attached to. */
//UPROPERTY(Transient)
//UMaterialInstanceDynamic* PostProcessMaterialInstance;
/** The slate window that contains the user widget content. */
TSharedPtr<SVirtualWindow> SlateWindow;
/** The slate viewport we are registered to. */
TWeakPtr<SViewport> ViewportWidget;
/** Helper class for drawing widgets to a render target. */
FWidgetRenderer* WidgetRenderer;
/** The size of the rendered widget */
FIntPoint CurrentWidgetDrawSize;
/** Hit tester when we want the hardware input. */
TSharedPtr<FVRWidgetPostProcessHitTester> CustomHitTestPath;
};
/**
* Will set the Widgets on a viewport either by Widgets are first rendered to a render target, then that render target is displayed in the world.
*/
UCLASS(BlueprintType, meta=(ShowOnlyInnerProperties))
class VREXPANSIONPLUGIN_API UVRFullScreenUserWidget : public UObject
{
GENERATED_BODY()
public:
UVRFullScreenUserWidget(const FObjectInitializer& ObjectInitializer);
//~ Begin UObject interface
virtual void BeginDestroy() override;
#if WITH_EDITOR
virtual void PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) override;
#endif
//~ End UObject Interface
bool ShouldDisplay(UWorld* World) const;
EVRWidgetDisplayType GetDisplayType(UWorld* World) const;
bool IsDisplayed() const;
UFUNCTION(BlueprintCallable, Category = "FullScreenWidgetComp")
bool IsDisplayRequested()
{
return bDisplayRequested;
}
UFUNCTION(BlueprintCallable, Category = "FullScreenWidgetComp")
virtual bool Display(UWorld* World);
UFUNCTION(BlueprintCallable, Category = "FullScreenWidgetComp")
virtual void Hide();
UFUNCTION(BlueprintCallable, Category = "FullScreenWidgetComp")
void SetIsHidden(bool bNewHidden)
{
if (bNewHidden)
{
Hide();
}
else
{
if (World.IsValid())
{
Display(World.Get());
}
else
{
UWorld* myWorld = this->GetWorld();
if (myWorld)
{
Display(myWorld);
}
}
}
}
virtual void Tick(float DeltaTime);
void SetDisplayTypes(EVRWidgetDisplayType InEditorDisplayType, EVRWidgetDisplayType InGameDisplayType, EVRWidgetDisplayType InPIEDisplayType);
void SetOverrideWidget(UUserWidget* InWidget);
/**
* If using EVPWidgetDisplayType::PostProcess, you can specify a custom post process settings that should be modified.
* By default, a new post process component is added to AWorldSettings.
*
* @param InCustomPostProcessSettingsSource An object containing a FPostProcessSettings UPROPERTY()
*/
//void SetCustomPostProcessSettingsSource(TWeakObjectPtr<UObject> InCustomPostProcessSettingsSource);
#if WITH_EDITOR
/**
* Sets the TargetViewport to use on both the Viewport and the PostProcess class.
*
* Overrides the viewport to use for displaying.
* Defaults to GetFirstActiveLevelViewport().
*/
void SetEditorTargetViewport(TWeakPtr<FSceneViewport> InTargetViewport);
/** Resets the TargetViewport */
void ResetEditorTargetViewport();
#endif
protected:
bool InitWidget();
void ReleaseWidget();
FVector2D FindSceneViewportSize();
float GetViewportDPIScale();
private:
void OnLevelRemovedFromWorld(ULevel* InLevel, UWorld* InWorld);
void OnWorldCleanup(UWorld* InWorld, bool bSessionEnded, bool bCleanupResources);
public:
/** The display type when the world is an editor world. */
UPROPERTY(EditAnywhere, Category = "User Interface")
EVRWidgetDisplayType EditorDisplayType;
/** The display type when the world is a game world. */
UPROPERTY(EditAnywhere, Category = "User Interface")
EVRWidgetDisplayType GameDisplayType;
/** The display type when the world is a PIE world. */
UPROPERTY(EditAnywhere, Category = "User Interface", meta = (DisplayName = "PIE Display Type"))
EVRWidgetDisplayType PIEDisplayType;
/** Behavior when the widget should be display by the slate attached to the viewport. */
UPROPERTY(EditAnywhere, Category = "Viewport", meta = (ShowOnlyInnerProperties))
FVRFullScreenUserWidget_Viewport ViewportDisplayType;
/** The class of User Widget to create and display an instance of */
UPROPERTY(EditAnywhere, Category = "User Interface")
TSubclassOf<UUserWidget> WidgetClass;
/** Behavior when the widget should be display by a post process. */
UPROPERTY(EditAnywhere, Category = "Post Process", meta = (ShowOnlyInnerProperties))
FVRFullScreenUserWidget_PostProcess PostProcessDisplayType;
// Get a pointer to the inner widget.
// Note: This should not be stored!
UFUNCTION(BlueprintCallable, Category = "FullScreenWidgetComp")
UUserWidget* GetWidget() const { return Widget; };
UFUNCTION(BlueprintCallable, Category = "FullScreenWidgetComp")
UTextureRenderTarget2D* GetPostProcessRenderTarget() const { return PostProcessDisplayType.WidgetRenderTarget; };
private:
/** The User Widget object displayed and managed by this component */
UPROPERTY(Transient, DuplicateTransient)
TObjectPtr<UUserWidget> Widget;
/** The world the widget is attached to. */
TWeakObjectPtr<UWorld> World;
/** How we currently displaying the widget. */
EVRWidgetDisplayType CurrentDisplayType;
/** The user requested the widget to be displayed. It's possible that some setting are invalid and the widget will not be displayed. */
bool bDisplayRequested;
#if WITH_EDITOR
/**
* The viewport to use for displaying.
* Defaults to GetFirstActiveLevelViewport().
*/
TWeakPtr<FSceneViewport> EditorTargetViewport;
#endif
};
/**
* Widgets are first rendered to a render target, then that render target is displayed in the world.
*/
UCLASS(Blueprintable, ClassGroup = "UserInterface", HideCategories = (Actor, Input, Movement, Collision, Rendering, "Utilities|Transformation", LOD), ShowCategories = ("Input|MouseInput", "Input|TouchInput"))
class VREXPANSIONPLUGIN_API AVRFullScreenUserWidgetActor : public AInfo
{
GENERATED_BODY()
public:
AVRFullScreenUserWidgetActor(const FObjectInitializer& ObjectInitializer);
//~ Begin AActor interface
virtual void PostInitializeComponents() override;
virtual void PostLoad() override;
virtual void PostActorCreated() override;
virtual void Destroyed() override;
virtual void BeginPlay() override;
virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override;
virtual void Tick(float DeltaSeconds) override;
/** If true the widget will be shown right away, if false you will need to set SetWidgetVisible(true) to show it */
UPROPERTY(EditAnywhere, Category = "User Interface")
bool bShowOnInit;
/** */
UPROPERTY(VisibleAnywhere, Instanced, NoClear, Category = "User Interface", meta = (ShowOnlyInnerProperties))
UVRFullScreenUserWidget* ScreenUserWidget;
UFUNCTION(BlueprintCallable, Category = "FullScreenWidgetActor")
UVRFullScreenUserWidget* GetPreviewWidgetComp();
UFUNCTION(BlueprintCallable, Category = "FullScreenWidgetActor")
void SetPIEDisplayType(EVRWidgetDisplayType NewDisplayType)
{
if (IsValid(ScreenUserWidget))
{
ScreenUserWidget->PIEDisplayType = NewDisplayType;
ScreenUserWidget->Hide();
RequestGameDisplay();
}
}
UFUNCTION(BlueprintCallable, Category = "FullScreenWidgetActor")
void SetGameDisplayType(EVRWidgetDisplayType NewDisplayType)
{
if (IsValid(ScreenUserWidget))
{
ScreenUserWidget->GameDisplayType = NewDisplayType;
ScreenUserWidget->Hide();
RequestGameDisplay();
}
}
// Set the widget to visible or not, this will be overriden by any changed to the actors hidden state
// IE: Setting actor to hidden will force this hidden as well, also setting the actor to visible will do the opposite
UFUNCTION(BlueprintCallable, Category = "FullScreenWidgetActor")
void SetWidgetVisible(bool bIsVisible);
virtual void SetActorHiddenInGame(bool bNewHidden) override
{
SetWidgetVisible(bNewHidden);
Super::SetActorHiddenInGame(bNewHidden);
}
UFUNCTION(BlueprintCallable, Category = "FullScreenWidgetActor")
UUserWidget* GetWidget();
UFUNCTION(BlueprintCallable, Category = "FullScreenWidgetActor")
UTextureRenderTarget2D* GetPostProcessRenderTarget();
#if WITH_EDITOR
virtual bool ShouldTickIfViewportsOnly() const override { return true; }
#endif //WITH_EDITOR
//~ End AActor Interface
private:
void RequestEditorDisplay();
void RequestGameDisplay();
protected:
#if WITH_EDITORONLY_DATA
/** Display requested and will be executed on the first frame because we can't call BP function in the loading phase */
bool bEditorDisplayRequested;
#endif
};

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