Adding Json Mapping for Gestures
This commit is contained in:
@@ -9,6 +9,104 @@ float UGestureDefinition::Evaluate(AHandActor* Hand) const
|
||||
return 0.f;
|
||||
}
|
||||
|
||||
int32 UGestureDefinition::GetFingerTipIndex(EHandFinger Finger) const
|
||||
{
|
||||
switch (Finger)
|
||||
{
|
||||
case EHandFinger::Thumb: return 4;
|
||||
case EHandFinger::Index: return 8;
|
||||
case EHandFinger::Middle: return 12;
|
||||
case EHandFinger::Ring: return 16;
|
||||
case EHandFinger::Pinky: return 20;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int32 UGestureDefinition::GetFingerBaseIndex(EHandFinger Finger) const
|
||||
{
|
||||
switch (Finger)
|
||||
{
|
||||
case EHandFinger::Thumb: return 1; // thumb base is special
|
||||
case EHandFinger::Index: return 5;
|
||||
case EHandFinger::Middle: return 9;
|
||||
case EHandFinger::Ring: return 13;
|
||||
case EHandFinger::Pinky: return 17;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
const FVector& UGestureDefinition::GetPoint(const AHandActor* Hand, int32 Index) const
|
||||
{
|
||||
return Hand->Points[Index];
|
||||
}
|
||||
|
||||
float UGestureDefinition::GetHandScale(const AHandActor* Hand) const
|
||||
{
|
||||
const TArray<FVector>& P = Hand->Points;
|
||||
return FVector::Dist(P[0], P[9]); // wrist -> middle base
|
||||
}
|
||||
|
||||
float UGestureDefinition::GetNormalizedDistance(const AHandActor* Hand, int32 A, int32 B) const
|
||||
{
|
||||
float Dist = FVector::Dist(GetPoint(Hand, A), GetPoint(Hand, B));
|
||||
return Dist / GetHandScale(Hand);
|
||||
}
|
||||
float UGestureDefinition::GetPinchScore(const AHandActor* Hand, EHandFinger A, EHandFinger B) const
|
||||
{
|
||||
float Dist = GetNormalizedDistance(
|
||||
Hand,
|
||||
GetFingerTipIndex(A),
|
||||
GetFingerTipIndex(B)
|
||||
);
|
||||
|
||||
const float Threshold = 0.25f;
|
||||
|
||||
return 1.0f - FMath::Clamp(Dist / Threshold, 0.f, 1.f);
|
||||
}
|
||||
|
||||
float UGestureDefinition::GetFingerExtendedScore(const AHandActor* Hand, EHandFinger Finger) const
|
||||
{
|
||||
const TArray<FVector>& P = Hand->Points;
|
||||
|
||||
const FVector& Wrist = P[0];
|
||||
const FVector& Base = P[GetFingerBaseIndex(Finger)];
|
||||
const FVector& Tip = P[GetFingerTipIndex(Finger)];
|
||||
|
||||
float Dot = FVector::DotProduct(
|
||||
(Tip - Base).GetSafeNormal(),
|
||||
(Base - Wrist).GetSafeNormal()
|
||||
);
|
||||
|
||||
return FMath::Clamp(Dot, 0.f, 1.f);
|
||||
}
|
||||
|
||||
float UGestureDefinition::GetFingerCurledScore(const AHandActor* Hand, EHandFinger Finger) const
|
||||
{
|
||||
return 1.0f - GetFingerExtendedScore(Hand, Finger);
|
||||
}
|
||||
|
||||
float UGestureDefinition::GetFingersAverage(const AHandActor* Hand, const TArray<EHandFinger>& Fingers, bool bExtended) const
|
||||
{
|
||||
float Sum = 0.f;
|
||||
|
||||
for (EHandFinger Finger : Fingers)
|
||||
{
|
||||
Sum += bExtended
|
||||
? GetFingerExtendedScore(Hand, Finger)
|
||||
: GetFingerCurledScore(Hand, Finger);
|
||||
}
|
||||
|
||||
return Fingers.Num() > 0 ? Sum / Fingers.Num() : 0.f;
|
||||
}
|
||||
|
||||
FVector UGestureDefinition::GetFingerDirection(const AHandActor* Hand, EHandFinger Finger) const
|
||||
{
|
||||
const FVector& Base = GetPoint(Hand, GetFingerBaseIndex(Finger));
|
||||
const FVector& Tip = GetPoint(Hand, GetFingerTipIndex(Finger));
|
||||
|
||||
return (Tip - Base).GetSafeNormal();
|
||||
}
|
||||
|
||||
float UGesture_PinchPoint::Evaluate(AHandActor* Hand) const
|
||||
{
|
||||
float Pinch = GetPinchScore(Hand, EHandFinger::Thumb, EHandFinger::Index);
|
||||
|
||||
@@ -25,6 +25,8 @@ void UGestureManagerComponent::BeginPlay()
|
||||
|
||||
HandManager->OnHandAdded.AddDynamic(this, &UGestureManagerComponent::OnHandAdded);
|
||||
HandManager->OnHandRemoved.AddDynamic(this, &UGestureManagerComponent::OnHandRemoved);
|
||||
|
||||
LoadBindings("C:/git/Table/Test/Gestures/gesture_config.json");
|
||||
}
|
||||
|
||||
void UGestureManagerComponent::EvaluateHands()
|
||||
@@ -64,6 +66,107 @@ void UGestureManagerComponent::OnHandRemoved(AHandActor* Hand)
|
||||
Hand->ResetGesture();
|
||||
}
|
||||
|
||||
void UGestureManagerComponent::LoadBindings(const FString& FilePath)
|
||||
{
|
||||
FString JsonString;
|
||||
|
||||
if (!FFileHelper::LoadFileToString(JsonString, *FilePath))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
TSharedPtr<FJsonObject> Root;
|
||||
TSharedRef<TJsonReader<>> Reader = TJsonReaderFactory<>::Create(JsonString);
|
||||
|
||||
if (!FJsonSerializer::Deserialize(Reader, Root) || !Root.IsValid())
|
||||
return;
|
||||
|
||||
const TArray<TSharedPtr<FJsonValue>>* BindingsArray;
|
||||
|
||||
if (Root->TryGetArrayField(TEXT("bindings"), BindingsArray))
|
||||
{
|
||||
for (const TSharedPtr<FJsonValue>& Value : *BindingsArray)
|
||||
{
|
||||
TSharedPtr<FJsonObject> Obj = Value->AsObject();
|
||||
if (!Obj.IsValid()) continue;
|
||||
|
||||
FGestureBinding Binding;
|
||||
|
||||
Obj->TryGetStringField(TEXT("action"), Binding.ActionName);
|
||||
Obj->TryGetStringField(TEXT("left"), Binding.LeftGesture);
|
||||
Obj->TryGetStringField(TEXT("right"), Binding.RightGesture);
|
||||
|
||||
const TArray<TSharedPtr<FJsonValue>>* ExtArray;
|
||||
if (Obj->TryGetArrayField(TEXT("extensions"), ExtArray))
|
||||
{
|
||||
for (const TSharedPtr<FJsonValue>& ExtVal : *ExtArray)
|
||||
{
|
||||
TSharedPtr<FJsonObject> ExtObj = ExtVal->AsObject();
|
||||
if (!ExtObj.IsValid()) continue;
|
||||
|
||||
FExtendedGestureBinding Ext;
|
||||
|
||||
ExtObj->TryGetStringField(TEXT("gesture"), Ext.GestureName);
|
||||
ExtObj->TryGetStringField(TEXT("action"), Ext.GestureAction);
|
||||
|
||||
Binding.ExentdedGestures.Add(Ext);
|
||||
}
|
||||
}
|
||||
|
||||
GestureBindings.Add(Binding);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UGestureManagerComponent::Initialize(UDataTable* InGestureTable, UDataTable* InActionTable)
|
||||
{
|
||||
GestureDefinitionTable = InGestureTable;
|
||||
GestureActionTable = InActionTable;
|
||||
|
||||
BuildDefinitionMap();
|
||||
BuildActionMap();
|
||||
}
|
||||
|
||||
void UGestureManagerComponent::BuildDefinitionMap()
|
||||
{
|
||||
if (!GestureDefinitionTable) return;
|
||||
|
||||
static const FString Context = TEXT("GestureDefinitionContext");
|
||||
|
||||
TArray<FGestureDefinitionRow*> Rows;
|
||||
GestureDefinitionTable->GetAllRows(Context, Rows);
|
||||
|
||||
for (FGestureDefinitionRow* Row : Rows)
|
||||
{
|
||||
if (!Row || !Row->DefinitionClass) continue;
|
||||
|
||||
UGestureDefinition* Def = NewObject<UGestureDefinition>(this, Row->DefinitionClass);
|
||||
|
||||
DefinitionMap.Add(Def->GestureName, Def);
|
||||
}
|
||||
}
|
||||
|
||||
void UGestureManagerComponent::BuildActionMap()
|
||||
{
|
||||
if (!GestureActionTable) return;
|
||||
|
||||
static const FString Context = TEXT("GestureActionContext");
|
||||
|
||||
TArray<FGestureActionRow*> Rows;
|
||||
GestureActionTable->GetAllRows(Context, Rows);
|
||||
|
||||
for (FGestureActionRow* Row : Rows)
|
||||
{
|
||||
if (!Row || !Row->ActionClass) continue;
|
||||
|
||||
ActionClassMap.Add(
|
||||
Row->ActionClass->GetName(),
|
||||
Row->ActionClass
|
||||
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void UGestureManagerComponent::CheckGestureDefinitions(TArray<AHandActor*>& Hands)
|
||||
{
|
||||
const float MinScoreThreshold = 0.5f;
|
||||
|
||||
@@ -10,6 +10,7 @@ AHandManager::AHandManager()
|
||||
// Set this actor to call Tick() every frame. You can turn this off to improve performance if you don't need it.
|
||||
PrimaryActorTick.bCanEverTick = true;
|
||||
GestureManager = CreateDefaultSubobject<UGestureManagerComponent>(TEXT("GestureManager"));
|
||||
GestureManager->Initialize(GestureDefinitionTable, GestureActionTable);
|
||||
}
|
||||
|
||||
// Called when the game starts or when spawned
|
||||
|
||||
Reference in New Issue
Block a user