Added GestureAction Ruler, added TableLineActor
This commit is contained in:
@@ -197,8 +197,8 @@ bool ADetectionManager::ParseHandPacket(const TArray<uint8>& Bytes, int32& OutSe
|
||||
|
||||
FHandPacketHand Hand;
|
||||
Hand.HandId = (int32)HandId;
|
||||
Hand.Handedness = (HandednessByte == 1) ? EHandedness::Left :
|
||||
(HandednessByte == 2) ? EHandedness::Right :
|
||||
Hand.Handedness = (HandednessByte == 1) ? EHandedness::Right :
|
||||
(HandednessByte == 2) ? EHandedness::Left :
|
||||
EHandedness::Unknown;
|
||||
Hand.Confidence = Confidence;
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ void AGestureAction::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;
|
||||
bRequiresSecondHand = Binding.LeftGesture != "" && Binding.RightGesture != "";
|
||||
@@ -52,6 +52,8 @@ void AGestureAction::AddOtherHand_Implementation(AHandActor* OtherHand)
|
||||
|
||||
void AGestureAction::EndGesture_Implementation()
|
||||
{
|
||||
HandA = nullptr;
|
||||
HandB = nullptr;
|
||||
DestroyIcons();
|
||||
Destroy();
|
||||
}
|
||||
|
||||
@@ -31,6 +31,54 @@ void UGestureManagerComponent::BeginPlay()
|
||||
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()
|
||||
{
|
||||
TArray<AHandActor*>& Hands = HandManager->HandList;
|
||||
@@ -86,7 +134,7 @@ void UGestureManagerComponent::LoadBindings(const FString& FilePath)
|
||||
|
||||
const TArray<TSharedPtr<FJsonValue>>* BindingsArray;
|
||||
|
||||
if (Root->TryGetArrayField(TEXT("bindings"), BindingsArray))
|
||||
if (Root->TryGetArrayField(TEXT("rules"), 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)
|
||||
{
|
||||
@@ -272,7 +278,7 @@ void UGestureManagerComponent::CheckGestureDefinitions(TArray<AHandActor*>& Hand
|
||||
Hand->CandidateFrames = 1;
|
||||
Hand->FailFrames = 0;
|
||||
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++;
|
||||
//else Hand->FailFrames = 0;
|
||||
if (Hand->ActiveGestureName != BestGesture)
|
||||
@@ -313,8 +319,9 @@ void UGestureManagerComponent::CheckGestureDefinitions(TArray<AHandActor*>& Hand
|
||||
|
||||
void UGestureManagerComponent::TriggerAction(AHandActor* Hand)
|
||||
{
|
||||
|
||||
if (Hand->bUsedThisTick) return;
|
||||
|
||||
|
||||
if (TryExtendGestureAction(Hand)) return;
|
||||
|
||||
for (const FGestureBinding& Binding : GestureBindings)
|
||||
@@ -323,9 +330,12 @@ void UGestureManagerComponent::TriggerAction(AHandActor* Hand)
|
||||
|| (Hand->Handedness == EHandedness::Right && Binding.RightGesture == Hand->ActiveGestureName);
|
||||
|
||||
if (!bMatches) continue;
|
||||
|
||||
|
||||
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;
|
||||
}
|
||||
@@ -334,6 +344,7 @@ void UGestureManagerComponent::TriggerAction(AHandActor* Hand)
|
||||
|
||||
void UGestureManagerComponent::UpdateOngoingActions()
|
||||
{
|
||||
|
||||
for (AGestureAction* Action : ActiveGestureActions)
|
||||
{
|
||||
if (!Action) continue;
|
||||
@@ -344,7 +355,6 @@ void UGestureManagerComponent::UpdateOngoingActions()
|
||||
|
||||
AGestureAction* UGestureManagerComponent::SpawnGestureAction(FName ActionName, AHandActor* Hand)
|
||||
{
|
||||
|
||||
TSubclassOf<AGestureAction>* ActionClassPtr = ActionClassMap.Find(ActionName);
|
||||
|
||||
if (!ActionClassPtr || !Hand) return nullptr;
|
||||
@@ -362,6 +372,7 @@ AGestureAction* UGestureManagerComponent::SpawnGestureAction(FName ActionName, A
|
||||
|
||||
if (!NewAction) return nullptr;
|
||||
|
||||
ActiveGestureActions.Add(NewAction);
|
||||
|
||||
Hand->ActiveAction = NewAction;
|
||||
Hand->bUsedThisTick = true;
|
||||
@@ -424,7 +435,11 @@ bool UGestureManagerComponent::TryCreateExtension(AHandActor* 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;
|
||||
Hand->bUsedThisTick = true;
|
||||
OtherAction->AddExtension(NewAction);
|
||||
|
||||
@@ -4,80 +4,64 @@
|
||||
#include "MyGestureActions.h"
|
||||
#include "HandActor.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;
|
||||
|
||||
// Update end point to current fingertip
|
||||
Anchors.Add("End", HandA->Points[8]);
|
||||
|
||||
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]);
|
||||
Super::InitAction_Implementation(Hand, NewBinding, NewUEUnitsPerUnit, TableHeight);
|
||||
DesiredHeight = -500;
|
||||
StartPoint = HandA->Points[8];
|
||||
StartPoint.Z = DesiredHeight;
|
||||
|
||||
FActorSpawnParameters SpawnParams;
|
||||
SpawnParams.Owner = GetOwner();
|
||||
SpawnParams.SpawnCollisionHandlingOverride =
|
||||
ESpawnActorCollisionHandlingMethod::AlwaysSpawn;
|
||||
|
||||
AGestureAction* NewAction = nullptr;
|
||||
/*AGestureAction* NewAction = GetWorld()->SpawnActor<AGestureAction>(
|
||||
*ActionClassPtr,
|
||||
FVector::ZeroVector,
|
||||
FRotator::ZeroRotator,
|
||||
SpawnParams);
|
||||
*/
|
||||
if (!NewAction) return;
|
||||
SpawnParams.Owner = this;
|
||||
SpawnParams.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AlwaysSpawn;
|
||||
|
||||
if (LineIconClass)
|
||||
{
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
float ARulerAction::GetSnappedInches(float RawDistance) const
|
||||
void AGestureAction_Ruler::UpdateAction_Implementation()
|
||||
{
|
||||
if (!HandA || !IsValid(LineIcon)) return;
|
||||
const FVector Current = HandA->Points[8];
|
||||
|
||||
//if (FVector::DistSquared(Current, RulerPoints[RulerPoints.Num() - 1]) < FMath::Square(UEUnitsPerUnit)) return;
|
||||
RulerPoints[RulerPoints.Num() - 1] = Current - StartPoint;
|
||||
RulerPoints[RulerPoints.Num() - 1].Z = 0;
|
||||
LineIcon->SetPoints(RulerPoints);
|
||||
}
|
||||
|
||||
float AGestureAction_Ruler::GetSnappedUnits(float RawDistance) const
|
||||
{
|
||||
if (UEUnitsPerUnit <= 0.f) return 0.f;
|
||||
float Unit = RawDistance / UEUnitsPerUnit;
|
||||
return FMath::RoundToFloat(Unit); // snap to whole inches
|
||||
return FMath::RoundToFloat(RawDistance / UEUnitsPerUnit);
|
||||
}
|
||||
|
||||
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,118 +10,104 @@
|
||||
|
||||
ATableIconLineActor::ATableIconLineActor()
|
||||
{
|
||||
PrimaryActorTick.bCanEverTick = false; // purely driven by SetPoints
|
||||
|
||||
USceneComponent* Root = CreateDefaultSubobject<USceneComponent>(TEXT("Root"));
|
||||
SetRootComponent(Root);
|
||||
|
||||
MeshComponent = CreateDefaultSubobject<UProceduralMeshComponent>(TEXT("LineMesh"));
|
||||
MeshComponent->SetupAttachment(RootComponent);
|
||||
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
|
||||
if (Start.Equals(LastStart, 0.1f) && End.Equals(LastEnd, 0.1f)) return;
|
||||
LastStart = Start;
|
||||
LastEnd = End;
|
||||
const FTransform& ActorTransform = GetActorTransform();
|
||||
|
||||
BuildLineMesh(Start, End);
|
||||
if (bShowTickMarks) BuildTickMesh(Start, End);
|
||||
Points.Reset(NewPoints.Num());
|
||||
|
||||
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;
|
||||
TArray<FVector> Vertices;
|
||||
TArray<int32> Triangles;
|
||||
TArray<FVector> Normals;
|
||||
TArray<FVector2D> UVs;
|
||||
TArray<FColor> Colors;
|
||||
TArray<FProcMeshTangent> Tangents;
|
||||
|
||||
// A flat quad along the line, facing up
|
||||
TArray<FVector> Vertices;
|
||||
TArray<int32> Triangles;
|
||||
TArray<FVector> Normals;
|
||||
TArray<FVector2D> UVs;
|
||||
TArray<FColor> Colors;
|
||||
TArray<FProcMeshTangent> Tangents;
|
||||
FVector Direction = FVector::ZeroVector;
|
||||
const float HalfLineThickness = LineThickness * 0.5f;
|
||||
|
||||
for (int32 i = 0; i < Points.Num() - 1; i++) {
|
||||
const FVector& PointA = Points[i];
|
||||
const FVector& PointB = Points[i + 1];
|
||||
|
||||
Vertices.Add(Start - Right); // 0
|
||||
Vertices.Add(Start + Right); // 1
|
||||
Vertices.Add(End + Right); // 2
|
||||
Vertices.Add(End - Right); // 3
|
||||
Direction = (PointB - PointA).GetSafeNormal();
|
||||
|
||||
Triangles = { 0, 1, 2, 0, 2, 3 };
|
||||
const FVector Right = FVector::CrossProduct(FVector::UpVector, Direction).GetSafeNormal();
|
||||
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
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
|
||||
MeshComponent->CreateMeshSection(0, Vertices, Triangles, Normals, UVs,
|
||||
Colors, Tangents, false);
|
||||
}
|
||||
|
||||
void ATableIconLineActor::BuildTickMesh(const FVector& Start, const FVector& End)
|
||||
{
|
||||
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;
|
||||
const FVector Offset = Right * (LineThickness * 0.5f);
|
||||
|
||||
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);
|
||||
Triangles.Add(Base); Triangles.Add(Base + 2); Triangles.Add(Base + 3);
|
||||
Vertices.Add(PointA - Offset);
|
||||
Vertices.Add(PointA + Offset);
|
||||
Vertices.Add(PointB + Offset);
|
||||
Vertices.Add(PointB - Offset);
|
||||
|
||||
for (int j = 0; j < 4; j++)
|
||||
{
|
||||
Normals.Add(FVector::UpVector);
|
||||
UVs.Add(FVector2D::ZeroVector);
|
||||
Colors.Add(LineColor.ToFColor(true));
|
||||
Tangents.Add(FProcMeshTangent(Dir, false));
|
||||
}
|
||||
Triangles.Add(Base + 0);
|
||||
Triangles.Add(Base + 1);
|
||||
Triangles.Add(Base + 2);
|
||||
|
||||
Triangles.Add(Base + 0);
|
||||
Triangles.Add(Base + 2);
|
||||
Triangles.Add(Base + 3);
|
||||
}
|
||||
|
||||
// Section 1 = tick marks
|
||||
MeshComponent->CreateMeshSection(1, Vertices, Triangles, Normals, UVs,
|
||||
Colors, Tangents, false);
|
||||
|
||||
for (int i = 0; i < Vertices.Num(); i++)
|
||||
{
|
||||
Normals.Add(FVector::UpVector);
|
||||
}
|
||||
|
||||
for (int i = 0; i < Vertices.Num(); i++)
|
||||
{
|
||||
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()
|
||||
{
|
||||
USceneComponent* Root = CreateDefaultSubobject<USceneComponent>(TEXT("Root"));
|
||||
SetRootComponent(Root);
|
||||
|
||||
PrimaryActorTick.bCanEverTick = true;
|
||||
|
||||
WidgetComponent = CreateDefaultSubobject<UWidgetComponent>(TEXT("WidgetComponent"));
|
||||
@@ -140,15 +126,13 @@ void ATableIconWidgetActor::Tick(float DeltaTime)
|
||||
{
|
||||
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
|
||||
// Only rotate on yaw � keeps widget flat relative to table
|
||||
ToCamera.Z = 0.f;
|
||||
if (!ToCamera.IsNearlyZero())
|
||||
SetActorRotation(ToCamera.ToOrientationRotator());
|
||||
@@ -164,4 +148,12 @@ void ATableIconWidgetActor::SetWidgetClass(TSubclassOf<UUserWidget> InWidgetClas
|
||||
UUserWidget* ATableIconWidgetActor::GetWidget() const
|
||||
{
|
||||
return WidgetComponent->GetUserWidgetObject();
|
||||
}
|
||||
|
||||
|
||||
|
||||
void ATableIconLabelActor::SetDistance(float RealWorldUnits, const FString& UnitSuffix)
|
||||
{
|
||||
int32 Snapped = FMath::RoundToInt(RealWorldUnits);
|
||||
SetLabelText(FString::Printf(TEXT("%d%s"), Snapped, *UnitSuffix));
|
||||
}
|
||||
@@ -26,53 +26,22 @@ protected:
|
||||
|
||||
public:
|
||||
|
||||
UPROPERTY(EditDefaultsOnly, BlueprintReadWrite)
|
||||
float UEUnitsPerUnit = 100.f;
|
||||
UPROPERTY(EditDefaultsOnly, BlueprintReadWrite) float UEUnitsPerUnit = 10.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)
|
||||
bool bRequiresSecondHand = false;
|
||||
|
||||
UPROPERTY(EditDefaultsOnly)
|
||||
bool bExtendable = false;
|
||||
|
||||
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();
|
||||
UFUNCTION(BlueprintCallable, BlueprintNativeEvent) void InitAction(AHandActor* Hand, const FGestureBinding& NewBinding, float NewUEUnitsPerUnit, float TableHeight);
|
||||
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();
|
||||
};
|
||||
|
||||
@@ -24,34 +24,25 @@ protected:
|
||||
|
||||
public:
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Scale")
|
||||
float UEUnitsPerUnit = 100.f; // set via calibration rectangle
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Scale") float UEUnitsPerUnit = 10.f; // set via calibration rectangle
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Scale") float TableHeight = -650.f;
|
||||
UPROPERTY(BlueprintReadOnly) AHandManager* HandManager;
|
||||
|
||||
UPROPERTY(BlueprintReadOnly)
|
||||
AHandManager* HandManager;
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "Gesture")
|
||||
void EvaluateHands();
|
||||
UFUNCTION(BlueprintCallable, Category = "Gesture") void EvaluateHands();
|
||||
|
||||
UFUNCTION()
|
||||
void OnHandAdded(AHandActor* Hand);
|
||||
UFUNCTION() void OnHandAdded(AHandActor* Hand);
|
||||
|
||||
UFUNCTION()
|
||||
void OnHandRemoved(AHandActor* Hand);
|
||||
UFUNCTION() void OnHandRemoved(AHandActor* Hand);
|
||||
|
||||
UPROPERTY(EditDefaultsOnly, Category = "Gesture")
|
||||
FString BindingConfigPath; // path to the per-game JSON
|
||||
UPROPERTY(EditDefaultsOnly, Category = "Gesture") FString BindingConfigPath; // path to the per-game JSON
|
||||
|
||||
// Loaded from JSON — gesture name -> definition object
|
||||
UPROPERTY(BlueprintReadOnly, Category = "Gesture")
|
||||
TArray<UGestureDefinition*> GestureDefinitions;
|
||||
UPROPERTY(BlueprintReadOnly, Category = "Gesture") TArray<UGestureDefinition*> GestureDefinitions;
|
||||
|
||||
UPROPERTY(BlueprintReadOnly, Category = "Gesture")
|
||||
TArray<AGestureAction*> ActiveGestureActions;
|
||||
UPROPERTY(BlueprintReadOnly, Category = "Gesture") TArray<AGestureAction*> ActiveGestureActions;
|
||||
|
||||
// Populated on BeginPlay from JSON
|
||||
UPROPERTY(BlueprintReadOnly, Category = "Gesture")
|
||||
TArray<FGestureBinding> GestureBindings;
|
||||
UPROPERTY(BlueprintReadOnly, Category = "Gesture") TArray<FGestureBinding> GestureBindings;
|
||||
void LoadBindings(const FString& FilePath);
|
||||
|
||||
void Initialize(UDataTable* InGestureTable, UDataTable* InActionTable);
|
||||
@@ -59,19 +50,15 @@ public:
|
||||
void BuildActionMap();
|
||||
|
||||
// Populated on BeginPlay from the tables
|
||||
UPROPERTY(BlueprintReadOnly, Category = "Gesture")
|
||||
TMap<FName, UGestureDefinition*> DefinitionMap;
|
||||
UPROPERTY(BlueprintReadOnly, Category = "Gesture") TMap<FName, UGestureDefinition*> DefinitionMap;
|
||||
|
||||
UPROPERTY(BlueprintReadOnly, Category = "Gesture")
|
||||
TMap<FName, TSubclassOf<AGestureAction>> ActionClassMap;
|
||||
UPROPERTY(BlueprintReadOnly, Category = "Gesture") TMap<FName, TSubclassOf<AGestureAction>> ActionClassMap;
|
||||
|
||||
private:
|
||||
//
|
||||
UPROPERTY(EditDefaultsOnly, Category = "Gesture")
|
||||
UDataTable* GestureDefinitionTable;
|
||||
UPROPERTY(EditDefaultsOnly, Category = "Gesture") UDataTable* GestureDefinitionTable;
|
||||
|
||||
UPROPERTY(EditDefaultsOnly, Category = "Gesture")
|
||||
UDataTable* GestureActionTable;
|
||||
UPROPERTY(EditDefaultsOnly, Category = "Gesture") UDataTable* GestureActionTable;
|
||||
|
||||
|
||||
void CheckGestureDefinitions(TArray<AHandActor*>& Hands);
|
||||
|
||||
@@ -6,6 +6,8 @@
|
||||
#include "GestureAction.h"
|
||||
#include "MyGestureActions.generated.h"
|
||||
|
||||
class ATableIconLineActor;
|
||||
class ATableIconLabelActor;
|
||||
/**
|
||||
*
|
||||
*/
|
||||
@@ -14,23 +16,39 @@ class TABLE_API AMyGestureActions : public AGestureAction
|
||||
{
|
||||
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()
|
||||
class TABLE_API ARulerAction : public AGestureAction
|
||||
UCLASS(Blueprintable)
|
||||
class TABLE_API AGestureAction_Ruler : public AGestureAction
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
void UpdateAction_Implementation() override;
|
||||
void AbortAction_Implementation() override;
|
||||
void AddExtension_Implementation(AGestureAction* OtherAction) override;
|
||||
void EndGesture_Implementation() override;
|
||||
void InitAction_Implementation(AHandActor* Hand, const FGestureBinding& NewBinding, float NewUEUnitsPerUnit) override;
|
||||
// Set these in BP defaults — point at your BP subclasses
|
||||
UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = "Ruler") TSubclassOf<ATableIconLineActor> LineIconClass;
|
||||
UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = "Ruler") TSubclassOf<ATableIconLabelActor> LabelIconClass;
|
||||
UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = "Ruler") FString UnitSuffix = TEXT("\"");
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Ruler") TArray<FVector> RulerPoints;
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Line") 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:
|
||||
float GetSnappedInches(float RawDistance) const;
|
||||
UPROPERTY() ATableIconLineActor* LineIcon = nullptr;
|
||||
UPROPERTY() ATableIconLabelActor* LabelIcon = nullptr;
|
||||
UPROPERTY() FVector StartPoint;
|
||||
|
||||
float GetSnappedUnits(float RawDistance) const;
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -30,34 +30,20 @@ class TABLE_API ATableIconLineActor : public ATableIconActor
|
||||
public:
|
||||
ATableIconLineActor();
|
||||
|
||||
UFUNCTION(BlueprintCallable)
|
||||
void SetEndpoints(const FVector& Start, const FVector& End);
|
||||
UFUNCTION(BlueprintCallable) void SetPoints(const TArray<FVector>& InPoints);
|
||||
|
||||
UPROPERTY(EditDefaultsOnly, BlueprintReadWrite)
|
||||
float LineThickness = 1.f;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Line") float LineThickness = 10.f;
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Line") FLinearColor LineColor = FLinearColor::Blue;
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Line") UMaterialInterface* LineMaterial = nullptr;
|
||||
|
||||
UPROPERTY(EditDefaultsOnly, BlueprintReadWrite)
|
||||
FLinearColor LineColor = FLinearColor::White;
|
||||
|
||||
// Tick markers along the line — e.g. every inch
|
||||
UPROPERTY(EditDefaultsOnly, BlueprintReadWrite)
|
||||
bool bShowTickMarks = true;
|
||||
|
||||
UPROPERTY(EditDefaultsOnly, BlueprintReadWrite)
|
||||
float TickInterval = 1.f; // in real-world units
|
||||
|
||||
UPROPERTY(EditDefaultsOnly, BlueprintReadWrite)
|
||||
float UEUnitsPerUnit = 100.f;
|
||||
protected:
|
||||
UPROPERTY(BlueprintReadOnly, Category = "Line") TArray<FVector> Points;
|
||||
|
||||
private:
|
||||
UPROPERTY()
|
||||
UProceduralMeshComponent* MeshComponent;
|
||||
UPROPERTY() UProceduralMeshComponent* MeshComponent;
|
||||
|
||||
void BuildLineMesh(const FVector& Start, const FVector& End);
|
||||
void BuildTickMesh(const FVector& Start, const FVector& End);
|
||||
|
||||
FVector LastStart;
|
||||
FVector LastEnd;
|
||||
void BuildLineMesh();
|
||||
};
|
||||
|
||||
|
||||
@@ -70,24 +56,22 @@ class TABLE_API ATableIconWidgetActor : public ATableIconActor
|
||||
public:
|
||||
ATableIconWidgetActor();
|
||||
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly)
|
||||
UWidgetComponent* WidgetComponent;
|
||||
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly)
|
||||
UWidgetInteractionComponent* WidgetInteraction;
|
||||
|
||||
// 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;
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly) UWidgetComponent* WidgetComponent;
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly) UWidgetInteractionComponent* WidgetInteraction;
|
||||
UFUNCTION(BlueprintCallable) void SetWidgetClass(TSubclassOf<UUserWidget> InWidgetClass);
|
||||
UFUNCTION(BlueprintCallable, BlueprintPure) UUserWidget* GetWidget() const;
|
||||
|
||||
protected:
|
||||
virtual void Tick(float DeltaTime) override;
|
||||
};
|
||||
|
||||
UCLASS(Blueprintable)
|
||||
class TABLE_API ATableIconLabelActor : public ATableIconWidgetActor
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
|
||||
UFUNCTION(BlueprintImplementableEvent, BlueprintCallable) void SetLabelText(const FString& Text);
|
||||
UFUNCTION(BlueprintCallable) void SetDistance(float RealWorldUnits, const FString& UnitSuffix = TEXT("\""));
|
||||
};
|
||||
@@ -6,6 +6,8 @@
|
||||
#include "GameFramework/Actor.h"
|
||||
#include "TableIconActor.generated.h"
|
||||
|
||||
class AGestureAction;
|
||||
|
||||
UCLASS()
|
||||
class TABLE_API ATableIconActor : public AActor
|
||||
{
|
||||
@@ -21,16 +23,14 @@ protected:
|
||||
|
||||
public:
|
||||
// If true, GestureAction will not destroy this on EndGesture
|
||||
UPROPERTY(EditDefaultsOnly, BlueprintReadWrite)
|
||||
bool bPersistOnActionEnd = false;
|
||||
UPROPERTY(EditDefaultsOnly, BlueprintReadWrite) bool bPersistOnActionEnd = false;
|
||||
UPROPERTY(EditAnywhere) AGestureAction* OwningAction;
|
||||
|
||||
// Called by GestureAction on end — BP overrides this for fade/lock behavior
|
||||
UFUNCTION(BlueprintNativeEvent)
|
||||
void OnActionEnded();
|
||||
UFUNCTION(BlueprintNativeEvent) void OnActionEnded();
|
||||
|
||||
// Called by GestureAction every tick
|
||||
UFUNCTION(BlueprintNativeEvent)
|
||||
void OnActionUpdated();
|
||||
UFUNCTION(BlueprintNativeEvent) void OnActionUpdated();
|
||||
|
||||
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user