From 257f0455157483ff7611844f6cec25e0ffc5d483 Mon Sep 17 00:00:00 2001 From: DuOtto Date: Wed, 29 Jul 2026 00:07:47 +0200 Subject: [PATCH] Added GestureAction Ruler, added TableLineActor --- .../GestureActionDataTable.uasset | Bin 2286 -> 2436 bytes .../WorldObjects/BP_HandManager.uasset | Bin 23116 -> 25363 bytes Table/Content/WorldObjects/GameLevel.umap | Bin 25874 -> 25270 bytes .../BP_GestureAction_Ruler.uasset | Bin 0 -> 25243 bytes .../HUD/ContentWidgets/W_Content.uasset | Bin 73260 -> 71930 bytes .../TableIcons/BP_TableIconLineActor.uasset | Bin 0 -> 24990 bytes .../TableIcons/M_RulerLine.uasset | Bin 0 -> 11309 bytes .../Source/Table/Private/DetectionManager.cpp | 4 +- Table/Source/Table/Private/GestureAction.cpp | 4 +- .../Table/Private/GestureManagerComponent.cpp | 113 ++++++------ .../Source/Table/Private/MyGestureActions.cpp | 112 +++++------- .../Table/Private/MyTableIconActors.cpp | 166 +++++++++--------- Table/Source/Table/Public/GestureAction.h | 61 ++----- .../Table/Public/GestureManagerComponent.h | 41 ++--- Table/Source/Table/Public/MyGestureActions.h | 38 ++-- Table/Source/Table/Public/MyTableIconActors.h | 64 +++---- Table/Source/Table/Public/TableIconActor.h | 12 +- .../MyGestureActions.cpp | 69 ++++++++ .../MyTableIconActors.cpp | 161 +++++++++++++++++ 19 files changed, 513 insertions(+), 332 deletions(-) create mode 100644 Table/Content/WorldObjects/GestureActions/BP_GestureAction_Ruler.uasset create mode 100644 Table/Content/WorldObjects/TableIcons/BP_TableIconLineActor.uasset create mode 100644 Table/Content/WorldObjects/TableIcons/M_RulerLine.uasset create mode 100644 Table/enc_temp_folder/818ed75f49bf8f964599b398d81a43/MyGestureActions.cpp create mode 100644 Table/enc_temp_folder/d06d726b1f8919e2123e6effd51e7/MyTableIconActors.cpp diff --git a/Table/Content/GeneralStuff/GestureActionDataTable.uasset b/Table/Content/GeneralStuff/GestureActionDataTable.uasset index e7ef1e6c91588fd07e59286c4e58644d7ad37438..ed7398d3a94e6ff24be0cccdf228288661a5be16 100644 GIT binary patch delta 551 zcmaDS*djbZLM&XyPQ2Mf>3g`Hz~jEauHZI}A5IzU6ZI1Kq!}0(7Bd0~AaG}yI7d6y zorQsc3CNqm%D})5q-%hD5GY|o&>%H1ehp9@geO4d8-TRnz61ObP23*c<~KtF-MgNE zEC87)Jz0e@Sm_{8MF1znG#L3G2r_`y2?7N|I5*E^+{jpO31sWLC+4Q=hvygNr1&Rg zr6!jY>$|5Gmy{NzIwqH7=I0gbI|am}h{Ok#=A;%e?0vqPl^{Nz3sj>&l}3XGDI`&ra^K?cKs z@MLcmX`uLa7Ij4_sNgT4ZeXZ`0SA-@QU50YXHjO#V4wVuRdTW|tD)Loph6^h29S?H yfE$RfKx4B8qI@lD6#|K{1;uFXekV9VLQHl<@~>F!SVzEYiHl zA|favzkzDRfg;M2Sy&Y&uVLYs9Kx!=D*-hDB+Ckv6=wWDc`B&bc0*i>3H_KyrxZwMN@>~~DKzyNifeWlzc9C6tC4wZiPR5B1 zbD~bhs!?N|wn?L+Y1E8PlhoGKnP{h`Y4dQBCUG=TlN#G<$J+lt_uRY7vIw#1Veb9U z{r~TOobNpDT`r#B$$9?yn5cbIjzn$B3wydY^(kGtWy{gJ+{26RS^A8#bRj&)^)nR1Zen0@o-m2<}`!h5tAZnVE}A z3NouDn;mJIVeROQ?d1n={Q+eIq`BINJ$HKu?Vg3>+CcR;+16j^_qzZQQX@Vop@)n@sS?x0^DBD zye$zAMG$Gsfkuz|y%Ux$`)$d1cm2Mix6Ly3#ke7Ci1Qrn9S*x> zcX2Od^9T9^LoB?e9#GgKh@jEZsfCAn(ia*@o)0A&YNMKWLk#H(cnoN&r6$R`PO7yw zJ)weFCx$!>8c&kK4w9Eb1NdNaJ}h~00NC`*PT9)H5K7iTEyDUX$u4x*#En9u)M~Xm zg%(Gr;A*ov1+mHH=rjr$Ib0I+M6%{0Y9ZjcVsbRxkgqJBG_Z{imxZ0#nnddCZyR<=U@32XNNM{6W>qVP9KD)~ST34sF6*h~J#xI450zT|6 zr#y*{#x+tCJhX}HFhZlvA#>Bj%IvWZk)+M)av_N#rn6-UPDv8d%B`+67G6#_2g;QM zbrCxShh1od5wOK#Z-$N}$U}lX1I4v>IbCu!jkuFnb&4*jN*+Ds#tE5R0u0R6*(ITD zrTZPSFpfO)yH_Eed^7=FiY%bK8r3PD0~6uR{|`w2P}KJf#r@+`|Gj9h^2PgO)6P|q z_ajU_c3kh!zJ~XLOYU-JCQ$&@w0Yt-G2L*uCx^%Na9=^%4&Y z=#9Gxxb;48%1cWr>Neo;z{lpvG~nd%D^=l0*2=g6K!usj%!UF#V6b@x^o6s^upw|B z{I7W=kQqTJNrp?Tu=PkBzIhb7%w&ILTJR-*In_N28M`>1+=@&Ny{M>wo;zfscB0>f zh2)qvo~+W&i0WXKO$FJ^`afPo^7N_XACYm!)lBs-@MT>(f-d%epCzYQ|Ay>Vsxw7N z9_C}Lf2f*9eO;Gq_|7957N^Xv0bcU+8N9XzI^=3(^eD<0xgD8goX2ec@$kn@jVB7C zu$nqH+Wj>&U22pm3e89-nwPUf;BFl??_y?jLURio35 zm^;CY%>Y*h?QvYGNX=hH=f>j4Ij#~V!dDE^Aw!VkYfN?5V^ml!ZeGRhM+~!~kw!ir zV=5R8Dl8d41Q8Yt7dGspO>sr3Vo2}8N%-kUR_Rmxt_x(Vep29F0e(tY$XUIEpG=Ig zCja8160&b@wBOW{{_fb1`0xv`Qv)C#{v~ta#2VM9k)L?ZkMId)%|EQ~af*AaZ(Q2su7ct6GA{{;Xc+F(EsLj7yIv zp9^tJ^FVlp^)@HWSAE9NXc37fW-Cf`vfi=8Z7M2^d|Q}JT9Oh~O*pF}UDZU8-h>nq zpR7cxN+vAQ)#M5l6-oU%Qf8grzw6-bf z?%MR!|6uw;hQ6J?LPd|KHk^l748i1OgL@w0$ftRFa>;N)$&qnb^%7`)OJ=O9Ns!}Y zx@%0Nnpfh3h%sx5ic6xNx%oliE!;+~18S7PzL9Jm&j=P2o?{$%%8o8KT1GU(cYCvYPj6ozeZ>}0Ki)x?F6IiYL z^A54z?ELL2DvzSR$=5O)KS^0@y^~I=C=*4UHn}+~pqhw+d?jXqvie>t5ScEtutAM5 zm5#3?C5XIPsI0S_g*6O0bMkK1yF9sF?)6WfazBXABV&utk-J4}2W?!^9s5cD&Uush z_GsAtZSbDOV*oGO7BUIHnPg`%hd-CG&5#Ax%48Fh`Ap7aQh8-&a+nR8 zNfz83T3-X1Q)&urU^?VvCFv~{G)tMLfm|xf<(Cn@JZ^##Dn+q*dBu_3vVh0)3`IJ9 zLKt8O%>5KJD z0skE1IINDo42i=m=%%0wn&d}P-Apo~=G zV4xu*f~H)4;AWGH3$~Alp9Qsn(M-OnAF`;$Fyci7kV|IC-p zo1MMh{QNz&tts;End5V|-mEQ@ z9;Q_K(s`3#OUXy%)9;wizO}h@!M+!Eoop?te$|)GC;2*x-1vrG`?Sa33EZNAFkd9?BVan*KatXdgqC7L=UZ4ki2syVbda`d5KqgXuALncc L7|bMe`RxA!$L28f delta 4531 zcmb_g3sBU@8QU*Fz!Q(>$sKTTfV?~p1dgX7Dsnsw5s4rg6I(;I>5xtu z)AO@!GObOunMtcPn$kRIOl)Fm9<>jrvFWri$~3K`X-Z7YbTXZc-EaTm$u8? ze!JiAx8Jw>?YF!C0nX9$KcnB&Yo8o`Bsc9u|Ky(@f9CkI1jm6r^w9Myb-w&c%?u%A z-^IOm9o4o5`WB_s;8g2kUVDyhircw@KcO^_SR@s8UQ z$p?%V5h8C(h`c+6aINOFx2RHolF!GMIdjM@wNxy4GNu% zA#ZB{kNcMmjtdY4CLWexk@4rrV{gY}Cz6){UNTqu8$4!kV4yLOYSd}( z5`coqY0=&xk#-nu8m}MxlxXLGwEIkhxexy zB;#F$NhVvkDkvu;3dZ0spfu^Um|!yLenC34p4;g&*^;7|3NI6!B+_DpjDlk(1H9R3 z1)6S-aIqWgV5|{b>6)lE);?bc-t_IX2ri`CjrP{+>grVugCnIwt2_Fv!((f9w-338 zySkelrBmq{Z__0a|G=Y7`QJ67gxIA%8V1(<)$sn(r8Hc$6!0bUA{aNv!$anSG!m?K z5A0s%fM=I&rO})-mbDy?WbKCuM{b&A4Qq|Cyq2_(elkq9u-x7SA2@Zooea?nv<9_g zl#CN6xUHEfi-o?H@rTG(vYoV&5zXpn>PRj`S-xd{6A3L zXO=D%6jQyhq=0&B28>%DfrJ%ZFur0NTwU=Sc%kqpHF3s6wq!VIy9WyF55O6_18&*B zSWHYoV)Bz>8~nZ~mzueUyy6C!DDH);#dlK+--)M795nl2(Y{cFPj|xaiZm7{Y3CcV zg@t%GYjrX?K(?_7M2SfV4P0@I(_HR+U#SUxT>1lOEqefF%E}k(P?BDTf=CE!4?-zLkx% zoU;~J)lIFd`lo_gr#$sm8p2V4^o)0=o?KHxooaT*hA!j$r8CC_A&tBy#4RKWereof zSD<{uIlCoG5i5@Ao1o=UAJn)DOZ9F;2msM&n=ghx|%|DmA zprtRdD(I%kX3z@mH%h*7*kC195=>`;_|^hus;1RG;XjH$PW}qAR*aeFklDYrN`O(H z8J?SYD%7J*B*^arN-Rqx0iU)l(%7Xo@!u|7t`R2NR4f0-31Ql@r~JJ(Hz_)biX=yk)Xjv-C0=*?ereW$9z ze=_n{Q4||-b~;A+z)-?^k=(!KG!m#!6j6il~CSc&5n^sHv1;j#9ZIa&F|W6~_vjWIFB)QO%G z9$xog$Pqf-rPBCEh$k+y6b&<73GAK6dZ8#ZvvmxZqYgiD{g0kajgOtKzc`W+z7^9l zP!}u$17^$4x<7who7wpM*XeU_UHz*QM+WNZS?tO)R*SXCGsQg+DoOmmLfRrb3JB@7 z4WWlryGg~i_}5)J^YmnY!pG15wCZc`cN7oaGB;r+2kI6IZVOK_9I^kEh8JJn>uUb~ zp>IEZM|InYV7g!d?$vPSz0V~jzME7(HU0Ca`oC59Lj}Cm(?Odcce4o-(Ub|Jo0~&Q z#L5K^r&o@ekP9Ekp5D0!5^q&3_?`Ru_s%>xyJ^_-=*`Js`GXog>SEt)=|nSXzz_Ni*i1Um0Zl3xKiDLJoQu<;(i if(zuYe9~gpibNGF^Uw@L(>ONw=I7H#&;@Vyt^7B1FGx24 diff --git a/Table/Content/WorldObjects/GameLevel.umap b/Table/Content/WorldObjects/GameLevel.umap index 8c9440f45c83ec82257f6c2889f29fde7130cc5f..623254cff333cb2f189d5c1922e964c773c5bdb8 100644 GIT binary patch delta 7047 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 X7i`>(@TG5CrG40QkI!JYD@gx;f*co? 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 0000000000000000000000000000000000000000..8604fdab9dca53e98624e74287cdfd9522810476 GIT binary patch literal 25243 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 literal 0 HcmV?d00001 diff --git a/Table/Content/WorldObjects/HUD/ContentWidgets/W_Content.uasset b/Table/Content/WorldObjects/HUD/ContentWidgets/W_Content.uasset index da1cd4099380a16fc630b086c15c167ea491c7a0..29a77bf881df7e58d0c455f439481efa2d501fbc 100644 GIT binary patch delta 5733 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 ZXRUjIexYaEx5tBAQSv)$t{6#<{~PZLHn9Kz delta 7103 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 diff --git a/Table/Content/WorldObjects/TableIcons/BP_TableIconLineActor.uasset b/Table/Content/WorldObjects/TableIcons/BP_TableIconLineActor.uasset new file mode 100644 index 0000000000000000000000000000000000000000..caa1280f561ef219723243d15bc343c653fd5360 GIT binary patch literal 24990 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 literal 0 HcmV?d00001 diff --git a/Table/Content/WorldObjects/TableIcons/M_RulerLine.uasset b/Table/Content/WorldObjects/TableIcons/M_RulerLine.uasset new file mode 100644 index 0000000000000000000000000000000000000000..a3e7dc5192fffa04bc9515efd4b6b25e6204bc18 GIT binary patch literal 11309 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 literal 0 HcmV?d00001 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