2024-05-03 11:29:17 +02:00
|
|
|
/**
|
|
|
|
|
* @file
|
|
|
|
|
*
|
|
|
|
|
* @author Mamadou Babaei <mamadou@senseglove.com>
|
|
|
|
|
*
|
|
|
|
|
* @section LICENSE
|
|
|
|
|
*
|
|
|
|
|
* (The MIT License)
|
|
|
|
|
*
|
|
|
|
|
* Copyright (c) 2020 - 2024 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 "SGTracking/SGGloveTracker.h"
|
|
|
|
|
|
|
|
|
|
#if WITH_EDITOR
|
|
|
|
|
#include "Editor.h"
|
|
|
|
|
#endif /* WITH_EDITOR */
|
|
|
|
|
#include "Engine/Engine.h"
|
|
|
|
|
#include "Engine/TimerHandle.h"
|
|
|
|
|
#include "Engine/World.h"
|
2024-05-15 03:43:20 +02:00
|
|
|
#if WITH_EDITOR
|
2024-05-03 11:29:17 +02:00
|
|
|
#include "Subsystems/UnrealEditorSubsystem.h"
|
2024-05-15 03:43:20 +02:00
|
|
|
#endif /* WITH_EDITOR */
|
2024-05-03 11:29:17 +02:00
|
|
|
#include "TimerManager.h"
|
|
|
|
|
|
|
|
|
|
#include "SGBackend/SGBackend.h"
|
|
|
|
|
#include "SGBuildHacks/SGPlatform.h"
|
|
|
|
|
#include "SGCore/SGHapticGlove.h"
|
|
|
|
|
#include "SGLog/SGLog.h"
|
|
|
|
|
#include "SGSettings/SGSettings.h"
|
|
|
|
|
#include "SGSettings/SGTrackingSettings.h"
|
|
|
|
|
|
|
|
|
|
struct USGGloveTracker::FImpl
|
|
|
|
|
{
|
|
|
|
|
/************************
|
|
|
|
|
* Static methods
|
|
|
|
|
************************/
|
|
|
|
|
|
|
|
|
|
static UWorld* GetWorld();
|
|
|
|
|
|
|
|
|
|
FORCEINLINE static float GetGloveConnectivityCheckInterval()
|
|
|
|
|
{
|
|
|
|
|
const USGSettings* Settings{USGSettings::GetInstance()};
|
|
|
|
|
ensureAlwaysMsgf(Settings, TEXT("The settings subsystem has not been initialized!"));
|
|
|
|
|
return Settings->GetTrackingSettings().GloveTrackingSettings.GloveConnectivityCheckInterval;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/************************
|
|
|
|
|
* Member variables
|
|
|
|
|
************************/
|
|
|
|
|
|
|
|
|
|
FTimerHandle GloveConnectivityCheckTimer;
|
|
|
|
|
|
|
|
|
|
/************************
|
|
|
|
|
* Owner object
|
|
|
|
|
************************/
|
|
|
|
|
|
|
|
|
|
USGGloveTracker* Owner;
|
|
|
|
|
|
|
|
|
|
/************************
|
|
|
|
|
* Constructor / Destructor
|
|
|
|
|
************************/
|
|
|
|
|
|
|
|
|
|
explicit FImpl(USGGloveTracker* InOwner);
|
|
|
|
|
~FImpl();
|
|
|
|
|
|
|
|
|
|
/************************
|
|
|
|
|
* Default copy constructor & copy assignment operator
|
|
|
|
|
************************/
|
|
|
|
|
|
|
|
|
|
FImpl(const FImpl& Rhs) = default;
|
|
|
|
|
FImpl& operator=(const FImpl& Rhs) = default;
|
|
|
|
|
|
|
|
|
|
/************************
|
|
|
|
|
* Methods
|
|
|
|
|
************************/
|
|
|
|
|
|
|
|
|
|
void UpdateGloves() const;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
UWorld* USGGloveTracker::FImpl::GetWorld()
|
|
|
|
|
{
|
|
|
|
|
UWorld* World{nullptr};
|
|
|
|
|
|
|
|
|
|
#if WITH_EDITOR
|
|
|
|
|
if (GIsEditor)
|
|
|
|
|
{
|
|
|
|
|
if (IsValid(GEditor))
|
|
|
|
|
{
|
|
|
|
|
const bool bIsPIE = GPlayInEditorID != -1;
|
|
|
|
|
if (!bIsPIE)
|
|
|
|
|
{
|
|
|
|
|
const FWorldContext* WorldContext{GEditor->GetPIEWorldContext(1)};
|
|
|
|
|
if (WorldContext)
|
|
|
|
|
{
|
|
|
|
|
World = WorldContext->World();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
const FWorldContext* WorldContext{GEditor->GetPIEWorldContext(GPlayInEditorID)};
|
|
|
|
|
if (WorldContext)
|
|
|
|
|
{
|
|
|
|
|
World = WorldContext->World();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-06-07 04:56:02 +02:00
|
|
|
/// NOTE
|
|
|
|
|
/// IsInGameThread() check is to avoid EngineScriptHelpers::CheckIfInEditorAndPIE emitting:
|
|
|
|
|
/// "You are not on the main thread."
|
|
|
|
|
/// !(GEditor->PlayWorld || GIsPlayInEditorWorld) check is to avoid
|
|
|
|
|
/// EngineScriptHelpers::CheckIfInEditorAndPIE emitting:
|
|
|
|
|
/// The Editor is currently in a play mode.
|
|
|
|
|
if (!IsValid(World) && IsInGameThread() && !(GEditor->PlayWorld || GIsPlayInEditorWorld))
|
2024-05-03 11:29:17 +02:00
|
|
|
{
|
|
|
|
|
UUnrealEditorSubsystem* UnrealEditorSubsystem{GEditor->GetEditorSubsystem<UUnrealEditorSubsystem>()};
|
|
|
|
|
if (IsValid(UnrealEditorSubsystem))
|
|
|
|
|
{
|
|
|
|
|
World = UnrealEditorSubsystem->GetEditorWorld();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!IsValid(World) && IsValid(GEngine))
|
|
|
|
|
{
|
|
|
|
|
const UGameViewportClient* Viewport{GEngine->GameViewport};
|
|
|
|
|
if (IsValid(Viewport))
|
|
|
|
|
{
|
|
|
|
|
World = Viewport->GetWorld();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
if (IsValid(GEngine))
|
|
|
|
|
{
|
|
|
|
|
World = GEngine->GetCurrentPlayWorld(nullptr);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
#else /* WITH_EDITOR */
|
|
|
|
|
if (IsValid(GEngine))
|
|
|
|
|
{
|
|
|
|
|
World = GEngine->GetCurrentPlayWorld(nullptr);
|
|
|
|
|
}
|
|
|
|
|
#endif /* WITH_EDITOR */
|
|
|
|
|
|
|
|
|
|
if (!IsValid(World) && IsValid(GEngine))
|
|
|
|
|
{
|
|
|
|
|
UEngineSubsystem* EngineSubsystem{GEngine->GetEngineSubsystem<UEngineSubsystem>()};
|
|
|
|
|
if (IsValid(EngineSubsystem))
|
|
|
|
|
{
|
|
|
|
|
World = EngineSubsystem->GetWorld();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return World;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
USGGloveTracker* USGGloveTracker::GetInstance()
|
|
|
|
|
{
|
|
|
|
|
UWorld* World{FImpl::GetWorld()};
|
|
|
|
|
USGGloveTracker* Instance{IsValid(World) ? World->GetSubsystem<USGGloveTracker>() : nullptr};
|
|
|
|
|
return Instance;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
USGGloveTracker::USGGloveTracker()
|
|
|
|
|
: Pimpl(TUniquePtr<FImpl, FImplDeleter>(new FImpl{this}, PimplDeleter)),
|
|
|
|
|
LeftGlove(nullptr),
|
|
|
|
|
RightGlove(nullptr)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool USGGloveTracker::ShouldCreateSubsystem(UObject* Outer) const
|
|
|
|
|
{
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void USGGloveTracker::Initialize(FSubsystemCollectionBase& Collection)
|
|
|
|
|
{
|
|
|
|
|
SGLOG("Initializing the SenseGlove tracking singleton with the world lifetime...");
|
|
|
|
|
|
|
|
|
|
Super::Initialize(Collection);
|
|
|
|
|
|
|
|
|
|
const UWorld* World{GetWorld()};
|
|
|
|
|
if (ensureAlwaysMsgf(World, TEXT("Invalid world!")))
|
|
|
|
|
{
|
|
|
|
|
SGLOG("Starting the glove connectivity check timer...");
|
|
|
|
|
|
|
|
|
|
const float GloveConnectivityCheckInterval = FImpl::GetGloveConnectivityCheckInterval();
|
|
|
|
|
|
|
|
|
|
FTimerDelegate GloveConnectivityCheckHandler;
|
|
|
|
|
GloveConnectivityCheckHandler.BindLambda([= SG_CAPTURE_THIS]()-> void
|
|
|
|
|
{
|
|
|
|
|
Pimpl->UpdateGloves();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
FTimerManager& TimerManager = World->GetTimerManager();
|
|
|
|
|
TimerManager.SetTimer(
|
|
|
|
|
Pimpl->GloveConnectivityCheckTimer, GloveConnectivityCheckHandler, GloveConnectivityCheckInterval,
|
|
|
|
|
true, -1.0f);
|
|
|
|
|
|
|
|
|
|
SGLOG("The glove connectivity check timer has been started successfully!");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SGLOG("The SenseGlove tracking singleton has been successfully initialized with the world lifetime!");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void USGGloveTracker::Deinitialize()
|
|
|
|
|
{
|
|
|
|
|
SGLOG("De-initializing the SenseGlove tracking singleton due to the world lifetime expiration...");
|
|
|
|
|
|
|
|
|
|
Super::Deinitialize();
|
|
|
|
|
|
|
|
|
|
const UWorld* World{GetWorld()};
|
|
|
|
|
if (ensureAlwaysMsgf(World, TEXT("Invalid world!")))
|
|
|
|
|
{
|
|
|
|
|
SGLOG("Stopping the glove connectivity check timer...");
|
|
|
|
|
|
|
|
|
|
FTimerManager& TimerManager = World->GetTimerManager();
|
|
|
|
|
TimerManager.ClearTimer(Pimpl->GloveConnectivityCheckTimer);
|
|
|
|
|
|
|
|
|
|
SGLOG("The glove connectivity check timer has been stopped successfully!");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
(void) Pimpl.Release();
|
|
|
|
|
|
|
|
|
|
SGLOG("The SenseGlove tracking singleton has been successfully de-initialized!");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
USGGloveTracker::FImpl::FImpl(USGGloveTracker* InOwner)
|
|
|
|
|
: Owner(InOwner)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
USGGloveTracker::FImpl::~FImpl() = default;
|
|
|
|
|
|
|
|
|
|
void USGGloveTracker::FImpl::UpdateGloves() const
|
|
|
|
|
{
|
|
|
|
|
if (!USGBackend::GetInstance()->IsBackendInitialized())
|
|
|
|
|
{
|
|
|
|
|
const bool bSucceeded = USGBackend::GetInstance()->InitializeBackend();
|
|
|
|
|
if (!bSucceeded)
|
|
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const bool bIsLeftGloveConnected = USGHapticGlove::GetGlove(Owner, false, Owner->LeftGlove);
|
|
|
|
|
if (!bIsLeftGloveConnected)
|
|
|
|
|
{
|
|
|
|
|
Owner->LeftGlove = nullptr;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const bool bIsRightGloveConnected = USGHapticGlove::GetGlove(Owner, true, Owner->RightGlove);
|
|
|
|
|
if (!bIsRightGloveConnected)
|
|
|
|
|
{
|
|
|
|
|
Owner->RightGlove = nullptr;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void USGGloveTracker::FImplDeleter::operator()(const FImpl* P) const
|
|
|
|
|
{
|
|
|
|
|
delete P;
|
|
|
|
|
}
|