323 lines
11 KiB
C++
323 lines
11 KiB
C++
/**
|
|
* @file
|
|
*
|
|
* @author Mamadou Babaei <mamadou@senseglove.com>
|
|
*
|
|
* @section LICENSE
|
|
*
|
|
* (The MIT License)
|
|
*
|
|
* Copyright (c) 2020 - 2023 SenseGlove
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
* of this software and associated documentation files (the "Software"), to deal
|
|
* in the Software without restriction, including without limitation the rights
|
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
* copies of the Software, and to permit persons to whom the Software is
|
|
* furnished to do so, subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be included in all
|
|
* copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
* SOFTWARE.
|
|
*
|
|
* @section DESCRIPTION
|
|
*
|
|
*
|
|
*/
|
|
|
|
|
|
#include "SenseGlove/GameFramework/SGPlayerController.h"
|
|
|
|
#if ENGINE_MAJOR_VERSION > 5 || ( ENGINE_MAJOR_VERSION == 5 && ENGINE_MINOR_VERSION >= 2 )
|
|
#include "Engine/TimerHandle.h"
|
|
#else /* ENGINE_MAJOR_VERSION > 5 || ( ENGINE_MAJOR_VERSION == 5 && ENGINE_MINOR_VERSION >= 2 ) */
|
|
#include "Engine/EngineTypes.h"
|
|
#endif /* ENGINE_MAJOR_VERSION > 5 || ( ENGINE_MAJOR_VERSION == 5 && ENGINE_MINOR_VERSION >= 2 ) */
|
|
|
|
#include "TimerManager.h"
|
|
|
|
#include "SenseGlove/Components/SGGrabComponent.h"
|
|
#include "SenseGlove/Components/SGTouchComponent.h"
|
|
#include "SenseGlove/GameFramework/SGPawn.h"
|
|
#include "SGCore/SGBuzzCommand.h"
|
|
#include "SGCore/SGForceFeedbackCommand.h"
|
|
#include "SGCore/SGHapticGlove.h"
|
|
|
|
struct ASGPlayerController::FImpl
|
|
{
|
|
/************************
|
|
* Static methods
|
|
************************/
|
|
|
|
static int32 GetBuzzLevel(const AActor* Actor);
|
|
static float GetBuzzDuration(const AActor* ActorThumbTouching, const AActor* ActorIndexTouching,
|
|
const AActor* ActorMiddleTouching, const AActor* ActorRingTouching,
|
|
const AActor* ActorPinkyTouching);
|
|
|
|
static int32 GetForceFeedbackLevel(const AActor* Actor);
|
|
|
|
/************************
|
|
* Member variables
|
|
************************/
|
|
|
|
FTimerHandle TouchBuzzStopTimer;
|
|
|
|
/************************
|
|
* Owner object
|
|
************************/
|
|
|
|
ASGPlayerController* Owner;
|
|
|
|
/************************
|
|
* Constructor / Destructor
|
|
************************/
|
|
|
|
explicit FImpl(ASGPlayerController* InOwner);
|
|
~FImpl();
|
|
|
|
/************************
|
|
* Default copy constructor & copy assignment operator
|
|
************************/
|
|
|
|
FImpl(const FImpl& Rhs) = default;
|
|
FImpl& operator=(const FImpl& Rhs) = default;
|
|
|
|
/************************
|
|
* Methods
|
|
************************/
|
|
|
|
USGBuzzCommand* GetBuzzCommand(const AActor* ActorThumbTouching, const AActor* ActorIndexTouching,
|
|
const AActor* ActorMiddleTouching, const AActor* ActorRingTouching,
|
|
const AActor* ActorPinkyTouching) const;
|
|
USGForceFeedbackCommand* GetForceFeedbackCommand(const AActor* ActorThumbTouching, const AActor* ActorIndexTouching,
|
|
const AActor* ActorMiddleTouching, const AActor* ActorRingTouching,
|
|
const AActor* ActorPinkyTouching) const;
|
|
};
|
|
|
|
int32 ASGPlayerController::FImpl::GetBuzzLevel(const AActor* Actor)
|
|
{
|
|
if (IsValid(Actor))
|
|
{
|
|
const USGTouchComponent* TouchComponent = USGTouchComponent::GetTouchComponent(Actor);
|
|
if (TouchComponent)
|
|
{
|
|
return TouchComponent->GetBuzzLevel();
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
float ASGPlayerController::FImpl::GetBuzzDuration(const AActor* ActorThumbTouching, const AActor* ActorIndexTouching,
|
|
const AActor* ActorMiddleTouching, const AActor* ActorRingTouching,
|
|
const AActor* ActorPinkyTouching)
|
|
{
|
|
if (IsValid(ActorThumbTouching))
|
|
{
|
|
const USGTouchComponent* TouchComponent = USGTouchComponent::GetTouchComponent(ActorThumbTouching);
|
|
if (TouchComponent)
|
|
{
|
|
return TouchComponent->GetBuzzDuration();
|
|
}
|
|
}
|
|
|
|
if (IsValid(ActorIndexTouching))
|
|
{
|
|
const USGTouchComponent* TouchComponent = USGTouchComponent::GetTouchComponent(ActorIndexTouching);
|
|
if (TouchComponent)
|
|
{
|
|
return TouchComponent->GetBuzzDuration();
|
|
}
|
|
}
|
|
|
|
if (IsValid(ActorMiddleTouching))
|
|
{
|
|
const USGTouchComponent* TouchComponent = USGTouchComponent::GetTouchComponent(ActorMiddleTouching);
|
|
if (TouchComponent)
|
|
{
|
|
return TouchComponent->GetBuzzDuration();
|
|
}
|
|
}
|
|
|
|
if (IsValid(ActorRingTouching))
|
|
{
|
|
const USGTouchComponent* TouchComponent = USGTouchComponent::GetTouchComponent(ActorRingTouching);
|
|
if (TouchComponent)
|
|
{
|
|
return TouchComponent->GetBuzzDuration();
|
|
}
|
|
}
|
|
|
|
if (IsValid(ActorPinkyTouching))
|
|
{
|
|
const USGTouchComponent* TouchComponent = USGTouchComponent::GetTouchComponent(ActorPinkyTouching);
|
|
if (TouchComponent)
|
|
{
|
|
return TouchComponent->GetBuzzDuration();
|
|
}
|
|
}
|
|
|
|
return 0.0f;
|
|
}
|
|
|
|
int32 ASGPlayerController::FImpl::GetForceFeedbackLevel(const AActor* Actor)
|
|
{
|
|
if (IsValid(Actor))
|
|
{
|
|
const USGTouchComponent* TouchComponent = USGTouchComponent::GetTouchComponent(Actor);
|
|
if (TouchComponent)
|
|
{
|
|
return TouchComponent->GetForceFeedbackLevel();
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
ASGPlayerController::ASGPlayerController(const FObjectInitializer& ObjectInitializer)
|
|
: Super(ObjectInitializer),
|
|
Pimpl(TUniquePtr<FImpl, FImplDeleter>(new FImpl{this}, PimplDeleter))
|
|
{
|
|
}
|
|
|
|
void ASGPlayerController::BeginPlay()
|
|
{
|
|
Super::BeginPlay();
|
|
|
|
ASGPawn* SGPawn = Cast<ASGPawn>(GetPawn());
|
|
if (!ensureAlwaysMsgf(IsValid(SGPawn), TEXT("%s"), TEXT("ERROR: invalid SenseGlove pawn!")))
|
|
{
|
|
return;
|
|
}
|
|
|
|
SGPawn->OnGrabStateUpdated().AddWeakLambda(
|
|
this, [=](
|
|
const USGVirtualHandComponent* Hand,
|
|
AActor* ActorThumbCanGrab, const AActor* ActorIndexCanGrab,
|
|
const AActor* ActorMiddleCanGrab) -> void
|
|
{
|
|
if (!IsValid(SGPawn))
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (!IsValid(Hand))
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (SGPawn->IsGrabbing(Hand))
|
|
{
|
|
if (!IsValid(ActorThumbCanGrab) ||
|
|
(ActorIndexCanGrab != ActorThumbCanGrab || ActorMiddleCanGrab != ActorThumbCanGrab))
|
|
{
|
|
SGPawn->Release(Hand);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (SGPawn->CanGrab(Hand, ActorThumbCanGrab))
|
|
{
|
|
SGPawn->Grab(Hand, ActorThumbCanGrab);
|
|
}
|
|
}
|
|
});
|
|
|
|
SGPawn->OnTouchStateUpdated().AddWeakLambda(
|
|
this, [=](
|
|
const USGVirtualHandComponent* Hand,
|
|
const AActor* ActorThumbTouching, const AActor* ActorIndexTouching, const AActor* ActorMiddleTouching,
|
|
const AActor* ActorRingTouching, const AActor* ActorPinkyTouching) -> void
|
|
{
|
|
if (!IsValid(SGPawn))
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (!IsValid(Hand))
|
|
{
|
|
return;
|
|
}
|
|
|
|
USGHapticGlove* Glove = Hand->GetConnectedGlove();
|
|
if (!IsValid(Glove))
|
|
{
|
|
return;
|
|
}
|
|
|
|
const USGBuzzCommand* BuzzCommand{Pimpl->GetBuzzCommand(
|
|
ActorThumbTouching, ActorIndexTouching, ActorMiddleTouching, ActorRingTouching, ActorPinkyTouching)};
|
|
Glove->SendHaptics(BuzzCommand);
|
|
|
|
const float Duration = FImpl::GetBuzzDuration(
|
|
ActorThumbTouching, ActorIndexTouching, ActorMiddleTouching, ActorRingTouching, ActorPinkyTouching);
|
|
|
|
GetWorldTimerManager().ClearTimer(Pimpl->TouchBuzzStopTimer);
|
|
|
|
FTimerDelegate TouchBuzzStopHandler;
|
|
TouchBuzzStopHandler.BindLambda([=]()-> void
|
|
{
|
|
Glove->SendHaptics(USGBuzzCommand::Off(this));
|
|
});
|
|
|
|
GetWorldTimerManager().SetTimer(
|
|
Pimpl->TouchBuzzStopTimer, TouchBuzzStopHandler, Duration, false, -1.0f);
|
|
|
|
const USGForceFeedbackCommand* ForceFeedbackCommand{Pimpl->GetForceFeedbackCommand(
|
|
ActorThumbTouching, ActorIndexTouching, ActorMiddleTouching, ActorRingTouching, ActorPinkyTouching)};
|
|
Glove->SendHaptics(ForceFeedbackCommand);
|
|
});
|
|
}
|
|
|
|
ASGPlayerController::FImpl::FImpl(ASGPlayerController* InOwner)
|
|
: Owner(InOwner)
|
|
{
|
|
}
|
|
|
|
ASGPlayerController::FImpl::~FImpl() = default;
|
|
|
|
USGBuzzCommand* ASGPlayerController::FImpl::GetBuzzCommand(const AActor* ActorThumbTouching,
|
|
const AActor* ActorIndexTouching,
|
|
const AActor* ActorMiddleTouching,
|
|
const AActor* ActorRingTouching,
|
|
const AActor* ActorPinkyTouching) const
|
|
{
|
|
USGBuzzCommand* BuzzCommand = USGBuzzCommand::NewBuzzCommand(
|
|
Owner,
|
|
GetBuzzLevel(ActorThumbTouching),
|
|
GetBuzzLevel(ActorIndexTouching),
|
|
GetBuzzLevel(ActorMiddleTouching),
|
|
GetBuzzLevel(ActorRingTouching),
|
|
GetBuzzLevel(ActorPinkyTouching)
|
|
);
|
|
return BuzzCommand;
|
|
}
|
|
|
|
USGForceFeedbackCommand* ASGPlayerController::FImpl::GetForceFeedbackCommand(const AActor* ActorThumbTouching,
|
|
const AActor* ActorIndexTouching,
|
|
const AActor* ActorMiddleTouching,
|
|
const AActor* ActorRingTouching,
|
|
const AActor* ActorPinkyTouching) const
|
|
{
|
|
USGForceFeedbackCommand* ForceFeedbackCommand = USGForceFeedbackCommand::NewForceFeedbackCommand(
|
|
Owner,
|
|
GetForceFeedbackLevel(ActorThumbTouching),
|
|
GetForceFeedbackLevel(ActorIndexTouching),
|
|
GetForceFeedbackLevel(ActorMiddleTouching),
|
|
GetForceFeedbackLevel(ActorRingTouching),
|
|
GetForceFeedbackLevel(ActorPinkyTouching)
|
|
);
|
|
return ForceFeedbackCommand;
|
|
}
|
|
|
|
void ASGPlayerController::FImplDeleter::operator()(const FImpl* P) const
|
|
{
|
|
delete P;
|
|
} |