diff --git a/Table/Content/GeneralStuff/GestureActionDataTable.uasset b/Table/Content/GeneralStuff/GestureActionDataTable.uasset index e7ef1e6..ed7398d 100644 Binary files a/Table/Content/GeneralStuff/GestureActionDataTable.uasset and b/Table/Content/GeneralStuff/GestureActionDataTable.uasset differ diff --git a/Table/Content/WorldObjects/BP_HandManager.uasset b/Table/Content/WorldObjects/BP_HandManager.uasset index 3375587..75f382f 100644 Binary files a/Table/Content/WorldObjects/BP_HandManager.uasset and b/Table/Content/WorldObjects/BP_HandManager.uasset differ diff --git a/Table/Content/WorldObjects/GameLevel.umap b/Table/Content/WorldObjects/GameLevel.umap index 8c9440f..623254c 100644 Binary files a/Table/Content/WorldObjects/GameLevel.umap and b/Table/Content/WorldObjects/GameLevel.umap differ diff --git a/Table/Content/WorldObjects/GestureActions/BP_GestureAction_Ruler.uasset b/Table/Content/WorldObjects/GestureActions/BP_GestureAction_Ruler.uasset new file mode 100644 index 0000000..8604fda Binary files /dev/null and b/Table/Content/WorldObjects/GestureActions/BP_GestureAction_Ruler.uasset differ diff --git a/Table/Content/WorldObjects/HUD/ContentWidgets/W_Content.uasset b/Table/Content/WorldObjects/HUD/ContentWidgets/W_Content.uasset index da1cd40..29a77bf 100644 Binary files a/Table/Content/WorldObjects/HUD/ContentWidgets/W_Content.uasset and b/Table/Content/WorldObjects/HUD/ContentWidgets/W_Content.uasset differ diff --git a/Table/Content/WorldObjects/TableIcons/BP_TableIconLineActor.uasset b/Table/Content/WorldObjects/TableIcons/BP_TableIconLineActor.uasset new file mode 100644 index 0000000..caa1280 Binary files /dev/null and b/Table/Content/WorldObjects/TableIcons/BP_TableIconLineActor.uasset differ diff --git a/Table/Content/WorldObjects/TableIcons/M_RulerLine.uasset b/Table/Content/WorldObjects/TableIcons/M_RulerLine.uasset new file mode 100644 index 0000000..a3e7dc5 Binary files /dev/null and b/Table/Content/WorldObjects/TableIcons/M_RulerLine.uasset differ diff --git a/Table/Source/Table/Private/DetectionManager.cpp b/Table/Source/Table/Private/DetectionManager.cpp index 5864116..eb8b9c6 100644 --- a/Table/Source/Table/Private/DetectionManager.cpp +++ b/Table/Source/Table/Private/DetectionManager.cpp @@ -197,8 +197,8 @@ bool ADetectionManager::ParseHandPacket(const TArray& 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; diff --git a/Table/Source/Table/Private/GestureAction.cpp b/Table/Source/Table/Private/GestureAction.cpp index 5aa93a4..f7558b8 100644 --- a/Table/Source/Table/Private/GestureAction.cpp +++ b/Table/Source/Table/Private/GestureAction.cpp @@ -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(); } diff --git a/Table/Source/Table/Private/GestureManagerComponent.cpp b/Table/Source/Table/Private/GestureManagerComponent.cpp index 9fbc670..f96ac9f 100644 --- a/Table/Source/Table/Private/GestureManagerComponent.cpp +++ b/Table/Source/Table/Private/GestureManagerComponent.cpp @@ -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 Rows; + GestureDefinitionTable->GetAllRows(Context, Rows); + + for (FGestureDefinitionRow* Row : Rows) + { + if (!Row || !Row->DefinitionClass) continue; + + UGestureDefinition* Def = NewObject(this, Row->DefinitionClass); + + DefinitionMap.Add(Def->GestureName, Def); + GestureDefinitions.Add(Def); + } +} + +void UGestureManagerComponent::BuildActionMap() +{ + if (!GestureActionTable) return; + + static const FString Context = TEXT("GestureActionContext"); + + TArray RowNames = GestureActionTable->GetRowNames(); + + for (const FName& RowName : RowNames) + { + FGestureActionRow* Row = GestureActionTable->FindRow( RowName, Context); + + if (!Row || !Row->ActionClass) continue; + + ActionClassMap.Add(RowName, Row->ActionClass); + } + +} + void UGestureManagerComponent::EvaluateHands() { TArray& Hands = HandManager->HandList; @@ -86,7 +134,7 @@ void UGestureManagerComponent::LoadBindings(const FString& FilePath) const TArray>* BindingsArray; - if (Root->TryGetArrayField(TEXT("bindings"), BindingsArray)) + if (Root->TryGetArrayField(TEXT("rules"), BindingsArray)) { for (const TSharedPtr& 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 Rows; - GestureDefinitionTable->GetAllRows(Context, Rows); - - for (FGestureDefinitionRow* Row : Rows) - { - if (!Row || !Row->DefinitionClass) continue; - - UGestureDefinition* Def = NewObject(this, Row->DefinitionClass); - - DefinitionMap.Add(Def->GestureName, Def); - GestureDefinitions.Add(Def); - } -} - -void UGestureManagerComponent::BuildActionMap() -{ - if (!GestureActionTable) return; - - static const FString Context = TEXT("GestureActionContext"); - - TArray 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& Hands) { @@ -272,7 +278,7 @@ void UGestureManagerComponent::CheckGestureDefinitions(TArray& 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& 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* 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); diff --git a/Table/Source/Table/Private/MyGestureActions.cpp b/Table/Source/Table/Private/MyGestureActions.cpp index 345a3aa..75eb032 100644 --- a/Table/Source/Table/Private/MyGestureActions.cpp +++ b/Table/Source/Table/Private/MyGestureActions.cpp @@ -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( - *ActionClassPtr, - FVector::ZeroVector, - FRotator::ZeroRotator, - SpawnParams); -*/ - if (!NewAction) return; + SpawnParams.Owner = this; + SpawnParams.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AlwaysSpawn; + if (LineIconClass) + { + + LineIcon = GetWorld()->SpawnActor(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); +} diff --git a/Table/Source/Table/Private/MyTableIconActors.cpp b/Table/Source/Table/Private/MyTableIconActors.cpp index 93a4bd1..5abebc6 100644 --- a/Table/Source/Table/Private/MyTableIconActors.cpp +++ b/Table/Source/Table/Private/MyTableIconActors.cpp @@ -10,118 +10,104 @@ ATableIconLineActor::ATableIconLineActor() { + PrimaryActorTick.bCanEverTick = false; // purely driven by SetPoints + + USceneComponent* Root = CreateDefaultSubobject(TEXT("Root")); + SetRootComponent(Root); + MeshComponent = CreateDefaultSubobject(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& 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 Vertices; + TArray Triangles; + TArray Normals; + TArray UVs; + TArray Colors; + TArray Tangents; - // A flat quad along the line, facing up - TArray Vertices; - TArray Triangles; - TArray Normals; - TArray UVs; - TArray Colors; - TArray 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 Vertices; - TArray Triangles; - TArray Normals; - TArray UVs; - TArray Colors; - TArray 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(TEXT("Root")); + SetRootComponent(Root); + PrimaryActorTick.bCanEverTick = true; WidgetComponent = CreateDefaultSubobject(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 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)); } \ No newline at end of file diff --git a/Table/Source/Table/Public/GestureAction.h b/Table/Source/Table/Public/GestureAction.h index 44002c4..0f17104 100644 --- a/Table/Source/Table/Public/GestureAction.h +++ b/Table/Source/Table/Public/GestureAction.h @@ -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 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 Icons; - - UPROPERTY(BlueprintReadWrite) - TMap 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(); }; diff --git a/Table/Source/Table/Public/GestureManagerComponent.h b/Table/Source/Table/Public/GestureManagerComponent.h index d736a27..e7b9d03 100644 --- a/Table/Source/Table/Public/GestureManagerComponent.h +++ b/Table/Source/Table/Public/GestureManagerComponent.h @@ -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 GestureDefinitions; + UPROPERTY(BlueprintReadOnly, Category = "Gesture") TArray GestureDefinitions; - UPROPERTY(BlueprintReadOnly, Category = "Gesture") - TArray ActiveGestureActions; + UPROPERTY(BlueprintReadOnly, Category = "Gesture") TArray ActiveGestureActions; // Populated on BeginPlay from JSON - UPROPERTY(BlueprintReadOnly, Category = "Gesture") - TArray GestureBindings; + UPROPERTY(BlueprintReadOnly, Category = "Gesture") TArray 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 DefinitionMap; + UPROPERTY(BlueprintReadOnly, Category = "Gesture") TMap DefinitionMap; - UPROPERTY(BlueprintReadOnly, Category = "Gesture") - TMap> ActionClassMap; + UPROPERTY(BlueprintReadOnly, Category = "Gesture") TMap> 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& Hands); diff --git a/Table/Source/Table/Public/MyGestureActions.h b/Table/Source/Table/Public/MyGestureActions.h index 31282d6..ff35c01 100644 --- a/Table/Source/Table/Public/MyGestureActions.h +++ b/Table/Source/Table/Public/MyGestureActions.h @@ -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 LineIconClass; + UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = "Ruler") TSubclassOf LabelIconClass; + UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = "Ruler") FString UnitSuffix = TEXT("\""); + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Ruler") TArray 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; }; diff --git a/Table/Source/Table/Public/MyTableIconActors.h b/Table/Source/Table/Public/MyTableIconActors.h index a0a6c99..fc78e23 100644 --- a/Table/Source/Table/Public/MyTableIconActors.h +++ b/Table/Source/Table/Public/MyTableIconActors.h @@ -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& 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 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 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 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("\"")); }; \ No newline at end of file diff --git a/Table/Source/Table/Public/TableIconActor.h b/Table/Source/Table/Public/TableIconActor.h index d347c02..7698655 100644 --- a/Table/Source/Table/Public/TableIconActor.h +++ b/Table/Source/Table/Public/TableIconActor.h @@ -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(); }; diff --git a/Table/enc_temp_folder/818ed75f49bf8f964599b398d81a43/MyGestureActions.cpp b/Table/enc_temp_folder/818ed75f49bf8f964599b398d81a43/MyGestureActions.cpp new file mode 100644 index 0000000..a7e4280 --- /dev/null +++ b/Table/enc_temp_folder/818ed75f49bf8f964599b398d81a43/MyGestureActions.cpp @@ -0,0 +1,69 @@ +// Fill out your copyright notice in the Description page of Project Settings. + + +#include "MyGestureActions.h" +#include "HandActor.h" +#include "TableIconActor.h" +#include "MyTableIconActors.h" + + +void AGestureAction_Ruler::InitAction_Implementation(AHandActor* Hand, const FGestureBinding& NewBinding, float NewUEUnitsPerUnit, float TableHeight) +{ + Super::InitAction_Implementation(Hand, NewBinding, NewUEUnitsPerUnit, TableHeight); + DesiredHeight = -500; + StartPoint = HandA->Points[8]; + StartPoint.Z = DesiredHeight; + + FActorSpawnParameters SpawnParams; + SpawnParams.Owner = this; + SpawnParams.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AlwaysSpawn; + + if (LineIconClass) + { + + LineIcon = GetWorld()->SpawnActor(LineIconClass, StartPoint, FRotator::ZeroRotator, SpawnParams); + + if (LineIcon) + { + LineIcon->OwningAction = this; + LineIcon->bPersistOnActionEnd = true; + LineIcon->LineThickness = 50; + Icons.Add(LineIcon); + } + } + RulerPoints.Add(FVector(0, 0, 0)); + RulerPoints.Add(FVector(0, 0, DesiredHeight)); +} + +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; + 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; + 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]; + //Current.Z = DesiredHeight; + RulerPoints.Add(Current - StartPoint); +} diff --git a/Table/enc_temp_folder/d06d726b1f8919e2123e6effd51e7/MyTableIconActors.cpp b/Table/enc_temp_folder/d06d726b1f8919e2123e6effd51e7/MyTableIconActors.cpp new file mode 100644 index 0000000..bc53753 --- /dev/null +++ b/Table/enc_temp_folder/d06d726b1f8919e2123e6effd51e7/MyTableIconActors.cpp @@ -0,0 +1,161 @@ +// Fill out your copyright notice in the Description page of Project Settings. + + +#include "MyTableIconActors.h" +#include "KismetProceduralMeshLibrary.h" +#include "TableIconActor.h" + +#include "Kismet/GameplayStatics.h" +#include "Camera/CameraActor.h" + +ATableIconLineActor::ATableIconLineActor() +{ + PrimaryActorTick.bCanEverTick = false; // purely driven by SetPoints + + USceneComponent* Root = CreateDefaultSubobject(TEXT("Root")); + SetRootComponent(Root); + + MeshComponent = CreateDefaultSubobject(TEXT("LineMesh")); + MeshComponent->SetupAttachment(RootComponent); + MeshComponent->SetCastShadow(false); + MeshComponent->SetCollisionEnabled(ECollisionEnabled::NoCollision); +} + +void ATableIconLineActor::SetPoints(const TArray& NewPoints) +{ + const FTransform& ActorTransform = GetActorTransform(); + + Points.Reset(NewPoints.Num()); + + Points = NewPoints; + + MeshComponent->ClearMeshSection(0); + + BuildLineMesh(); + + if (LineMaterial) MeshComponent->SetMaterial(0, LineMaterial); +} + + + +void ATableIconLineActor::BuildLineMesh() +{ + TArray Vertices; + TArray Triangles; + TArray Normals; + TArray UVs; + TArray Colors; + TArray 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]; + + Direction = (PointB - PointA).GetSafeNormal(); + + const FVector Right = FVector::CrossProduct(FVector::UpVector, Direction).GetSafeNormal(); + + const FVector Offset = Right * (LineThickness * 0.5f); + + int32 Base = Vertices.Num(); + + Vertices.Add(PointA - Offset); + Vertices.Add(PointA + Offset); + Vertices.Add(PointB + Offset); + Vertices.Add(PointB - Offset); + + 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); + } + + 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(TEXT("Root")); + SetRootComponent(Root); + + PrimaryActorTick.bCanEverTick = true; + + WidgetComponent = CreateDefaultSubobject(TEXT("WidgetComponent")); + WidgetComponent->SetupAttachment(RootComponent); + WidgetComponent->SetDrawAtDesiredSize(true); + WidgetComponent->SetWidgetSpace(EWidgetSpace::World); + WidgetComponent->SetCollisionEnabled(ECollisionEnabled::NoCollision); + + WidgetInteraction = CreateDefaultSubobject(TEXT("WidgetInteraction")); + WidgetInteraction->SetupAttachment(RootComponent); + WidgetInteraction->InteractionSource = EWidgetInteractionSource::Custom; + WidgetInteraction->InteractionDistance = 10.f; +} + +void ATableIconWidgetActor::Tick(float DeltaTime) +{ + Super::Tick(DeltaTime); + + 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 InWidgetClass) +{ + if (!InWidgetClass) return; + WidgetComponent->SetWidgetClass(InWidgetClass); + WidgetComponent->InitWidget(); +} + +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)); +} \ No newline at end of file