增加多屏显示,视频播放
This commit is contained in:
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,28 @@
|
||||
{
|
||||
"FileVersion": 3,
|
||||
"Version": 1,
|
||||
"VersionName": "1.0",
|
||||
"FriendlyName": "MultiWindow",
|
||||
"Description": "",
|
||||
"Category": "Other",
|
||||
"CreatedBy": "YWT20",
|
||||
"CreatedByURL": "https://www.unrealengine.com/marketplace/en-US/profile/YWT20",
|
||||
"DocsURL": "",
|
||||
"MarketplaceURL": "com.epicgames.launcher://ue/marketplace/content/1056b6c5d31c49809416547f44545453",
|
||||
"SupportURL": "https://www.youtube.com/channel/UCWYg44Cjom34vYb1Zedt__A?view_as=subscriber",
|
||||
"EngineVersion": "5.3.0",
|
||||
"CanContainContent": true,
|
||||
"Installed": true,
|
||||
"Modules": [
|
||||
{
|
||||
"Name": "MultiWindow",
|
||||
"Type": "Runtime",
|
||||
"LoadingPhase": "PreLoadingScreen",
|
||||
"PlatformAllowList": [
|
||||
"Win64",
|
||||
"Mac",
|
||||
"Linux"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 12 KiB |
@@ -0,0 +1,59 @@
|
||||
/************************************************************************/
|
||||
/* Author: YWT20 */
|
||||
/* Expected release year : 2024 */
|
||||
/************************************************************************/
|
||||
|
||||
using UnrealBuildTool;
|
||||
|
||||
public class MultiWindow : ModuleRules
|
||||
{
|
||||
public MultiWindow(ReadOnlyTargetRules Target) : base(Target)
|
||||
{
|
||||
PCHUsage = ModuleRules.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[]
|
||||
{
|
||||
"Core",
|
||||
// ... add other public dependencies that you statically link with here ...
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
PrivateDependencyModuleNames.AddRange(
|
||||
new string[]
|
||||
{
|
||||
"CoreUObject",
|
||||
"Engine",
|
||||
"Slate",
|
||||
"SlateCore",
|
||||
"UMG",
|
||||
"InputCore",
|
||||
"ApplicationCore",
|
||||
// ... add private dependencies that you statically link with here ...
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
DynamicallyLoadedModuleNames.AddRange(
|
||||
new string[]
|
||||
{
|
||||
// ... add any modules that your module loads dynamically here ...
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
/************************************************************************/
|
||||
/* Author: YWT20 */
|
||||
/* Expected release year : 2024 */
|
||||
/************************************************************************/
|
||||
|
||||
#include "MultiWindow.h"
|
||||
|
||||
#define LOCTEXT_NAMESPACE "FMultiWindowModule"
|
||||
|
||||
void FMultiWindowModule::StartupModule()
|
||||
{
|
||||
// This code will execute after your module is loaded into memory; the exact timing is specified in the .uplugin file per-module
|
||||
|
||||
}
|
||||
|
||||
void FMultiWindowModule::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.
|
||||
|
||||
}
|
||||
|
||||
#undef LOCTEXT_NAMESPACE
|
||||
|
||||
IMPLEMENT_MODULE(FMultiWindowModule, MultiWindow)
|
||||
+242
@@ -0,0 +1,242 @@
|
||||
/************************************************************************/
|
||||
/* Author: YWT20 */
|
||||
/* Expected release year : 2024 */
|
||||
/************************************************************************/
|
||||
|
||||
|
||||
#include "MultiWindowActor.h"
|
||||
#include "Components/Widget.h"
|
||||
#include "Widgets/SWindow.h"
|
||||
#include "Widgets/Layout/SConstraintCanvas.h"
|
||||
#include "Engine.h"
|
||||
#include <SMWindow.h>
|
||||
|
||||
|
||||
AMultiWindowActor::AMultiWindowActor()
|
||||
{
|
||||
PrimaryActorTick.bCanEverTick = true;
|
||||
}
|
||||
|
||||
void AMultiWindowActor::BeginPlay()
|
||||
{
|
||||
Super::BeginPlay();
|
||||
viewPort = GEngine->GameViewport->Viewport;
|
||||
}
|
||||
|
||||
void AMultiWindowActor::EndPlay(const EEndPlayReason::Type EndPlayReason)
|
||||
{
|
||||
Super::EndPlay(EndPlayReason);
|
||||
SetWindowClose();
|
||||
}
|
||||
|
||||
void AMultiWindowActor::Tick(float DeltaTime)
|
||||
{
|
||||
Super::Tick(DeltaTime);
|
||||
UpdateContentDPI();
|
||||
|
||||
if (WindowPos != GetWindowPositoin())
|
||||
{
|
||||
WindowPos = GetWindowPositoin();
|
||||
OnWindowMoved.Broadcast(WindowPos);
|
||||
}
|
||||
|
||||
if (ForceFocusToGameViewport && !viewPort->HasFocus())
|
||||
{
|
||||
if (!timerHandle.IsValid())
|
||||
{
|
||||
GetWorldTimerManager().SetTimer(timerHandle, this, &AMultiWindowActor::CheckFocus, focusTime, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AMultiWindowActor::InitWindow(TSharedPtr<SWindow> InWindow, UUserWidget* InContentWidget)
|
||||
{
|
||||
if (IsPendingKillPending()) return;
|
||||
|
||||
window = InWindow;
|
||||
contentWidget = InContentWidget;
|
||||
window->GetOnWindowClosedEvent().AddUObject(this, &AMultiWindowActor::EventOnWindowClose);
|
||||
WindowPos = GetWindowPositoin();
|
||||
|
||||
window->GetOnWindowActivatedEvent().AddLambda([&]() {
|
||||
OnWindowActivated.Broadcast();
|
||||
});
|
||||
window->GetOnWindowDeactivatedEvent().AddLambda([&]() {
|
||||
OnWindowDeactivated.Broadcast();
|
||||
});
|
||||
}
|
||||
|
||||
void AMultiWindowActor::UpdateContentDPI()
|
||||
{
|
||||
if (bIsAutoContentDPIScale && WindowOriginSize != GetWindowSize())
|
||||
{
|
||||
FVector2D TempWindowDPI = GetWindowSize() / WindowOriginSize;
|
||||
if (TempWindowDPI != WindowDPI)
|
||||
{
|
||||
WindowDPI = TempWindowDPI;
|
||||
contentWidget->SetRenderScale(WindowDPI);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AMultiWindowActor::CheckFocus()
|
||||
{
|
||||
if (!viewPort->HasFocus())
|
||||
{
|
||||
FSlateApplication::Get().SetAllUserFocusToGameViewport();
|
||||
}
|
||||
GetWorldTimerManager().ClearTimer(timerHandle);
|
||||
}
|
||||
|
||||
void AMultiWindowActor::SetWindowShow(bool bIsShow)
|
||||
{
|
||||
if (window.IsValid())
|
||||
{
|
||||
if (bIsShow)
|
||||
{
|
||||
window->ShowWindow();
|
||||
}
|
||||
else
|
||||
{
|
||||
window->HideWindow();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AMultiWindowActor::SetWindowContentWidget(UUserWidget* ContentWidget)
|
||||
{
|
||||
if (ContentWidget && window.IsValid())
|
||||
{
|
||||
window->SetContent(ContentWidget->TakeWidget());
|
||||
contentWidget = ContentWidget;
|
||||
}
|
||||
}
|
||||
|
||||
UUserWidget* AMultiWindowActor::GetWindowContentWidget()
|
||||
{
|
||||
return contentWidget;
|
||||
}
|
||||
|
||||
void AMultiWindowActor::SetWindowSize(FVector2D WindowSize)
|
||||
{
|
||||
if (window)
|
||||
{
|
||||
window->Resize(WindowSize);
|
||||
WindowOriginSize = WindowSize;
|
||||
}
|
||||
}
|
||||
|
||||
void AMultiWindowActor::SetWindowPosition(FVector2D WindowPosition)
|
||||
{
|
||||
if (window)
|
||||
{
|
||||
window->MoveWindowTo(WindowPosition);
|
||||
}
|
||||
}
|
||||
|
||||
FVector2D AMultiWindowActor::GetWindowSize()
|
||||
{
|
||||
if (window)
|
||||
{
|
||||
FVector2D tempSize = window->GetSizeInScreen();
|
||||
FMargin tempBorderSize = window->GetWindowBorderSize(false);
|
||||
tempSize.X -= tempBorderSize.GetDesiredSize().X;
|
||||
tempSize.Y -= tempBorderSize.GetDesiredSize().Y + window->GetTitleBarSize().Get();
|
||||
return tempSize;
|
||||
}
|
||||
return FVector2D();
|
||||
}
|
||||
|
||||
FVector2D AMultiWindowActor::GetWindowPositoin()
|
||||
{
|
||||
if (window)
|
||||
{
|
||||
FVector2D tempSize = window->GetPositionInScreen();
|
||||
return tempSize;
|
||||
}
|
||||
return FVector2D();
|
||||
}
|
||||
|
||||
//FVector2D AMultiWindowActor::GetWindowDPI()
|
||||
//{
|
||||
// return WindowDPI;
|
||||
//}
|
||||
|
||||
void AMultiWindowActor::SetWindowTitle(const FString& WindowTitle)
|
||||
{
|
||||
if (window)
|
||||
{
|
||||
window->SetTitle(FText::FromString(WindowTitle));
|
||||
}
|
||||
}
|
||||
|
||||
void AMultiWindowActor::SetWindowMaximize()
|
||||
{
|
||||
if (window)
|
||||
{
|
||||
window->Maximize();
|
||||
}
|
||||
}
|
||||
|
||||
void AMultiWindowActor::SetWindowRestore()
|
||||
{
|
||||
if (window)
|
||||
{
|
||||
window->Restore();
|
||||
}
|
||||
}
|
||||
|
||||
void AMultiWindowActor::SetWindowMinimize()
|
||||
{
|
||||
if (window)
|
||||
{
|
||||
window->Minimize();
|
||||
}
|
||||
}
|
||||
|
||||
void AMultiWindowActor::SetWindowClose()
|
||||
{
|
||||
if (window)
|
||||
{
|
||||
if (OnWindowClose.IsBound())
|
||||
{
|
||||
OnWindowClose.Broadcast();
|
||||
}
|
||||
OnWindowClose.Clear();
|
||||
|
||||
window->GetOnWindowClosedEvent().RemoveAll(this);
|
||||
window->GetOnWindowActivatedEvent().RemoveAll(this);
|
||||
window->GetOnWindowDeactivatedEvent().RemoveAll(this);
|
||||
|
||||
//window->DestroyWindowImmediately();
|
||||
window->RequestDestroyWindow();
|
||||
window.Reset();
|
||||
Destroy();
|
||||
}
|
||||
}
|
||||
|
||||
FText AMultiWindowActor::GetWindowTitle()
|
||||
{
|
||||
if (window)
|
||||
{
|
||||
return window->GetTitle();
|
||||
}
|
||||
return FText();
|
||||
}
|
||||
|
||||
void AMultiWindowActor::SetDPIScaleFactor(const float Factor)
|
||||
{
|
||||
if (window)
|
||||
{
|
||||
window->SetDPIScaleFactor(Factor);
|
||||
}
|
||||
}
|
||||
|
||||
float AMultiWindowActor::GetDPIScaleFactor() const
|
||||
{
|
||||
if (window)
|
||||
{
|
||||
return window->GetDPIScaleFactor();
|
||||
}
|
||||
return 0.0f;
|
||||
}
|
||||
+171
@@ -0,0 +1,171 @@
|
||||
/************************************************************************/
|
||||
/* Author: YWT20 */
|
||||
/* Expected release year : 2024 */
|
||||
/************************************************************************/
|
||||
|
||||
#include "MultiWindowBPLibrary.h"
|
||||
#include "Engine.h"
|
||||
#include "Blueprint/UserWidget.h"
|
||||
#include "SMWindow.h"
|
||||
#include "Components/Widget.h"
|
||||
#include "Widgets/SWidget.h"
|
||||
#include "Widgets/Layout/SConstraintCanvas.h"
|
||||
#include "Framework/Application/SlateApplication.h"
|
||||
#include "Engine/GameViewportClient.h"
|
||||
#include "GenericPlatform/GenericApplication.h"
|
||||
#include "GameFramework/GameUserSettings.h"
|
||||
#include "MultiWindow.h"
|
||||
|
||||
|
||||
UMultiWindowBPLibrary::UMultiWindowBPLibrary(const FObjectInitializer& ObjectInitializer)
|
||||
: Super(ObjectInitializer)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
bool UMultiWindowBPLibrary::CreateMultiWindow(
|
||||
UObject* WorldContextObject,
|
||||
UUserWidget* ContentWidget,
|
||||
AMultiWindowActor*& MultiWindowActor,
|
||||
FVector2D WindowPositon,
|
||||
FVector2D WindowSize,
|
||||
bool bIsAutoCenter,
|
||||
bool bIsCreateTitle,
|
||||
FString WindowTitle,
|
||||
float WindowBorderMarginLeft,
|
||||
float WindowBorderMarginTop,
|
||||
float WindowBorderMarginRight,
|
||||
float WindowBorderMarginBottom,
|
||||
EWindowSizingRule WindowSizingRule,
|
||||
bool bIsAutoContentDPIScale,
|
||||
bool IsTopmostWindow,
|
||||
bool ForceFocusToGameViewport
|
||||
)
|
||||
{
|
||||
TSharedPtr<SMWindow> window = SNew(SMWindow)
|
||||
.Title(FText::FromString(WindowTitle))
|
||||
.ScreenPosition(WindowPositon)
|
||||
.ClientSize(WindowSize)
|
||||
.FocusWhenFirstShown(true)
|
||||
.HasCloseButton(true)
|
||||
.IsTopmostWindow(IsTopmostWindow)
|
||||
.SupportsMaximize(true)
|
||||
.LayoutBorder(FMargin(WindowBorderMarginLeft, WindowBorderMarginTop, WindowBorderMarginRight, WindowBorderMarginBottom))
|
||||
.SupportsMinimize(true)
|
||||
.CreateTitleBar(bIsCreateTitle)
|
||||
.AutoCenter(bIsAutoCenter ? (EAutoCenter::PrimaryWorkArea) : (EAutoCenter::None))
|
||||
.SizingRule((ESizingRule)WindowSizingRule);
|
||||
|
||||
if (!window.IsValid()) { return false; }
|
||||
|
||||
window->SetViewportSizeDrivenByWindow(true);
|
||||
FSlateApplication& slateApp = FSlateApplication::Get();
|
||||
|
||||
|
||||
TArray<TSharedRef <SWindow>> OutWindows;
|
||||
|
||||
slateApp.GetAllVisibleWindowsOrdered(OutWindows);
|
||||
|
||||
if (TSharedPtr <SWindow> MainMenu = OutWindows[0])
|
||||
{
|
||||
slateApp.AddWindowAsNativeChild(window.ToSharedRef(), MainMenu.ToSharedRef());
|
||||
}
|
||||
else
|
||||
{
|
||||
slateApp.AddWindow(window.ToSharedRef(), true);
|
||||
}
|
||||
|
||||
if (ContentWidget)
|
||||
{
|
||||
window->SetContent(ContentWidget->TakeWidget());
|
||||
}
|
||||
window->SetWindowMode(EWindowMode::Windowed);
|
||||
if (UWorld* World = GEngine->GetWorldFromContextObjectChecked(WorldContextObject)) {
|
||||
MultiWindowActor = World->SpawnActor<AMultiWindowActor>(FVector::ZeroVector, FRotator::ZeroRotator);
|
||||
MultiWindowActor->InitWindow(window, ContentWidget);
|
||||
MultiWindowActor->WindowOriginSize = WindowSize;
|
||||
MultiWindowActor->bIsAutoContentDPIScale = bIsAutoContentDPIScale;
|
||||
MultiWindowActor->ForceFocusToGameViewport = ForceFocusToGameViewport;
|
||||
window->MultiWindowActor = MultiWindowActor;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool UMultiWindowBPLibrary::CreateRelativeMultiWindow(UObject* WorldContextObject, UUserWidget* ContentWidget, AMultiWindowActor*& MultiWindowActor, int32 MonitorIndex, FVector2D WindowPositon, FVector2D WindowSize, bool bIsAutoCenter, bool bIsCreateTitle, FString WindowTitle, float WindowBorderMarginLeft, float WindowBorderMarginTop, float WindowBorderMarginRight, float WindowBorderMarginBottom, EWindowSizingRule WindowSizingRule, bool bIsAutoContentDPIScale, bool IsTopmostWindow, bool ForceFocusToGameViewport)
|
||||
{
|
||||
CreateMultiWindow(WorldContextObject, ContentWidget, MultiWindowActor, WindowPositon, WindowSize, bIsAutoCenter, bIsCreateTitle, WindowTitle, WindowBorderMarginLeft, WindowBorderMarginTop, WindowBorderMarginRight, WindowBorderMarginBottom, WindowSizingRule, bIsAutoContentDPIScale, IsTopmostWindow);
|
||||
|
||||
if (MonitorIndex < 0) return false;
|
||||
|
||||
FDisplayMetrics displayMetrics;
|
||||
displayMetrics.RebuildDisplayMetrics(displayMetrics);
|
||||
|
||||
if (!displayMetrics.MonitorInfo.IsValidIndex(MonitorIndex)) return false;
|
||||
FMonitorInfo monitorInfo = displayMetrics.MonitorInfo[MonitorIndex];
|
||||
|
||||
if (!GEngine) return false;
|
||||
|
||||
if (bIsAutoCenter)
|
||||
{
|
||||
WindowPositon = FVector2D(monitorInfo.WorkArea.Left, monitorInfo.WorkArea.Top);
|
||||
WindowPositon += FVector2D(monitorInfo.NativeWidth * 0.5f, monitorInfo.NativeHeight * 0.5f);
|
||||
WindowPositon -= WindowSize / 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
WindowPositon += FVector2D(monitorInfo.WorkArea.Left, monitorInfo.WorkArea.Top);
|
||||
}
|
||||
|
||||
|
||||
MultiWindowActor->ForceFocusToGameViewport = ForceFocusToGameViewport;
|
||||
MultiWindowActor->SetWindowPosition(WindowPositon);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool UMultiWindowBPLibrary::CreateMultiWindowByMonitor(UObject* WorldContextObject, UUserWidget* ContentWidget, AMultiWindowActor*& MultiWindowActor, int32 MonitorIndex, bool bIsCreateTitle, FString WindowTitle, bool ForceFocusToGameViewport)
|
||||
{
|
||||
CreateMultiWindow(WorldContextObject, ContentWidget, MultiWindowActor, FVector2D(), FVector2D(800.0f, 640.0f), false, bIsCreateTitle, WindowTitle, 0, 0, 0, 0, EWindowSizingRule::FixedSize, false, true);
|
||||
if (MonitorIndex < 0) return false;
|
||||
|
||||
FDisplayMetrics displayMetrics;
|
||||
displayMetrics.RebuildDisplayMetrics(displayMetrics);
|
||||
|
||||
if (!displayMetrics.MonitorInfo.IsValidIndex(MonitorIndex)) return false;
|
||||
FMonitorInfo monitorInfo = displayMetrics.MonitorInfo[MonitorIndex];
|
||||
|
||||
if (!GEngine) return false;
|
||||
|
||||
MultiWindowActor->SetWindowPosition(FVector2D(monitorInfo.WorkArea.Left, monitorInfo.WorkArea.Top));
|
||||
MultiWindowActor->ForceFocusToGameViewport = ForceFocusToGameViewport;
|
||||
MultiWindowActor->SetWindowMaximize();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int32 UMultiWindowBPLibrary::GetMonitorCount()
|
||||
{
|
||||
FDisplayMetrics displayMetrics;
|
||||
displayMetrics.RebuildDisplayMetrics(displayMetrics);
|
||||
return displayMetrics.MonitorInfo.Num();
|
||||
}
|
||||
|
||||
int32 UMultiWindowBPLibrary::GetPrimaryMonitorIndex()
|
||||
{
|
||||
FDisplayMetrics displayMetrics;
|
||||
displayMetrics.RebuildDisplayMetrics(displayMetrics);
|
||||
|
||||
for (int32 i = 0; i < displayMetrics.MonitorInfo.Num(); i++)
|
||||
{
|
||||
if (displayMetrics.MonitorInfo[i].bIsPrimary) return i;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
void UMultiWindowBPLibrary::MW_PrintToLog()
|
||||
{
|
||||
FDisplayMetrics displayMetrics;
|
||||
displayMetrics.RebuildDisplayMetrics(displayMetrics);
|
||||
displayMetrics.PrintToLog();
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
/************************************************************************/
|
||||
/* Author: YWT20 */
|
||||
/* Expected release year : 2024 */
|
||||
/************************************************************************/
|
||||
|
||||
|
||||
#include "SMWindow.h"
|
||||
#include "MultiWindowActor.h"
|
||||
|
||||
FReply SMWindow::OnKeyChar(const FGeometry& MyGeometry, const FCharacterEvent& InCharacterEvent)
|
||||
{
|
||||
if (MultiWindowActor)
|
||||
{
|
||||
return MultiWindowActor->contentWidget->OnKeyChar(MyGeometry, InCharacterEvent).NativeReply;
|
||||
}
|
||||
return FReply::Unhandled();
|
||||
}
|
||||
|
||||
FReply SMWindow::OnPreviewKeyDown(const FGeometry& MyGeometry, const FKeyEvent& InKeyEvent)
|
||||
{
|
||||
if (MultiWindowActor)
|
||||
{
|
||||
return MultiWindowActor->contentWidget->OnPreviewKeyDown(MyGeometry, InKeyEvent).NativeReply;
|
||||
}
|
||||
return FReply::Unhandled();
|
||||
}
|
||||
|
||||
FReply SMWindow::OnKeyDown(const FGeometry& MyGeometry, const FKeyEvent& InKeyEvent)
|
||||
{
|
||||
if (MultiWindowActor)
|
||||
{
|
||||
return MultiWindowActor->contentWidget->OnKeyDown(MyGeometry, InKeyEvent).NativeReply;
|
||||
}
|
||||
return FReply::Unhandled();
|
||||
}
|
||||
|
||||
FReply SMWindow::OnKeyUp(const FGeometry& MyGeometry, const FKeyEvent& InKeyEvent)
|
||||
{
|
||||
if (MultiWindowActor)
|
||||
{
|
||||
return MultiWindowActor->contentWidget->OnKeyUp(MyGeometry, InKeyEvent).NativeReply;
|
||||
}
|
||||
return FReply::Unhandled();
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
/************************************************************************/
|
||||
/* Author: YWT20 */
|
||||
/* Expected release year : 2024 */
|
||||
/************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Modules/ModuleManager.h"
|
||||
|
||||
class FMultiWindowModule : public IModuleInterface
|
||||
{
|
||||
public:
|
||||
|
||||
/** IModuleInterface implementation */
|
||||
virtual void StartupModule() override;
|
||||
virtual void ShutdownModule() override;
|
||||
};
|
||||
@@ -0,0 +1,147 @@
|
||||
/************************************************************************/
|
||||
/* Author: YWT20 */
|
||||
/* Expected release year : 2024 */
|
||||
/************************************************************************/
|
||||
|
||||
#pragma once
|
||||
#include "CoreMinimal.h"
|
||||
#include "Blueprint/UserWidget.h"
|
||||
#include "GameFramework/Actor.h"
|
||||
#include "Framework/Application/SlateApplication.h"
|
||||
#include "TimerManager.h"
|
||||
#include "MultiWindowActor.generated.h"
|
||||
|
||||
DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnWindowEvent);
|
||||
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnWindowMovedEvent, FVector2D, pos);
|
||||
|
||||
UCLASS()
|
||||
class MULTIWINDOW_API AMultiWindowActor : public AActor
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
AMultiWindowActor();
|
||||
|
||||
protected:
|
||||
virtual void BeginPlay() override;
|
||||
|
||||
virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override;
|
||||
public:
|
||||
virtual void Tick(float DeltaTime) override;
|
||||
|
||||
TSharedPtr<SWindow> window = nullptr;
|
||||
|
||||
UUserWidget* contentWidget;
|
||||
|
||||
FViewport* viewPort;
|
||||
|
||||
FTimerHandle timerHandle;
|
||||
public:
|
||||
FVector2D WindowOriginSize;
|
||||
|
||||
FVector2D WindowPos;
|
||||
|
||||
FVector2D WindowDPI;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "MultiWindow")
|
||||
float focusTime = 0.25f;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "MultiWindow")
|
||||
bool ForceFocusToGameViewport;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "MultiWindow")
|
||||
bool bIsAutoContentDPIScale;
|
||||
|
||||
// Invoked when the window is about to be closed.
|
||||
UPROPERTY(BlueprintAssignable, Category = "MultiWindow|Event")
|
||||
FOnWindowEvent OnWindowClose;
|
||||
|
||||
// Invoked when the window has been activated.
|
||||
UPROPERTY(BlueprintAssignable, Category = "MultiWindow|Event")
|
||||
FOnWindowEvent OnWindowActivated;
|
||||
|
||||
// Invoked when the window has been deactivated.
|
||||
UPROPERTY(BlueprintAssignable, Category = "MultiWindow|Event")
|
||||
FOnWindowEvent OnWindowDeactivated;
|
||||
|
||||
UPROPERTY(BlueprintAssignable, Category = "MultiWindow|Event")
|
||||
FOnWindowMovedEvent OnWindowMoved;
|
||||
|
||||
public:
|
||||
void EventOnWindowClose(const TSharedRef<SWindow>& InSWindow)
|
||||
{
|
||||
if (IsPendingKillPending()) return;
|
||||
if (OnWindowClose.IsBound())
|
||||
{
|
||||
OnWindowClose.Broadcast();
|
||||
}
|
||||
};
|
||||
|
||||
public:
|
||||
// Initialize multi-window data
|
||||
void InitWindow(TSharedPtr<SWindow> InWindow, UUserWidget* InContentWidget);
|
||||
|
||||
void UpdateContentDPI();
|
||||
|
||||
void CheckFocus();
|
||||
|
||||
// Set the window visible
|
||||
UFUNCTION(BlueprintCallable, Category = "MultiWindow")
|
||||
void SetWindowShow(bool bIsShow);
|
||||
|
||||
// Set the widget content for this window
|
||||
UFUNCTION(BlueprintCallable, Category = "MultiWindow")
|
||||
void SetWindowContentWidget(UUserWidget* ContentWidget);
|
||||
|
||||
// Set the widget content for this window
|
||||
UFUNCTION(BlueprintPure, Category = "MultiWindow")
|
||||
UUserWidget* GetWindowContentWidget();
|
||||
|
||||
// Resize the window to be dpi scaled NewClientSize immediately
|
||||
UFUNCTION(BlueprintCallable, Category = "MultiWindow")
|
||||
void SetWindowSize(FVector2D WindowSize);
|
||||
|
||||
UFUNCTION(BlueprintPure, Category = "MultiWindow")
|
||||
FVector2D GetWindowSize();
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "MultiWindow")
|
||||
void SetWindowPosition(FVector2D WindowPosition);
|
||||
|
||||
UFUNCTION(BlueprintPure, Category = "MultiWindow")
|
||||
FVector2D GetWindowPositoin();
|
||||
|
||||
//UFUNCTION(BlueprintPure, Category = "MultiWindow")
|
||||
// FVector2D GetWindowDPI();
|
||||
|
||||
// Sets the current window title
|
||||
UFUNCTION(BlueprintCallable, Category = "MultiWindow")
|
||||
void SetWindowTitle(const FString& WindowTitle);
|
||||
|
||||
// Maximize window size
|
||||
UFUNCTION(BlueprintCallable, Category = "MultiWindow")
|
||||
void SetWindowMaximize();
|
||||
|
||||
// Restore window size
|
||||
UFUNCTION(BlueprintCallable, Category = "MultiWindow")
|
||||
void SetWindowRestore();
|
||||
|
||||
// Minimize window size
|
||||
UFUNCTION(BlueprintCallable, Category = "MultiWindow")
|
||||
void SetWindowMinimize();
|
||||
|
||||
// Close the window
|
||||
UFUNCTION(BlueprintCallable, Category = "MultiWindow")
|
||||
void SetWindowClose();
|
||||
|
||||
// Grabs the current window title
|
||||
UFUNCTION(BlueprintPure, Category = "MultiWindow | V2")
|
||||
FText GetWindowTitle();
|
||||
|
||||
// Overrides the DPI scale factor of the native window
|
||||
UFUNCTION(BlueprintCallable, Category = "MultiWindow | V2")
|
||||
void SetDPIScaleFactor(const float Factor);
|
||||
|
||||
// Returns the DPI scale factor of the native window
|
||||
UFUNCTION(BlueprintPure, Category = "MultiWindow | V2")
|
||||
float GetDPIScaleFactor() const;
|
||||
};
|
||||
+96
@@ -0,0 +1,96 @@
|
||||
/************************************************************************/
|
||||
/* Author: YWT20 */
|
||||
/* Expected release year : 2024 */
|
||||
/************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Kismet/BlueprintFunctionLibrary.h"
|
||||
#include "MultiWindowActor.h"
|
||||
#include "MultiWindowBPLibrary.generated.h"
|
||||
|
||||
UENUM(BlueprintType)
|
||||
enum class EWindowSizingRule : uint8
|
||||
{
|
||||
/* The windows size fixed and cannot be resized **/
|
||||
FixedSize,
|
||||
|
||||
/** The window size is computed from its content and cannot be resized by users */
|
||||
Autosized,
|
||||
|
||||
/** The window can be resized by users */
|
||||
UserSized,
|
||||
};
|
||||
|
||||
UCLASS()
|
||||
class UMultiWindowBPLibrary : public UBlueprintFunctionLibrary
|
||||
{
|
||||
GENERATED_UCLASS_BODY()
|
||||
public:
|
||||
// Create new window
|
||||
UFUNCTION(BlueprintCallable, Category = "MultiWindow | V1", meta = (WorldContext = "WorldContextObject", AdvancedDisplay = "5"))
|
||||
static bool CreateMultiWindow(
|
||||
UObject* WorldContextObject,
|
||||
UUserWidget* ContentWidget,
|
||||
AMultiWindowActor*& MultiWindowActor,
|
||||
FVector2D WindowPositon = FVector2D(0, 0),
|
||||
FVector2D WindowSize = FVector2D(500, 500),
|
||||
bool bIsAutoCenter = false,
|
||||
bool bIsCreateTitle = true,
|
||||
FString WindowTitle = FString(TEXT("MultiWindow")),
|
||||
float WindowBorderMarginLeft = 5.0f,
|
||||
float WindowBorderMarginTop = 5.0f,
|
||||
float WindowBorderMarginRight = 5.0f,
|
||||
float WindowBorderMarginBottom = 5.0f,
|
||||
EWindowSizingRule WindowSizingRule = EWindowSizingRule::UserSized,
|
||||
bool bIsAutoContentDPIScale = false,
|
||||
bool IsTopmostWindow = false,
|
||||
bool ForceFocusToGameViewport = false
|
||||
);
|
||||
|
||||
// Create new relative window
|
||||
UFUNCTION(BlueprintCallable, Category = "MultiWindow | V2", meta = (WorldContext = "WorldContextObject", AdvancedDisplay = "5"))
|
||||
static bool CreateRelativeMultiWindow(
|
||||
UObject* WorldContextObject,
|
||||
UUserWidget* ContentWidget,
|
||||
AMultiWindowActor*& MultiWindowActor,
|
||||
int32 MonitorIndex,
|
||||
FVector2D WindowPositon = FVector2D(0, 0),
|
||||
FVector2D WindowSize = FVector2D(500, 500),
|
||||
bool bIsAutoCenter = false,
|
||||
bool bIsCreateTitle = true,
|
||||
FString WindowTitle = FString(TEXT("MultiWindow")),
|
||||
float WindowBorderMarginLeft = 5.0f,
|
||||
float WindowBorderMarginTop = 5.0f,
|
||||
float WindowBorderMarginRight = 5.0f,
|
||||
float WindowBorderMarginBottom = 5.0f,
|
||||
EWindowSizingRule WindowSizingRule = EWindowSizingRule::UserSized,
|
||||
bool bIsAutoContentDPIScale = false,
|
||||
bool IsTopmostWindow = false,
|
||||
bool ForceFocusToGameViewport = false
|
||||
);
|
||||
|
||||
// Create new window by Monitor
|
||||
UFUNCTION(BlueprintCallable, Category = "MultiWindow | V2", meta = (WorldContext = "WorldContextObject"))
|
||||
static bool CreateMultiWindowByMonitor(
|
||||
UObject* WorldContextObject,
|
||||
UUserWidget* ContentWidget,
|
||||
AMultiWindowActor*& MultiWindowActor,
|
||||
int32 MonitorIndex,
|
||||
bool bIsCreateTitle = false,
|
||||
FString WindowTitle = FString(TEXT("MultiWindow")),
|
||||
bool ForceFocusToGameViewport = false
|
||||
);
|
||||
|
||||
// Get monitor count
|
||||
UFUNCTION(BlueprintPure, Category = "MultiWindow | V2")
|
||||
static int32 GetMonitorCount();
|
||||
|
||||
// Get primary monitor index
|
||||
UFUNCTION(BlueprintPure, Category = "MultiWindow | V2")
|
||||
static int32 GetPrimaryMonitorIndex();
|
||||
|
||||
// Logs out display metrics
|
||||
UFUNCTION(BlueprintCallable, Category = "MultiWindow | V2")
|
||||
static void MW_PrintToLog();
|
||||
};
|
||||
@@ -0,0 +1,27 @@
|
||||
/************************************************************************/
|
||||
/* Author: YWT20 */
|
||||
/* Expected release year : 2024 */
|
||||
/************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Framework/Application/SlateApplication.h"
|
||||
#include "Widgets/SWindow.h"
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
class MULTIWINDOW_API SMWindow : public SWindow
|
||||
{
|
||||
public:
|
||||
class AMultiWindowActor* MultiWindowActor;
|
||||
public:
|
||||
virtual FReply OnKeyChar(const FGeometry& MyGeometry, const FCharacterEvent& InCharacterEvent) override;
|
||||
|
||||
virtual FReply OnPreviewKeyDown(const FGeometry& MyGeometry, const FKeyEvent& InKeyEvent) override;
|
||||
|
||||
virtual FReply OnKeyDown(const FGeometry& MyGeometry, const FKeyEvent& InKeyEvent) override;
|
||||
|
||||
virtual FReply OnKeyUp(const FGeometry& MyGeometry, const FKeyEvent& InKeyEvent) override;
|
||||
};
|
||||
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"FileVersion": 3,
|
||||
"Version": 2,
|
||||
"VersionName": "1.2",
|
||||
"FriendlyName": "NeoKinect",
|
||||
"Description": "Allow Blueprints to use the Kinect v2 sensor capabilities.",
|
||||
"Category": "Input Devices",
|
||||
"CreatedBy": "Rodrigo Villani",
|
||||
"CreatedByURL": "http://rvillani.com",
|
||||
"DocsURL": "http://files.rvillani.com/neokinect/NeoKinect-QuickStart.pdf",
|
||||
"MarketplaceURL": "com.epicgames.launcher://ue/marketplace/content/130cfe979a6e4159b634d4b1d23e60da",
|
||||
"SupportURL": "mailto:contact@rvillani.com",
|
||||
"EngineVersion": "5.3.0",
|
||||
"CanContainContent": false,
|
||||
"Installed": true,
|
||||
"Modules": [
|
||||
{
|
||||
"Name": "NeoKinectUnreal",
|
||||
"Type": "Runtime",
|
||||
"LoadingPhase": "Default",
|
||||
"PlatformAllowList": [
|
||||
"Win64"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 9.6 KiB |
+68
@@ -0,0 +1,68 @@
|
||||
// Copyright 2015-2023 Rodrigo Villani Pereira
|
||||
|
||||
using System.IO;
|
||||
using UnrealBuildTool;
|
||||
|
||||
public class NeoKinectUnreal : ModuleRules
|
||||
{
|
||||
public NeoKinectUnreal(ReadOnlyTargetRules Target) : base(Target)
|
||||
{
|
||||
PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs;
|
||||
|
||||
PrivateIncludePaths.AddRange(new[]
|
||||
{
|
||||
"NeoKinectUnreal/Private"
|
||||
// ... add other private include paths required here ...
|
||||
});
|
||||
|
||||
PublicDependencyModuleNames.AddRange(new[]
|
||||
{
|
||||
"Core",
|
||||
"CoreUObject",
|
||||
// ... add other public dependencies that you statically link with here ...
|
||||
});
|
||||
|
||||
PrivateDependencyModuleNames.AddRange(new[]
|
||||
{
|
||||
"Engine",
|
||||
"RenderCore",
|
||||
"RHI",
|
||||
// ... add private dependencies that you statically link with here ...
|
||||
});
|
||||
|
||||
string thirdPartyPath = Path.GetFullPath(Path.Combine(ModuleDirectory, "../ThirdParty/"));
|
||||
|
||||
string neoKinectPath = Path.Combine(thirdPartyPath, "NeoKinect");
|
||||
string kinectPath = Path.Combine(thirdPartyPath, "Kinect");
|
||||
|
||||
PublicSystemLibraryPaths.Add(Path.Combine(neoKinectPath, "Lib"));
|
||||
PublicIncludePaths.Add(Path.Combine(neoKinectPath, "Inc"));
|
||||
PublicSystemLibraryPaths.Add(Path.Combine(kinectPath, "Lib"));
|
||||
PublicIncludePaths.Add(Path.Combine(kinectPath, "Inc"));
|
||||
|
||||
const string FaceLibraryName = "Kinect20.Face";
|
||||
|
||||
// Add static libraries
|
||||
PublicSystemLibraries.AddRange(new[]
|
||||
{
|
||||
"NeoKinect.lib",
|
||||
"Kinect20.lib",
|
||||
FaceLibraryName + ".lib"
|
||||
});
|
||||
|
||||
// Add dynamic libraries
|
||||
const string KinectFaceDllName = FaceLibraryName + ".dll";
|
||||
PublicDelayLoadDLLs.AddRange(new[]
|
||||
{
|
||||
KinectFaceDllName
|
||||
});
|
||||
|
||||
// Define library name for code to be able to check for its presence
|
||||
PrivateDefinitions.Add("KINECT_FACE_DLL_NAME=\"" + KinectFaceDllName + "\"");
|
||||
|
||||
// Stage face library files when packaging
|
||||
const string ProjectDLLDir = "$(ProjectDir)/Binaries/Win64";
|
||||
RuntimeDependencies.Add(Path.Combine(ProjectDLLDir, KinectFaceDllName));
|
||||
RuntimeDependencies.Add(Path.Combine(ProjectDLLDir, "NuiDatabase/..."), StagedFileType.NonUFS);
|
||||
}
|
||||
}
|
||||
+329
@@ -0,0 +1,329 @@
|
||||
// Copyright 2015-2023 Rodrigo Villani Pereira
|
||||
|
||||
#include "KinectThread.h"
|
||||
|
||||
#include "HAL/RunnableThread.h"
|
||||
|
||||
/*-----------------------------------------------------------------------------
|
||||
ThreadFrameBuffer
|
||||
-----------------------------------------------------------------------------*/
|
||||
|
||||
using namespace NeoKinect;
|
||||
|
||||
FThreadFrameBuffer::~FThreadFrameBuffer()
|
||||
{
|
||||
DeallocateBuffers();
|
||||
}
|
||||
|
||||
void FThreadFrameBuffer::Configure(
|
||||
KinectFrameType Frame,
|
||||
KinectPixelFormat Format,
|
||||
KinectCoordinateSpace Space,
|
||||
bool IsRemapped)
|
||||
{
|
||||
this->FrameType = Frame;
|
||||
this->PixelFormat = Format;
|
||||
this->TargetSpace = Space;
|
||||
this->bRemapped = IsRemapped;
|
||||
}
|
||||
|
||||
bool FThreadFrameBuffer::AllocateBuffers(uint32 Size)
|
||||
{
|
||||
TRACE_CPUPROFILER_EVENT_SCOPE_TEXT(*FString::Printf(TEXT("Kinect_AllocateBuffers_%d"), FrameType));
|
||||
|
||||
if (IsAllocated())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
Buffer = new uint8[Size];
|
||||
BackBuffer = new uint8[Size];
|
||||
return Buffer && BackBuffer;
|
||||
}
|
||||
|
||||
void FThreadFrameBuffer::DeallocateBuffers()
|
||||
{
|
||||
TRACE_CPUPROFILER_EVENT_SCOPE_TEXT(*FString::Printf(TEXT("Kinect_DeallocateBuffers_%d"), FrameType));
|
||||
|
||||
bIsNew = false;
|
||||
|
||||
if (Buffer)
|
||||
{
|
||||
Lock();
|
||||
delete[] Buffer;
|
||||
Buffer = nullptr;
|
||||
Unlock();
|
||||
}
|
||||
|
||||
if (BackBuffer)
|
||||
{
|
||||
LockWrite();
|
||||
delete[] BackBuffer;
|
||||
BackBuffer = nullptr;
|
||||
UnlockWrite();
|
||||
}
|
||||
}
|
||||
|
||||
bool FThreadFrameBuffer::IsAllocated() const
|
||||
{
|
||||
return Buffer && BackBuffer;
|
||||
}
|
||||
|
||||
const uint8* FThreadFrameBuffer::GetReadBuffer(bool bConsume /*= true*/)
|
||||
{
|
||||
if (bConsume)
|
||||
{
|
||||
if (Buffer && bIsNew)
|
||||
{
|
||||
bIsNew = false;
|
||||
return Buffer;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return Buffer;
|
||||
}
|
||||
|
||||
uint8* FThreadFrameBuffer::GetWriteBuffer() const
|
||||
{
|
||||
return BackBuffer;
|
||||
}
|
||||
|
||||
void FThreadFrameBuffer::SetDirty()
|
||||
{
|
||||
SwapBuffers();
|
||||
bIsNew = true;
|
||||
}
|
||||
|
||||
void FThreadFrameBuffer::SwapBuffers()
|
||||
{
|
||||
TRACE_CPUPROFILER_EVENT_SCOPE_TEXT(*FString::Printf(TEXT("Kinect_SwapBuffers_%d"), FrameType));
|
||||
|
||||
Lock();
|
||||
|
||||
uint8* Temp = Buffer;
|
||||
Buffer = BackBuffer;
|
||||
BackBuffer = Temp;
|
||||
|
||||
Unlock();
|
||||
}
|
||||
|
||||
bool FThreadFrameBuffer::TryLock()
|
||||
{
|
||||
return ReadMutex.TryLock();
|
||||
}
|
||||
|
||||
void FThreadFrameBuffer::Lock()
|
||||
{
|
||||
ReadMutex.Lock();
|
||||
}
|
||||
|
||||
void FThreadFrameBuffer::Unlock()
|
||||
{
|
||||
ReadMutex.Unlock();
|
||||
}
|
||||
|
||||
void FThreadFrameBuffer::LockWrite()
|
||||
{
|
||||
WriteMutex.Lock();
|
||||
}
|
||||
|
||||
void FThreadFrameBuffer::UnlockWrite()
|
||||
{
|
||||
WriteMutex.Unlock();
|
||||
}
|
||||
|
||||
|
||||
/*-----------------------------------------------------------------------------
|
||||
KinectThread
|
||||
-----------------------------------------------------------------------------*/
|
||||
|
||||
FKinectThread* FKinectThread::Runnable = nullptr;
|
||||
FCriticalSection FKinectThread::BodiesFacesCritSec;
|
||||
|
||||
FThreadFrameBuffer FKinectThread::FrameBuffers[static_cast<uint64>(EKinectFrame::Count)];
|
||||
bool FKinectThread::bUseBodyTracking = false;
|
||||
bool FKinectThread::bUseFaceTracking = false;
|
||||
|
||||
FKinectThread::FKinectThread(KinectSensor *pSensor) :
|
||||
pKinect(pSensor),
|
||||
bBodiesDirty(false),
|
||||
bFacesDirty(false)
|
||||
{
|
||||
Thread = FRunnableThread::Create(this, TEXT("FKinectThread")); // Windows give 8MB memory by default
|
||||
if (!Thread)
|
||||
UE_LOG(KinectThreadLog, Error, TEXT("Failed to create Kinect thread!"));
|
||||
}
|
||||
|
||||
FKinectThread::~FKinectThread()
|
||||
{
|
||||
if (Thread) delete Thread;
|
||||
Thread = nullptr;
|
||||
}
|
||||
|
||||
FKinectThread* FKinectThread::InitKinectPolling(KinectSensor *pSensor)
|
||||
{
|
||||
// Create new instance of thread if it does not exist and the platform
|
||||
// supports multi threading.
|
||||
if (!Runnable && FPlatformProcess::SupportsMultithreading())
|
||||
{
|
||||
Runnable = new FKinectThread(pSensor);
|
||||
}
|
||||
return Runnable;
|
||||
}
|
||||
|
||||
void FKinectThread::EnsureCompletion()
|
||||
{
|
||||
TRACE_CPUPROFILER_EVENT_SCOPE(Kinect_EnsureThreadCompletion)
|
||||
|
||||
Stop();
|
||||
if (Thread)
|
||||
Thread->WaitForCompletion();
|
||||
}
|
||||
|
||||
void FKinectThread::Shutdown()
|
||||
{
|
||||
TRACE_CPUPROFILER_EVENT_SCOPE(Kinect_ShutdownThread)
|
||||
|
||||
if (Runnable)
|
||||
{
|
||||
Runnable->EnsureCompletion();
|
||||
delete Runnable;
|
||||
Runnable = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
const KinectBody* FKinectThread::GetBodiesData()
|
||||
{
|
||||
if (bBodiesDirty)
|
||||
{
|
||||
bBodiesDirty = false;
|
||||
return Bodies;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const KinectFace* FKinectThread::GetFacesData()
|
||||
{
|
||||
if (bFacesDirty)
|
||||
{
|
||||
bFacesDirty = false;
|
||||
return Faces;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool FKinectThread::Init()
|
||||
{
|
||||
UE_LOG(KinectThreadLog, Log, TEXT("Kinect thread initialized."));
|
||||
return true;
|
||||
}
|
||||
|
||||
uint32 FKinectThread::Run()
|
||||
{
|
||||
constexpr float UpdateInterval = 1.f / cDesiredFPS;
|
||||
|
||||
UE_LOG(KinectThreadLog, Log, TEXT("Kinect thread running."));
|
||||
|
||||
// while not told to stop this thread
|
||||
while (StopTaskCounter.GetValue() == 0)
|
||||
{
|
||||
// try locking any frames necessary
|
||||
constexpr int FrameTypesCount = static_cast<int>(KinectFrameType::Count);
|
||||
|
||||
TRACE_CPUPROFILER_EVENT_SCOPE(KinectThread_Run)
|
||||
|
||||
{
|
||||
TRACE_CPUPROFILER_EVENT_SCOPE(Kinect_LockFrames)
|
||||
for (int i = 0; i < FrameTypesCount; ++i)
|
||||
{
|
||||
const KinectFrameType FrameType = static_cast<KinectFrameType>(i);
|
||||
if (pKinect->GetIsUsingFrame(FrameType))
|
||||
{
|
||||
pKinect->LockLatestFrame(FrameType);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (bUseBodyTracking)
|
||||
{
|
||||
TRACE_CPUPROFILER_EVENT_SCOPE(Kinect_GetBodyData)
|
||||
// process bodies before face tracking - face tracking depends on bodies data
|
||||
BodiesFacesCritSec.Lock();
|
||||
if (pKinect->GetLatestFrameData(Bodies))
|
||||
{
|
||||
bBodiesDirty = true;
|
||||
}
|
||||
BodiesFacesCritSec.Unlock();
|
||||
}
|
||||
|
||||
if (bUseFaceTracking)
|
||||
{
|
||||
TRACE_CPUPROFILER_EVENT_SCOPE(Kinect_GetFaceData)
|
||||
// process face tracking
|
||||
BodiesFacesCritSec.Lock();
|
||||
if (pKinect->GetLatestFrameData(Faces))
|
||||
{
|
||||
bFacesDirty = true;
|
||||
}
|
||||
BodiesFacesCritSec.Unlock();
|
||||
}
|
||||
|
||||
// update any necessary buffers
|
||||
for (FThreadFrameBuffer& FrameBuffer : FrameBuffers)
|
||||
{
|
||||
TRACE_CPUPROFILER_EVENT_SCOPE_TEXT(*FString::Printf(TEXT("Kinect_UpdateBuffer_%d"), FrameBuffer.FrameType));
|
||||
if (FrameBuffer.IsAllocated() && pKinect->GetIsFrameLocked(FrameBuffer.FrameType))
|
||||
{
|
||||
FrameBuffer.LockWrite();
|
||||
// If this is a remapped type, use the correct function.
|
||||
// If the update works, marks buffer as new
|
||||
if (FrameBuffer.bRemapped)
|
||||
{
|
||||
if (pKinect->GetRemappedFrameData(
|
||||
FrameBuffer.FrameType, FrameBuffer.TargetSpace,
|
||||
FrameBuffer.GetWriteBuffer(), FrameBuffer.PixelFormat))
|
||||
{
|
||||
FrameBuffer.SetDirty();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
uint8* Buffer = FrameBuffer.GetWriteBuffer();
|
||||
if (pKinect->GetLatestFrameData(
|
||||
FrameBuffer.FrameType, Buffer,
|
||||
FrameBuffer.PixelFormat))
|
||||
{
|
||||
FrameBuffer.SetDirty();
|
||||
}
|
||||
}
|
||||
FrameBuffer.UnlockWrite();
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
TRACE_CPUPROFILER_EVENT_SCOPE(Kinect_UnlockFrames)
|
||||
// unlock frames
|
||||
for (int i = 0; i < FrameTypesCount; ++i)
|
||||
{
|
||||
pKinect->UnlockLatestFrame(static_cast<KinectFrameType>(i));
|
||||
}
|
||||
}
|
||||
|
||||
// give time for Kinect to update its frames.
|
||||
FPlatformProcess::Sleep(UpdateInterval);
|
||||
}
|
||||
|
||||
// ended successfully!
|
||||
return 0;
|
||||
}
|
||||
|
||||
void FKinectThread::Stop()
|
||||
{
|
||||
StopTaskCounter.Increment();
|
||||
}
|
||||
|
||||
void FKinectThread::Exit()
|
||||
{
|
||||
UE_LOG(KinectThreadLog, Log, TEXT("Kinect thread ended."));
|
||||
}
|
||||
+547
@@ -0,0 +1,547 @@
|
||||
// Copyright 2015-2023 Rodrigo Villani Pereira
|
||||
|
||||
#include "NeoKinectBody.h"
|
||||
|
||||
#include "Helpers/GuardedNeoKinect.h"
|
||||
|
||||
#include "NeoKinectManager.h"
|
||||
|
||||
UNeoKinectBody::UNeoKinectBody()
|
||||
: BodyIndex(0),
|
||||
TrackingId(0),
|
||||
bIsTracked(false),
|
||||
bIsRestricted(false),
|
||||
HandLeftState(EKinectHandState::NotTracked),
|
||||
HandLeftTrackingState(EKinectTrackingState::NotTracked),
|
||||
HandRightState(EKinectHandState::NotTracked),
|
||||
HandRightTrackingState(EKinectTrackingState::NotTracked),
|
||||
LeanTrackingState(EKinectTrackingState::NotTracked),
|
||||
Lean(FVector2D(0.f, 0.f))
|
||||
{
|
||||
using namespace NeoKinect;
|
||||
|
||||
Joints.SetNum(KinectJointCount);
|
||||
|
||||
for (int32 JointIdx = 0; JointIdx < KinectJointCount; ++JointIdx)
|
||||
{
|
||||
Joints[JointIdx].Type = static_cast<EKinectJointType>(JointIdx);
|
||||
}
|
||||
}
|
||||
|
||||
UNeoKinectBody::~UNeoKinectBody()
|
||||
{
|
||||
Joints.Empty();
|
||||
}
|
||||
|
||||
void UNeoKinectBody::GetHandStateAsExec(EKinectBodySide Hand, EKinectHandState& States)
|
||||
{
|
||||
if (Hand == EKinectBodySide::Left)
|
||||
{
|
||||
States = HandLeftState;
|
||||
}
|
||||
else
|
||||
{
|
||||
States = HandRightState;
|
||||
}
|
||||
}
|
||||
|
||||
EKinectHandState UNeoKinectBody::GetHandState(EKinectBodySide Hand) const
|
||||
{
|
||||
if (Hand == EKinectBodySide::Left)
|
||||
return HandLeftState;
|
||||
else
|
||||
return HandRightState;
|
||||
}
|
||||
|
||||
void UNeoKinectBody::GetHandConfidenceAsExec(EKinectBodySide Hand, EKinectTrackingState& States)
|
||||
{
|
||||
if (Hand == EKinectBodySide::Left)
|
||||
{
|
||||
States = HandLeftTrackingState;
|
||||
}
|
||||
else
|
||||
{
|
||||
States = HandRightTrackingState;
|
||||
}
|
||||
}
|
||||
|
||||
EKinectTrackingState UNeoKinectBody::GetHandConfidence(EKinectBodySide Hand) const
|
||||
{
|
||||
if (Hand == EKinectBodySide::Left)
|
||||
return HandLeftTrackingState;
|
||||
else
|
||||
return HandRightTrackingState;
|
||||
}
|
||||
|
||||
void UNeoKinectBody::GetLeanConfidenceAsExec(EKinectTrackingState& States)
|
||||
{
|
||||
States = LeanTrackingState;
|
||||
}
|
||||
|
||||
TArray<FKinectJoint> UNeoKinectBody::GetJoints()
|
||||
{
|
||||
return Joints;
|
||||
}
|
||||
|
||||
FVector UNeoKinectBody::GetJointLocation(EKinectJointType Joint)
|
||||
{
|
||||
return Joints[static_cast<int32>(Joint)].Location;
|
||||
}
|
||||
|
||||
FRotator UNeoKinectBody::GetJointOrientation(EKinectJointType Joint)
|
||||
{
|
||||
return Joints[static_cast<int32>(Joint)].Orientation;
|
||||
}
|
||||
|
||||
FVector UNeoKinectBody::GetJointLocationColor(EKinectJointType Joint)
|
||||
{
|
||||
return Joints[static_cast<int32>(Joint)].ColorLocation;
|
||||
}
|
||||
|
||||
FRotator UNeoKinectBody::GetJointOrientationColor(EKinectJointType Joint)
|
||||
{
|
||||
return Joints[static_cast<int32>(Joint)].ColorOrientation;
|
||||
}
|
||||
|
||||
void UNeoKinectBody::GetJointConfidenceAsExec(EKinectJointType Joint, EKinectTrackingState& States)
|
||||
{
|
||||
States = Joints[static_cast<int32>(Joint)].Confidence;
|
||||
}
|
||||
|
||||
EKinectTrackingState UNeoKinectBody::GetJointConfidence(EKinectJointType Joint)
|
||||
{
|
||||
return Joints[static_cast<int32>(Joint)].Confidence;
|
||||
}
|
||||
|
||||
float UNeoKinectBody::GetJointsDistance(EKinectJointType JointA, EKinectJointType JointB)
|
||||
{
|
||||
return FVector::Dist(Joints[static_cast<int32>(JointB)].Location, Joints[static_cast<int32>(JointA)].Location);
|
||||
}
|
||||
|
||||
float UNeoKinectBody::GetBoneLength(EKinectJointType Joint)
|
||||
{
|
||||
const FVector p1 = Joints[static_cast<int32>(Joint)].Location;
|
||||
switch (Joint)
|
||||
{
|
||||
case EKinectJointType::SpineBase:
|
||||
return FVector::Dist(p1, Joints[static_cast<int32>(EKinectJointType::SpineMid)].Location);
|
||||
case EKinectJointType::SpineMid:
|
||||
return FVector::Dist(p1, Joints[static_cast<int32>(EKinectJointType::SpineBase)].Location);
|
||||
case EKinectJointType::Neck:
|
||||
return FVector::Dist(p1, Joints[static_cast<int32>(EKinectJointType::SpineShoulder)].Location);
|
||||
case EKinectJointType::Head:
|
||||
return FVector::Dist(p1, Joints[static_cast<int32>(EKinectJointType::Neck)].Location);
|
||||
case EKinectJointType::ShoulderLeft:
|
||||
return FVector::Dist(p1, Joints[static_cast<int32>(EKinectJointType::SpineShoulder)].Location);
|
||||
case EKinectJointType::ElbowLeft:
|
||||
return FVector::Dist(p1, Joints[static_cast<int32>(EKinectJointType::ShoulderLeft)].Location);
|
||||
case EKinectJointType::WristLeft:
|
||||
return FVector::Dist(p1, Joints[static_cast<int32>(EKinectJointType::ElbowLeft)].Location);
|
||||
case EKinectJointType::HandLeft:
|
||||
return FVector::Dist(p1, Joints[static_cast<int32>(EKinectJointType::WristLeft)].Location);
|
||||
case EKinectJointType::ShoulderRight:
|
||||
return FVector::Dist(p1, Joints[static_cast<int32>(EKinectJointType::SpineShoulder)].Location);
|
||||
case EKinectJointType::ElbowRight:
|
||||
return FVector::Dist(p1, Joints[static_cast<int32>(EKinectJointType::ShoulderRight)].Location);
|
||||
case EKinectJointType::WristRight:
|
||||
return FVector::Dist(p1, Joints[static_cast<int32>(EKinectJointType::ElbowRight)].Location);
|
||||
case EKinectJointType::HandRight:
|
||||
return FVector::Dist(p1, Joints[static_cast<int32>(EKinectJointType::WristRight)].Location);
|
||||
case EKinectJointType::HipLeft:
|
||||
return FVector::Dist(p1, Joints[static_cast<int32>(EKinectJointType::SpineBase)].Location);
|
||||
case EKinectJointType::KneeLeft:
|
||||
return FVector::Dist(p1, Joints[static_cast<int32>(EKinectJointType::HipLeft)].Location);
|
||||
case EKinectJointType::AnkleLeft:
|
||||
return FVector::Dist(p1, Joints[static_cast<int32>(EKinectJointType::KneeLeft)].Location);
|
||||
case EKinectJointType::FootLeft:
|
||||
return FVector::Dist(p1, Joints[static_cast<int32>(EKinectJointType::AnkleLeft)].Location);
|
||||
case EKinectJointType::HipRight:
|
||||
return FVector::Dist(p1, Joints[static_cast<int32>(EKinectJointType::SpineBase)].Location);
|
||||
case EKinectJointType::KneeRight:
|
||||
return FVector::Dist(p1, Joints[static_cast<int32>(EKinectJointType::HipRight)].Location);
|
||||
case EKinectJointType::AnkleRight:
|
||||
return FVector::Dist(p1, Joints[static_cast<int32>(EKinectJointType::KneeRight)].Location);
|
||||
case EKinectJointType::FootRight:
|
||||
return FVector::Dist(p1, Joints[static_cast<int32>(EKinectJointType::AnkleRight)].Location);
|
||||
case EKinectJointType::SpineShoulder:
|
||||
return FVector::Dist(p1, Joints[static_cast<int32>(EKinectJointType::SpineMid)].Location);
|
||||
case EKinectJointType::HandTipLeft:
|
||||
return FVector::Dist(p1, Joints[static_cast<int32>(EKinectJointType::WristLeft)].Location);
|
||||
case EKinectJointType::ThumbLeft:
|
||||
return FVector::Dist(p1, Joints[static_cast<int32>(EKinectJointType::HandLeft)].Location);
|
||||
case EKinectJointType::HandTipRight:
|
||||
return FVector::Dist(p1, Joints[static_cast<int32>(EKinectJointType::WristRight)].Location);
|
||||
case EKinectJointType::ThumbRight:
|
||||
return FVector::Dist(p1, Joints[static_cast<int32>(EKinectJointType::HandRight)].Location);
|
||||
default:
|
||||
return 0.f;
|
||||
}
|
||||
}
|
||||
|
||||
float UNeoKinectBody::JointDotVector(EKinectJointType Joint, FVector Vector)
|
||||
{
|
||||
Vector.Normalize();
|
||||
return FVector::DotProduct(
|
||||
Vector,
|
||||
FRotationMatrix(Joints[static_cast<int32>(Joint)].Orientation).GetScaledAxis(EAxis::Z));
|
||||
}
|
||||
|
||||
void UNeoKinectBody::UpdateFromKinectBody(const NeoKinect::KinectBody& kBody, const NeoKinect::KinectSensor& Sensor)
|
||||
{
|
||||
using namespace NeoKinect;
|
||||
|
||||
// For throwing BeginTrack/Lost events after update
|
||||
bool bWasTracked = bIsTracked;
|
||||
|
||||
ClippedEdges.Left = kBody.clippedEdges.left;
|
||||
ClippedEdges.Right = kBody.clippedEdges.right;
|
||||
ClippedEdges.Top = kBody.clippedEdges.top;
|
||||
ClippedEdges.Bottom = kBody.clippedEdges.bottom;
|
||||
|
||||
bIsTracked = kBody.isTracked;
|
||||
bIsRestricted = kBody.isRestricted;
|
||||
TrackingId = kBody.trackingId;
|
||||
|
||||
HandLeftState = static_cast<EKinectHandState>(kBody.handLeftState);
|
||||
HandLeftTrackingState = static_cast<EKinectTrackingState>(kBody.handLeftTrackingState);
|
||||
|
||||
HandRightState = static_cast<EKinectHandState>(kBody.handRightState);
|
||||
HandRightTrackingState = static_cast<EKinectTrackingState>(kBody.handRightTrackingState);
|
||||
|
||||
LeanTrackingState = static_cast<EKinectTrackingState>(kBody.leanTrackingState);
|
||||
Lean.Set(kBody.lean.x, kBody.lean.y);
|
||||
|
||||
// update joints only if being tracked
|
||||
if (bIsTracked)
|
||||
{
|
||||
// camera space joints transforms
|
||||
for (int32 j = 0; j < KinectJointCount; ++j)
|
||||
{
|
||||
FKinectJoint& uJoint = Joints[j];
|
||||
KinectJoint joint = kBody.joints[j];
|
||||
|
||||
uJoint.Confidence = static_cast<EKinectTrackingState>(joint.trackingState);
|
||||
uJoint.Location.Set(
|
||||
joint.position.Z * 100.f,
|
||||
-joint.position.X * 100.f,
|
||||
joint.position.Y * 100.f);
|
||||
|
||||
// Quaternion conversion from Kinect camera's coord system to Unreal's
|
||||
FQuat Quat = FQuat(
|
||||
-joint.orientation.z,
|
||||
joint.orientation.x,
|
||||
-joint.orientation.y,
|
||||
joint.orientation.w
|
||||
);
|
||||
// In order to match the joints orientations to Unreal Mannequin, we
|
||||
// still have some problems:
|
||||
// - Kinect's bones extend along its UP axis, which is Z in Unreal.
|
||||
// But Unreal's Mannequin skeleton's bones extend along X (as it is
|
||||
// in Maya). So we re-create the orientation swapping the axes.
|
||||
//
|
||||
// - Some bones extend towards X, some against it. That's good for doing
|
||||
// mirrored rotations in the animation software, so we'll keep it here
|
||||
// by flipping some axes.
|
||||
//
|
||||
// - The pelvis and spine bones front axis is the same as the limbs side axis.
|
||||
// In the Mannequin these match up, so we'll have to invert some here.
|
||||
#define JNT_ID(Name) static_cast<uint8>(EKinectJointType::Name)
|
||||
|
||||
uint8 JointIndex = static_cast<uint8>(uJoint.Type);
|
||||
|
||||
if (JointIndex <= JNT_ID(Head) || JointIndex == JNT_ID(SpineShoulder))
|
||||
{ // for spine bones
|
||||
uJoint.Orientation = FRotationMatrix::MakeFromXZ(
|
||||
Quat.GetAxisZ(),
|
||||
Quat.GetAxisY()).Rotator();
|
||||
}
|
||||
else if (JointIndex <= JNT_ID(WristLeft)
|
||||
|| JointIndex >= JNT_ID(HipRight) && JointIndex <= JNT_ID(FootRight))
|
||||
{ // for left arm and right leg
|
||||
uJoint.Orientation = FRotationMatrix::MakeFromXZ(
|
||||
Quat.GetAxisZ(),
|
||||
Quat.GetAxisX()).Rotator();
|
||||
}
|
||||
else if ( (JointIndex >= JNT_ID(ShoulderRight) && JointIndex <= JNT_ID(WristRight))
|
||||
|| (JointIndex >= JNT_ID(HipLeft) && JointIndex <= JNT_ID(FootLeft))
|
||||
|| JointIndex == JNT_ID(HandTipRight) || JointIndex == JNT_ID(ThumbRight)
|
||||
)
|
||||
{ // for right arm and left leg
|
||||
uJoint.Orientation = FRotationMatrix::MakeFromXZ(
|
||||
-Quat.GetAxisZ(),
|
||||
-Quat.GetAxisX()).Rotator();
|
||||
}
|
||||
else if (JointIndex == JNT_ID(HandTipLeft) || JointIndex == JNT_ID(ThumbLeft))
|
||||
{ // Left hand
|
||||
uJoint.Orientation = FRotationMatrix::MakeFromXZ(
|
||||
Quat.GetAxisZ(),
|
||||
Quat.GetAxisX()).Rotator();
|
||||
}
|
||||
else if (JointIndex == JNT_ID(HandLeft))
|
||||
{ // left hand
|
||||
uJoint.Orientation = FRotationMatrix::MakeFromXZ(
|
||||
Quat.GetAxisZ(),
|
||||
-Quat.GetAxisX()).Rotator();
|
||||
}
|
||||
else if (JointIndex == JNT_ID(HandRight))
|
||||
{ // right hand
|
||||
uJoint.Orientation = FRotationMatrix::MakeFromXZ(
|
||||
-Quat.GetAxisZ(),
|
||||
Quat.GetAxisX()).Rotator();
|
||||
}
|
||||
#undef JNT_ID
|
||||
|
||||
} // camera space joints transforms
|
||||
|
||||
if (UNeoKinectManager::IsUsingJointsColorSpaceTransforms())
|
||||
{
|
||||
// color space with depth joints transforms
|
||||
for (int32 j = 0; j < KinectJointCount; ++j)
|
||||
{
|
||||
FKinectJoint& uJoint = Joints[j];
|
||||
KinectJoint joint = kBody.joints[j];
|
||||
|
||||
// Convert camera space location to color space with depth
|
||||
Vector3 ColorDepthLocation = UNeoKinectManager::CameraLocationToColorWithDepth(Vector3(joint.position));
|
||||
if (ColorDepthLocation.IsZero())
|
||||
{
|
||||
// fallback for when the joint can not be seen in color camera
|
||||
if (uJoint.Type == EKinectJointType::SpineBase) // oh shit! Our reference is gone!
|
||||
{
|
||||
uJoint.ColorLocation = uJoint.Location;
|
||||
continue;
|
||||
}
|
||||
|
||||
FKinectJoint &SpineBase = Joints[static_cast<int32>(EKinectJointType::SpineBase)];
|
||||
uJoint.ColorLocation = SpineBase.ColorLocation + uJoint.Location - SpineBase.Location;
|
||||
continue;
|
||||
}
|
||||
|
||||
uJoint.ColorLocation.Set(
|
||||
ColorDepthLocation.z * 100.0f,
|
||||
-ColorDepthLocation.x * 100.0f,
|
||||
ColorDepthLocation.y * 100.0f);
|
||||
} // color space with depth joints transforms
|
||||
|
||||
// orientations in color space with depth
|
||||
#define JOINT(Name) Joints[static_cast<int32>(EKinectJointType::Name)]
|
||||
#define J_DIR(FromJoint, ToJoint) JOINT(ToJoint).ColorLocation - JOINT(FromJoint).ColorLocation
|
||||
#define J_ROT(JointName, Twist) JOINT(JointName).ColorOrientation = FRotationMatrix::MakeFromX##Twist(XDir, Twist##Dir).Rotator()
|
||||
|
||||
FVector XDir, YDir, ZDir, Dir1, Dir2;
|
||||
FQuat Rot1, Rot2, RLerp;
|
||||
float JointsDot;
|
||||
|
||||
// Spine Base
|
||||
XDir = J_DIR(SpineBase, SpineMid);
|
||||
ZDir = J_DIR(HipLeft, HipRight);
|
||||
J_ROT(SpineBase, Z);
|
||||
|
||||
// Spine Middle
|
||||
// same XDir
|
||||
ZDir = (ZDir + J_DIR(ShoulderLeft, ShoulderRight)) / 2.0f; // intermediary between hip and shoulders twist
|
||||
J_ROT(SpineMid, Z);
|
||||
|
||||
// Spine Shoulder
|
||||
XDir = J_DIR(SpineMid, SpineShoulder);
|
||||
ZDir = J_DIR(ShoulderLeft, ShoulderRight);
|
||||
J_ROT(SpineShoulder, Z);
|
||||
|
||||
// Neck
|
||||
XDir = J_DIR(SpineShoulder, Neck);
|
||||
// same ZDir
|
||||
J_ROT(Neck, Z);
|
||||
|
||||
// Head
|
||||
XDir = J_DIR(Neck, Head);
|
||||
// same ZDir
|
||||
J_ROT(Head, Z);
|
||||
|
||||
// shoulder left
|
||||
XDir = J_DIR(SpineShoulder, ShoulderLeft);
|
||||
ZDir = J_DIR(SpineMid, SpineShoulder);
|
||||
J_ROT(ShoulderLeft, Z);
|
||||
|
||||
// elbow left
|
||||
XDir = J_DIR(ShoulderLeft, ElbowLeft);
|
||||
// interpolate between a rot for fully stretched arm and one for bent
|
||||
Dir1 = FVector::CrossProduct(J_DIR(ElbowLeft, WristLeft), J_DIR(ElbowLeft, ShoulderLeft)); // bent
|
||||
Rot1 = FRotationMatrix::MakeFromXZ(XDir, Dir1).ToQuat(); // bent
|
||||
Dir2 = FRotationMatrix(JOINT(ShoulderLeft).ColorOrientation).GetScaledAxis(EAxis::Y); // stretched
|
||||
Rot2 = FRotationMatrix::MakeFromXY(XDir, Dir2).ToQuat(); // stretched
|
||||
Dir1 = J_DIR(ShoulderLeft, ElbowLeft); Dir2 = J_DIR(ElbowLeft, WristLeft);
|
||||
Dir1.Normalize(); Dir2.Normalize();
|
||||
JointsDot = FVector::DotProduct(Dir1, Dir2);
|
||||
RLerp = FQuat::Slerp(Rot2, Rot1,
|
||||
FMath::Clamp(FMath::Acos(JointsDot) / 0.349f /*~20°*/, 0.f, 1.f)
|
||||
);
|
||||
RLerp.Normalize();
|
||||
JOINT(ElbowLeft).ColorOrientation = RLerp.Rotator();
|
||||
|
||||
// wrist left
|
||||
XDir = J_DIR(ElbowLeft, WristLeft);
|
||||
YDir = J_DIR(HandLeft, ThumbLeft);
|
||||
J_ROT(WristLeft, Y);
|
||||
|
||||
// hand left
|
||||
XDir = J_DIR(WristLeft, HandTipLeft);
|
||||
ZDir = J_DIR(ThumbLeft, HandLeft);
|
||||
J_ROT(HandLeft, Z);
|
||||
|
||||
// hand tip Left
|
||||
JOINT(HandTipLeft).ColorOrientation = JOINT(HandLeft).ColorOrientation;
|
||||
|
||||
// thumb left
|
||||
XDir = J_DIR(HandLeft, ThumbLeft);
|
||||
YDir = FRotationMatrix(JOINT(HandLeft).ColorOrientation).GetScaledAxis(EAxis::Y);
|
||||
J_ROT(ThumbLeft, Y);
|
||||
|
||||
// shoulder right
|
||||
XDir = J_DIR(ShoulderRight, SpineShoulder);
|
||||
ZDir = J_DIR(SpineShoulder, SpineMid);
|
||||
J_ROT(ShoulderRight, Z);
|
||||
|
||||
// elbow right
|
||||
XDir = J_DIR(ElbowRight, ShoulderRight);
|
||||
// interpolate between a rot for fully stretched arm and one for bent
|
||||
Dir1 = FVector::CrossProduct(J_DIR(ElbowRight, WristRight), J_DIR(ElbowRight, ShoulderRight)); // bent
|
||||
Rot1 = FRotationMatrix::MakeFromXZ(XDir, Dir1).ToQuat(); // bent
|
||||
Dir2 = FRotationMatrix(JOINT(ShoulderRight).ColorOrientation).GetScaledAxis(EAxis::Y); // stretched
|
||||
Rot2 = FRotationMatrix::MakeFromXY(XDir, Dir2).ToQuat(); // stretched
|
||||
Dir1 = J_DIR(ShoulderRight, ElbowRight); Dir2 = J_DIR(ElbowRight, WristRight);
|
||||
Dir1.Normalize(); Dir2.Normalize();
|
||||
JointsDot = FVector::DotProduct(Dir1, Dir2);
|
||||
RLerp = FQuat::Slerp(Rot2, Rot1,
|
||||
FMath::Clamp(FMath::Acos(JointsDot) / 0.349f /*~20°*/, 0.f, 1.f)
|
||||
);
|
||||
RLerp.Normalize();
|
||||
JOINT(ElbowRight).ColorOrientation = RLerp.Rotator();
|
||||
|
||||
// wrist right
|
||||
XDir = J_DIR(WristRight, ElbowRight);
|
||||
YDir = J_DIR(ThumbRight, HandRight);
|
||||
J_ROT(WristRight, Y);
|
||||
|
||||
// hand right
|
||||
XDir = J_DIR(HandTipRight, WristRight);
|
||||
ZDir = -YDir;
|
||||
J_ROT(HandRight, Z);
|
||||
|
||||
// hand tip right
|
||||
JOINT(HandTipRight).ColorOrientation = JOINT(HandRight).ColorOrientation;
|
||||
|
||||
// thumb right
|
||||
XDir = J_DIR(ThumbRight, HandRight);
|
||||
YDir = FRotationMatrix(JOINT(HandRight).ColorOrientation).GetScaledAxis(EAxis::Y);
|
||||
J_ROT(ThumbRight, Y);
|
||||
|
||||
// Hip Left
|
||||
XDir = J_DIR(HipLeft, SpineBase);
|
||||
ZDir = J_DIR(SpineMid, SpineBase);
|
||||
J_ROT(HipLeft, Z);
|
||||
|
||||
// Knee left
|
||||
XDir = J_DIR(KneeLeft, HipLeft);
|
||||
Dir1 = FVector::CrossProduct(J_DIR(KneeLeft, AnkleLeft), J_DIR(KneeLeft, HipLeft)); // bent
|
||||
Rot1 = FRotationMatrix::MakeFromXZ(XDir, Dir1).ToQuat(); // bent
|
||||
Dir2 = FRotationMatrix(JOINT(SpineMid).ColorOrientation).GetScaledAxis(EAxis::Y); // stretched
|
||||
Rot2 = FRotationMatrix::MakeFromXY(XDir, Dir2).ToQuat(); // stretched
|
||||
Dir1 = J_DIR(HipLeft, KneeLeft); Dir2 = J_DIR(KneeLeft, AnkleLeft);
|
||||
Dir1.Normalize(); Dir2.Normalize();
|
||||
JointsDot = FVector::DotProduct(Dir1, Dir2);
|
||||
RLerp = FQuat::Slerp(Rot2, Rot1, FMath::Clamp((FMath::Acos(JointsDot) - 0.2618f) / 0.5236f, 0.f, 1.f));
|
||||
RLerp.Normalize();
|
||||
JOINT(KneeLeft).ColorOrientation = RLerp.Rotator();
|
||||
|
||||
// ankle left
|
||||
XDir = J_DIR(AnkleLeft, KneeLeft);
|
||||
ZDir = FRotationMatrix(JOINT(KneeLeft).ColorOrientation).GetScaledAxis(EAxis::Z);
|
||||
J_ROT(AnkleLeft, Z);
|
||||
|
||||
// foot left
|
||||
// same X and Z dirs
|
||||
J_ROT(FootLeft, Z);
|
||||
|
||||
// hip right
|
||||
XDir = J_DIR(SpineBase, HipRight);
|
||||
ZDir = J_DIR(SpineBase, SpineMid);
|
||||
J_ROT(HipRight, Z);
|
||||
|
||||
// Knee Right
|
||||
XDir = J_DIR(HipRight, KneeRight);
|
||||
Dir1 = FVector::CrossProduct(J_DIR(KneeRight, AnkleRight), J_DIR(KneeRight, HipRight)); // bent
|
||||
Rot1 = FRotationMatrix::MakeFromXZ(XDir, Dir1).ToQuat(); // bent
|
||||
Dir2 = -FRotationMatrix(JOINT(SpineMid).ColorOrientation).GetScaledAxis(EAxis::Y); // stretched
|
||||
Rot2 = FRotationMatrix::MakeFromXY(XDir, Dir2).ToQuat(); // stretched
|
||||
Dir1 = J_DIR(HipRight, KneeRight); Dir2 = J_DIR(KneeRight, AnkleRight);
|
||||
Dir1.Normalize(); Dir2.Normalize();
|
||||
JointsDot = FVector::DotProduct(Dir1, Dir2);
|
||||
RLerp = FQuat::Slerp(Rot2, Rot1, FMath::Clamp((FMath::Acos(JointsDot) - 0.2618f) / 0.5236f, 0.f, 1.f));
|
||||
RLerp.Normalize();
|
||||
JOINT(KneeRight).ColorOrientation = RLerp.Rotator();
|
||||
|
||||
// Ankle right
|
||||
XDir = J_DIR(KneeRight, AnkleRight);
|
||||
ZDir = FRotationMatrix(JOINT(KneeRight).ColorOrientation).GetScaledAxis(EAxis::Z);
|
||||
J_ROT(AnkleRight, Z);
|
||||
|
||||
// foot right
|
||||
// same X and Z dirs
|
||||
J_ROT(FootRight, Z);
|
||||
#undef JOINT
|
||||
#undef J_DIR
|
||||
#undef J_ROT
|
||||
} // if (bUsingJointsColorSpaceTransforms)
|
||||
} // if (bIsTracked)
|
||||
|
||||
if (bWasTracked != bIsTracked)
|
||||
{
|
||||
if (bIsTracked)
|
||||
OnBodyBeginTrack.Broadcast(BodyIndex);
|
||||
else
|
||||
OnBodyLost.Broadcast(BodyIndex);
|
||||
}
|
||||
}
|
||||
|
||||
void UNeoKinectBody::Reset()
|
||||
{
|
||||
using namespace NeoKinect;
|
||||
|
||||
ClippedEdges.Left =
|
||||
ClippedEdges.Right =
|
||||
ClippedEdges.Top =
|
||||
ClippedEdges.Bottom = false;
|
||||
|
||||
bIsTracked = false;
|
||||
bIsRestricted = false;
|
||||
TrackingId = 0Ui64;
|
||||
|
||||
HandLeftState = EKinectHandState::NotTracked;
|
||||
HandLeftTrackingState = EKinectTrackingState::NotTracked;
|
||||
|
||||
HandRightState = EKinectHandState::NotTracked;
|
||||
HandRightTrackingState = EKinectTrackingState::NotTracked;
|
||||
|
||||
LeanTrackingState = EKinectTrackingState::NotTracked;
|
||||
Lean.Set(0.f, 0.f);
|
||||
|
||||
if (Joints.Num() != KinectJointCount)
|
||||
return;
|
||||
|
||||
for (int32 j = 0; j < KinectJointCount; ++j)
|
||||
{
|
||||
if (!Joints.IsValidIndex(j))
|
||||
continue;
|
||||
FKinectJoint& Joint = Joints[j];
|
||||
|
||||
Joint.Confidence = EKinectTrackingState::NotTracked;
|
||||
Joint.Location.Set(0.f, 0.f, 0.f);
|
||||
Joint.ColorLocation.Set(0.f, 0.f, 0.f);
|
||||
Joint.Orientation.Roll =
|
||||
Joint.Orientation.Pitch =
|
||||
Joint.Orientation.Yaw = 0.f;
|
||||
Joint.ColorOrientation.Roll =
|
||||
Joint.ColorOrientation.Pitch =
|
||||
Joint.ColorOrientation.Yaw = 0.f;
|
||||
}
|
||||
}
|
||||
+216
@@ -0,0 +1,216 @@
|
||||
// Copyright 2015-2023 Rodrigo Villani Pereira
|
||||
|
||||
#include "NeoKinectFace.h"
|
||||
#include "NeoKinectBody.h"
|
||||
#include "NeoKinectManager.h"
|
||||
|
||||
void FIntBoundingBox::FromRectI(const RectI& Rect)
|
||||
{
|
||||
Left = Rect.Left;
|
||||
Top = Rect.Top;
|
||||
Right = Rect.Right;
|
||||
Bottom = Rect.Bottom;
|
||||
}
|
||||
|
||||
void FFacePoints2D::FromKinectFacePoints(const NeoKinect::KinectFacePoints& Points)
|
||||
{
|
||||
LeftEye.X = Points.leftEye.x;
|
||||
LeftEye.Y = Points.leftEye.y;
|
||||
|
||||
RightEye.X = Points.rightEye.x;
|
||||
RightEye.Y = Points.rightEye.y;
|
||||
|
||||
Nose.X = Points.nose.x;
|
||||
Nose.Y = Points.nose.y;
|
||||
|
||||
LeftMouthCorner.X = Points.leftMouthCorner.x;
|
||||
LeftMouthCorner.Y = Points.leftMouthCorner.y;
|
||||
|
||||
RightMouthCorner.X = Points.rightMouthCorner.x;
|
||||
RightMouthCorner.Y = Points.rightMouthCorner.y;
|
||||
}
|
||||
|
||||
void FFacePoints3D::FromKinectFacePoints(const NeoKinect::KinectFacePoints& ColorPoints)
|
||||
{
|
||||
NeoKinect::Vector3 Location = UNeoKinectManager::ColorWithDepthFromColorOnly(ColorPoints.leftEye) * 100.f;
|
||||
LeftEye.Set(-Location.z, Location.x, Location.y);
|
||||
|
||||
Location = UNeoKinectManager::ColorWithDepthFromColorOnly(ColorPoints.rightEye) * 100.f;
|
||||
RightEye.Set(-Location.z, Location.x, Location.y);
|
||||
|
||||
Location = UNeoKinectManager::ColorWithDepthFromColorOnly(ColorPoints.nose) * 100.f;
|
||||
Nose.Set(-Location.z, Location.x, Location.y);
|
||||
|
||||
Location = UNeoKinectManager::ColorWithDepthFromColorOnly(ColorPoints.leftMouthCorner) * 100.f;
|
||||
LeftMouthCorner.Set(-Location.z, Location.x, Location.y);
|
||||
|
||||
Location = UNeoKinectManager::ColorWithDepthFromColorOnly(ColorPoints.rightMouthCorner) * 100.f;
|
||||
RightMouthCorner.Set(-Location.z, Location.x, Location.y);
|
||||
}
|
||||
|
||||
UNeoKinectFace::UNeoKinectFace()
|
||||
: Index(INDEX_NONE)
|
||||
, bIsTracked(false)
|
||||
, TrackingId(INDEX_NONE)
|
||||
, IsEngaged(EDetectionResult::Unknown)
|
||||
, IsHappy(EDetectionResult::Unknown)
|
||||
, IsLookingAway(EDetectionResult::Unknown)
|
||||
, IsMouthMoved(EDetectionResult::Unknown)
|
||||
, IsMouthOpen(EDetectionResult::Unknown)
|
||||
, IsLeftEyeClosed(EDetectionResult::Unknown)
|
||||
, IsRightEyeClosed(EDetectionResult::Unknown)
|
||||
, IsWearingGlasses(EDetectionResult::Unknown)
|
||||
{}
|
||||
|
||||
UNeoKinectFace::~UNeoKinectFace()
|
||||
{}
|
||||
|
||||
void UNeoKinectFace::GetEyeClosedAsExec(EKinectBodySide Eye, EDetectionResult& Result)
|
||||
{
|
||||
if (Eye == EKinectBodySide::Left)
|
||||
{
|
||||
Result = IsLeftEyeClosed;
|
||||
}
|
||||
else
|
||||
{
|
||||
Result = IsRightEyeClosed;
|
||||
}
|
||||
}
|
||||
|
||||
EDetectionResult UNeoKinectFace::GetEyeClosed(EKinectBodySide Eye) const
|
||||
{
|
||||
if (Eye == EKinectBodySide::Left)
|
||||
return IsLeftEyeClosed;
|
||||
|
||||
return IsRightEyeClosed;
|
||||
}
|
||||
|
||||
void UNeoKinectFace::GetIsEngagedAsExec(EDetectionResult& Result)
|
||||
{
|
||||
Result = IsEngaged;
|
||||
}
|
||||
|
||||
void UNeoKinectFace::GetIsHappyAsExec(EDetectionResult& Result)
|
||||
{
|
||||
Result = IsHappy;
|
||||
}
|
||||
|
||||
void UNeoKinectFace::GetIsLookingAwayAsExec(EDetectionResult& Result)
|
||||
{
|
||||
Result = IsLookingAway;
|
||||
}
|
||||
|
||||
void UNeoKinectFace::GetIsMouthMovedAsExec(EDetectionResult& Result)
|
||||
{
|
||||
Result = IsMouthMoved;
|
||||
}
|
||||
|
||||
void UNeoKinectFace::GetIsMouthOpenAsExec(EDetectionResult& Result)
|
||||
{
|
||||
Result = IsMouthOpen;
|
||||
}
|
||||
|
||||
void UNeoKinectFace::GetIsWearingGlassesAsExec(EDetectionResult& Result)
|
||||
{
|
||||
Result = IsWearingGlasses;
|
||||
}
|
||||
|
||||
bool UNeoKinectFace::IsFromBody(const UNeoKinectBody* Body) const
|
||||
{
|
||||
return Body->TrackingId == TrackingId;
|
||||
}
|
||||
|
||||
void UNeoKinectFace::UpdateFromKinectFace(const NeoKinect::KinectFace& kFace)
|
||||
{
|
||||
const bool bWasTracked = bIsTracked;
|
||||
|
||||
bIsTracked = kFace.bIsTracked;
|
||||
TrackingId = kFace.trackingId;
|
||||
|
||||
ColorBoundingBox.FromRectI(kFace.boxColor);
|
||||
InfraredBoundingBox.FromRectI(kFace.boxInfrared);
|
||||
|
||||
ColorAlignmentPoints.FromKinectFacePoints(kFace.pointsColor);
|
||||
InfraredAlignmentPoints.FromKinectFacePoints(kFace.pointsInfrared);
|
||||
ColorWithDepthAlignmentPoints.FromKinectFacePoints(kFace.pointsColor);
|
||||
|
||||
/* Camera space face transform */
|
||||
Location.Set(
|
||||
kFace.pivot.Z * 100.f,
|
||||
-kFace.pivot.X * 100.f,
|
||||
kFace.pivot.Y * 100.f);
|
||||
|
||||
const FQuat Quat = FQuat(
|
||||
-kFace.rotation.z,
|
||||
kFace.rotation.x,
|
||||
-kFace.rotation.y,
|
||||
kFace.rotation.w
|
||||
);
|
||||
Orientation = FRotationMatrix::MakeFromXZ(
|
||||
Quat.GetAxisZ(),
|
||||
-Quat.GetAxisY()).Rotator();
|
||||
|
||||
/* Color space face location */
|
||||
if (UNeoKinectManager::IsUsingJointsColorSpaceTransforms())
|
||||
{
|
||||
using namespace NeoKinect;
|
||||
// Convert camera space location to color space with depth
|
||||
const Vector3 ColorDepthLocation = UNeoKinectManager::CameraLocationToColorWithDepth(Vector3(kFace.pivot));
|
||||
if (ColorDepthLocation.IsZero())
|
||||
{
|
||||
ColorLocation = Location;
|
||||
}
|
||||
else
|
||||
{
|
||||
ColorLocation.Set(
|
||||
ColorDepthLocation.z * 100.0f,
|
||||
-ColorDepthLocation.x * 100.0f,
|
||||
ColorDepthLocation.y * 100.0f);
|
||||
}
|
||||
}
|
||||
|
||||
IsEngaged = static_cast<EDetectionResult>(kFace.isEngaged);
|
||||
IsHappy = static_cast<EDetectionResult>(kFace.isHappy);
|
||||
IsLookingAway = static_cast<EDetectionResult>(kFace.isLookingAway);
|
||||
IsMouthMoved = static_cast<EDetectionResult>(kFace.isMouthMoved);
|
||||
IsMouthOpen = static_cast<EDetectionResult>(kFace.isMouthOpen);
|
||||
IsLeftEyeClosed = static_cast<EDetectionResult>(kFace.isLeftEyeClosed);
|
||||
IsRightEyeClosed = static_cast<EDetectionResult>(kFace.isRightEyeClosed);
|
||||
IsWearingGlasses = static_cast<EDetectionResult>(kFace.isWearingGlasses);
|
||||
|
||||
if (bWasTracked != bIsTracked)
|
||||
{
|
||||
if (bIsTracked)
|
||||
OnFaceBeginTrack.Broadcast(Index);
|
||||
else
|
||||
OnFaceLost.Broadcast(Index);
|
||||
}
|
||||
}
|
||||
|
||||
void UNeoKinectFace::Reset()
|
||||
{
|
||||
bIsTracked = false;
|
||||
TrackingId = 0ULL;
|
||||
|
||||
ColorBoundingBox = FIntBoundingBox();
|
||||
InfraredBoundingBox = FIntBoundingBox();
|
||||
|
||||
ColorAlignmentPoints = FFacePoints2D();
|
||||
InfraredAlignmentPoints = FFacePoints2D();
|
||||
|
||||
Location.Set(0.f, 0.f, 0.f);
|
||||
ColorLocation = Location;
|
||||
|
||||
Orientation.Yaw =
|
||||
Orientation.Pitch =
|
||||
Orientation.Roll = 0.f;
|
||||
|
||||
IsEngaged =
|
||||
IsHappy =
|
||||
IsLookingAway =
|
||||
IsMouthMoved =
|
||||
IsMouthOpen =
|
||||
IsLeftEyeClosed =
|
||||
IsRightEyeClosed =
|
||||
IsWearingGlasses = EDetectionResult::Unknown;
|
||||
}
|
||||
+1311
File diff suppressed because it is too large
Load Diff
+5
@@ -0,0 +1,5 @@
|
||||
// Copyright 2015-2023 Rodrigo Villani Pereira
|
||||
|
||||
#include "Modules/ModuleManager.h"
|
||||
|
||||
IMPLEMENT_MODULE(FDefaultModuleImpl, NeoKinectUnreal)
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
// Copyright 2015-2023 Rodrigo Villani Pereira
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Windows/AllowWindowsPlatformTypes.h"
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable : 4471)
|
||||
#include "NeoKinect.h"
|
||||
#pragma warning(pop)
|
||||
#include "Windows/HideWindowsPlatformTypes.h"
|
||||
+127
@@ -0,0 +1,127 @@
|
||||
// Copyright 2015-2023 Rodrigo Villani Pereira
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Helpers/GuardedNeoKinect.h"
|
||||
#include "NeoKinectEnums.h"
|
||||
|
||||
#include <atomic>
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "HAL/Runnable.h"
|
||||
|
||||
DECLARE_LOG_CATEGORY_CLASS(KinectThreadLog, Log, All);
|
||||
|
||||
struct FThreadFrameBuffer
|
||||
{
|
||||
NeoKinect::KinectFrameType FrameType = NeoKinect::KinectFrameType::Count;
|
||||
NeoKinect::KinectPixelFormat PixelFormat = NeoKinect::KinectPixelFormat::None;
|
||||
NeoKinect::KinectCoordinateSpace TargetSpace = NeoKinect::KinectCoordinateSpace::Count;
|
||||
bool bRemapped = false;
|
||||
|
||||
~FThreadFrameBuffer();
|
||||
|
||||
void Configure(
|
||||
NeoKinect::KinectFrameType Frame,
|
||||
NeoKinect::KinectPixelFormat PixelFormat,
|
||||
NeoKinect::KinectCoordinateSpace Space,
|
||||
bool IsRemapped);
|
||||
bool AllocateBuffers(uint32 Size);
|
||||
void DeallocateBuffers();
|
||||
bool IsAllocated() const;
|
||||
|
||||
// Returns an updated buffer. If it was already consumed, return nullptr
|
||||
const uint8* GetReadBuffer(bool bConsume = true);
|
||||
uint8* GetWriteBuffer() const;
|
||||
|
||||
void SetDirty();
|
||||
|
||||
bool TryLock();
|
||||
void Lock();
|
||||
void Unlock();
|
||||
|
||||
void LockWrite();
|
||||
void UnlockWrite();
|
||||
|
||||
private:
|
||||
std::atomic<bool> bIsNew = false;
|
||||
uint8 *Buffer = nullptr;
|
||||
uint8 *BackBuffer = nullptr;
|
||||
FCriticalSection ReadMutex;
|
||||
FCriticalSection WriteMutex;
|
||||
|
||||
void SwapBuffers();
|
||||
};
|
||||
|
||||
// Kinect polling thread
|
||||
class FKinectThread : public FRunnable
|
||||
{
|
||||
private:
|
||||
/*
|
||||
Kinect updates its frames, at most, at 30fps.
|
||||
But, as some of them will be available at slightly different times, I'll poll
|
||||
it at double that rate to be sure.
|
||||
*/
|
||||
static constexpr float cDesiredFPS = 60.f;
|
||||
|
||||
// Singleton instance, can access the thread any time via static accessor, if it is active
|
||||
static FKinectThread *Runnable;
|
||||
// Thread to run FRunnable on
|
||||
FRunnableThread *Thread;
|
||||
// Stop this thread? Uses Thread Safe Counter
|
||||
FThreadSafeCounter StopTaskCounter;
|
||||
|
||||
// Kinect sensor
|
||||
NeoKinect::KinectSensor *pKinect;
|
||||
|
||||
// Bodies data
|
||||
NeoKinect::KinectBody Bodies[NeoKinect::KinectBodyCount];
|
||||
|
||||
// Face tracking data
|
||||
NeoKinect::KinectFace Faces[NeoKinect::KinectBodyCount];
|
||||
|
||||
public:
|
||||
// Constructor
|
||||
FKinectThread(NeoKinect::KinectSensor *pSensor);
|
||||
// Destructor
|
||||
~FKinectThread() override;
|
||||
|
||||
// Frames data. These control which frame buffers should or not be updated.
|
||||
static FThreadFrameBuffer FrameBuffers[static_cast<uint64>(EKinectFrame::Count)];
|
||||
// Controls whether body data should be read
|
||||
static bool bUseBodyTracking;
|
||||
// True if body data was updated since last reading
|
||||
bool bBodiesDirty;
|
||||
// Controls whether face tracking data should be read
|
||||
static bool bUseFaceTracking;
|
||||
// True if faces data was updated since last reading
|
||||
bool bFacesDirty;
|
||||
|
||||
// Mutex for body/face data thread safety
|
||||
static FCriticalSection BodiesFacesCritSec;
|
||||
|
||||
/*
|
||||
Start the thread and the worker from static (easy access)
|
||||
This code ensures only 1 KinectThread will be able to run at a time.
|
||||
This function returns a handle to the newly started instance.
|
||||
*/
|
||||
static FKinectThread *InitKinectPolling(NeoKinect::KinectSensor *pSensor);
|
||||
|
||||
// Makes sure this thread has stopped properly
|
||||
void EnsureCompletion();
|
||||
|
||||
// Shuts down the thread. Static so it can easily be called from outside the thread context
|
||||
static void Shutdown();
|
||||
|
||||
// Gets last polled bodies data. Returns nullptr if no new data
|
||||
const NeoKinect::KinectBody *GetBodiesData();
|
||||
// Gets last polled faces data. Returns nullptr if no new data
|
||||
const NeoKinect::KinectFace *GetFacesData();
|
||||
|
||||
// Begin FRunnable interface
|
||||
virtual bool Init() override;
|
||||
virtual uint32 Run() override;
|
||||
virtual void Stop() override;
|
||||
virtual void Exit() override;
|
||||
// End FRunnable interface
|
||||
};
|
||||
+297
@@ -0,0 +1,297 @@
|
||||
// Copyright 2015-2023 Rodrigo Villani Pereira
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "NeoKinectEnums.h"
|
||||
|
||||
#include "NeoKinectBody.generated.h"
|
||||
|
||||
namespace NeoKinect
|
||||
{
|
||||
struct KinectBody;
|
||||
class KinectSensor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Joint properties.
|
||||
*
|
||||
* The Location and Orientation of joints, by default, is given in camera space,
|
||||
* which center is a point in the Kinect sensor not matching the color camera.
|
||||
* So, to use joints over the color frame image, these coordinates must be reprojected.
|
||||
* To get these reprojected values, use ColorLocation and ColorOrientation.
|
||||
*/
|
||||
USTRUCT(BlueprintType, Category = "NeoKinect|Structs")
|
||||
struct FKinectJoint
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
/** Joint type. */
|
||||
UPROPERTY(BlueprintReadOnly, Category = "NeoKinect|Body|Joints")
|
||||
EKinectJointType Type;
|
||||
/** Joint tracking confidence. */
|
||||
UPROPERTY(BlueprintReadOnly, Category = "NeoKinect|Body|Joints")
|
||||
EKinectTrackingState Confidence;
|
||||
|
||||
/** Joint location relative to Kinect sensor in camera space. */
|
||||
UPROPERTY(BlueprintReadOnly, Category = "NeoKinect|Body|Joints", Meta = (Keywords = "position translation transform camera"))
|
||||
FVector Location;
|
||||
/** Joint orientation relative to Kinect sensor in camera space. */
|
||||
UPROPERTY(BlueprintReadOnly, Category = "NeoKinect|Body|Joints", Meta = (Keywords = "transform rotation angles camera"))
|
||||
FRotator Orientation;
|
||||
|
||||
/** Joint location relative to Kinect sensor in color space with depth. */
|
||||
UPROPERTY(BlueprintReadOnly, Category = "NeoKinect|Body|Joints", Meta = (Keywords = "position translation transform color"))
|
||||
FVector ColorLocation;
|
||||
/** Joint orientation relative to Kinect sensor in color space. */
|
||||
UPROPERTY(BlueprintReadOnly, Category = "NeoKinect|Body|Joints", Meta = (Keywords = "transform rotation angles color"))
|
||||
FRotator ColorOrientation;
|
||||
|
||||
FKinectJoint() :
|
||||
Type(EKinectJointType::AnkleLeft),
|
||||
Confidence(EKinectTrackingState::NotTracked),
|
||||
Location(FVector::ZeroVector),
|
||||
Orientation(FRotator::ZeroRotator),
|
||||
ColorLocation(FVector::ZeroVector),
|
||||
ColorOrientation(FRotator::ZeroRotator)
|
||||
{}
|
||||
};
|
||||
|
||||
/**
|
||||
* Which edges are clipping a body on the view frustum.
|
||||
*/
|
||||
USTRUCT(BlueprintType, Category = "NeoKinect|Structs")
|
||||
struct FKinectClippedEdges
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
UPROPERTY(BlueprintReadOnly, Category = "NeoKinect|Body")
|
||||
bool Left;
|
||||
UPROPERTY(BlueprintReadOnly, Category = "NeoKinect|Body")
|
||||
bool Right;
|
||||
UPROPERTY(BlueprintReadOnly, Category = "NeoKinect|Body")
|
||||
bool Top;
|
||||
UPROPERTY(BlueprintReadOnly, Category = "NeoKinect|Body")
|
||||
bool Bottom;
|
||||
|
||||
FKinectClippedEdges() :
|
||||
Left(false), Right(false), Top(false), Bottom(false)
|
||||
{}
|
||||
};
|
||||
|
||||
/**
|
||||
* Represents a trackable body.
|
||||
*/
|
||||
UCLASS(BlueprintType, Category = "NeoKinect|Body")
|
||||
class NEOKINECTUNREAL_API UNeoKinectBody : public UObject
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FBodyTrackDelegate, int32, BodyIndex);
|
||||
|
||||
UNeoKinectBody();
|
||||
~UNeoKinectBody() override;
|
||||
|
||||
/** Retrieves this body index (0~5). */
|
||||
UPROPERTY(BlueprintReadOnly, Category = "NeoKinect|Body")
|
||||
int32 BodyIndex;
|
||||
/** Body's unique Tracking ID. */
|
||||
UPROPERTY()
|
||||
int64 TrackingId;
|
||||
|
||||
/** Body clipped edges from the sensor's view. */
|
||||
UPROPERTY(BlueprintReadOnly, Category = "NeoKinect|Body", Meta = (Keywords = "cut borders"))
|
||||
FKinectClippedEdges ClippedEdges;
|
||||
|
||||
/** If this body represents a tracked user. */
|
||||
UPROPERTY(BlueprintReadOnly, Category = "NeoKinect|Body")
|
||||
bool bIsTracked;
|
||||
/** If body's movement is restricted somehow. */
|
||||
UPROPERTY(BlueprintReadOnly, Category = "NeoKinect|Body")
|
||||
bool bIsRestricted;
|
||||
|
||||
/** Left hand pose */
|
||||
UPROPERTY(BlueprintReadOnly, Category = "NeoKinect|Body|Hands")
|
||||
EKinectHandState HandLeftState;
|
||||
/** Left hand tracking state */
|
||||
UPROPERTY(BlueprintReadOnly, Category = "NeoKinect|Body|Hands")
|
||||
EKinectTrackingState HandLeftTrackingState;
|
||||
|
||||
/** Right hand pose */
|
||||
UPROPERTY(BlueprintReadOnly, Category = "NeoKinect|Body|Hands")
|
||||
EKinectHandState HandRightState;
|
||||
/** Right hand tracking state */
|
||||
UPROPERTY(BlueprintReadOnly, Category = "NeoKinect|Body|Hands")
|
||||
EKinectTrackingState HandRightTrackingState;
|
||||
|
||||
/** Body lean tracking confidence. */
|
||||
UPROPERTY(BlueprintReadOnly, Category = "NeoKinect|Body")
|
||||
EKinectTrackingState LeanTrackingState;
|
||||
/**
|
||||
* Body lean.
|
||||
* Ranges from -1 to 1 being left to right in X and back to front in Y.
|
||||
* Values of -1 and 1 represent roughly 45 degrees of inclination.
|
||||
*/
|
||||
UPROPERTY(BlueprintReadOnly, Category = "NeoKinect|Body", Meta = (Keywords = "inclination"))
|
||||
FVector2D Lean;
|
||||
|
||||
UPROPERTY()
|
||||
TArray<FKinectJoint> Joints;
|
||||
|
||||
/** Called when this Body object starts tracking an user */
|
||||
UPROPERTY(BlueprintAssignable, Category = "NeoKinect|Body")
|
||||
FBodyTrackDelegate OnBodyBeginTrack;
|
||||
|
||||
/** Called when this Body object loses track of its user */
|
||||
UPROPERTY(BlueprintAssignable, Category = "NeoKinect|Body")
|
||||
FBodyTrackDelegate OnBodyLost;
|
||||
|
||||
/**
|
||||
* Get Left/Right hand state as execution pins.
|
||||
* Useful for redirecting code path, like the Branch node.
|
||||
*
|
||||
* @param Hand Which hand?
|
||||
* @param States Current Hand state.
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category = "NeoKinect|Body|Hands", Meta = (ExpandEnumAsExecs = "States"))
|
||||
void GetHandStateAsExec(EKinectBodySide Hand, EKinectHandState& States);
|
||||
|
||||
/**
|
||||
* Get Left/Right hand state.
|
||||
*
|
||||
* @param Hand Which hand?
|
||||
* @return Current Hand state.
|
||||
*/
|
||||
UFUNCTION(BlueprintPure, Category = "SamrtKinect|Body|Hands")
|
||||
EKinectHandState GetHandState(EKinectBodySide Hand) const;
|
||||
|
||||
/**
|
||||
* Get Left/Right hand tracking state as execution pins.
|
||||
* Useful for redirecting code path, like the Branch node.
|
||||
*
|
||||
* @param Hand Which hand?
|
||||
* @param States Current Hand tracking state.
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category = "NeoKinect|Body|Hands", Meta = (ExpandEnumAsExecs = "States"))
|
||||
void GetHandConfidenceAsExec(EKinectBodySide Hand, EKinectTrackingState& States);
|
||||
|
||||
/**
|
||||
* Get Left/Right hand tracking state.
|
||||
*
|
||||
* @param Hand Which hand?
|
||||
* @return Current Hand tracking state.
|
||||
*/
|
||||
UFUNCTION(BlueprintPure, Category = "NeoKinect|Body|Hands")
|
||||
EKinectTrackingState GetHandConfidence(EKinectBodySide Hand) const;
|
||||
|
||||
/**
|
||||
* Get body lean tracking confidence as execution pins.
|
||||
* Useful for redirecting code path, like the Branch node.
|
||||
*
|
||||
* @param States Lean tracking state.
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category = "NeoKinect|Body", Meta = (ExpandEnumAsExecs = "States", Keywords = "inclination"))
|
||||
void GetLeanConfidenceAsExec(EKinectTrackingState& States);
|
||||
|
||||
/**
|
||||
* Contains this body's each joint tracking state and transform.
|
||||
*
|
||||
* @return This body's each joint tracking state and transform.
|
||||
*/
|
||||
UFUNCTION(BlueprintPure, Category = "NeoKinect|Body|Joints")
|
||||
TArray<FKinectJoint> GetJoints();
|
||||
|
||||
/**
|
||||
* Get a Joint's location relative to Kinect sensor.
|
||||
*
|
||||
* @param Joint The joint type.
|
||||
* @return Joint's location relative to Kinect sensor.
|
||||
*/
|
||||
UFUNCTION(BlueprintPure, Category = "NeoKinect|Body|Joints", Meta = (Keywords = "position translation transform"))
|
||||
FVector GetJointLocation(EKinectJointType Joint);
|
||||
|
||||
/**
|
||||
* Get a Joint's orientation relative to Kinect sensor.
|
||||
*
|
||||
* @param Joint The joint type.
|
||||
* @return Joint's orientation relative to Kinect sensor.
|
||||
*/
|
||||
UFUNCTION(BlueprintPure, Category = "NeoKinect|Body|Joints", Meta = (Keywords = "rotation angles transform"))
|
||||
FRotator GetJointOrientation(EKinectJointType Joint);
|
||||
|
||||
/**
|
||||
* Get a Joint's location relative to Kinect sensor aligned with the Color frame.
|
||||
* So, if a camera is created with the same FOV as the Kinect Color camera's and
|
||||
* the Color frame texture is used as background, this location will align perfectly
|
||||
* with the corresponding joint in the Color image.
|
||||
*
|
||||
* @param Joint The joint type.
|
||||
* @return Joint's location relative to Kinect sensor.
|
||||
*/
|
||||
UFUNCTION(BlueprintPure, Category = "NeoKinect|Body|Joints", Meta = (Keywords = "position translation transform"))
|
||||
FVector GetJointLocationColor(EKinectJointType Joint);
|
||||
|
||||
/**
|
||||
* Get a Joint's orientation relative to Kinect sensor aligned with the Color
|
||||
* aligned joint location.
|
||||
*
|
||||
* @param Joint The joint type.
|
||||
* @return Joint's orientation relative to Kinect sensor.
|
||||
*/
|
||||
UFUNCTION(BlueprintPure, Category = "NeoKinect|Body|Joints", Meta = (Keywords = "rotation angles transform"))
|
||||
FRotator GetJointOrientationColor(EKinectJointType Joint);
|
||||
|
||||
/**
|
||||
* Get a Joint's tracking confidence as exec pins.
|
||||
* Useful for redirecting the code path, like the Branch node.
|
||||
*
|
||||
* @param Joint The joint type.
|
||||
* @param States Joint's tracking confidence.
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category = "NeoKinect|Body|Joints", Meta = (ExpandEnumAsExecs = "States"))
|
||||
void GetJointConfidenceAsExec(EKinectJointType Joint, EKinectTrackingState& States);
|
||||
|
||||
/**
|
||||
* Get a Joint's tracking confidence.
|
||||
*
|
||||
* @param Joint The joint type.
|
||||
* @return Joint's tracking confidence.
|
||||
*/
|
||||
UFUNCTION(BlueprintPure, Category = "NeoKinect|Body|Joints")
|
||||
EKinectTrackingState GetJointConfidence(EKinectJointType Joint);
|
||||
|
||||
/**
|
||||
* Calculate the distance between two joints in the Kinect sensor space.
|
||||
*
|
||||
* @param JointA First joint.
|
||||
* @param JointB Second joint.
|
||||
* @return Distance between the two joints.
|
||||
*/
|
||||
UFUNCTION(BlueprintPure, Category = "NeoKinect|Body|Joints", Meta = (Keywords = "size length"))
|
||||
float GetJointsDistance(EKinectJointType JointA, EKinectJointType JointB);
|
||||
|
||||
/**
|
||||
* Calculate the distance from a Joint's location to it's parent's location.
|
||||
* Useful for getting bone sizes. Eg.: for Left Elbow, the result will be the
|
||||
* distance from Left Elbow to Left Shoulder. Spine Base is the skeleton's root,
|
||||
* so it will return 0.
|
||||
*
|
||||
* @param Joint The joint type.
|
||||
* @return Distance from Joint to it's parent.
|
||||
*/
|
||||
UFUNCTION(BlueprintPure, Category = "NeoKinect|Body|Joints", Meta = (Keywords = "size length"))
|
||||
float GetBoneLength(EKinectJointType Joint);
|
||||
|
||||
/**
|
||||
* The Dot product between a Joint direction and a Vector.
|
||||
*
|
||||
* @return -1 ~ 1: 1 = same direction as vector. -1 = opposite from vector.
|
||||
*/
|
||||
UFUNCTION(BlueprintPure, Category = "NeoKinect|Body|Joints", Meta = (Keywords = "pointing direction lookat"))
|
||||
float JointDotVector(EKinectJointType Joint, FVector Vector);
|
||||
|
||||
void UpdateFromKinectBody(const NeoKinect::KinectBody& kBody, const NeoKinect::KinectSensor& Sensor);
|
||||
|
||||
void Reset();
|
||||
};
|
||||
+134
@@ -0,0 +1,134 @@
|
||||
// Copyright 2015-2023 Rodrigo Villani Pereira
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "NeoKinectEnums.generated.h"
|
||||
|
||||
/**
|
||||
* The types of frames that can be read from the Kinect sensor.
|
||||
*/
|
||||
UENUM(BlueprintType, Category = "NeoKinect|Enums")
|
||||
enum class EKinectFrame : uint8
|
||||
{
|
||||
/** Normal camera texture. Standard RGBA format (alpha = 1) */
|
||||
Color = 0,
|
||||
/** Depth sensor texture. Red channel only. Values are in millimeters */
|
||||
Depth = 1,
|
||||
/** Depth sensor ranging from 0 to 1. Standard RGBA format (alpha = 1) */
|
||||
NormalizedDepth = 2,
|
||||
/** Body index value in Red channel. In material editor, multiply Red by 255
|
||||
* to get actual index value (0~5) */
|
||||
BodyIndex = 3,
|
||||
/** Body indexes with a color for each index. Standard RGBA format.
|
||||
* Alpha = 1 where there's a body, 0 otherwise */
|
||||
BodyIndexColor = 4 UMETA(DisplayName = "Colored Body Index"),
|
||||
/** Not implemented */
|
||||
Color_DepthSpace = 5 UMETA(DisplayName = "Color in Depth Space"),
|
||||
/** Depth sensor image remapped to fit the color frame. Red channel only.
|
||||
* Values in millimeters, 0 where remapping is not possible. */
|
||||
Depth_ColorSpace = 6 UMETA(DisplayName = "Depth in Color Space"),
|
||||
/** Body index values remapped to fit the bodies in the color frame.
|
||||
* Red channel only. In material editor, multiply Red by 255 to get actual
|
||||
* index value (0~5). */
|
||||
BodyIndex_ColorSpace= 7 UMETA(DisplayName = "Body Index in Color Space"),
|
||||
/** Infrared sensor image. Red channel only, usually very dark. */
|
||||
Infrared = 8,
|
||||
/** Long exposure infrared sensor image. Red channel only. Can lower the
|
||||
* sensor's FPS to get brighter image */
|
||||
LongExposureInfrared= 9,
|
||||
/** Cast to int to get the number of frame types */
|
||||
Count = 10
|
||||
};
|
||||
|
||||
/**
|
||||
* Body sides.
|
||||
*/
|
||||
UENUM(BlueprintType, Category = "NeoKinect|Enums")
|
||||
enum class EKinectBodySide : uint8
|
||||
{
|
||||
Left = 0,
|
||||
Right = 1
|
||||
};
|
||||
|
||||
/**
|
||||
* Possible hand states
|
||||
*/
|
||||
UENUM(BlueprintType, Category = "NeoKinect|Enums")
|
||||
enum class EKinectHandState : uint8
|
||||
{
|
||||
/** Pretty close to NotTracked. */
|
||||
Unknown = 0,
|
||||
NotTracked = 1,
|
||||
Open = 2,
|
||||
Closed = 3,
|
||||
/** Closed hand with index and middle fingers pointing up, near each other */
|
||||
Lasso = 4
|
||||
};
|
||||
|
||||
/**
|
||||
* Tracking confidence.
|
||||
*/
|
||||
UENUM(BlueprintType, Category = "NeoKinect|Enums")
|
||||
enum class EKinectTrackingState : uint8
|
||||
{
|
||||
NotTracked = 0,
|
||||
/** Tracked by deduction. Sometimes good, many times terrible. */
|
||||
Inferred = 1,
|
||||
Tracked = 2
|
||||
};
|
||||
|
||||
/**
|
||||
* The joints that the Kinect sensor tracks on each body. 25 total.
|
||||
*/
|
||||
UENUM(BlueprintType, Category = "NeoKinect|Enums")
|
||||
enum class EKinectJointType : uint8
|
||||
{
|
||||
/** Near the pelvis */
|
||||
SpineBase = 0,
|
||||
/** Near the stomach */
|
||||
SpineMid = 1 UMETA(DisplayName = "Spine Middle"),
|
||||
Neck = 2,
|
||||
Head = 3,
|
||||
ShoulderLeft = 4 UMETA(DisplayName = "Left Shoulder"),
|
||||
ElbowLeft = 5 UMETA(DisplayName = "Left Elbow"),
|
||||
WristLeft = 6 UMETA(DisplayName = "Left Wrist"),
|
||||
/** In the palm center */
|
||||
HandLeft = 7 UMETA(DisplayName = "Left Hand"),
|
||||
ShoulderRight = 8 UMETA(DisplayName = "Right Shoulder"),
|
||||
ElbowRight = 9 UMETA(DisplayName = "Right Elbow"),
|
||||
WristRight = 10 UMETA(DisplayName = "Right Wrist"),
|
||||
/** In the palm center */
|
||||
HandRight = 11 UMETA(DisplayName = "Right Hand"),
|
||||
/** Femur/Hip joint */
|
||||
HipLeft = 12 UMETA(DisplayName = "Left Hip"),
|
||||
KneeLeft = 13 UMETA(DisplayName = "Left Knee"),
|
||||
AnkleLeft = 14 UMETA(DisplayName = "Left Ankle"),
|
||||
/** Tip of the foot */
|
||||
FootLeft = 15 UMETA(DisplayName = "Left Foot"),
|
||||
/** Femur/Hip joint */
|
||||
HipRight = 16 UMETA(DisplayName = "Right Hip"),
|
||||
KneeRight = 17 UMETA(DisplayName = "Right Knee"),
|
||||
AnkleRight = 18 UMETA(DisplayName = "Right Ankle"),
|
||||
/** Tip of the foot */
|
||||
FootRight = 19 UMETA(DisplayName = "Right Foot"),
|
||||
/** Between the collar bones */
|
||||
SpineShoulder = 20,
|
||||
HandTipLeft = 21 UMETA(DisplayName = "Left Hand Tip"),
|
||||
ThumbLeft = 22 UMETA(DisplayName = "Left Thumb"),
|
||||
HandTipRight = 23 UMETA(DisplayName = "Right Hand Tip"),
|
||||
ThumbRight = 24 UMETA(DisplayName = "Right Thumb")
|
||||
};
|
||||
|
||||
/**
|
||||
* Face properties detection results
|
||||
*/
|
||||
UENUM(BlueprintType, Category = "NeoKinect|Enums")
|
||||
enum class EDetectionResult : uint8
|
||||
{
|
||||
Unknown = 0,
|
||||
No = 1,
|
||||
/** Consider it more as a "No" */
|
||||
Maybe = 2,
|
||||
Yes = 3
|
||||
};
|
||||
+290
@@ -0,0 +1,290 @@
|
||||
// Copyright 2015-2023 Rodrigo Villani Pereira
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "NeoKinectEnums.h"
|
||||
|
||||
#include "Windows/AllowWindowsPlatformTypes.h"
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable : 4471)
|
||||
#include "Kinect.Face.h"
|
||||
#pragma warning(pop)
|
||||
#include "Windows/HideWindowsPlatformTypes.h"
|
||||
|
||||
#include "NeoKinectFace.generated.h"
|
||||
|
||||
class UNeoKinectBody;
|
||||
|
||||
namespace NeoKinect
|
||||
{
|
||||
struct KinectFace;
|
||||
struct KinectFacePoints;
|
||||
}
|
||||
|
||||
/**
|
||||
* Describes the edges of a rectangle in a texture (int values)
|
||||
*/
|
||||
USTRUCT(BlueprintType, Category = "NeoKinect|Structs")
|
||||
struct FIntBoundingBox
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
// Left rectangle edge
|
||||
UPROPERTY(BlueprintReadOnly, Category = "NeoKinect|Face")
|
||||
int32 Left = 0;
|
||||
// Top rectangle edge
|
||||
UPROPERTY(BlueprintReadOnly, Category = "NeoKinect|Face")
|
||||
int32 Top = 0;
|
||||
// Right rectangle edge
|
||||
UPROPERTY(BlueprintReadOnly, Category = "NeoKinect|Face")
|
||||
int32 Right = 0;
|
||||
// Bottom rectangle edge
|
||||
UPROPERTY(BlueprintReadOnly, Category = "NeoKinect|Face")
|
||||
int32 Bottom = 0;
|
||||
|
||||
void FromRectI(const RectI& Rect);
|
||||
};
|
||||
|
||||
/**
|
||||
* Lists the 2D of each face alignment point.
|
||||
*/
|
||||
USTRUCT(BlueprintType, Category = "NeoKinect|Structs")
|
||||
struct FFacePoints2D
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
// Left eye location
|
||||
UPROPERTY(BlueprintReadOnly, Category = "NeoKinect|Face")
|
||||
FVector2D LeftEye = FVector2D::Zero();
|
||||
// Right eye location
|
||||
UPROPERTY(BlueprintReadOnly, Category = "NeoKinect|Face")
|
||||
FVector2D RightEye = FVector2D::Zero();
|
||||
// Nose location
|
||||
UPROPERTY(BlueprintReadOnly, Category = "NeoKinect|Face")
|
||||
FVector2D Nose = FVector2D::Zero();
|
||||
// Left mouth corner location
|
||||
UPROPERTY(BlueprintReadOnly, Category = "NeoKinect|Face")
|
||||
FVector2D LeftMouthCorner = FVector2D::Zero();
|
||||
// Right mouth corner location
|
||||
UPROPERTY(BlueprintReadOnly, Category = "NeoKinect|Face")
|
||||
FVector2D RightMouthCorner = FVector2D::Zero();
|
||||
|
||||
void FromKinectFacePoints(const NeoKinect::KinectFacePoints& Points);
|
||||
};
|
||||
|
||||
/**
|
||||
* Lists the 3D locations of each face alignment point.
|
||||
*/
|
||||
USTRUCT(BlueprintType, Category = "NeoKinect|Structs")
|
||||
struct FFacePoints3D
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
// Left eye location
|
||||
UPROPERTY(BlueprintReadOnly, Category = "NeoKinect|Face")
|
||||
FVector LeftEye = FVector::Zero();
|
||||
// Right eye location
|
||||
UPROPERTY(BlueprintReadOnly, Category = "NeoKinect|Face")
|
||||
FVector RightEye = FVector::Zero();
|
||||
// Nose location
|
||||
UPROPERTY(BlueprintReadOnly, Category = "NeoKinect|Face")
|
||||
FVector Nose = FVector::Zero();
|
||||
// Left mouth corner location
|
||||
UPROPERTY(BlueprintReadOnly, Category = "NeoKinect|Face")
|
||||
FVector LeftMouthCorner = FVector::Zero();
|
||||
// Right mouth corner location
|
||||
UPROPERTY(BlueprintReadOnly, Category = "NeoKinect|Face")
|
||||
FVector RightMouthCorner = FVector::Zero();
|
||||
|
||||
void FromKinectFacePoints(const NeoKinect::KinectFacePoints& ColorPoints);
|
||||
};
|
||||
|
||||
/**
|
||||
* Represents a trackable body's face.
|
||||
*/
|
||||
UCLASS(BlueprintType, Category = "NeoKinect|Face")
|
||||
class NEOKINECTUNREAL_API UNeoKinectFace : public UObject
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FFaceTrackDelegate, int32, FaceIndex);
|
||||
|
||||
UNeoKinectFace();
|
||||
~UNeoKinectFace() override;
|
||||
|
||||
// This face's index in the faces array
|
||||
UPROPERTY(BlueprintReadOnly, Category = "NeoKinect|Face")
|
||||
int32 Index;
|
||||
|
||||
// If this face is being tracked.
|
||||
UPROPERTY(BlueprintReadOnly, Category = "NeoKinect|Face")
|
||||
bool bIsTracked;
|
||||
|
||||
// Face tracking Id. Matches it's body TrackingId.
|
||||
UPROPERTY()
|
||||
int64 TrackingId;
|
||||
|
||||
// The face bounding box in Color space.
|
||||
UPROPERTY(BlueprintReadOnly, Category = "NeoKinect|Face")
|
||||
FIntBoundingBox ColorBoundingBox;
|
||||
|
||||
// The face bounding box in Depth space.
|
||||
UPROPERTY(BlueprintReadOnly, Category = "NeoKinect|Face")
|
||||
FIntBoundingBox InfraredBoundingBox;
|
||||
|
||||
// The face alignment points in Color space.
|
||||
UPROPERTY(BlueprintReadOnly, Category = "NeoKinect|Face")
|
||||
FFacePoints2D ColorAlignmentPoints;
|
||||
|
||||
// The face alignment points in Depth space.
|
||||
UPROPERTY(BlueprintReadOnly, Category = "NeoKinect|Face")
|
||||
FFacePoints2D InfraredAlignmentPoints;
|
||||
|
||||
/**
|
||||
* The face alignment points aligned with the Color frame from the camera's
|
||||
* point of view, at a distance taken from Depth frame.
|
||||
* For this to have valid values the remapped Depth in Color Space frame must be activated.
|
||||
*/
|
||||
UPROPERTY(BlueprintReadOnly, Category = "NeoKinect|Face")
|
||||
FFacePoints3D ColorWithDepthAlignmentPoints;
|
||||
|
||||
// The face rotation.
|
||||
UPROPERTY(BlueprintReadOnly, Meta = (Keywords = "rotation quaternion angles transform"), Category = "NeoKinect|Face")
|
||||
FRotator Orientation;
|
||||
|
||||
// The head pivot location.
|
||||
UPROPERTY(BlueprintReadOnly, Meta = (Keywords = "location position center head translation transform"), Category = "NeoKinect|Face")
|
||||
FVector Location;
|
||||
|
||||
/** Head location aligned with the Color frame and with depth.
|
||||
* Only valid if color transforms were activated using SetUseJointsColorSpaceTransforms */
|
||||
UPROPERTY(BlueprintReadOnly, Category = "NeoKinect|Face")
|
||||
FVector ColorLocation;
|
||||
|
||||
// Is the user engaged?
|
||||
UPROPERTY(BlueprintReadOnly, Category = "NeoKinect|Face")
|
||||
EDetectionResult IsEngaged;
|
||||
|
||||
// Does the user looks happy?
|
||||
UPROPERTY(BlueprintReadOnly, Category = "NeoKinect|Face")
|
||||
EDetectionResult IsHappy;
|
||||
|
||||
// Is the user looking away?
|
||||
UPROPERTY(BlueprintReadOnly, Category = "NeoKinect|Face")
|
||||
EDetectionResult IsLookingAway;
|
||||
|
||||
// Did the user mouth move?
|
||||
UPROPERTY(BlueprintReadOnly, Category = "NeoKinect|Face")
|
||||
EDetectionResult IsMouthMoved;
|
||||
|
||||
// Is the user mouth opened?
|
||||
UPROPERTY(BlueprintReadOnly, Category = "NeoKinect|Face")
|
||||
EDetectionResult IsMouthOpen;
|
||||
|
||||
// Is the user left eye closed?
|
||||
UPROPERTY(BlueprintReadOnly, Category = "NeoKinect|Face")
|
||||
EDetectionResult IsLeftEyeClosed;
|
||||
|
||||
// Is the user right eye closed?
|
||||
UPROPERTY(BlueprintReadOnly, Category = "NeoKinect|Face")
|
||||
EDetectionResult IsRightEyeClosed;
|
||||
|
||||
// Is the user wearing glasses?
|
||||
UPROPERTY(BlueprintReadOnly, Category = "NeoKinect|Face")
|
||||
EDetectionResult IsWearingGlasses;
|
||||
|
||||
/** Called when this face object starts tracking a user's face */
|
||||
UPROPERTY(BlueprintAssignable, Category = "NeoKinect|Face")
|
||||
FFaceTrackDelegate OnFaceBeginTrack;
|
||||
|
||||
/** Called when this face object loses its user's face */
|
||||
UPROPERTY(BlueprintAssignable, Category = "NeoKinect|Face")
|
||||
FFaceTrackDelegate OnFaceLost;
|
||||
|
||||
/**
|
||||
* Checks if left/right eye is closed and use the result as execution pins.
|
||||
* Useful for redirecting code path, like the Branch node.
|
||||
*
|
||||
* @param Eye Which eye?
|
||||
* @param Result Is selected eye closed? Consider 'Maybe' more as a 'No'.
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category = "NeoKinect|Face", Meta = (ExpandEnumAsExecs = "Result"))
|
||||
void GetEyeClosedAsExec(EKinectBodySide Eye, EDetectionResult& Result);
|
||||
|
||||
/**
|
||||
* Checks if left/right eye is closed.
|
||||
*
|
||||
* @param Eye Which eye?
|
||||
* @return Is selected eye closed? Consider 'Maybe' more as a 'No'.
|
||||
*/
|
||||
UFUNCTION(BlueprintPure, Category = "NeoKinect|Face")
|
||||
EDetectionResult GetEyeClosed(EKinectBodySide Eye) const;
|
||||
|
||||
/**
|
||||
* Get IsEngaged as execution pins.
|
||||
* Useful for redirecting code path, like the Branch node.
|
||||
*
|
||||
* @param Result Consider 'Maybe' more as a 'No'.
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category = "NeoKinect|Face", Meta = (ExpandEnumAsExecs = "Result"))
|
||||
void GetIsEngagedAsExec(EDetectionResult& Result);
|
||||
|
||||
/**
|
||||
* Gets IsHappy as execution pins.
|
||||
* Useful for redirecting code path, like the Branch node.
|
||||
*
|
||||
* @param Result Consider 'Maybe' more as a 'No'.
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category = "NeoKinect|Face", Meta = (ExpandEnumAsExecs = "Result"))
|
||||
void GetIsHappyAsExec(EDetectionResult& Result);
|
||||
|
||||
/**
|
||||
* Gets IsLookingAway as execution pins.
|
||||
* Useful for redirecting code path, like the Branch node.
|
||||
*
|
||||
* @param Result Consider 'Maybe' more as a 'No'.
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category = "NeoKinect|Face", Meta = (ExpandEnumAsExecs = "Result"))
|
||||
void GetIsLookingAwayAsExec(EDetectionResult& Result);
|
||||
|
||||
/**
|
||||
* Gets IsMouthMoved as execution pins.
|
||||
* Useful for redirecting code path, like the Branch node.
|
||||
*
|
||||
* @param Result Consider 'Maybe' more as a 'No'.
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category = "NeoKinect|Face", Meta = (ExpandEnumAsExecs = "Result"))
|
||||
void GetIsMouthMovedAsExec(EDetectionResult& Result);
|
||||
|
||||
/**
|
||||
* Gets IsMouthOpen as execution pins.
|
||||
* Useful for redirecting code path, like the Branch node.
|
||||
*
|
||||
* @param Result Consider 'Maybe' more as a 'No'.
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category = "NeoKinect|Face", Meta = (ExpandEnumAsExecs = "Result"))
|
||||
void GetIsMouthOpenAsExec(EDetectionResult& Result);
|
||||
|
||||
/**
|
||||
* Gets IsWearingGlasses as execution pins.
|
||||
* Useful for redirecting code path, like the Branch node.
|
||||
*
|
||||
* @param Result Consider 'Maybe' more as a 'No'.
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category = "NeoKinect|Face", Meta = (ExpandEnumAsExecs = "Result"))
|
||||
void GetIsWearingGlassesAsExec(EDetectionResult& Result);
|
||||
|
||||
/**
|
||||
* Checks if this face belongs to the passed Body instance.
|
||||
*
|
||||
* @param Body The body to test from.
|
||||
* @return True if this face belongs to the passed body.
|
||||
*/
|
||||
UFUNCTION(BlueprintPure, Category = "NeoKinect|Face")
|
||||
bool IsFromBody(const UNeoKinectBody* Body) const;
|
||||
|
||||
void UpdateFromKinectFace(const NeoKinect::KinectFace& kFace);
|
||||
|
||||
void Reset();
|
||||
};
|
||||
+612
@@ -0,0 +1,612 @@
|
||||
// Copyright 2015-2023 Rodrigo Villani Pereira
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Helpers/GuardedNeoKinect.h"
|
||||
|
||||
#include "NeoKinectEnums.h"
|
||||
#include "PixelFormat.h"
|
||||
#include "Containers/Ticker.h"
|
||||
|
||||
#include "NeoKinectManager.generated.h"
|
||||
|
||||
class FKinectThread;
|
||||
class UNeoKinectBody;
|
||||
class UNeoKinectFace;
|
||||
class UTextureRenderTarget2D;
|
||||
|
||||
DECLARE_LOG_CATEGORY_CLASS(NeoKinectLog, Log, All);
|
||||
|
||||
/** Structure to manage Kinect frame types lifetime and updating */
|
||||
struct FFrameTextureData
|
||||
{
|
||||
UTextureRenderTarget2D *Texture = nullptr;
|
||||
uint32 Width = 0;
|
||||
uint32 Height = 0;
|
||||
uint32 PixelSize = 0;
|
||||
uint32 BufferSize = 0;
|
||||
EPixelFormat UPixelFormat = EPixelFormat::PF_Unknown;
|
||||
NeoKinect::KinectPixelFormat KPixelFormat = NeoKinect::KinectPixelFormat::None;
|
||||
EKinectFrame UFrameType = EKinectFrame::Count;
|
||||
NeoKinect::KinectFrameType KFrameType = NeoKinect::KinectFrameType::Count;
|
||||
NeoKinect::KinectCoordinateSpace TargetSpace = NeoKinect::KinectCoordinateSpace::Count;
|
||||
bool bIsSRGB = false;
|
||||
bool bRemapped = false;
|
||||
|
||||
~FFrameTextureData();
|
||||
|
||||
inline bool IsTextureValid() const;
|
||||
void Release();
|
||||
NeoKinect::FrameDescription GetFrameDescription(const NeoKinect::KinectSensor *Kinect) const;
|
||||
};
|
||||
|
||||
/**
|
||||
* Access the Kinect v2 sensor capabilities.
|
||||
*
|
||||
* When using any Remapped type of frame or any Coordinate Remapping methods,
|
||||
* Depth frame use will automatically be switched on, since those functionalities
|
||||
* depend on it. But don't worry about performance. The depth frame will only
|
||||
* get written and updated to a texture on the GPU if the user explicitly
|
||||
* asks for it using SetUseFrame().
|
||||
*
|
||||
* When using Face Tracking, Body Tracking will also be automatically switched on.
|
||||
* Face Tracking depends on Body Tracking for the head pivot position and for
|
||||
* fixing issues when looking for a Face.
|
||||
*/
|
||||
UCLASS(Category = "NeoKinect", BlueprintType)
|
||||
class NEOKINECTUNREAL_API UNeoKinectManager : public UObject
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
|
||||
/** Stores the bodies that Kinect can track. The array is created only once
|
||||
* and lasts until the end of the program. */
|
||||
static TArray<UNeoKinectBody*> Bodies;
|
||||
/** Stores the faces that Kinect can track. The array is created only once
|
||||
* and lasts until the end of the program. */
|
||||
static TArray<UNeoKinectFace*> Faces;
|
||||
/** Stores all the possible frame types Kinect or the program can create.
|
||||
* The array is created only once and lasts until the end of the program. */
|
||||
static FFrameTextureData FramesData[static_cast<int32>(EKinectFrame::Count)];
|
||||
|
||||
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FBodyTrackDelegate, UNeoKinectBody*, Body);
|
||||
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FFaceTrackDelegate, UNeoKinectFace*, Face);
|
||||
|
||||
/** Constructor: used only to bind the static dispatcher to the bodies/faces
|
||||
* tracking events. */
|
||||
UNeoKinectManager();
|
||||
/** Destructor: unbind the static dispatcher events and makes sure to
|
||||
* un-initialize the Kinect sensor before the program exits. */
|
||||
~UNeoKinectManager() override;
|
||||
|
||||
/*-------------------------------------------------------------------------
|
||||
EVENTS
|
||||
-------------------------------------------------------------------------*/
|
||||
|
||||
private:
|
||||
/** Class singleton used to give Blueprints access to the tracking events. */
|
||||
static UNeoKinectManager* EventsDispatcher;
|
||||
|
||||
public:
|
||||
/** Called when the Kinect sensor has just detected a new user's body. */
|
||||
UPROPERTY(BlueprintAssignable, Category = "NeoKinect")
|
||||
FBodyTrackDelegate OnBodyBeginTrack;
|
||||
|
||||
/** Called when the Kinect sensor has just lost track of a user's body. */
|
||||
UPROPERTY(BlueprintAssignable, Category = "NeoKinect")
|
||||
FBodyTrackDelegate OnBodyLost;
|
||||
|
||||
/** Called when the Kinect sensor has just detected a new user's face. */
|
||||
UPROPERTY(BlueprintAssignable, Category = "NeoKinect")
|
||||
FFaceTrackDelegate OnFaceBeginTrack;
|
||||
|
||||
/** Called when the Kinect sensor has just lost track of a user's face. */
|
||||
UPROPERTY(BlueprintAssignable, Category = "NeoKinect")
|
||||
FFaceTrackDelegate OnFaceLost;
|
||||
|
||||
/**
|
||||
* Use the object result of this function to bind to the Kinect body/face
|
||||
* tracking events. You can also bind to individual bodies/faces using their
|
||||
* respective objects.
|
||||
*
|
||||
* @return Kinect events dispatcher object.
|
||||
*/
|
||||
UFUNCTION(BlueprintPure, Category = "NeoKinect")
|
||||
static UNeoKinectManager* GetKinectEventsDispatcher();
|
||||
|
||||
/*////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
BLUEPRINT FUNCTIONS
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
/*
|
||||
* BASICS
|
||||
*/
|
||||
|
||||
/**
|
||||
* Initializes the Kinect sensor.
|
||||
*
|
||||
* Even though this function always works without errors, check if the sensor
|
||||
* has really started by calling IsKinectAvailable. It can take a while, so
|
||||
* check in intervals until it does.
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, meta = (DisplayName = "Init Kinect Sensor", Keywords = "turn on initialize enable start begin"), Category = "NeoKinect")
|
||||
static void InitSensor();
|
||||
|
||||
/**
|
||||
* Returns if sensor is already on.
|
||||
*
|
||||
* It may take a while for the sensor to initialize, so keep checking. If it
|
||||
* takes too long, either there's no connected sensor or it's malfunctioning.
|
||||
*
|
||||
* @return true if Sensor is available and operational.
|
||||
*/
|
||||
UFUNCTION(BlueprintPure, meta = (Keywords = "on working works usable"), Category = "NeoKinect")
|
||||
static bool IsKinectAvailable();
|
||||
|
||||
/**
|
||||
* Use very carefully! Will shutdown the sensor and also invalidate any of its
|
||||
* frame textures (color, depth etc). Make sure you call this when none of them
|
||||
* are in use (preferably when quitting the game). Remember: with great power,
|
||||
* comes great responsibility!
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, meta = (DisplayName = "Uninit Kinect Sensor", Keywords = "turn off uninitialize disable end"), Category = "NeoKinect")
|
||||
static void UninitSensor();
|
||||
|
||||
/**
|
||||
* Retrieves the Kinect sensor or NULL if not initialized.
|
||||
*
|
||||
* @return The Kinect sensor or NULL if not initialized.
|
||||
*/
|
||||
static NeoKinect::KinectSensor* GetSensor();
|
||||
|
||||
/**
|
||||
* Gets sensor tilt in degrees. It represents the pitch of the sensor in Unreal
|
||||
* coordinates (X pointing the same direction as the Kinect camera). Negative
|
||||
* values mean it's pointing down.
|
||||
*
|
||||
* @return Kinect sensor tilt in degrees. Negative values mean it's pointing down.
|
||||
*/
|
||||
UFUNCTION(BlueprintPure, Category = "NeoKinect", meta = (DisplayName = "Get Kinect Tilt"))
|
||||
static float GetSensorTilt();
|
||||
|
||||
/**
|
||||
* Kinect height from the ground and ground normal. Only works if the sensor
|
||||
* can see the ground.
|
||||
* Bodies reading must be in use, as this data is read from the Body frames.
|
||||
*
|
||||
* @param Height Kinect distance from the ground
|
||||
* @return Ground normal
|
||||
*/
|
||||
UFUNCTION(BlueprintPure, Category = "NeoKinect", meta = (DisplayName = "Get Kinect Ground Plane", Keywords = "height normal location floor"))
|
||||
static FVector GetGroundPlane(float &Height);
|
||||
|
||||
/*
|
||||
* EXPOSURE CONTROL
|
||||
*/
|
||||
|
||||
/*
|
||||
* Fully automatic exposure setting with exposure compensation. Only affects the Color frame.
|
||||
* @param exposureCompensation Negative value gives an underexposed image,
|
||||
* positive gives an overexposed image.
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category = "NeoKinect|ColorExposure", meta = (DisplayName = "Set Kinect to Auto Exposure", Keywords = "image frame color brightness"))
|
||||
static void SetAutoExposure(float exposureCompensation = 0.0f);
|
||||
|
||||
/*
|
||||
* Sets a pseudo-exposure time in ms. Only affects the Color frame.
|
||||
*
|
||||
* The actual frame integration time is set to a multiple of fluorescent light period
|
||||
* that is shorter than the requested time e.g. requesting 16 ms will set 10 ms
|
||||
* in Australia (100Hz), 8.33 ms in USA (120Hz).
|
||||
* The gain is automatically set to compensate for the reduced integration time.
|
||||
*
|
||||
* Requesting less than a single fluorescent light period will set the integration time
|
||||
* to the requested value.
|
||||
*
|
||||
* To set the shortest non-flickering integration period for any country, simply set
|
||||
* a pseudo-exposure time of between (10.0, 16.667) ms, which will automatically drop
|
||||
* the integration time to 10 or 8.3 ms depending on country, while setting the analog
|
||||
* gain control to a brighter value.
|
||||
*
|
||||
* @param pseudoExposureTime Pseudo-exposure time in ms. [0.0, 640]ms
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category = "NeoKinect|ColorExposure", meta = (DisplayName = "Set Kinect to Semi Auto Exposure", Keywords = "image frame color brightness"))
|
||||
static void SetSemiAutoExposure(float pseudoExposureTime);
|
||||
|
||||
/*
|
||||
* Manually set frame exposure time in ms and analog gain. Only affects the Color frame.
|
||||
* @param frameExposureTime Time of exposure for each frame, in ms. [0.0, 640]ms
|
||||
* @param analogGain Exposure analog gain.
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category = "NeoKinect|ColorExposure", meta = (DisplayName = "Set Kinect to Manual Exposure", Keywords = "image frame color brightness"))
|
||||
static void SetManualExposure(float frameExposureTime, float analogGain);
|
||||
|
||||
/*
|
||||
* COORDINATE REMAPPING
|
||||
*/
|
||||
|
||||
/**
|
||||
* Converts a 3D location in Camera space to Color space with depth, so the new
|
||||
* location matches the Color frame position from camera's point of view
|
||||
* without turning into 2D.
|
||||
*
|
||||
* @param CameraLocation The 3D Camera space coordinate to be converted (in Unreal units)
|
||||
* @return Realigned 3D coordinate that matches the Color frame from the camera's
|
||||
* point of view (in Unreal units)
|
||||
*/
|
||||
UFUNCTION(BlueprintPure, Category = "NeoKinect|Remapping", Meta = (Keywords = "convert coordinate space remap"))
|
||||
static FVector CameraLocationToColorWithDepth(const FVector& CameraLocation);
|
||||
/** Native NeoKinect implementation of the above */
|
||||
static NeoKinect::Vector3 CameraLocationToColorWithDepth(NeoKinect::Vector3 CameraLocation);
|
||||
|
||||
/**
|
||||
* Remaps a 3D camera location to a 2D point in the Color frame.
|
||||
*
|
||||
* @param CameraLocation The 3D Camera space coordinate to be remapped (in Unreal units)
|
||||
* @return The point location in the Color frame that matches the Camera
|
||||
* location input (in pixels)
|
||||
*/
|
||||
UFUNCTION(BlueprintPure, Category = "NeoKinect|Remapping", Meta = (Keywords = "convert coordinate space remap"))
|
||||
static FVector2D CameraLocationToColorPoint(const FVector& CameraLocation);
|
||||
|
||||
/**
|
||||
* Remaps a 3D camera location to a point in the Depth frame.
|
||||
*
|
||||
* @param CameraLocation The 3D Camera space coordinate to be remapped (in Unreal units)
|
||||
* @return The point location in the Depth frame that matches the Camera
|
||||
* location input (in pixels)
|
||||
*/
|
||||
UFUNCTION(BlueprintPure, Category = "NeoKinect|Remapping", Meta = (Keywords = "convert coordinate space remap"))
|
||||
static FVector2D CameraLocationToDepthPoint(const FVector& CameraLocation);
|
||||
|
||||
/**
|
||||
* Creates a Color with depth point from a Color point only. Viewed from the
|
||||
* camera perspective, this point aligns with the Color frame without losing
|
||||
* it's depth.
|
||||
*
|
||||
* This method uses the remapped Color buffer in Depth space so, for it to
|
||||
* work, the Depth in Color Space frame must be in use.
|
||||
*
|
||||
* @param ColorLocation A point in the Color frame to be remapped (in pixels)
|
||||
* @return The 3D version of the Color point (in Unreal units)
|
||||
*/
|
||||
static NeoKinect::Vector3 ColorWithDepthFromColorOnly(const NeoKinect::Vector2& ColorLocation);
|
||||
|
||||
/**
|
||||
* Remaps a point location from the Depth frame to the Color frame.
|
||||
*
|
||||
* @param DepthPoint A point in the Depth frame (in pixels)
|
||||
* @return Remapped Depth frame point to a Color frame point (in pixels)
|
||||
*/
|
||||
UFUNCTION(BlueprintPure, Category = "NeoKinect|Remapping", Meta = (Keywords = "convert coordinate space remap"))
|
||||
static FVector2D DepthPointToColorPoint(FVector2D DepthPoint);
|
||||
|
||||
/**
|
||||
* Remaps a point location from the Depth frame to the 3D camera space.
|
||||
*
|
||||
* @param DepthPoint A point in the Depth frame (in pixels)
|
||||
* @return Remapped Depth frame point to a 3D Camera location (in Unreal units)
|
||||
*/
|
||||
UFUNCTION(BlueprintPure, Category = "NeoKinect|Remapping", Meta = (Keywords = "convert coordinate space remap"))
|
||||
static FVector DepthPointToCameraLocation(FVector2D DepthPoint);
|
||||
|
||||
/**
|
||||
* Remaps a point from the Color frame to the Depth frame.
|
||||
*
|
||||
* @param ColorPoint A point in the Color frame (in pixels)
|
||||
* @return Color point remapped to its related location in the Depth frame (in pixels)
|
||||
*/
|
||||
UFUNCTION(BlueprintPure, Category = "NeoKinect|Remapping", Meta = (Keywords = "convert coordinate space remap"))
|
||||
static FVector2D ColorPointToDepthPoint(FVector2D ColorPoint);
|
||||
|
||||
|
||||
/*
|
||||
* TEXTURES (Color, Depth etc)
|
||||
*/
|
||||
|
||||
/**
|
||||
* Get a frame horizontal and vertical field of view.
|
||||
*
|
||||
* @param FrameType The frame type to request the FOV from.
|
||||
* @return x = horizontal FOV, y = vertical FOV
|
||||
*/
|
||||
UFUNCTION(BlueprintPure, Category = "NeoKinect|Frames", Meta = (Keywords = "width height FOV texture angle field of view", DisplayName = "Get Kinect FOV"))
|
||||
static FVector2D GetFrameFieldOfView(EKinectFrame FrameType);
|
||||
|
||||
/**
|
||||
* Get a frame texture width and height in pixels.
|
||||
*
|
||||
* @param FrameType The frame type to request the size from.
|
||||
* @return x = width, y = height
|
||||
*/
|
||||
UFUNCTION(BlueprintPure, Category = "NeoKinect|Frames", Meta = (Keywords = "width height texture resolution", DisplayName = "Get Kinect Frame Size"))
|
||||
static FIntPoint GetFrameSize(EKinectFrame FrameType);
|
||||
|
||||
/**
|
||||
* Enables or disables updating of a Kinect frame type.
|
||||
*
|
||||
* @param FrameType The frame type to enable/disable
|
||||
* @param bEnable If the selected frame type should be enabled
|
||||
* @return The Frame texture. Null if Enable = false.
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category = "NeoKinect|Frames", Meta = (Keywords = "texture sensor camera enable disable toggle", DisplayName = "Set Use Kinect Frame"))
|
||||
static UTextureRenderTarget2D* SetUseFrame(EKinectFrame FrameType, bool bEnable);
|
||||
|
||||
/**
|
||||
* Returns if a Kinect frame type is enabled. If it is, it means you can get
|
||||
* it's texture with GetFrame.
|
||||
*
|
||||
* @param FrameType The frame type to ask for.
|
||||
* @return If FrameType is enabled or not.
|
||||
*/
|
||||
UFUNCTION(BlueprintPure, Category = "NeoKinect|Frames", Meta = (Keywords = "texture sensor camera enabled", DisplayName = "Get Is Using Kinect Frame"))
|
||||
static bool GetIsUsingFrame(EKinectFrame FrameType);
|
||||
|
||||
/**
|
||||
* Get the texture that represents the requested frame type.
|
||||
*
|
||||
* @param FrameType The frame type to get the texture from.
|
||||
* @return The texture that represents the requested frame type. Null if it has
|
||||
* not been enabled.
|
||||
*/
|
||||
UFUNCTION(BlueprintPure, Category = "NeoKinect|Frames", Meta = (Keywords = "texture sensor camera", DisplayName = "Get Kinect Frame"))
|
||||
static UTextureRenderTarget2D* GetFrame(EKinectFrame FrameType);
|
||||
|
||||
/**
|
||||
* Gets the depth value from a specific pixel on the Depth frame.
|
||||
*
|
||||
* @param Coordinate A point in the Depth frame (in pixels)
|
||||
* @return The Depth value (distance from the Kinect sensor) in the input point
|
||||
* (in centimeters)
|
||||
*/
|
||||
UFUNCTION(BlueprintPure, Category = "NeoKinect|Frames", Meta = (Keywords = "sample texture sensor camera"))
|
||||
static float GetDepthFramePixel(FVector2D Coordinate);
|
||||
|
||||
/**
|
||||
* Gets the depth value from a specific pixel on the Color frame
|
||||
*
|
||||
* @param Coordinate A point in the Color frame (in pixels)
|
||||
* @return The Depth value (distance from the Kinect sensor) in the input point
|
||||
* (in centimeters)
|
||||
*/
|
||||
UFUNCTION(BlueprintPure, Category = "NeoKinect|Frames", Meta = (Keywords = "sample texture remap"))
|
||||
static float GetDepthFromColorFramePixel(FVector2D Coordinate);
|
||||
|
||||
/**
|
||||
* Gets the color value from a specific pixel on the Color frame.
|
||||
*
|
||||
* @param Coordinate A point in the Color frame (in pixels)
|
||||
* @return The Color value in the input point
|
||||
*/
|
||||
UFUNCTION(BlueprintPure, Category = "NeoKinect|Frames", Meta = (Keywords = "sample texture sensor camera"))
|
||||
static FLinearColor GetColorFramePixel(FVector2D Coordinate);
|
||||
|
||||
/*
|
||||
* BODIES / JOINTS
|
||||
*/
|
||||
|
||||
/**
|
||||
* Array of all bodies (tracked and not tracked).
|
||||
* The bodies in the array are updated automatically, so you don't need to
|
||||
* request the array more than once.
|
||||
*
|
||||
* @return Bodies array.
|
||||
*/
|
||||
UFUNCTION(BlueprintPure, Category = "NeoKinect|Body", meta = (DisplayName = "Get Kinect Bodies"))
|
||||
static TArray<UNeoKinectBody*> GetBodies();
|
||||
|
||||
/**
|
||||
* Array with only the currently tracked bodies.
|
||||
*
|
||||
* @return The currently tracked bodies.
|
||||
*/
|
||||
UFUNCTION(BlueprintPure, Category = "NeoKinect|Body", meta = (DisplayName = "Get Kinect Tracked Bodies"))
|
||||
static TArray<UNeoKinectBody*> GetTrackedBodies();
|
||||
|
||||
/**
|
||||
* Gets tracked body whose Spine Base joint is nearest to the Kinect sensor.
|
||||
*
|
||||
* @param HasBody If there's at least one tracked body.
|
||||
* @return The nearest body from the sensor. Null if no bodies are tracked.
|
||||
*/
|
||||
UFUNCTION(BlueprintPure, Category = "NeoKinect|Body", meta = (DisplayName = "Get Kinect Nearest Body"))
|
||||
static UNeoKinectBody* GetNearestBody(bool& HasBody);
|
||||
|
||||
/**
|
||||
* Toggle the use of Color texture aligned joint locations and orientations.
|
||||
*
|
||||
* Calculating these remapped transforms introduce a small overhead, so it's
|
||||
* disabled by default and reading the Color values from a Joint will return
|
||||
* Zero values until this is enabled.
|
||||
*
|
||||
* @param bUse Enable usage of Joints' Color space transforms?
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category = "NeoKinect|Body", meta = (Keywords = "joints remap"))
|
||||
static void SetUseJointsColorSpaceTransforms(const bool bUse);
|
||||
|
||||
/**
|
||||
* Return if Joints' Color space transforms usage is enabled
|
||||
*
|
||||
* @return True if Joints' Color space transforms usage is enabled
|
||||
*/
|
||||
UFUNCTION(BlueprintPure, Category = "NeoKinect|Body")
|
||||
static bool IsUsingJointsColorSpaceTransforms();
|
||||
|
||||
/**
|
||||
* Cast a Body in the bodies array to its index number.
|
||||
*
|
||||
* @param Body The body instance.
|
||||
* @return Body index.
|
||||
*/
|
||||
UFUNCTION(BlueprintPure, Category = "NeoKinect|Body", Meta = (CompactNodeTitle = "->", Keywords = "convert cast"))
|
||||
static int32 BodyToIndex(const UNeoKinectBody* Body);
|
||||
|
||||
/**
|
||||
* Cast an index to a body from the bodies array.
|
||||
*
|
||||
* @param Index Index.
|
||||
* @return Body instance. If index > 5 or index < 0, it's the body at index 0.
|
||||
*/
|
||||
UFUNCTION(BlueprintPure, Category = "NeoKinect|Body", Meta = (CompactNodeTitle = "->", Keywords = "convert cast"))
|
||||
static UNeoKinectBody* IndexToBody(const int32 Index);
|
||||
|
||||
/**
|
||||
* Cast the Joint type to its joint index number.
|
||||
*
|
||||
* @param Joint Joint type.
|
||||
* @return Index.
|
||||
*/
|
||||
UFUNCTION(BlueprintPure, Category = "NeoKinect|Body|Joints", Meta = (CompactNodeTitle = "->", Keywords = "convert cast", BlueprintThreadSafe))
|
||||
static int32 JointToIndex(const EKinectJointType Joint);
|
||||
|
||||
/**
|
||||
* Cast a joint index to its Joint type.
|
||||
*
|
||||
* @param Index Index.
|
||||
* @return Joint type. If index >= joint count or index < 0, it's Spine Base joint.
|
||||
*/
|
||||
UFUNCTION(BlueprintPure, Category = "NeoKinect|Body|Joints", Meta = (CompactNodeTitle = "->", Keywords = "convert cast"))
|
||||
static EKinectJointType IndexToJoint(const int32 Index);
|
||||
|
||||
/*
|
||||
* FACE TRACKING
|
||||
*/
|
||||
|
||||
/**
|
||||
* Array of faces (tracked and not tracked). The faces instances are updated
|
||||
* automatically, so you don't need to request the array more than once.
|
||||
*
|
||||
* @return Faces array.
|
||||
*/
|
||||
UFUNCTION(BlueprintPure, Category = "NeoKinect|Face", meta = (DisplayName = "Get Kinect Faces"))
|
||||
static TArray<UNeoKinectFace*> GetFaces();
|
||||
|
||||
/**
|
||||
* Array with only the currently tracked faces.
|
||||
*
|
||||
* @return The currently tracked faces.
|
||||
*/
|
||||
UFUNCTION(BlueprintPure, Category = "NeoKinect|Face", meta = (DisplayName = "Get Kinect Tracked Faces"))
|
||||
static TArray<UNeoKinectFace*> GetTrackedFaces();
|
||||
|
||||
/**
|
||||
* Gets tracked face whose Head joint is nearest from the sensor.
|
||||
*
|
||||
* @param bHasAnyFace If there's at least one tracked face.
|
||||
* @return The nearest face from the sensor. Null if no faces are tracked.
|
||||
*/
|
||||
UFUNCTION(BlueprintPure, Category = "NeoKinect|Face", meta = (DisplayName = "Get Kinect Nearest Face"))
|
||||
static UNeoKinectFace* GetNearestFace(bool& bHasAnyFace);
|
||||
|
||||
/**
|
||||
* Cast a Face to its index number.
|
||||
*
|
||||
* @param Face The face instance.
|
||||
* @return Face index.
|
||||
*/
|
||||
UFUNCTION(BlueprintPure, Category = "NeoKinect|Face", Meta = (CompactNodeTitle = "->", Keywords = "convert cast"))
|
||||
static int32 FaceToIndex(const UNeoKinectFace* Face);
|
||||
|
||||
/**
|
||||
* Cast a face index to a Face object.
|
||||
*
|
||||
* @param Index Index.
|
||||
* @return Face instance. If index > 5 or index < 0, it's the face at index 0.
|
||||
*/
|
||||
UFUNCTION(BlueprintPure, Category = "NeoKinect|Face", Meta = (CompactNodeTitle = "->", Keywords = "convert cast"))
|
||||
static UNeoKinectFace* IndexToFace(const int32 Index);
|
||||
|
||||
/*
|
||||
* CONSTANTS
|
||||
*/
|
||||
|
||||
/**
|
||||
* The number of bodies the Kinect device can track simultaneously.
|
||||
*
|
||||
* @return The number of bodies the Kinect device can track simultaneously.
|
||||
*/
|
||||
UFUNCTION(BlueprintPure, Category = "NeoKinect|Constants", meta = (Keywords = "length number size capability"))
|
||||
static int32 GetTrackableBodyCount();
|
||||
|
||||
/**
|
||||
* The number of faces the Kinect device can track simultaneously.
|
||||
*
|
||||
* @return The number of faces the Kinect device can track simultaneously.
|
||||
*/
|
||||
UFUNCTION(BlueprintPure, Category = "NeoKinect|Constants", meta = (Keywords = "length number size capability"))
|
||||
static int32 GetTrackableFaceCount();
|
||||
|
||||
/**
|
||||
* The number of joints the Kinect device can track in each tracked body.
|
||||
*
|
||||
* @return The number of joints the Kinect device can track in each tracked body.
|
||||
*/
|
||||
UFUNCTION(BlueprintPure, Category = "NeoKinect|Constants", meta = (Keywords = "length number size capability"))
|
||||
static int32 GetJointCount();
|
||||
|
||||
/*////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
BLUEPRINT FUNCTIONS END
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
|
||||
private:
|
||||
/** NeoKinect API sensor */
|
||||
static NeoKinect::KinectSensor* Kinect;
|
||||
/** Does the sensor polling */
|
||||
static FKinectThread* KinectThread;
|
||||
/** FTicker handle so we can stop ticking after disconnecting from the sensor */
|
||||
static FTSTicker::FDelegateHandle TickerHandle;
|
||||
|
||||
//TODO: figure out a way to know when a level is unloaded
|
||||
//static bool bAutoShutdown;
|
||||
|
||||
/** Should we read and update the bodies data each frame? */
|
||||
static bool bUsingBodyFrame;
|
||||
/** Should we remap the bodies transforms into 3D Color space each frame? */
|
||||
static bool bUsingJointsColorSpaceTransforms;
|
||||
/** Should we read and update the faces data each frame? */
|
||||
static bool bUsingFaceFrame;
|
||||
|
||||
/** Initialize the bodies, faces and frames arrays */
|
||||
static void InitStatics();
|
||||
/** Updates bodies, faces and frames each frame */
|
||||
static bool OnTick(float DeltaTime);
|
||||
|
||||
static bool IsUsingDepth();
|
||||
static bool IsUsingBodyIndex();
|
||||
static bool IsUsingColor();
|
||||
/** Creates a 2D dynamic texture for frames usage */
|
||||
static UTextureRenderTarget2D* CreateTexture(int32 Width, int32 Height, EPixelFormat PixelFormat, bool sRGB);
|
||||
/** Updates all currently enabled frames. Executed in the Render thread */
|
||||
static void UpdateFrames();
|
||||
/** Delete frames textures memory */
|
||||
static void ReleaseFrameResources(FFrameTextureData&);
|
||||
|
||||
/** Enable/disable bodies frame reading */
|
||||
static void SetUseBodyFrame(bool bUse = true);
|
||||
/** Converts bodies joints transforms to Unreal coordinate space */
|
||||
static void ProcessBodies(const NeoKinect::KinectBody* KBodies);
|
||||
/** Zero all bodies values. Used when disconnecting from the sensor */
|
||||
static void ResetBodies();
|
||||
|
||||
/** Enable/disable faces frame reading */
|
||||
static void SetUseFaceFrame(bool bUse = true);
|
||||
/** Converts faces points transforms to Unreal coordinate space */
|
||||
static void ProcessFaces(const NeoKinect::KinectFace* KFaces);
|
||||
/** Zero all faces values. Used when disconnecting from the sensor */
|
||||
static void ResetFaces();
|
||||
|
||||
UFUNCTION()
|
||||
void BodyBeginTrackBroadcast(int32 BodyIndex);
|
||||
UFUNCTION()
|
||||
void BodyLostBroadcast(int32 BodyIndex);
|
||||
UFUNCTION()
|
||||
void FaceBeginTrackBroadcast(int32 FaceIndex);
|
||||
UFUNCTION()
|
||||
void FaceLostBroadcast(int32 FaceIndex);
|
||||
};
|
||||
+3096
File diff suppressed because it is too large
Load Diff
+298
@@ -0,0 +1,298 @@
|
||||
|
||||
|
||||
/* this ALWAYS GENERATED file contains the definitions for the interfaces */
|
||||
|
||||
|
||||
/* File created by MIDL compiler version 8.00.0595 */
|
||||
/* at Sun Oct 19 12:54:09 2014
|
||||
*/
|
||||
/* Compiler settings for ..\..\idl\Kinect.INPC.idl:
|
||||
Oicf, W1, Zp8, env=Win64 (32b run), target_arch=AMD64 8.00.0595
|
||||
protocol : dce , ms_ext, c_ext, robust
|
||||
error checks: allocation ref bounds_check enum stub_data
|
||||
VC __declspec() decoration level:
|
||||
__declspec(uuid()), __declspec(selectany), __declspec(novtable)
|
||||
DECLSPEC_UUID(), MIDL_INTERFACE()
|
||||
*/
|
||||
/* @@MIDL_FILE_HEADING( ) */
|
||||
|
||||
#pragma warning( disable: 4049 ) /* more than 64k source lines */
|
||||
|
||||
|
||||
/* verify that the <rpcndr.h> version is high enough to compile this file*/
|
||||
#ifndef __REQUIRED_RPCNDR_H_VERSION__
|
||||
#define __REQUIRED_RPCNDR_H_VERSION__ 475
|
||||
#endif
|
||||
|
||||
/* verify that the <rpcsal.h> version is high enough to compile this file*/
|
||||
#ifndef __REQUIRED_RPCSAL_H_VERSION__
|
||||
#define __REQUIRED_RPCSAL_H_VERSION__ 100
|
||||
#endif
|
||||
|
||||
#include "rpc.h"
|
||||
#include "rpcndr.h"
|
||||
|
||||
#ifndef __RPCNDR_H_VERSION__
|
||||
#error this stub requires an updated version of <rpcndr.h>
|
||||
#endif // __RPCNDR_H_VERSION__
|
||||
|
||||
#ifndef COM_NO_WINDOWS_H
|
||||
#include "windows.h"
|
||||
#include "ole2.h"
|
||||
#endif /*COM_NO_WINDOWS_H*/
|
||||
|
||||
#ifndef __Kinect2EINPC_h__
|
||||
#define __Kinect2EINPC_h__
|
||||
|
||||
#if defined(_MSC_VER) && (_MSC_VER >= 1020)
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
/* Forward Declarations */
|
||||
|
||||
#ifndef __INotifyPropertyChanged_FWD_DEFINED__
|
||||
#define __INotifyPropertyChanged_FWD_DEFINED__
|
||||
typedef interface INotifyPropertyChanged INotifyPropertyChanged;
|
||||
|
||||
#endif /* __INotifyPropertyChanged_FWD_DEFINED__ */
|
||||
|
||||
|
||||
#ifndef __IPropertyChangedEventArgs_FWD_DEFINED__
|
||||
#define __IPropertyChangedEventArgs_FWD_DEFINED__
|
||||
typedef interface IPropertyChangedEventArgs IPropertyChangedEventArgs;
|
||||
|
||||
#endif /* __IPropertyChangedEventArgs_FWD_DEFINED__ */
|
||||
|
||||
|
||||
/* header files for imported files */
|
||||
#include "oaidl.h"
|
||||
#include "ocidl.h"
|
||||
#include "mmreg.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"{
|
||||
#endif
|
||||
|
||||
|
||||
/* interface __MIDL_itf_Kinect2EINPC_0000_0000 */
|
||||
/* [local] */
|
||||
|
||||
|
||||
typedef INT_PTR WAITABLE_HANDLE;
|
||||
|
||||
typedef INT64 TIMESPAN;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
extern RPC_IF_HANDLE __MIDL_itf_Kinect2EINPC_0000_0000_v0_0_c_ifspec;
|
||||
extern RPC_IF_HANDLE __MIDL_itf_Kinect2EINPC_0000_0000_v0_0_s_ifspec;
|
||||
|
||||
#ifndef __INotifyPropertyChanged_INTERFACE_DEFINED__
|
||||
#define __INotifyPropertyChanged_INTERFACE_DEFINED__
|
||||
|
||||
/* interface INotifyPropertyChanged */
|
||||
/* [object][local][uuid] */
|
||||
|
||||
|
||||
EXTERN_C const IID IID_INotifyPropertyChanged;
|
||||
|
||||
#if defined(__cplusplus) && !defined(CINTERFACE)
|
||||
|
||||
MIDL_INTERFACE("D27A5C77-32E9-4283-A046-9D693E29E3E7")
|
||||
INotifyPropertyChanged : public IUnknown
|
||||
{
|
||||
public:
|
||||
virtual HRESULT STDMETHODCALLTYPE SubscribePropertyChanged(
|
||||
/* [annotation][out][retval] */
|
||||
_Out_ WAITABLE_HANDLE *waitableHandle) = 0;
|
||||
|
||||
virtual HRESULT STDMETHODCALLTYPE UnsubscribePropertyChanged(
|
||||
/* [annotation][in] */
|
||||
_In_ WAITABLE_HANDLE waitableHandle) = 0;
|
||||
|
||||
virtual HRESULT STDMETHODCALLTYPE GetPropertyChangedEventData(
|
||||
/* [annotation][in] */
|
||||
_In_ WAITABLE_HANDLE waitableHandle,
|
||||
UINT bufferSize,
|
||||
/* [annotation][out][retval] */
|
||||
_Out_writes_z_(bufferSize) WCHAR *propertyName) = 0;
|
||||
|
||||
};
|
||||
|
||||
|
||||
#else /* C style interface */
|
||||
|
||||
typedef struct INotifyPropertyChangedVtbl
|
||||
{
|
||||
BEGIN_INTERFACE
|
||||
|
||||
HRESULT ( STDMETHODCALLTYPE *QueryInterface )(
|
||||
INotifyPropertyChanged * This,
|
||||
/* [in] */ REFIID riid,
|
||||
/* [annotation][iid_is][out] */
|
||||
_COM_Outptr_ void **ppvObject);
|
||||
|
||||
ULONG ( STDMETHODCALLTYPE *AddRef )(
|
||||
INotifyPropertyChanged * This);
|
||||
|
||||
ULONG ( STDMETHODCALLTYPE *Release )(
|
||||
INotifyPropertyChanged * This);
|
||||
|
||||
HRESULT ( STDMETHODCALLTYPE *SubscribePropertyChanged )(
|
||||
INotifyPropertyChanged * This,
|
||||
/* [annotation][out][retval] */
|
||||
_Out_ WAITABLE_HANDLE *waitableHandle);
|
||||
|
||||
HRESULT ( STDMETHODCALLTYPE *UnsubscribePropertyChanged )(
|
||||
INotifyPropertyChanged * This,
|
||||
/* [annotation][in] */
|
||||
_In_ WAITABLE_HANDLE waitableHandle);
|
||||
|
||||
HRESULT ( STDMETHODCALLTYPE *GetPropertyChangedEventData )(
|
||||
INotifyPropertyChanged * This,
|
||||
/* [annotation][in] */
|
||||
_In_ WAITABLE_HANDLE waitableHandle,
|
||||
UINT bufferSize,
|
||||
/* [annotation][out][retval] */
|
||||
_Out_writes_z_(bufferSize) WCHAR *propertyName);
|
||||
|
||||
END_INTERFACE
|
||||
} INotifyPropertyChangedVtbl;
|
||||
|
||||
interface INotifyPropertyChanged
|
||||
{
|
||||
CONST_VTBL struct INotifyPropertyChangedVtbl *lpVtbl;
|
||||
};
|
||||
|
||||
|
||||
|
||||
#ifdef COBJMACROS
|
||||
|
||||
|
||||
#define INotifyPropertyChanged_QueryInterface(This,riid,ppvObject) \
|
||||
( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) )
|
||||
|
||||
#define INotifyPropertyChanged_AddRef(This) \
|
||||
( (This)->lpVtbl -> AddRef(This) )
|
||||
|
||||
#define INotifyPropertyChanged_Release(This) \
|
||||
( (This)->lpVtbl -> Release(This) )
|
||||
|
||||
|
||||
#define INotifyPropertyChanged_SubscribePropertyChanged(This,waitableHandle) \
|
||||
( (This)->lpVtbl -> SubscribePropertyChanged(This,waitableHandle) )
|
||||
|
||||
#define INotifyPropertyChanged_UnsubscribePropertyChanged(This,waitableHandle) \
|
||||
( (This)->lpVtbl -> UnsubscribePropertyChanged(This,waitableHandle) )
|
||||
|
||||
#define INotifyPropertyChanged_GetPropertyChangedEventData(This,waitableHandle,bufferSize,propertyName) \
|
||||
( (This)->lpVtbl -> GetPropertyChangedEventData(This,waitableHandle,bufferSize,propertyName) )
|
||||
|
||||
#endif /* COBJMACROS */
|
||||
|
||||
|
||||
#endif /* C style interface */
|
||||
|
||||
|
||||
|
||||
|
||||
#endif /* __INotifyPropertyChanged_INTERFACE_DEFINED__ */
|
||||
|
||||
|
||||
#ifndef __IPropertyChangedEventArgs_INTERFACE_DEFINED__
|
||||
#define __IPropertyChangedEventArgs_INTERFACE_DEFINED__
|
||||
|
||||
/* interface IPropertyChangedEventArgs */
|
||||
/* [object][local][uuid] */
|
||||
|
||||
|
||||
EXTERN_C const IID IID_IPropertyChangedEventArgs;
|
||||
|
||||
#if defined(__cplusplus) && !defined(CINTERFACE)
|
||||
|
||||
MIDL_INTERFACE("574E9321-9DD9-41C8-BC51-C11F3F38D6B5")
|
||||
IPropertyChangedEventArgs : public IUnknown
|
||||
{
|
||||
public:
|
||||
virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_PropertyName(
|
||||
UINT bufferSize,
|
||||
/* [annotation][out][retval] */
|
||||
_Out_writes_z_(bufferSize) WCHAR *propertyName) = 0;
|
||||
|
||||
};
|
||||
|
||||
|
||||
#else /* C style interface */
|
||||
|
||||
typedef struct IPropertyChangedEventArgsVtbl
|
||||
{
|
||||
BEGIN_INTERFACE
|
||||
|
||||
HRESULT ( STDMETHODCALLTYPE *QueryInterface )(
|
||||
IPropertyChangedEventArgs * This,
|
||||
/* [in] */ REFIID riid,
|
||||
/* [annotation][iid_is][out] */
|
||||
_COM_Outptr_ void **ppvObject);
|
||||
|
||||
ULONG ( STDMETHODCALLTYPE *AddRef )(
|
||||
IPropertyChangedEventArgs * This);
|
||||
|
||||
ULONG ( STDMETHODCALLTYPE *Release )(
|
||||
IPropertyChangedEventArgs * This);
|
||||
|
||||
/* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_PropertyName )(
|
||||
IPropertyChangedEventArgs * This,
|
||||
UINT bufferSize,
|
||||
/* [annotation][out][retval] */
|
||||
_Out_writes_z_(bufferSize) WCHAR *propertyName);
|
||||
|
||||
END_INTERFACE
|
||||
} IPropertyChangedEventArgsVtbl;
|
||||
|
||||
interface IPropertyChangedEventArgs
|
||||
{
|
||||
CONST_VTBL struct IPropertyChangedEventArgsVtbl *lpVtbl;
|
||||
};
|
||||
|
||||
|
||||
|
||||
#ifdef COBJMACROS
|
||||
|
||||
|
||||
#define IPropertyChangedEventArgs_QueryInterface(This,riid,ppvObject) \
|
||||
( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) )
|
||||
|
||||
#define IPropertyChangedEventArgs_AddRef(This) \
|
||||
( (This)->lpVtbl -> AddRef(This) )
|
||||
|
||||
#define IPropertyChangedEventArgs_Release(This) \
|
||||
( (This)->lpVtbl -> Release(This) )
|
||||
|
||||
|
||||
#define IPropertyChangedEventArgs_get_PropertyName(This,bufferSize,propertyName) \
|
||||
( (This)->lpVtbl -> get_PropertyName(This,bufferSize,propertyName) )
|
||||
|
||||
#endif /* COBJMACROS */
|
||||
|
||||
|
||||
#endif /* C style interface */
|
||||
|
||||
|
||||
|
||||
|
||||
#endif /* __IPropertyChangedEventArgs_INTERFACE_DEFINED__ */
|
||||
|
||||
|
||||
/* Additional Prototypes for ALL interfaces */
|
||||
|
||||
/* end of Additional Prototypes */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
+10667
File diff suppressed because it is too large
Load Diff
BIN
Binary file not shown.
BIN
Binary file not shown.
+68
@@ -0,0 +1,68 @@
|
||||
#pragma once
|
||||
#include <memory>
|
||||
#include "NuiSensor.h"
|
||||
#include "Lockable.h"
|
||||
|
||||
namespace Xbox { namespace Kinect { class NuiSensor; } }
|
||||
|
||||
class KinectExposure
|
||||
{
|
||||
public:
|
||||
KinectExposure() = default;
|
||||
|
||||
// Fully automatic exposure setting.
|
||||
// Exposure compensation: negative value gives an underexposed image,
|
||||
// positive gives an overexposed image.
|
||||
void SetAutoExposure(float exposure_compensation = 0);
|
||||
|
||||
// Sets a pseudo-exposure time in ms, value in range [0.0, 640] ms.
|
||||
//
|
||||
// The actual frame integration time is set to a multiple of fluorescent light period
|
||||
// that is shorter than the requested time e.g. requesting 16 ms will set 10 ms
|
||||
// in Australia (100Hz), 8.33 ms in USA (120Hz).
|
||||
// The gain is automatically set to compensate for the reduced integration time.
|
||||
//
|
||||
// Requesting less than a single fluorescent light period will set the integration time
|
||||
// to the requested value.
|
||||
//
|
||||
// To set the shortest non-flickering integration period for any country, simply set
|
||||
// a pseudo-exposure time of between (10.0, 16.667) ms, which will automatically drop
|
||||
// the integration time to 10 or 8.3 ms depending on country, while setting the analog
|
||||
// gain control to a brighter value.
|
||||
void SetSemiAutoExposure(float pseudo_exposure_time_ms);
|
||||
|
||||
// Manually set true exposure time and analog gain.
|
||||
void SetManualExposure(float integration_time_ms, float analog_gain);
|
||||
|
||||
// Use one of the SET commands.
|
||||
void SetFloat(Xbox::Kinect::NUISENSOR_RGB_COMMAND_TYPE command, float value);
|
||||
void SetUint(Xbox::Kinect::NUISENSOR_RGB_COMMAND_TYPE command, uint32_t value);
|
||||
|
||||
// Use one of the GET commands.
|
||||
uint32_t GetCommand(Xbox::Kinect::NUISENSOR_RGB_COMMAND_TYPE command);
|
||||
float GetCommandFloat(Xbox::Kinect::NUISENSOR_RGB_COMMAND_TYPE command);
|
||||
|
||||
private:
|
||||
LockableSharedPtr<Xbox::Kinect::NuiSensor> _sensor;
|
||||
|
||||
void Connect();
|
||||
|
||||
//private Dictionary<NUISENSOR_RGB_COMMAND_TYPE, uint> GetCommands(NUISENSOR_RGB_COMMAND_TYPE[] commands)
|
||||
//{
|
||||
// NUISENSOR_RGB_CHANGE_STREAM_SETTING setting;
|
||||
// NUISENSOR_RGB_CHANGE_STREAM_SETTING_REPLY reply;
|
||||
|
||||
// setting.SequenceId = 0;
|
||||
// setting.Commands = commands.Select(command = > new NUISENSOR_RGB_CHANGE_SETTING_CMD{ Cmd = (uint)command, Arg = 0 }).ToArray();
|
||||
|
||||
// lock(_sensor)
|
||||
// {
|
||||
// _sensor.ColorChangeCameraSettings(ref setting, out reply);
|
||||
// }
|
||||
|
||||
// return commands.Zip(reply.Status, (c, r) = > { return Tuple.Create(c, r.Data); }).ToDictionary(t = > t.Item1, t = > t.Item2);
|
||||
//}
|
||||
|
||||
uint32_t SetCommand(Xbox::Kinect::NUISENSOR_RGB_COMMAND_TYPE command, uint32_t argument);
|
||||
void SetCommand(Xbox::Kinect::NUISENSOR_RGB_COMMAND_TYPE command, float argument);
|
||||
};
|
||||
+108
@@ -0,0 +1,108 @@
|
||||
#pragma once
|
||||
#ifndef LOCKABLE_H_
|
||||
#define LOCKABLE_H_
|
||||
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
|
||||
// Acts like a pointer to the locked object, but holds a lock for the given variable.
|
||||
template<typename T>
|
||||
class Locked
|
||||
{
|
||||
public:
|
||||
Locked(T& obj, std::mutex& mutex) : _obj(obj), _mutex(mutex) { _mutex.lock(); }
|
||||
Locked& operator=(const Locked&) = delete;
|
||||
~Locked() { _mutex.unlock(); }
|
||||
// Access members of the locked value.
|
||||
const T* operator->() const noexcept { return &_obj; }
|
||||
// Access members of the locked value.
|
||||
T* operator->() noexcept { return &_obj; }
|
||||
// Access the locked value.
|
||||
const T& operator*() const noexcept { return _obj; }
|
||||
// Access the locked value.
|
||||
T& operator*() noexcept { return _obj; }
|
||||
|
||||
private:
|
||||
T& _obj;
|
||||
std::mutex& _mutex;
|
||||
};
|
||||
|
||||
// Lockable value type.
|
||||
template<typename T>
|
||||
class Lockable
|
||||
{
|
||||
public:
|
||||
// Only exists if T() exists.
|
||||
Lockable() = default;
|
||||
explicit Lockable(T obj) : _obj(std::move(obj)) {}
|
||||
// Lock-guards the value for the lifetime of the returned object.
|
||||
auto lock() const noexcept { return Locked<const T>(_obj, _mutex); }
|
||||
// Lock-guards the value for the lifetime of the returned object.
|
||||
auto lock() noexcept { return Locked<T>(_obj, _mutex); }
|
||||
|
||||
private:
|
||||
T _obj;
|
||||
mutable std::mutex _mutex;
|
||||
};
|
||||
|
||||
|
||||
// Acts like a shared_ptr, but holds a lock for the given pointer.
|
||||
template<typename TPtr>
|
||||
class LockedPtr
|
||||
{
|
||||
public:
|
||||
LockedPtr(TPtr& obj, std::mutex& mutex) : _obj(obj), _mutex(mutex) { _mutex.lock(); }
|
||||
LockedPtr& operator=(const LockedPtr&) = delete;
|
||||
~LockedPtr() { _mutex.unlock(); }
|
||||
// Access members of the locked shared_ptr.
|
||||
auto* operator->() const noexcept { return _obj.get(); }
|
||||
// Dereference the locked shared_ptr.
|
||||
auto& operator*() const noexcept { return *_obj; }
|
||||
// Test if the locked shared_ptr is null.
|
||||
explicit operator bool() const noexcept { return bool(_obj); }
|
||||
// Assign to the locked shared_ptr (assigns the shared_ptr, not the inner T value).
|
||||
void operator=(TPtr obj) { _obj = std::move(obj); }
|
||||
// Reset the locked shared_ptr.
|
||||
void reset() noexcept { _obj.reset(); }
|
||||
// Get the pointer type.
|
||||
// WARNING: this is potentially unsafe since the data can be passed around unlocked.
|
||||
TPtr value() const noexcept { return _obj; }
|
||||
|
||||
private:
|
||||
TPtr& _obj;
|
||||
std::mutex& _mutex;
|
||||
};
|
||||
|
||||
// Lockable templated pointer type.
|
||||
template<typename TPtr>
|
||||
class LockablePtr
|
||||
{
|
||||
public:
|
||||
LockablePtr()
|
||||
: _obj()
|
||||
, _mutex(std::make_shared<std::mutex>())
|
||||
{
|
||||
}
|
||||
explicit LockablePtr(TPtr obj)
|
||||
: _obj(std::move(obj))
|
||||
, _mutex(std::make_shared<std::mutex>())
|
||||
{
|
||||
}
|
||||
// Lock-guards the pointer for the lifetime of the returned object.
|
||||
auto lock() const noexcept { return LockedPtr<const TPtr>(_obj, *_mutex); }
|
||||
// Lock-guards the pointer for the lifetime of the returned object.
|
||||
auto lock() noexcept { return LockedPtr<TPtr>(_obj, *_mutex); }
|
||||
|
||||
private:
|
||||
TPtr _obj;
|
||||
mutable std::shared_ptr<std::mutex> _mutex; // Shared so object is copyable e.g. TPtr is shared_ptr<T>
|
||||
};
|
||||
|
||||
|
||||
template<typename T>
|
||||
using LockableSharedPtr = LockablePtr<std::shared_ptr<T>>;
|
||||
|
||||
template<typename T>
|
||||
using LockableUniquePtr = LockablePtr<std::unique_ptr<T>>;
|
||||
|
||||
#endif // LOCKABLE_H_
|
||||
+479
@@ -0,0 +1,479 @@
|
||||
#pragma once
|
||||
|
||||
#include <Windows.h>
|
||||
#include <Ole2.h>
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include <Kinect.h>
|
||||
#include <Kinect.Face.h>
|
||||
|
||||
#include <KinectExposure.h>
|
||||
|
||||
namespace NeoKinect
|
||||
{
|
||||
constexpr unsigned int KinectColorWidth = 1920;
|
||||
constexpr unsigned int KinectColorHeight = 1080;
|
||||
|
||||
constexpr unsigned int KinectDepthWidth = 512;
|
||||
constexpr unsigned int KinectDepthHeight = 424;
|
||||
|
||||
constexpr unsigned int KinectBodyCount = BODY_COUNT;
|
||||
constexpr unsigned int KinectJointCount = JointType_Count;
|
||||
|
||||
struct Vector2
|
||||
{
|
||||
float x, y;
|
||||
|
||||
Vector2() : x(0.f), y(0.f) {}
|
||||
Vector2(float xy) : x(xy), y(xy) {}
|
||||
Vector2(float x, float y) : x(x), y(y) {}
|
||||
explicit Vector2(const DepthSpacePoint& dsp) : x(dsp.X), y(dsp.Y) {}
|
||||
explicit Vector2(const ColorSpacePoint& csp) : x(csp.X), y(csp.Y) {}
|
||||
explicit Vector2(const PointF& pf) : x(pf.X), y(pf.Y) {}
|
||||
|
||||
template<typename T>
|
||||
inline Vector2& operator=(const T& b);
|
||||
|
||||
inline DepthSpacePoint toDepthSpacePoint() const;
|
||||
inline ColorSpacePoint toColorSpacePoint() const;
|
||||
};
|
||||
|
||||
struct Vector3
|
||||
{
|
||||
float x, y, z;
|
||||
|
||||
Vector3() : x(0.f), y(0.f), z(0.f) {}
|
||||
Vector3(float xyz) : x(xyz), y(xyz), z(xyz) {}
|
||||
Vector3(float x, float y, float z) : x(x), y(y), z(z) {}
|
||||
explicit Vector3(const CameraSpacePoint& csp) : x(csp.X), y(csp.Y), z(csp.Z) {}
|
||||
|
||||
inline Vector3& operator=(const CameraSpacePoint& b);
|
||||
inline Vector3& operator/=(const float& b);
|
||||
inline const Vector3 operator/(const float& b) const;
|
||||
inline Vector3& operator*=(const float& b);
|
||||
inline const Vector3 operator*(const float& b) const;
|
||||
|
||||
inline bool IsZero() const;
|
||||
float magnitude() const; // not inline as it uses math header and I didn't want to polute the headers
|
||||
inline Vector3 normalized() const;
|
||||
|
||||
inline CameraSpacePoint toCameraSpacePoint() const;
|
||||
};
|
||||
|
||||
enum class KinectFrameType : BYTE
|
||||
{
|
||||
Color = 0,
|
||||
Depth = 1,
|
||||
Body = 2,
|
||||
BodyIndex = 3,
|
||||
Infrared = 4,
|
||||
LongExposureInfrared = 5,
|
||||
Face = 6,
|
||||
Count = 7
|
||||
};
|
||||
|
||||
enum class KinectCoordinateSpace : BYTE
|
||||
{
|
||||
Camera = 0,
|
||||
Depth = 1,
|
||||
Color = 2,
|
||||
Count = 3
|
||||
};
|
||||
|
||||
enum class KinectPixelFormat : BYTE
|
||||
{
|
||||
B8G8R8A8 = 0,
|
||||
G8 = 1,
|
||||
G16 = 2,
|
||||
Bayer = 3,
|
||||
None = 4,
|
||||
Count = 5
|
||||
};
|
||||
|
||||
struct KinectJoint
|
||||
{
|
||||
Vector4 orientation;
|
||||
CameraSpacePoint position;
|
||||
TrackingState trackingState;
|
||||
JointType type;
|
||||
};
|
||||
|
||||
struct KinectBodyEdges
|
||||
{
|
||||
bool left, right, top, bottom;
|
||||
};
|
||||
|
||||
struct KinectBody
|
||||
{
|
||||
UINT64 trackingId;
|
||||
bool isTracked, isRestricted;
|
||||
|
||||
KinectBodyEdges clippedEdges;
|
||||
|
||||
HandState handLeftState;
|
||||
TrackingState handLeftTrackingState;
|
||||
|
||||
HandState handRightState;
|
||||
TrackingState handRightTrackingState;
|
||||
|
||||
TrackingState leanTrackingState;
|
||||
Vector2 lean;
|
||||
|
||||
KinectJoint joints[JointType_Count];
|
||||
};
|
||||
|
||||
struct KinectFacePoints
|
||||
{
|
||||
Vector2 leftEye;
|
||||
Vector2 rightEye;
|
||||
Vector2 nose;
|
||||
Vector2 leftMouthCorner;
|
||||
Vector2 rightMouthCorner;
|
||||
};
|
||||
|
||||
struct KinectFace
|
||||
{
|
||||
UINT64 trackingId;
|
||||
// face bounding box in color space
|
||||
RectI boxColor;
|
||||
// face bounding box in infrared space
|
||||
RectI boxInfrared;
|
||||
// face rotation in quaternion
|
||||
Vector4 rotation;
|
||||
// face pivot point. The same as the skeleton head joint. It's here for conveninence.
|
||||
CameraSpacePoint pivot;
|
||||
// face alignment points in color space
|
||||
KinectFacePoints pointsColor;
|
||||
// face alignment points in infrared space
|
||||
KinectFacePoints pointsInfrared;
|
||||
|
||||
DetectionResult isEngaged;
|
||||
DetectionResult isHappy;
|
||||
DetectionResult isLookingAway;
|
||||
DetectionResult isMouthMoved;
|
||||
DetectionResult isMouthOpen;
|
||||
DetectionResult isLeftEyeClosed;
|
||||
DetectionResult isRightEyeClosed;
|
||||
DetectionResult isWearingGlasses;
|
||||
|
||||
bool bIsTracked;
|
||||
};
|
||||
|
||||
struct FrameDescription
|
||||
{
|
||||
bool valid;
|
||||
|
||||
float diagonalFieldOfView,
|
||||
horizontalFieldOfView,
|
||||
verticalFieldOfView;
|
||||
|
||||
UINT bytesPerPixel;
|
||||
UINT lengthInPixels;
|
||||
int width, height;
|
||||
|
||||
FrameDescription() : valid(false) {}
|
||||
};
|
||||
|
||||
class KinectSensor
|
||||
{
|
||||
public:
|
||||
KinectSensor();
|
||||
~KinectSensor();
|
||||
|
||||
/*
|
||||
Opens the Microsoft API to the sensor.
|
||||
@return Is the sensor API working?
|
||||
*/
|
||||
bool InitSensor();
|
||||
/*
|
||||
Is the sensor on already?
|
||||
It may take some time for the sensor to effectivelly turn on. Keep trying.
|
||||
If it's taking too long, there's probably no connected sensor or it's not working.
|
||||
@return True if the sensor is on.
|
||||
*/
|
||||
bool IsSensorAvailable();
|
||||
/*
|
||||
Is the Microsoft Kinect API responding?
|
||||
@return True if the sensor API is working.
|
||||
*/
|
||||
bool IsSensorOpen();
|
||||
/*
|
||||
Shuts the sensor down and closes the Microsoft API to it.
|
||||
@return True if it worked.
|
||||
*/
|
||||
bool UninitSensor();
|
||||
|
||||
|
||||
/*
|
||||
A Vector4 containing the floor normal (xyz) and height (w).
|
||||
Valid only if frequently locking and unlocking Body frame. That's when
|
||||
it gets updated.
|
||||
@return A Vector4 containing the floor normal (xyz) and height (w).
|
||||
*/
|
||||
Vector4 GetFloorClipPlane();
|
||||
/*
|
||||
Uses the floor clip plane to calculate how much the sensor is tilted.
|
||||
@return The sensor tilt in degrees. Looking down gives negative values.
|
||||
*/
|
||||
float GetSensorTilt();
|
||||
|
||||
FrameDescription GetFrameDescription(const KinectFrameType frameType) const;
|
||||
|
||||
bool SetUseFrame(const KinectFrameType frameType, const bool use = true);
|
||||
bool GetIsUsingFrame(const KinectFrameType frameType) const;
|
||||
bool LockLatestFrame(KinectFrameType frameType);
|
||||
void UnlockLatestFrame(KinectFrameType frameType);
|
||||
bool GetIsFrameLocked(KinectFrameType frameType) const;
|
||||
bool GetLatestFrameData(const KinectFrameType frameType, BYTE *& buffer, const KinectPixelFormat pixelFormat = KinectPixelFormat::None);
|
||||
bool GetLatestFrameData(KinectBody bodies[BODY_COUNT]);
|
||||
bool GetLatestFrameData(KinectFace faces[BODY_COUNT]);
|
||||
|
||||
/*
|
||||
Gets a frame remapped to another coordinate system.
|
||||
All the remapping methods depend on the Depth frame. So make sure you're
|
||||
frequently locking and unlocking it.
|
||||
@return True if it worked.
|
||||
*/
|
||||
bool GetRemappedFrameData(
|
||||
const KinectFrameType frameType, const KinectCoordinateSpace targetSpace,
|
||||
BYTE *const buffer, const KinectPixelFormat pixelFormat);
|
||||
/*
|
||||
Gets a point remapped to another coordinate system.
|
||||
All the remapping methods depend on the Depth frame. So make sure you're
|
||||
frequently locking and unlocking it.
|
||||
@return True if it worked.
|
||||
*/
|
||||
bool GetRemappedPoint(
|
||||
const KinectCoordinateSpace fromSpace, const KinectCoordinateSpace toSpace,
|
||||
const Vector2& srcPoint, Vector2& resultPoint);
|
||||
bool GetRemappedPoint(
|
||||
const KinectCoordinateSpace fromSpace, const KinectCoordinateSpace toSpace,
|
||||
const Vector2& srcPoint, Vector3& resultPoint) const;
|
||||
bool GetRemappedPoint(
|
||||
const KinectCoordinateSpace fromSpace, const KinectCoordinateSpace toSpace,
|
||||
const Vector3& srcPoint, Vector2& resultPoint) const;
|
||||
|
||||
/*
|
||||
Samples distance from the Depth frame.
|
||||
The pixel used will be the round result of X and Y.
|
||||
Note: this only works if the Depth frame is used and is being locked and
|
||||
unlocked frequently.
|
||||
*/
|
||||
UINT16 SampleDepthFrame(const Vector2& point);
|
||||
/*
|
||||
Samples distance from the Depth frame using a coordinate from the Color frame.
|
||||
The pixel used will be the round result of X and Y after remapped to the Depth frame.
|
||||
Note: this only works if the 'Depth' and 'Depth remapped to Color' frames are both
|
||||
used and are being locked and unlocked frequently.
|
||||
*/
|
||||
UINT16 SampleDepthFromColorCoords(const Vector2& colorPoint);
|
||||
|
||||
/*
|
||||
Fully automatic exposure setting.
|
||||
@param exposureCompensation Negative value gives an underexposed image,
|
||||
positive gives an overexposed image.
|
||||
*/
|
||||
void SetAutoExposure(float exposureCompensation = 0);
|
||||
|
||||
/*
|
||||
Sets a pseudo-exposure time in ms.
|
||||
|
||||
The actual frame integration time is set to a multiple of fluorescent light period
|
||||
that is shorter than the requested time e.g. requesting 16 ms will set 10 ms
|
||||
in Australia (100Hz), 8.33 ms in USA (120Hz).
|
||||
The gain is automatically set to compensate for the reduced integration time.
|
||||
|
||||
Requesting less than a single fluorescent light period will set the integration time
|
||||
to the requested value.
|
||||
|
||||
To set the shortest non-flickering integration period for any country, simply set
|
||||
a pseudo-exposure time of between (10.0, 16.667) ms, which will automatically drop
|
||||
the integration time to 10 or 8.3 ms depending on country, while setting the analog
|
||||
gain control to a brighter value.
|
||||
|
||||
@param pseudoExposureTime Pseudo-exposure time in ms. [0.0, 640]ms
|
||||
*/
|
||||
void SetSemiAutoExposure(float pseudoExposureTime);
|
||||
|
||||
/*
|
||||
Manually set true exposure time and analog gain.
|
||||
@param frameExposureTime Time of exposure for each frame, in ms. [0.0, 640]ms
|
||||
@param analogGain Exposure analog gain.
|
||||
*/
|
||||
void SetManualExposure(float frameExposureTime, float analogGain);
|
||||
|
||||
private:
|
||||
IKinectSensor *pSensor; // Kinect sensor
|
||||
//IMultiSourceFrameReader *pMultiFrameReader;
|
||||
//IMultiSourceFrame *pMultiFrame;
|
||||
|
||||
KinectExposure exposureControl;
|
||||
|
||||
// Frame types
|
||||
#define FRAME_VAR(name) I ##name## Frame *p ##name## Frame; I ##name## FrameReader *p ##name## FrameReader;
|
||||
FRAME_VAR(Body);
|
||||
FRAME_VAR(BodyIndex);
|
||||
FRAME_VAR(Color);
|
||||
FRAME_VAR(Depth);
|
||||
FRAME_VAR(Infrared);
|
||||
FRAME_VAR(LongExposureInfrared);
|
||||
#undef FRAME_VAR
|
||||
|
||||
// Face frame resources
|
||||
/* Features we'll use from face frames */
|
||||
static const DWORD cFaceFrameFeatures =
|
||||
FaceFrameFeatures_FaceEngagement
|
||||
| FaceFrameFeatures_Happy
|
||||
| FaceFrameFeatures_LookingAway
|
||||
| FaceFrameFeatures_Glasses
|
||||
| FaceFrameFeatures_LeftEyeClosed
|
||||
| FaceFrameFeatures_RightEyeClosed
|
||||
| FaceFrameFeatures_MouthMoved
|
||||
| FaceFrameFeatures_MouthOpen
|
||||
| FaceFrameFeatures_RotationOrientation
|
||||
| FaceFrameFeatures_PointsInColorSpace
|
||||
| FaceFrameFeatures_PointsInInfraredSpace
|
||||
| FaceFrameFeatures_BoundingBoxInColorSpace
|
||||
| FaceFrameFeatures_BoundingBoxInInfraredSpace;
|
||||
IFaceFrameSource *pFaceFrameSources[BODY_COUNT];
|
||||
IFaceFrameReader *pFaceFrameReaders[BODY_COUNT];
|
||||
IFaceFrame *pFaceFrames[BODY_COUNT];
|
||||
|
||||
// Converts between depth, color and 3d coords
|
||||
ICoordinateMapper *pMapper;
|
||||
DepthSpacePoint *pColorMappedToDepth;
|
||||
bool bIsColorMappedToDepthDirty;
|
||||
/* We keep a copy of the latest depth frame so the user can convert points
|
||||
between spaces anytime, not only when the depth frame is locked.
|
||||
This copy is updated everytime the frame is locked. Because of it, some
|
||||
coordinate space convertions may not work if the depth frame is not in use
|
||||
nor locked frequently. */
|
||||
UINT16 *pDepthRawCopy;
|
||||
|
||||
IBody *bodies[BODY_COUNT];
|
||||
Joint joints[JointType_Count]; // keep joints array in memory to avoid frequent allocations
|
||||
JointOrientation jointOrientations[JointType_Count];
|
||||
Vector4 floorClipPlane;
|
||||
|
||||
inline bool InitMapper();
|
||||
inline void UpdateColorMappedToDepth();
|
||||
inline int IndexFromXY(int pitch, float x, float y) const;
|
||||
};
|
||||
|
||||
/* Print formatted string to the Output window */
|
||||
void Printf(const char *szFormat, ...);
|
||||
void LogFailedHResult(const HRESULT hr, const char *functionName, const int line, const char *action = "");
|
||||
template <typename T>
|
||||
inline void SafeRelease(T*& pointer);
|
||||
template <typename T>
|
||||
inline void SafeReleaseArray(T*& pointer);
|
||||
|
||||
/*-------------------------------------------------------------------------
|
||||
Templates and inlines implementation
|
||||
-------------------------------------------------------------------------*/
|
||||
|
||||
template<typename T>
|
||||
Vector2& Vector2::operator=(const T& b)
|
||||
{
|
||||
if (this != &b)
|
||||
{
|
||||
this->x = b.X;
|
||||
this->y = b.Y;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
DepthSpacePoint Vector2::toDepthSpacePoint() const
|
||||
{
|
||||
DepthSpacePoint b;
|
||||
b.X = this->x;
|
||||
b.Y = this->y;
|
||||
return b;
|
||||
}
|
||||
|
||||
ColorSpacePoint Vector2::toColorSpacePoint() const
|
||||
{
|
||||
ColorSpacePoint b;
|
||||
b.X = this->x;
|
||||
b.Y = this->y;
|
||||
return b;
|
||||
}
|
||||
|
||||
Vector3& Vector3::operator=(const CameraSpacePoint& b)
|
||||
{
|
||||
this->x = b.X;
|
||||
this->y = b.Y;
|
||||
this->z = b.Z;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Vector3& Vector3::operator/=(const float& other)
|
||||
{
|
||||
x /= other;
|
||||
y /= other;
|
||||
z /= other;
|
||||
return *this;
|
||||
}
|
||||
|
||||
const Vector3 Vector3::operator/(const float& other) const
|
||||
{
|
||||
Vector3 result(*this);
|
||||
return result /= other;
|
||||
}
|
||||
|
||||
Vector3& Vector3::operator*=(const float& other)
|
||||
{
|
||||
x *= other;
|
||||
y *= other;
|
||||
z *= other;
|
||||
return *this;
|
||||
}
|
||||
|
||||
const Vector3 Vector3::operator*(const float& other) const
|
||||
{
|
||||
Vector3 result(*this);
|
||||
return result *= other;
|
||||
}
|
||||
|
||||
bool Vector3::IsZero() const
|
||||
{
|
||||
return x == 0.f && y == 0.f && z == 0.f;
|
||||
}
|
||||
|
||||
Vector3 Vector3::normalized() const
|
||||
{
|
||||
if (IsZero())
|
||||
return Vector3();
|
||||
return (*this / magnitude());
|
||||
}
|
||||
|
||||
CameraSpacePoint Vector3::toCameraSpacePoint() const
|
||||
{
|
||||
CameraSpacePoint b;
|
||||
b.X = this->x;
|
||||
b.Y = this->y;
|
||||
b.Z = this->z;
|
||||
return b;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline void SafeRelease(T*& pointer)
|
||||
{
|
||||
if (pointer)
|
||||
{
|
||||
pointer->Release();
|
||||
}
|
||||
pointer = nullptr;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline void SafeReleaseArray(T*& pointer)
|
||||
{
|
||||
if (pointer)
|
||||
{
|
||||
delete[] pointer;
|
||||
}
|
||||
pointer = nullptr;
|
||||
}
|
||||
}
|
||||
+145
@@ -0,0 +1,145 @@
|
||||
// Conversion from the original C++/CLI implementation.
|
||||
|
||||
#pragma once
|
||||
#include <vector>
|
||||
typedef struct _NUISENSOR_HANDLE *NUISENSOR_HANDLE, **PNUISENSOR_HANDLE;
|
||||
|
||||
//using namespace System;
|
||||
//using namespace System::Runtime::InteropServices;
|
||||
|
||||
namespace Xbox
|
||||
{
|
||||
namespace Kinect
|
||||
{
|
||||
|
||||
enum NUISENSOR_RGB_COMMAND_TYPE
|
||||
{
|
||||
NUISENSOR_RGB_COMMAND_SET_EXPOSURE_MODE = 0,
|
||||
NUISENSOR_RGB_COMMAND_SET_INTEGRATION_TIME = 1,
|
||||
NUISENSOR_RGB_COMMAND_GET_INTEGRATION_TIME = 2,
|
||||
|
||||
NUISENSOR_RGB_COMMAND_SET_WHITE_BALANCE_MODE = 10,
|
||||
NUISENSOR_RGB_COMMAND_SET_RED_CHANNEL_GAIN = 11,
|
||||
NUISENSOR_RGB_COMMAND_SET_GREEN_CHANNEL_GAIN = 12,
|
||||
NUISENSOR_RGB_COMMAND_SET_BLUE_CHANNEL_GAIN = 13,
|
||||
NUISENSOR_RGB_COMMAND_GET_RED_CHANNEL_GAIN = 14,
|
||||
NUISENSOR_RGB_COMMAND_GET_GREEN_CHANNEL_GAIN = 15,
|
||||
NUISENSOR_RGB_COMMAND_GET_BLUE_CHANNEL_GAIN = 16,
|
||||
NUISENSOR_RGB_COMMAND_SET_EXPOSURE_TIME_MS = 17,
|
||||
NUISENSOR_RGB_COMMAND_GET_EXPOSURE_TIME_MS = 18,
|
||||
NUISENSOR_RGB_COMMAND_SET_DIGITAL_GAIN = 19,
|
||||
NUISENSOR_RGB_COMMAND_GET_DIGITAL_GAIN = 20,
|
||||
NUISENSOR_RGB_COMMAND_SET_ANALOG_GAIN = 21,
|
||||
NUISENSOR_RGB_COMMAND_GET_ANALOG_GAIN = 22,
|
||||
NUISENSOR_RGB_COMMAND_SET_EXPOSURE_COMPENSATION = 23,
|
||||
NUISENSOR_RGB_COMMAND_GET_EXPOSURE_COMPENSATION = 24,
|
||||
NUISENSOR_RGB_COMMAND_SET_ACS = 25,
|
||||
NUISENSOR_RGB_COMMAND_GET_ACS = 26,
|
||||
NUISENSOR_RGB_COMMAND_SET_EXPOSURE_METERING_MODE = 27,
|
||||
NUISENSOR_RGB_COMMAND_SET_EXPOSURE_METERING_ZONES = 28,
|
||||
NUISENSOR_RGB_COMMAND_SET_EXPOSURE_METERING_ZONE_0_WEIGHT = 29,
|
||||
NUISENSOR_RGB_COMMAND_SET_EXPOSURE_METERING_ZONE_1_WEIGHT = 30,
|
||||
NUISENSOR_RGB_COMMAND_SET_EXPOSURE_METERING_ZONE_2_WEIGHT = 31,
|
||||
NUISENSOR_RGB_COMMAND_SET_EXPOSURE_METERING_ZONE_3_WEIGHT = 32,
|
||||
NUISENSOR_RGB_COMMAND_SET_EXPOSURE_METERING_ZONE_4_WEIGHT = 33,
|
||||
NUISENSOR_RGB_COMMAND_SET_EXPOSURE_METERING_ZONE_5_WEIGHT = 34,
|
||||
NUISENSOR_RGB_COMMAND_SET_EXPOSURE_METERING_ZONE_6_WEIGHT = 35,
|
||||
NUISENSOR_RGB_COMMAND_SET_EXPOSURE_METERING_ZONE_7_WEIGHT = 36,
|
||||
NUISENSOR_RGB_COMMAND_SET_EXPOSURE_METERING_ZONE_8_WEIGHT = 37,
|
||||
NUISENSOR_RGB_COMMAND_SET_EXPOSURE_METERING_ZONE_9_WEIGHT = 38,
|
||||
NUISENSOR_RGB_COMMAND_SET_EXPOSURE_METERING_ZONE_10_WEIGHT = 39,
|
||||
NUISENSOR_RGB_COMMAND_SET_EXPOSURE_METERING_ZONE_11_WEIGHT = 40,
|
||||
NUISENSOR_RGB_COMMAND_SET_EXPOSURE_METERING_ZONE_12_WEIGHT = 41,
|
||||
NUISENSOR_RGB_COMMAND_SET_EXPOSURE_METERING_ZONE_13_WEIGHT = 42,
|
||||
NUISENSOR_RGB_COMMAND_SET_EXPOSURE_METERING_ZONE_14_WEIGHT = 43,
|
||||
NUISENSOR_RGB_COMMAND_SET_EXPOSURE_METERING_ZONE_15_WEIGHT = 44,
|
||||
NUISENSOR_RGB_COMMAND_SET_EXPOSURE_METERING_ZONE_16_WEIGHT = 45,
|
||||
NUISENSOR_RGB_COMMAND_SET_EXPOSURE_METERING_ZONE_17_WEIGHT = 46,
|
||||
NUISENSOR_RGB_COMMAND_SET_EXPOSURE_METERING_ZONE_18_WEIGHT = 47,
|
||||
NUISENSOR_RGB_COMMAND_SET_EXPOSURE_METERING_ZONE_19_WEIGHT = 48,
|
||||
NUISENSOR_RGB_COMMAND_SET_EXPOSURE_METERING_ZONE_20_WEIGHT = 49,
|
||||
NUISENSOR_RGB_COMMAND_SET_EXPOSURE_METERING_ZONE_21_WEIGHT = 50,
|
||||
NUISENSOR_RGB_COMMAND_SET_EXPOSURE_METERING_ZONE_22_WEIGHT = 51,
|
||||
NUISENSOR_RGB_COMMAND_SET_EXPOSURE_METERING_ZONE_23_WEIGHT = 52,
|
||||
NUISENSOR_RGB_COMMAND_SET_EXPOSURE_METERING_ZONE_24_WEIGHT = 53,
|
||||
NUISENSOR_RGB_COMMAND_SET_EXPOSURE_METERING_ZONE_25_WEIGHT = 54,
|
||||
NUISENSOR_RGB_COMMAND_SET_EXPOSURE_METERING_ZONE_26_WEIGHT = 55,
|
||||
NUISENSOR_RGB_COMMAND_SET_EXPOSURE_METERING_ZONE_27_WEIGHT = 56,
|
||||
NUISENSOR_RGB_COMMAND_SET_EXPOSURE_METERING_ZONE_28_WEIGHT = 57,
|
||||
NUISENSOR_RGB_COMMAND_SET_EXPOSURE_METERING_ZONE_29_WEIGHT = 58,
|
||||
NUISENSOR_RGB_COMMAND_SET_EXPOSURE_METERING_ZONE_30_WEIGHT = 59,
|
||||
NUISENSOR_RGB_COMMAND_SET_EXPOSURE_METERING_ZONE_31_WEIGHT = 60,
|
||||
NUISENSOR_RGB_COMMAND_SET_EXPOSURE_METERING_ZONE_32_WEIGHT = 61,
|
||||
NUISENSOR_RGB_COMMAND_SET_EXPOSURE_METERING_ZONE_33_WEIGHT = 62,
|
||||
NUISENSOR_RGB_COMMAND_SET_EXPOSURE_METERING_ZONE_34_WEIGHT = 63,
|
||||
NUISENSOR_RGB_COMMAND_SET_EXPOSURE_METERING_ZONE_35_WEIGHT = 64,
|
||||
NUISENSOR_RGB_COMMAND_SET_EXPOSURE_METERING_ZONE_36_WEIGHT = 65,
|
||||
NUISENSOR_RGB_COMMAND_SET_EXPOSURE_METERING_ZONE_37_WEIGHT = 66,
|
||||
NUISENSOR_RGB_COMMAND_SET_EXPOSURE_METERING_ZONE_38_WEIGHT = 67,
|
||||
NUISENSOR_RGB_COMMAND_SET_EXPOSURE_METERING_ZONE_39_WEIGHT = 68,
|
||||
NUISENSOR_RGB_COMMAND_SET_EXPOSURE_METERING_ZONE_40_WEIGHT = 69,
|
||||
NUISENSOR_RGB_COMMAND_SET_EXPOSURE_METERING_ZONE_41_WEIGHT = 70,
|
||||
NUISENSOR_RGB_COMMAND_SET_EXPOSURE_METERING_ZONE_42_WEIGHT = 71,
|
||||
NUISENSOR_RGB_COMMAND_SET_EXPOSURE_METERING_ZONE_43_WEIGHT = 72,
|
||||
NUISENSOR_RGB_COMMAND_SET_EXPOSURE_METERING_ZONE_44_WEIGHT = 73,
|
||||
NUISENSOR_RGB_COMMAND_SET_EXPOSURE_METERING_ZONE_45_WEIGHT = 74,
|
||||
NUISENSOR_RGB_COMMAND_SET_EXPOSURE_METERING_ZONE_46_WEIGHT = 75,
|
||||
NUISENSOR_RGB_COMMAND_SET_EXPOSURE_METERING_ZONE_47_WEIGHT = 76,
|
||||
NUISENSOR_RGB_COMMAND_SET_MAX_ANALOG_GAIN_CAP = 77,
|
||||
NUISENSOR_RGB_COMMAND_SET_MAX_DIGITAL_GAIN_CAP = 78,
|
||||
NUISENSOR_RGB_COMMAND_SET_FLICKER_FREE_FREQUENCY = 79,
|
||||
NUISENSOR_RGB_COMMAND_GET_EXPOSURE_MODE = 80,
|
||||
NUISENSOR_RGB_COMMAND_GET_WHITE_BALANCE_MODE = 81,
|
||||
NUISENSOR_RGB_COMMAND_SET_FRAME_RATE = 82,
|
||||
NUISENSOR_RGB_COMMAND_GET_FRAME_RATE = 83,
|
||||
|
||||
// This range is reserved. Do not re-use.
|
||||
NUISENSOR_RGB_COMMAND_RESERVED_BASE = 400,
|
||||
NUISENSOR_RGB_COMMAND_RESERVED_END = NUISENSOR_RGB_COMMAND_RESERVED_BASE + 200,
|
||||
// End of reserved region
|
||||
|
||||
};
|
||||
|
||||
struct NUISENSOR_RGB_CHANGE_SETTING_CMD
|
||||
{
|
||||
uint32_t Cmd;
|
||||
uint32_t Arg;
|
||||
};
|
||||
|
||||
struct NUISENSOR_RGB_CHANGE_STREAM_SETTING
|
||||
{
|
||||
uint32_t SequenceId;
|
||||
std::vector<NUISENSOR_RGB_CHANGE_SETTING_CMD> Commands;
|
||||
};
|
||||
|
||||
struct NUISENSOR_RGB_CHANGE_STREAM_SETTING_REPLY_STATUS
|
||||
{
|
||||
uint32_t Status;
|
||||
uint32_t Data;
|
||||
};
|
||||
|
||||
struct NUISENSOR_RGB_CHANGE_STREAM_SETTING_REPLY
|
||||
{
|
||||
uint32_t CommandListStatus;
|
||||
std::vector<NUISENSOR_RGB_CHANGE_STREAM_SETTING_REPLY_STATUS> Status;
|
||||
};
|
||||
|
||||
|
||||
class NuiSensor
|
||||
{
|
||||
public:
|
||||
NuiSensor();
|
||||
~NuiSensor();
|
||||
|
||||
void Shutdown();
|
||||
|
||||
void ColorChangeCameraSettings(const NUISENSOR_RGB_CHANGE_STREAM_SETTING& settings, NUISENSOR_RGB_CHANGE_STREAM_SETTING_REPLY* p_reply);
|
||||
|
||||
private:
|
||||
void TestResult(int success);
|
||||
|
||||
NUISENSOR_HANDLE _sensor = nullptr;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
BIN
Binary file not shown.
Reference in New Issue
Block a user