Compare commits

..

3 Commits

Author SHA1 Message Date
DuOtto c9a5c2c6ca Added UI for adjusting TableHight and Scale 2026-07-30 19:31:20 +02:00
DuOtto 85ab894665 Added LineIcon 2026-07-29 14:50:46 +02:00
DuOtto 257f045515 Added GestureAction Ruler, added TableLineActor 2026-07-29 00:07:47 +02:00
28 changed files with 322 additions and 395 deletions
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -197,8 +197,8 @@ bool ADetectionManager::ParseHandPacket(const TArray<uint8>& Bytes, int32& OutSe
FHandPacketHand Hand; FHandPacketHand Hand;
Hand.HandId = (int32)HandId; Hand.HandId = (int32)HandId;
Hand.Handedness = (HandednessByte == 1) ? EHandedness::Left : Hand.Handedness = (HandednessByte == 1) ? EHandedness::Right :
(HandednessByte == 2) ? EHandedness::Right : (HandednessByte == 2) ? EHandedness::Left :
EHandedness::Unknown; EHandedness::Unknown;
Hand.Confidence = Confidence; Hand.Confidence = Confidence;
+3 -1
View File
@@ -18,7 +18,7 @@ void AGestureAction::BeginPlay()
Super::BeginPlay(); Super::BeginPlay();
} }
void AGestureAction::InitAction_Implementation(AHandActor* Hand, const FGestureBinding& NewBinding, float NewUEUnitsPerUnit) void AGestureAction::InitAction_Implementation(AHandActor* Hand, const FGestureBinding& NewBinding, float NewUEUnitsPerUnit, float TableHeight)
{ {
HandA = Hand; HandA = Hand;
bRequiresSecondHand = Binding.LeftGesture != "" && Binding.RightGesture != ""; bRequiresSecondHand = Binding.LeftGesture != "" && Binding.RightGesture != "";
@@ -52,6 +52,8 @@ void AGestureAction::AddOtherHand_Implementation(AHandActor* OtherHand)
void AGestureAction::EndGesture_Implementation() void AGestureAction::EndGesture_Implementation()
{ {
HandA = nullptr;
HandB = nullptr;
DestroyIcons(); DestroyIcons();
Destroy(); Destroy();
} }
@@ -31,6 +31,54 @@ void UGestureManagerComponent::BeginPlay()
LoadBindings("C:/git/Table/Test/Gestures/gesture_config.json"); LoadBindings("C:/git/Table/Test/Gestures/gesture_config.json");
} }
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);
GestureDefinitions.Add(Def);
}
}
void UGestureManagerComponent::BuildActionMap()
{
if (!GestureActionTable) return;
static const FString Context = TEXT("GestureActionContext");
TArray<FName> RowNames = GestureActionTable->GetRowNames();
for (const FName& RowName : RowNames)
{
FGestureActionRow* Row = GestureActionTable->FindRow<FGestureActionRow>( RowName, Context);
if (!Row || !Row->ActionClass) continue;
ActionClassMap.Add(RowName, Row->ActionClass);
}
}
void UGestureManagerComponent::EvaluateHands() void UGestureManagerComponent::EvaluateHands()
{ {
TArray<AHandActor*>& Hands = HandManager->HandList; TArray<AHandActor*>& Hands = HandManager->HandList;
@@ -86,7 +134,7 @@ void UGestureManagerComponent::LoadBindings(const FString& FilePath)
const TArray<TSharedPtr<FJsonValue>>* BindingsArray; const TArray<TSharedPtr<FJsonValue>>* BindingsArray;
if (Root->TryGetArrayField(TEXT("bindings"), BindingsArray)) if (Root->TryGetArrayField(TEXT("rules"), BindingsArray))
{ {
for (const TSharedPtr<FJsonValue>& Value : *BindingsArray) for (const TSharedPtr<FJsonValue>& Value : *BindingsArray)
{ {
@@ -126,51 +174,9 @@ void UGestureManagerComponent::LoadBindings(const FString& FilePath)
} }
} }
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);
GestureDefinitions.Add(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( FName(Row->ActionClass->GetName()), Row->ActionClass );
}
}
void UGestureManagerComponent::CheckGestureDefinitions(TArray<AHandActor*>& Hands) void UGestureManagerComponent::CheckGestureDefinitions(TArray<AHandActor*>& Hands)
{ {
@@ -272,7 +278,7 @@ void UGestureManagerComponent::CheckGestureDefinitions(TArray<AHandActor*>& Hand
Hand->CandidateFrames = 1; Hand->CandidateFrames = 1;
Hand->FailFrames = 0; Hand->FailFrames = 0;
break; break;
case EHandGesturePhase::Ongoing: // Checking if the gesture is still ongoing case EHandGesturePhase::Ongoing: // Checking if the gesture is still ongoinga
//if (Hand->ActiveGestureName != BestGesture) Hand->FailFrames++; //if (Hand->ActiveGestureName != BestGesture) Hand->FailFrames++;
//else Hand->FailFrames = 0; //else Hand->FailFrames = 0;
if (Hand->ActiveGestureName != BestGesture) if (Hand->ActiveGestureName != BestGesture)
@@ -313,6 +319,7 @@ void UGestureManagerComponent::CheckGestureDefinitions(TArray<AHandActor*>& Hand
void UGestureManagerComponent::TriggerAction(AHandActor* Hand) void UGestureManagerComponent::TriggerAction(AHandActor* Hand)
{ {
if (Hand->bUsedThisTick) return; if (Hand->bUsedThisTick) return;
if (TryExtendGestureAction(Hand)) return; if (TryExtendGestureAction(Hand)) return;
@@ -325,7 +332,10 @@ void UGestureManagerComponent::TriggerAction(AHandActor* Hand)
if (!bMatches) continue; if (!bMatches) continue;
AGestureAction* NewAction = SpawnGestureAction(Binding.ActionName, Hand); AGestureAction* NewAction = SpawnGestureAction(Binding.ActionName, Hand);
NewAction->InitAction(Hand, Binding, UEUnitsPerUnit); if (!NewAction) return;
NewAction->InitAction(Hand, Binding, UEUnitsPerUnit, TableHeight);
Hand->Phase = EHandGesturePhase::Ongoing;
return; return;
} }
@@ -334,6 +344,7 @@ void UGestureManagerComponent::TriggerAction(AHandActor* Hand)
void UGestureManagerComponent::UpdateOngoingActions() void UGestureManagerComponent::UpdateOngoingActions()
{ {
for (AGestureAction* Action : ActiveGestureActions) for (AGestureAction* Action : ActiveGestureActions)
{ {
if (!Action) continue; if (!Action) continue;
@@ -344,7 +355,6 @@ void UGestureManagerComponent::UpdateOngoingActions()
AGestureAction* UGestureManagerComponent::SpawnGestureAction(FName ActionName, AHandActor* Hand) AGestureAction* UGestureManagerComponent::SpawnGestureAction(FName ActionName, AHandActor* Hand)
{ {
TSubclassOf<AGestureAction>* ActionClassPtr = ActionClassMap.Find(ActionName); TSubclassOf<AGestureAction>* ActionClassPtr = ActionClassMap.Find(ActionName);
if (!ActionClassPtr || !Hand) return nullptr; if (!ActionClassPtr || !Hand) return nullptr;
@@ -362,6 +372,7 @@ AGestureAction* UGestureManagerComponent::SpawnGestureAction(FName ActionName, A
if (!NewAction) return nullptr; if (!NewAction) return nullptr;
ActiveGestureActions.Add(NewAction);
Hand->ActiveAction = NewAction; Hand->ActiveAction = NewAction;
Hand->bUsedThisTick = true; Hand->bUsedThisTick = true;
@@ -424,7 +435,11 @@ bool UGestureManagerComponent::TryCreateExtension(AHandActor* Hand)
AGestureAction* NewAction = SpawnGestureAction(ActionName, Hand); AGestureAction* NewAction = SpawnGestureAction(ActionName, Hand);
NewAction->InitAction(Hand, OtherHand->ActiveAction->Binding, UEUnitsPerUnit); if (!NewAction) return false;
ActiveGestureActions.Add(NewAction);
NewAction->InitAction(Hand, OtherHand->ActiveAction->Binding, UEUnitsPerUnit, TableHeight);
OtherHand->bUsedThisTick = true; OtherHand->bUsedThisTick = true;
Hand->bUsedThisTick = true; Hand->bUsedThisTick = true;
OtherAction->AddExtension(NewAction); OtherAction->AddExtension(NewAction);
+65 -63
View File
@@ -4,80 +4,82 @@
#include "MyGestureActions.h" #include "MyGestureActions.h"
#include "HandActor.h" #include "HandActor.h"
#include "TableIconActor.h" #include "TableIconActor.h"
#include "MyTableIconActors.h"
void ARulerAction::UpdateAction_Implementation() void AGestureAction_Ruler::InitAction_Implementation(AHandActor* Hand, const FGestureBinding& NewBinding, float NewUEUnitsPerUnit, float TableHeight)
{ {
if (!HandA) return; Super::InitAction_Implementation(Hand, NewBinding, NewUEUnitsPerUnit, TableHeight);
DesiredHeight = -500;
// Update end point to current fingertip StartPoint = HandA->Points[8];
Anchors.Add("End", HandA->Points[8]); StartPoint.Z = DesiredHeight;
FVector Start = Anchors["Start"];
FVector End = Anchors["End"];
float RawDist = FVector::Dist(Start, End);
float Inches = GetSnappedInches(RawDist);
// Move line icon between start and end
if (Icons.IsValidIndex(0) && IsValid(Icons[0]))
{
Icons[0]->SetActorLocation((Start + End) * 0.5f);
// You'll add a SetEndpoints(Start, End) function to ATableIconLineActor later
}
// Move label icon to midpoint, slightly above table
if (Icons.IsValidIndex(1) && IsValid(Icons[1]))
{
FVector Mid = (Start + End) * 0.5f + FVector(0, 0, 5.f);
Icons[1]->SetActorLocation(Mid);
// Icons[1]->SetLabelText(FString::Printf(TEXT("%d\""), (int32)Inches));
}
}
void ARulerAction::AbortAction_Implementation()
{
Super::AbortAction();
}
void ARulerAction::AddExtension_Implementation(AGestureAction* OtherAction)
{
return;
}
void ARulerAction::EndGesture_Implementation()
{
Super::EndGesture();
}
void ARulerAction::InitAction_Implementation(AHandActor* Hand, const FGestureBinding& NewBinding, float NewUEUnitsPerUnit)
{
Super::InitAction(Hand, NewBinding, NewUEUnitsPerUnit);
Anchors.Add("Start", Hand->Points[8]);
FActorSpawnParameters SpawnParams; FActorSpawnParameters SpawnParams;
SpawnParams.Owner = GetOwner(); SpawnParams.Owner = this;
SpawnParams.SpawnCollisionHandlingOverride = SpawnParams.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AlwaysSpawn;
ESpawnActorCollisionHandlingMethod::AlwaysSpawn;
AGestureAction* NewAction = nullptr; if (LineIconClass)
/*AGestureAction* NewAction = GetWorld()->SpawnActor<AGestureAction>( {
*ActionClassPtr,
FVector::ZeroVector,
FRotator::ZeroRotator,
SpawnParams);
*/
if (!NewAction) return;
LineIcon = GetWorld()->SpawnActor<ATableIconLineActor>(LineIconClass, StartPoint, FRotator::ZeroRotator, SpawnParams);
if (LineIcon)
{
LineIcon->OwningAction = this;
LineIcon->bPersistOnActionEnd = true;
LineIcon->LineThickness = 50;
Icons.Add(LineIcon);
}
}
RulerPoints.Add(FVector::ZeroVector);
RulerPoints.Add(FVector::ZeroVector);
if (LabelIconClass)
{
LabelIcon = GetWorld()->SpawnActor<ATableIconLabelActor>(LabelIconClass, StartPoint, FRotator::ZeroRotator, SpawnParams);
if (LabelIcon)
{
LabelIcon->OwningAction = this;
LabelIcon->bPersistOnActionEnd = true;
LabelIcon->UpdateLabel(StartPoint, 0.f);
Icons.Add(LabelIcon);
}
}
} }
float ARulerAction::GetSnappedInches(float RawDistance) const void AGestureAction_Ruler::UpdateAction_Implementation()
{
if (!HandA || !IsValid(LineIcon) || !IsValid(LabelIcon)) return;
const FVector Current = HandA->Points[8];
//if (FVector::DistSquared(Current, RulerPoints[RulerPoints.Num() - 1]) < FMath::Square(UEUnitsPerUnit)) return;
RulerPoints.Last() = Current - StartPoint;
RulerPoints.Last().Z = 0;
LineIcon->SetPoints(RulerPoints);
// RulerPoints are local to the line actor, so convert back to world
FVector LabelPosition = StartPoint + (RulerPoints.Last() * 0.5f);
LabelPosition.Z = 10;
LabelIcon->UpdateLabel(LabelPosition, GetSnappedUnits(GetTotalLength()));
}
float AGestureAction_Ruler::GetSnappedUnits(float RawDistance) const
{ {
if (UEUnitsPerUnit <= 0.f) return 0.f; if (UEUnitsPerUnit <= 0.f) return 0.f;
float Unit = RawDistance / UEUnitsPerUnit; return FMath::RoundToFloat(RawDistance / UEUnitsPerUnit);
return FMath::RoundToFloat(Unit); // snap to whole inches
} }
float AGestureAction_Ruler::GetTotalLength() const
{
float Total = 0.f;
for (int32 i = 0; i < RulerPoints.Num() - 1; i++)
Total += FVector::Dist(RulerPoints[i], RulerPoints[i + 1]);
return Total;
}
void AGestureAction_Ruler::AddRulerPoint()
{
FVector Current = HandA->Points[8];
RulerPoints.Add(Current - StartPoint);
}
@@ -10,29 +10,34 @@
ATableIconLineActor::ATableIconLineActor() ATableIconLineActor::ATableIconLineActor()
{ {
PrimaryActorTick.bCanEverTick = false; // purely driven by SetPoints
USceneComponent* Root = CreateDefaultSubobject<USceneComponent>(TEXT("Root"));
SetRootComponent(Root);
MeshComponent = CreateDefaultSubobject<UProceduralMeshComponent>(TEXT("LineMesh")); MeshComponent = CreateDefaultSubobject<UProceduralMeshComponent>(TEXT("LineMesh"));
MeshComponent->SetupAttachment(RootComponent); MeshComponent->SetupAttachment(RootComponent);
MeshComponent->SetCastShadow(false); MeshComponent->SetCastShadow(false);
MeshComponent->SetCollisionEnabled(ECollisionEnabled::NoCollision);
} }
void ATableIconLineActor::SetEndpoints(const FVector& Start, const FVector& End) void ATableIconLineActor::SetPoints(const TArray<FVector>& NewPoints)
{ {
// Skip rebuild if nothing meaningful changed const FTransform& ActorTransform = GetActorTransform();
if (Start.Equals(LastStart, 0.1f) && End.Equals(LastEnd, 0.1f)) return;
LastStart = Start;
LastEnd = End;
BuildLineMesh(Start, End); Points.Reset(NewPoints.Num());
if (bShowTickMarks) BuildTickMesh(Start, End);
Points = NewPoints;
MeshComponent->ClearMeshSection(0);
BuildLineMesh();
if (LineMaterial) MeshComponent->SetMaterial(0, LineMaterial);
} }
void ATableIconLineActor::BuildLineMesh(const FVector& Start, const FVector& End) void ATableIconLineActor::BuildLineMesh()
{ {
FVector Dir = (End - Start).GetSafeNormal();
FVector Up = FVector::UpVector;
FVector Right = FVector::CrossProduct(Dir, Up).GetSafeNormal() * LineThickness * 0.5f;
// A flat quad along the line, facing up
TArray<FVector> Vertices; TArray<FVector> Vertices;
TArray<int32> Triangles; TArray<int32> Triangles;
TArray<FVector> Normals; TArray<FVector> Normals;
@@ -40,89 +45,67 @@ void ATableIconLineActor::BuildLineMesh(const FVector& Start, const FVector& End
TArray<FColor> Colors; TArray<FColor> Colors;
TArray<FProcMeshTangent> Tangents; TArray<FProcMeshTangent> Tangents;
Vertices.Add(Start - Right); // 0 FVector Direction = FVector::ZeroVector;
Vertices.Add(Start + Right); // 1 const float HalfLineThickness = LineThickness * 0.5f;
Vertices.Add(End + Right); // 2
Vertices.Add(End - Right); // 3
Triangles = { 0, 1, 2, 0, 2, 3 }; for (int32 i = 0; i < Points.Num() - 1; i++) {
const FVector& PointA = Points[i];
const FVector& PointB = Points[i + 1];
for (int i = 0; i < 4; i++) Direction = (PointB - PointA).GetSafeNormal();
{
Normals.Add(FVector::UpVector);
UVs.Add(FVector2D(i < 2 ? 0.f : 1.f, i % 2 == 0 ? 0.f : 1.f));
Colors.Add(LineColor.ToFColor(true));
Tangents.Add(FProcMeshTangent(Dir, false));
}
// Section 0 = main line const FVector Right = FVector::CrossProduct(FVector::UpVector, Direction).GetSafeNormal();
MeshComponent->CreateMeshSection(0, Vertices, Triangles, Normals, UVs,
Colors, Tangents, false);
}
void ATableIconLineActor::BuildTickMesh(const FVector& Start, const FVector& End) const FVector Offset = Right * (LineThickness * 0.5f);
{
if (UEUnitsPerUnit <= 0.f || TickInterval <= 0.f) return;
float TotalDist = FVector::Dist(Start, End);
float TickSpacing = TickInterval * UEUnitsPerUnit;
int32 NumTicks = FMath::FloorToInt(TotalDist / TickSpacing);
if (NumTicks <= 0)
{
MeshComponent->ClearMeshSection(1);
return;
}
FVector Dir = (End - Start).GetSafeNormal();
FVector Right = FVector::CrossProduct(Dir, FVector::UpVector).GetSafeNormal();
TArray<FVector> Vertices;
TArray<int32> Triangles;
TArray<FVector> Normals;
TArray<FVector2D> UVs;
TArray<FColor> Colors;
TArray<FProcMeshTangent> Tangents;
float TickHalfLen = LineThickness * 3.f;
float TickHalfW = LineThickness * 0.5f;
for (int32 i = 1; i <= NumTicks; i++)
{
FVector Center = Start + Dir * (TickSpacing * i);
FVector A = Center - Right * TickHalfLen - Dir * TickHalfW;
FVector B = Center + Right * TickHalfLen - Dir * TickHalfW;
FVector C = Center + Right * TickHalfLen + Dir * TickHalfW;
FVector D = Center - Right * TickHalfLen + Dir * TickHalfW;
int32 Base = Vertices.Num(); int32 Base = Vertices.Num();
Vertices.Add(A); Vertices.Add(B);
Vertices.Add(C); Vertices.Add(D);
Triangles.Add(Base); Triangles.Add(Base + 1); Triangles.Add(Base + 2); Vertices.Add(PointA - Offset);
Triangles.Add(Base); Triangles.Add(Base + 2); Triangles.Add(Base + 3); Vertices.Add(PointA + Offset);
Vertices.Add(PointB + Offset);
Vertices.Add(PointB - Offset);
for (int j = 0; j < 4; j++) Triangles.Add(Base + 0);
Triangles.Add(Base + 1);
Triangles.Add(Base + 2);
Triangles.Add(Base + 0);
Triangles.Add(Base + 2);
Triangles.Add(Base + 3);
}
for (int i = 0; i < Vertices.Num(); i++)
{ {
Normals.Add(FVector::UpVector); Normals.Add(FVector::UpVector);
UVs.Add(FVector2D::ZeroVector);
Colors.Add(LineColor.ToFColor(true));
Tangents.Add(FProcMeshTangent(Dir, false));
}
} }
// Section 1 = tick marks for (int i = 0; i < Vertices.Num(); i++)
MeshComponent->CreateMeshSection(1, Vertices, Triangles, Normals, UVs, {
Colors, Tangents, false); UVs.Add(FVector2D::ZeroVector);
}
for (int i = 0; i < Vertices.Num(); i++)
{
Colors.Add(FColor::Red);
}
for (int i = 0; i < Vertices.Num(); i++)
{
Tangents.Add(FProcMeshTangent(Direction, false));
}
MeshComponent->CreateMeshSection(0, Vertices, Triangles, Normals, UVs, Colors, Tangents, false);
} }
//--------------------------------------------------------------------------------------------
ATableIconWidgetActor::ATableIconWidgetActor() ATableIconWidgetActor::ATableIconWidgetActor()
{ {
PrimaryActorTick.bCanEverTick = true; USceneComponent* Root = CreateDefaultSubobject<USceneComponent>(TEXT("Root"));
SetRootComponent(Root);
PrimaryActorTick.bCanEverTick = false;
WidgetComponent = CreateDefaultSubobject<UWidgetComponent>(TEXT("WidgetComponent")); WidgetComponent = CreateDefaultSubobject<UWidgetComponent>(TEXT("WidgetComponent"));
WidgetComponent->SetupAttachment(RootComponent); WidgetComponent->SetupAttachment(RootComponent);
@@ -134,24 +117,8 @@ ATableIconWidgetActor::ATableIconWidgetActor()
WidgetInteraction->SetupAttachment(RootComponent); WidgetInteraction->SetupAttachment(RootComponent);
WidgetInteraction->InteractionSource = EWidgetInteractionSource::Custom; WidgetInteraction->InteractionSource = EWidgetInteractionSource::Custom;
WidgetInteraction->InteractionDistance = 10.f; WidgetInteraction->InteractionDistance = 10.f;
}
void ATableIconWidgetActor::Tick(float DeltaTime) SetWidgetClass(LabelWidgetClass);
{
Super::Tick(DeltaTime);
if (!bFaceCamera) return;
APlayerCameraManager* CamManager = UGameplayStatics::GetPlayerCameraManager(this, 0);
if (!CamManager) return;
FVector CamLocation = CamManager->GetCameraLocation();
FVector ToCamera = (CamLocation - GetActorLocation()).GetSafeNormal();
// Only rotate on yaw — keeps widget flat relative to table
ToCamera.Z = 0.f;
if (!ToCamera.IsNearlyZero())
SetActorRotation(ToCamera.ToOrientationRotator());
} }
void ATableIconWidgetActor::SetWidgetClass(TSubclassOf<UUserWidget> InWidgetClass) void ATableIconWidgetActor::SetWidgetClass(TSubclassOf<UUserWidget> InWidgetClass)
@@ -165,3 +132,11 @@ UUserWidget* ATableIconWidgetActor::GetWidget() const
{ {
return WidgetComponent->GetUserWidgetObject(); return WidgetComponent->GetUserWidgetObject();
} }
//--------------------------------------------------------------------------------------------
void ATableIconLabelActor::UpdateLabel(const FVector& WorldLocation, float Value)
{
SetActorLocation(WorldLocation);
SetLabelText(FString::Printf(TEXT("%.1f%s"), Value, *UnitSuffix));
}
+15 -46
View File
@@ -26,53 +26,22 @@ protected:
public: public:
UPROPERTY(EditDefaultsOnly, BlueprintReadWrite) UPROPERTY(EditDefaultsOnly, BlueprintReadWrite) float UEUnitsPerUnit = 10.f;
float UEUnitsPerUnit = 100.f; UPROPERTY(EditDefaultsOnly) bool bRequiresSecondHand = false;
UPROPERTY(EditDefaultsOnly) bool bExtendable = false;
UPROPERTY(EditDefaultsOnly) FGestureBinding Binding;
UPROPERTY(BlueprintReadWrite) TArray<ATableIconActor*> Icons;
UPROPERTY(BlueprintReadWrite) AHandActor* HandA;
UPROPERTY(BlueprintReadWrite) AHandActor* HandB;
UPROPERTY(BlueprintReadWrite) AGestureAction* LinkedActions;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Stuff") AHandManager* HandManager;
UPROPERTY(EditDefaultsOnly) UFUNCTION(BlueprintCallable, BlueprintNativeEvent) void InitAction(AHandActor* Hand, const FGestureBinding& NewBinding, float NewUEUnitsPerUnit, float TableHeight);
bool bRequiresSecondHand = false; UFUNCTION(BlueprintCallable, BlueprintNativeEvent) void UpdateAction();
UFUNCTION(BlueprintCallable, BlueprintNativeEvent) void AbortAction();
UPROPERTY(EditDefaultsOnly) UFUNCTION(BlueprintCallable, BlueprintNativeEvent) void AddExtension(AGestureAction* OtherAction);
bool bExtendable = false; UFUNCTION(BlueprintCallable, BlueprintNativeEvent) void AddOtherHand(AHandActor* OtherHand);
UFUNCTION(BlueprintCallable, BlueprintNativeEvent) void EndGesture();
UPROPERTY(EditDefaultsOnly)
FGestureBinding Binding;
UPROPERTY(BlueprintReadWrite)
TArray<ATableIconActor*> Icons;
UPROPERTY(BlueprintReadWrite)
TMap<FName, FVector> Anchors;
UPROPERTY(BlueprintReadWrite)
AHandActor* HandA;
UPROPERTY(BlueprintReadWrite)
AHandActor* HandB;
UPROPERTY(BlueprintReadWrite)
AGestureAction* LinkedActions;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Stuff")
AHandManager* HandManager;
UFUNCTION(BlueprintCallable, BlueprintNativeEvent)
void InitAction(AHandActor* Hand, const FGestureBinding& NewBinding, float NewUEUnitsPerUnit);
UFUNCTION(BlueprintCallable, BlueprintNativeEvent)
void UpdateAction();
UFUNCTION(BlueprintCallable, BlueprintNativeEvent)
void AbortAction();
UFUNCTION(BlueprintCallable, BlueprintNativeEvent)
void AddExtension(AGestureAction* OtherAction);
UFUNCTION(BlueprintCallable, BlueprintNativeEvent)
void AddOtherHand(AHandActor* OtherHand);
UFUNCTION(BlueprintCallable, BlueprintNativeEvent)
void EndGesture();
void DestroyIcons(); void DestroyIcons();
}; };
@@ -24,34 +24,25 @@ protected:
public: public:
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Scale") UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Scale") float UEUnitsPerUnit = 10.f; // set via calibration rectangle
float UEUnitsPerUnit = 100.f; // set via calibration rectangle UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Scale") float TableHeight = -650.f;
UPROPERTY(BlueprintReadOnly) AHandManager* HandManager;
UPROPERTY(BlueprintReadOnly) UFUNCTION(BlueprintCallable, Category = "Gesture") void EvaluateHands();
AHandManager* HandManager;
UFUNCTION(BlueprintCallable, Category = "Gesture") UFUNCTION() void OnHandAdded(AHandActor* Hand);
void EvaluateHands();
UFUNCTION() UFUNCTION() void OnHandRemoved(AHandActor* Hand);
void OnHandAdded(AHandActor* Hand);
UFUNCTION() UPROPERTY(EditDefaultsOnly, Category = "Gesture") FString BindingConfigPath; // path to the per-game JSON
void OnHandRemoved(AHandActor* Hand);
UPROPERTY(EditDefaultsOnly, Category = "Gesture")
FString BindingConfigPath; // path to the per-game JSON
// Loaded from JSON — gesture name -> definition object // Loaded from JSON — gesture name -> definition object
UPROPERTY(BlueprintReadOnly, Category = "Gesture") UPROPERTY(BlueprintReadOnly, Category = "Gesture") TArray<UGestureDefinition*> GestureDefinitions;
TArray<UGestureDefinition*> GestureDefinitions;
UPROPERTY(BlueprintReadOnly, Category = "Gesture") UPROPERTY(BlueprintReadOnly, Category = "Gesture") TArray<AGestureAction*> ActiveGestureActions;
TArray<AGestureAction*> ActiveGestureActions;
// Populated on BeginPlay from JSON // Populated on BeginPlay from JSON
UPROPERTY(BlueprintReadOnly, Category = "Gesture") UPROPERTY(BlueprintReadOnly, Category = "Gesture") TArray<FGestureBinding> GestureBindings;
TArray<FGestureBinding> GestureBindings;
void LoadBindings(const FString& FilePath); void LoadBindings(const FString& FilePath);
void Initialize(UDataTable* InGestureTable, UDataTable* InActionTable); void Initialize(UDataTable* InGestureTable, UDataTable* InActionTable);
@@ -59,19 +50,15 @@ public:
void BuildActionMap(); void BuildActionMap();
// Populated on BeginPlay from the tables // Populated on BeginPlay from the tables
UPROPERTY(BlueprintReadOnly, Category = "Gesture") UPROPERTY(BlueprintReadOnly, Category = "Gesture") TMap<FName, UGestureDefinition*> DefinitionMap;
TMap<FName, UGestureDefinition*> DefinitionMap;
UPROPERTY(BlueprintReadOnly, Category = "Gesture") UPROPERTY(BlueprintReadOnly, Category = "Gesture") TMap<FName, TSubclassOf<AGestureAction>> ActionClassMap;
TMap<FName, TSubclassOf<AGestureAction>> ActionClassMap;
private: private:
// //
UPROPERTY(EditDefaultsOnly, Category = "Gesture") UPROPERTY(EditDefaultsOnly, Category = "Gesture") UDataTable* GestureDefinitionTable;
UDataTable* GestureDefinitionTable;
UPROPERTY(EditDefaultsOnly, Category = "Gesture") UPROPERTY(EditDefaultsOnly, Category = "Gesture") UDataTable* GestureActionTable;
UDataTable* GestureActionTable;
void CheckGestureDefinitions(TArray<AHandActor*>& Hands); void CheckGestureDefinitions(TArray<AHandActor*>& Hands);
+16 -37
View File
@@ -34,50 +34,29 @@ public:
void SpawnNewHand(const FVector& PalmCenter, const FHandPacketHand& NewHand); void SpawnNewHand(const FVector& PalmCenter, const FHandPacketHand& NewHand);
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Hand Tracking|Debug") UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Hand") ADetectionManager* DM;
ADetectionManager* DM; UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Hand") TArray<AHandActor*> HandList;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Hand") TArray<AHandActor*> DeadHands;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Hand") int32 HandAllocationDistance = FMath::Square(1000);;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Hand") TSubclassOf<class AHandActor> HandActorClass;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Hand") TSubclassOf<class AFingerTip> FingerTipClass;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Hand") UPROPERTY(BlueprintAssignable) FOnHandAdded OnHandAdded;
TArray<AHandActor*> HandList; UPROPERTY(BlueprintAssignable) FOnHandRemoved OnHandRemoved;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Hand") UFUNCTION(BlueprintCallable) void AddDeadHand(AHandActor* DeadHand);
TArray<AHandActor*> DeadHands; UFUNCTION(BlueprintCallable, BlueprintPure) TArray<AHandActor*>& GetHandList();
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Hand")
int32 HandAllocationDistance = FMath::Square(1000);;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Hand")
TSubclassOf<class AHandActor> HandActorClass;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Hand")
TSubclassOf<class AFingerTip> FingerTipClass;
UFUNCTION(BlueprintCallable)
void AddDeadHand(AHandActor* DeadHand);
UPROPERTY(BlueprintAssignable)
FOnHandAdded OnHandAdded;
UPROPERTY(BlueprintAssignable)
FOnHandRemoved OnHandRemoved;
UFUNCTION(BlueprintCallable)
TArray<AHandActor*>& GetHandList();
//Gestures //Gestures
UPROPERTY(VisibleAnywhere, Category = "Gesture") UGestureManagerComponent* GestureManager;
UPROPERTY(VisibleAnywhere, Category = "Gesture") UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Gesture") UDataTable* GestureDefinitionTable;
UGestureManagerComponent* GestureManager; UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Gesture") UDataTable* GestureActionTable;
const TArray<AHandActor*>& GetHandList() const { return HandList; } const TArray<AHandActor*>& GetHandList() const { return HandList; }
// GestureManager.h — add these //Gesture Actions, Icons
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Gesture") UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Scale") float UEUnitsPerUnit = 10.f;
UDataTable* GestureDefinitionTable; UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Scale") float TableHeight = -650.f;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Gesture")
UDataTable* GestureActionTable;
private: private:
void RemoveDeadHands(); void RemoveDeadHands();
+26 -8
View File
@@ -6,6 +6,8 @@
#include "GestureAction.h" #include "GestureAction.h"
#include "MyGestureActions.generated.h" #include "MyGestureActions.generated.h"
class ATableIconLineActor;
class ATableIconLabelActor;
/** /**
* *
*/ */
@@ -14,23 +16,39 @@ class TABLE_API AMyGestureActions : public AGestureAction
{ {
GENERATED_BODY() GENERATED_BODY()
//virtual void InitAction_Implementation(AHandActor* Hand, const FGestureBinding& NewBinding, float NewUEUnitsPerUnit) override;
//virtual void UpdateAction_Implementation() override;
//virtual void EndGesture_Implementation() override;
//virtual void AddOtherHand_Implementation(AHandActor* OtherHand) override;
}; };
UCLASS() UCLASS(Blueprintable)
class TABLE_API ARulerAction : public AGestureAction class TABLE_API AGestureAction_Ruler : public AGestureAction
{ {
GENERATED_BODY() GENERATED_BODY()
public: public:
void UpdateAction_Implementation() override; // Set these in BP defaults — point at your BP subclasses
void AbortAction_Implementation() override; UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = "Ruler") TSubclassOf<ATableIconLineActor> LineIconClass;
void AddExtension_Implementation(AGestureAction* OtherAction) override; UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = "Ruler") TSubclassOf<ATableIconLabelActor> LabelIconClass;
void EndGesture_Implementation() override; UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = "Ruler") FString UnitSuffix = TEXT("\"");
void InitAction_Implementation(AHandActor* Hand, const FGestureBinding& NewBinding, float NewUEUnitsPerUnit) override; UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Ruler") TArray<FVector> RulerPoints;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Ruler") float DesiredHeight = -650.f;
UFUNCTION(BlueprintCallable, BlueprintPure) float GetTotalLength() const;
UFUNCTION(BlueprintCallable) void AddRulerPoint();
virtual void InitAction_Implementation(AHandActor* Hand, const FGestureBinding& NewBinding, float NewUEUnitsPerUnit, float TableHeight) override;
virtual void UpdateAction_Implementation() override;
private: private:
float GetSnappedInches(float RawDistance) const; UPROPERTY() ATableIconLineActor* LineIcon = nullptr;
UPROPERTY() ATableIconLabelActor* LabelIcon = nullptr;
UPROPERTY() FVector StartPoint;
float GetSnappedUnits(float RawDistance) const;
}; };
+29 -49
View File
@@ -11,15 +11,6 @@
class UWidgetComponent; class UWidgetComponent;
class UWidgetInteractionComponent; class UWidgetInteractionComponent;
/**
*
*/
UCLASS()
class TABLE_API AMyTableIconActors : public ATableIconActor
{
GENERATED_BODY()
};
UCLASS(Blueprintable) UCLASS(Blueprintable)
@@ -30,36 +21,23 @@ class TABLE_API ATableIconLineActor : public ATableIconActor
public: public:
ATableIconLineActor(); ATableIconLineActor();
UFUNCTION(BlueprintCallable) UFUNCTION(BlueprintCallable) void SetPoints(const TArray<FVector>& InPoints);
void SetEndpoints(const FVector& Start, const FVector& End);
UPROPERTY(EditDefaultsOnly, BlueprintReadWrite)
float LineThickness = 1.f;
UPROPERTY(EditDefaultsOnly, BlueprintReadWrite) UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Line") float LineThickness = 10.f;
FLinearColor LineColor = FLinearColor::White; UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Line") FLinearColor LineColor = FLinearColor::Blue;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Line") UMaterialInterface* LineMaterial = nullptr;
// Tick markers along the line — e.g. every inch protected:
UPROPERTY(EditDefaultsOnly, BlueprintReadWrite) UPROPERTY(BlueprintReadOnly, Category = "Line") TArray<FVector> Points;
bool bShowTickMarks = true;
UPROPERTY(EditDefaultsOnly, BlueprintReadWrite)
float TickInterval = 1.f; // in real-world units
UPROPERTY(EditDefaultsOnly, BlueprintReadWrite)
float UEUnitsPerUnit = 100.f;
private: private:
UPROPERTY() UPROPERTY() UProceduralMeshComponent* MeshComponent;
UProceduralMeshComponent* MeshComponent;
void BuildLineMesh(const FVector& Start, const FVector& End); void BuildLineMesh();
void BuildTickMesh(const FVector& Start, const FVector& End);
FVector LastStart;
FVector LastEnd;
}; };
//--------------------------------------------------------------------------------------------
UCLASS(Blueprintable) UCLASS(Blueprintable)
@@ -70,24 +48,26 @@ class TABLE_API ATableIconWidgetActor : public ATableIconActor
public: public:
ATableIconWidgetActor(); ATableIconWidgetActor();
UPROPERTY(VisibleAnywhere, BlueprintReadOnly) UPROPERTY(VisibleAnywhere, BlueprintReadOnly) UWidgetComponent* WidgetComponent;
UWidgetComponent* WidgetComponent; UPROPERTY(VisibleAnywhere, BlueprintReadOnly) UWidgetInteractionComponent* WidgetInteraction;
UPROPERTY(VisibleAnywhere, BlueprintReadOnly) TSubclassOf<UUserWidget> LabelWidgetClass;
UPROPERTY(VisibleAnywhere, BlueprintReadOnly) UFUNCTION(BlueprintCallable) void SetWidgetClass(TSubclassOf<UUserWidget> InWidgetClass);
UWidgetInteractionComponent* WidgetInteraction; UFUNCTION(BlueprintCallable, BlueprintPure) UUserWidget* GetWidget() const;
};
//--------------------------------------------------------------------------------------------
UCLASS(Blueprintable)
class TABLE_API ATableIconLabelActor : public ATableIconWidgetActor
{
GENERATED_BODY()
public:
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Label") FString UnitSuffix = TEXT("\"");
UFUNCTION(BlueprintImplementableEvent, BlueprintCallable, Category = "Label") void SetLabelText(const FString& Text);
UFUNCTION(BlueprintCallable, Category = "Label") void UpdateLabel(const FVector& WorldLocation, float Value);
// Face toward a target each tick — useful for always facing the camera
UPROPERTY(EditDefaultsOnly, BlueprintReadWrite)
bool bFaceCamera = true;
// Set widget content — callable from GestureAction
UFUNCTION(BlueprintCallable)
void SetWidgetClass(TSubclassOf<UUserWidget> InWidgetClass);
// Get the underlying widget cast to a specific type in BP
UFUNCTION(BlueprintCallable, BlueprintPure)
UUserWidget* GetWidget() const;
protected:
virtual void Tick(float DeltaTime) override;
}; };
+6 -6
View File
@@ -6,6 +6,8 @@
#include "GameFramework/Actor.h" #include "GameFramework/Actor.h"
#include "TableIconActor.generated.h" #include "TableIconActor.generated.h"
class AGestureAction;
UCLASS() UCLASS()
class TABLE_API ATableIconActor : public AActor class TABLE_API ATableIconActor : public AActor
{ {
@@ -21,16 +23,14 @@ protected:
public: public:
// If true, GestureAction will not destroy this on EndGesture // If true, GestureAction will not destroy this on EndGesture
UPROPERTY(EditDefaultsOnly, BlueprintReadWrite) UPROPERTY(EditDefaultsOnly, BlueprintReadWrite) bool bPersistOnActionEnd = false;
bool bPersistOnActionEnd = false; UPROPERTY(EditAnywhere) AGestureAction* OwningAction;
// Called by GestureAction on end — BP overrides this for fade/lock behavior // Called by GestureAction on end — BP overrides this for fade/lock behavior
UFUNCTION(BlueprintNativeEvent) UFUNCTION(BlueprintNativeEvent) void OnActionEnded();
void OnActionEnded();
// Called by GestureAction every tick // Called by GestureAction every tick
UFUNCTION(BlueprintNativeEvent) UFUNCTION(BlueprintNativeEvent) void OnActionUpdated();
void OnActionUpdated();
}; };