A Demo Project for the UnrealEngineSDK
Loading...
Searching...
No Matches
VRGestureComponent.cpp
Go to the documentation of this file.
2#include "TimerManager.h"
3
4DECLARE_CYCLE_STAT(TEXT("TickGesture ~ TickingGesture"), STAT_TickGesture, STATGROUP_TickGesture);
5
6UVRGestureComponent::UVRGestureComponent(const FObjectInitializer& ObjectInitializer)
7 : Super(ObjectInitializer)
8{
9 PrimaryComponentTick.bCanEverTick = false;
10 //PrimaryComponentTick.bStartWithTickEnabled = false;
11 //PrimaryComponentTick.TickGroup = TG_PrePhysics;
12 //PrimaryComponentTick.bTickEvenWhenPaused = false;
13
14 maxSlope = 3;// INT_MAX;
15 //globalThreshold = 10.0f;
17 bGestureChanged = false;
19 bDrawSplinesCurved = true;
21 SplineMeshScaler = FVector2D(1.f);
22}
23
24void UGesturesDatabase::FillSplineWithGesture(FVRGesture &Gesture, USplineComponent * SplineComponent, bool bCenterPointsOnSpline, bool bScaleToBounds, float OptionalBounds, bool bUseCurvedPoints, bool bFillInSplineMeshComponents, UStaticMesh * Mesh, UMaterial * MeshMat)
25{
26 if (!SplineComponent || Gesture.Samples.Num() < 2)
27 return;
28
29 UWorld* InWorld = GEngine->GetWorldFromContextObject(SplineComponent, EGetWorldErrorMode::LogAndReturnNull);
30
31 if (!InWorld)
32 return;
33
34 SplineComponent->ClearSplinePoints(false);
35
36 FVector PointOffset = FVector::ZeroVector;
37 float Scaler = 1.0f;
38 if (bScaleToBounds && OptionalBounds > 0.0f)
39 {
40 Scaler = OptionalBounds / Gesture.GestureSize.GetSize().GetMax();
41 }
42
43 if (bCenterPointsOnSpline)
44 {
45 PointOffset = -Gesture.GestureSize.GetCenter();
46 }
47
48 int curIndex = 0;
49 for (int i = Gesture.Samples.Num() - 1; i >= 0; --i)
50 {
51 SplineComponent->AddSplinePoint((Gesture.Samples[i] + PointOffset) * Scaler, ESplineCoordinateSpace::Local, false);
52 curIndex++;
53 SplineComponent->SetSplinePointType(curIndex, bUseCurvedPoints ? ESplinePointType::Curve : ESplinePointType::Linear, false);
54 }
55
56 // Update spline now
57 SplineComponent->UpdateSpline();
58
59 if (bFillInSplineMeshComponents && Mesh != nullptr && MeshMat != nullptr)
60 {
61 TArray<USplineMeshComponent *> CurrentSplineChildren;
62
63 TArray<USceneComponent*> Children;
64 SplineComponent->GetChildrenComponents(false, Children);
65 for (auto Child : Children)
66 {
67 USplineMeshComponent* SplineMesh = Cast<USplineMeshComponent>(Child);
68 if (SplineMesh != nullptr && !SplineMesh->IsPendingKill())
69 {
70 CurrentSplineChildren.Add(SplineMesh);
71 }
72 }
73
74 if (CurrentSplineChildren.Num() > SplineComponent->GetNumberOfSplinePoints() - 1)
75 {
76 int diff = CurrentSplineChildren.Num() - (CurrentSplineChildren.Num() - (SplineComponent->GetNumberOfSplinePoints() -1));
77
78 for (int i = CurrentSplineChildren.Num()- 1; i >= diff; --i)
79 {
80 if (!CurrentSplineChildren[i]->IsBeingDestroyed())
81 {
82 CurrentSplineChildren[i]->SetVisibility(false);
83 CurrentSplineChildren[i]->Modify();
84 CurrentSplineChildren[i]->DestroyComponent();
85 CurrentSplineChildren.RemoveAt(i);
86 }
87 }
88 }
89 else
90 {
91 for (int i = CurrentSplineChildren.Num(); i < SplineComponent->GetNumberOfSplinePoints() -1; ++i)
92 {
93 USplineMeshComponent * newSplineMesh = NewObject<USplineMeshComponent>(SplineComponent);
94
95 newSplineMesh->RegisterComponentWithWorld(InWorld);
96 newSplineMesh->SetMobility(EComponentMobility::Movable);
97 CurrentSplineChildren.Add(newSplineMesh);
98 newSplineMesh->SetStaticMesh(Mesh);
99 newSplineMesh->SetMaterial(0, (UMaterialInterface*)MeshMat);
100
101 newSplineMesh->AttachToComponent(SplineComponent, FAttachmentTransformRules::SnapToTargetIncludingScale);
102 newSplineMesh->SetVisibility(true);
103 }
104 }
105
106
107 for(int i=0; i<SplineComponent->GetNumberOfSplinePoints() - 1; i++)
108 {
109 CurrentSplineChildren[i]->SetStartAndEnd(SplineComponent->GetLocationAtSplinePoint(i, ESplineCoordinateSpace::Local),
110 SplineComponent->GetTangentAtSplinePoint(i, ESplineCoordinateSpace::Local),
111 SplineComponent->GetLocationAtSplinePoint(i + 1, ESplineCoordinateSpace::Local),
112 SplineComponent->GetTangentAtSplinePoint(i + 1, ESplineCoordinateSpace::Local),
113 true);
114 }
115 }
116
117}
118
119void UVRGestureComponent::BeginRecording(bool bRunDetection, bool bFlattenGesture, bool bDrawGesture, bool bDrawAsSpline, int SamplingHTZ, int SampleBufferSize, float ClampingTolerance)
120{
121 RecordingBufferSize = SampleBufferSize;
122 RecordingDelta = 1.0f / SamplingHTZ;
123 RecordingClampingTolerance = ClampingTolerance;
124 bDrawRecordingGesture = bDrawGesture;
125 bDrawRecordingGestureAsSpline = bDrawAsSpline;
126 bRecordingFlattenGesture = bFlattenGesture;
127 GestureLog.GestureSize.Init();
128
129 // Reinit the drawing spline
130 if (!bDrawAsSpline || !bDrawGesture)
131 RecordingGestureDraw.Clear(); // Not drawing or not as a spline, remove the components if they exist
132 else
133 {
134 RecordingGestureDraw.Reset(); // Otherwise just clear points and hide mesh components
135
137 {
138 RecordingGestureDraw.SplineComponent = NewObject<USplineComponent>(GetAttachParent());
139 RecordingGestureDraw.SplineComponent->RegisterComponentWithWorld(GetWorld());
140 RecordingGestureDraw.SplineComponent->SetMobility(EComponentMobility::Movable);
141 RecordingGestureDraw.SplineComponent->AttachToComponent(GetAttachParent(), FAttachmentTransformRules::KeepRelativeTransform);
142 RecordingGestureDraw.SplineComponent->ClearSplinePoints(true);
143 }
144 }
145
146 // Reset does the reserve already
148
150
151 if (TargetCharacter != nullptr)
152 {
154 }
155 else if (AVRBaseCharacter * own = Cast<AVRBaseCharacter>(GetOwner()))
156 {
157 TargetCharacter = own;
159 }
160 else
161 OriginatingTransform = this->GetComponentTransform();
162
163 StartVector = OriginatingTransform.InverseTransformPosition(this->GetComponentLocation());
164 this->SetComponentTickEnabled(true);
165
166 if (!TickGestureTimer_Handle.IsValid())
167 GetWorld()->GetTimerManager().SetTimer(TickGestureTimer_Handle, this, &UVRGestureComponent::TickGesture, RecordingDelta, true);
168}
169
171{
172 FVector NewSample = OriginatingTransform.InverseTransformPosition(this->GetComponentLocation()) - StartVector;
173
175 NewSample.X = 0;
176
178 {
179 NewSample.X = FMath::GridSnap(NewSample.X, RecordingClampingTolerance);
180 NewSample.Y = FMath::GridSnap(NewSample.Y, RecordingClampingTolerance);
181 NewSample.Z = FMath::GridSnap(NewSample.Z, RecordingClampingTolerance);
182 }
183
184 // Add in newest sample at beginning (reverse order)
185 if (NewSample != FVector::ZeroVector && (GestureLog.Samples.Num() < 1 || !GestureLog.Samples[0].Equals(NewSample, SameSampleTolerance)))
186 {
187 bool bClearLatestSpline = false;
188 // Pop off oldest sample
190 {
191 GestureLog.Samples.Pop(false);
192 bClearLatestSpline = true;
193 }
194
195 GestureLog.GestureSize.Max.X = FMath::Max(NewSample.X, GestureLog.GestureSize.Max.X);
196 GestureLog.GestureSize.Max.Y = FMath::Max(NewSample.Y, GestureLog.GestureSize.Max.Y);
197 GestureLog.GestureSize.Max.Z = FMath::Max(NewSample.Z, GestureLog.GestureSize.Max.Z);
198
199 GestureLog.GestureSize.Min.X = FMath::Min(NewSample.X, GestureLog.GestureSize.Min.X);
200 GestureLog.GestureSize.Min.Y = FMath::Min(NewSample.Y, GestureLog.GestureSize.Min.Y);
201 GestureLog.GestureSize.Min.Z = FMath::Min(NewSample.Z, GestureLog.GestureSize.Min.Z);
202
203
205 {
206
207 if (bClearLatestSpline)
209
210 RecordingGestureDraw.SplineComponent->AddSplinePoint(NewSample, ESplineCoordinateSpace::Local, false);
211 int SplineIndex = RecordingGestureDraw.SplineComponent->GetNumberOfSplinePoints() - 1;
212 RecordingGestureDraw.SplineComponent->SetSplinePointType(SplineIndex, bDrawSplinesCurved ? ESplinePointType::Curve : ESplinePointType::Linear, true);
213
214 bool bFoundEmptyMesh = false;
215 USplineMeshComponent * MeshComp = nullptr;
216 int MeshIndex = 0;
217
218 for (int i = 0; i < RecordingGestureDraw.SplineMeshes.Num(); i++)
219 {
220 MeshIndex = i;
222 if (MeshComp == nullptr)
223 {
224 RecordingGestureDraw.SplineMeshes[i] = NewObject<USplineMeshComponent>(RecordingGestureDraw.SplineComponent);
226
227 MeshComp->RegisterComponentWithWorld(GetWorld());
228 MeshComp->SetMobility(EComponentMobility::Movable);
229 MeshComp->SetStaticMesh(SplineMesh);
230 MeshComp->SetMaterial(0, (UMaterialInterface*)SplineMaterial);
231 bFoundEmptyMesh = true;
232 break;
233 }
234 else if (!MeshComp->IsVisible())
235 {
236 bFoundEmptyMesh = true;
237 break;
238 }
239 }
240
241 if (!bFoundEmptyMesh)
242 {
243 USplineMeshComponent * newSplineMesh = NewObject<USplineMeshComponent>(RecordingGestureDraw.SplineComponent);
244 MeshComp = newSplineMesh;
245 MeshComp->RegisterComponentWithWorld(GetWorld());
246 MeshComp->SetMobility(EComponentMobility::Movable);
248 MeshIndex = RecordingGestureDraw.SplineMeshes.Num() - 1;
249 MeshComp->SetStaticMesh(SplineMesh);
250 MeshComp->SetMaterial(0, (UMaterialInterface*)SplineMaterial);
252 MeshComp->AttachToComponent(TargetCharacter->GetRootComponent(), FAttachmentTransformRules::KeepRelativeTransform);
253 }
254
255 if (MeshComp != nullptr)
256 {
257 // Fill in last mesh component tangent and end pos
259 {
260 RecordingGestureDraw.SplineMeshes[RecordingGestureDraw.LastIndexSet]->SetEndPosition(NewSample, false);
261 RecordingGestureDraw.SplineMeshes[RecordingGestureDraw.LastIndexSet]->SetEndTangent(RecordingGestureDraw.SplineComponent->GetTangentAtSplinePoint(SplineIndex, ESplineCoordinateSpace::Local), true);
262 }
263
264 MeshComp->SetStartScale(SplineMeshScaler);
265 MeshComp->SetEndScale(SplineMeshScaler);
266
267 MeshComp->SetStartAndEnd(NewSample,
268 RecordingGestureDraw.SplineComponent->GetTangentAtSplinePoint(SplineIndex, ESplineCoordinateSpace::Local),
269 NewSample,
270 FVector::ZeroVector,
271 true);
272
274 MeshComp->SetWorldLocationAndRotation(OriginatingTransform.TransformPosition(StartVector), OriginatingTransform.GetRotation());
275 else
276 MeshComp->SetRelativeLocationAndRotation(/*OriginatingTransform.TransformPosition(*/StartVector/*)*/, FQuat::Identity/*OriginatingTransform.GetRotation()*/);
277
279 MeshComp->SetVisibility(true);
280 }
281
282 }
283
284 GestureLog.Samples.Insert(NewSample, 0);
285 bGestureChanged = true;
286 }
287}
288
290{
291 SCOPE_CYCLE_COUNTER(STAT_TickGesture);
292
293 switch (CurrentState)
294 {
296 {
299 bGestureChanged = false;
300 }break;
301
303 {
305 }break;
306
308 default: {}break;
309 }
310
312 {
314 {
315 FTransform DrawTransform = FTransform(StartVector) * OriginatingTransform;
316 // Setting the lifetime to the recording htz now, should remove the flicker.
317 DrawDebugGesture(this, DrawTransform, GestureLog, FColor::White, false, 0, RecordingDelta, 0.0f);
318 }
319 }
320}
321
323{
324 if (!GesturesDB || inputGesture.Samples.Num() < 1 || !bGestureChanged)
325 return;
326
327 float minDist = MAX_FLT;
328
329 int OutGestureIndex = -1;
330 bool bMirrorGesture = false;
331
332 FVector Size = inputGesture.GestureSize.GetSize();
333 float Scaler = GesturesDB->TargetGestureScale / Size.GetMax();
334 float FinalScaler = Scaler;
335
336 for (int i = 0; i < GesturesDB->Gestures.Num(); i++)
337 {
338 FVRGesture &exampleGesture = GesturesDB->Gestures[i];
339
340 if (!exampleGesture.GestureSettings.bEnabled || exampleGesture.Samples.Num() < 1 || inputGesture.Samples.Num() < exampleGesture.GestureSettings.Minimum_Gesture_Length)
341 continue;
342
343 FinalScaler = exampleGesture.GestureSettings.bEnableScaling ? Scaler : 1.f;
344
346
347 if (GetGestureDistance(inputGesture.Samples[0] * FinalScaler, exampleGesture.Samples[0], bMirrorGesture) < FMath::Square(exampleGesture.GestureSettings.firstThreshold))
348 {
349 float d = dtw(inputGesture, exampleGesture, bMirrorGesture, FinalScaler) / (exampleGesture.Samples.Num());
350 if (d < minDist && d < FMath::Square(exampleGesture.GestureSettings.FullThreshold))
351 {
352 minDist = d;
353 OutGestureIndex = i;
354 }
355 }
357 {
358 bMirrorGesture = true;
359 if (GetGestureDistance(inputGesture.Samples[0] * FinalScaler, exampleGesture.Samples[0], bMirrorGesture) < FMath::Square(exampleGesture.GestureSettings.firstThreshold))
360 {
361 float d = dtw(inputGesture, exampleGesture, bMirrorGesture, FinalScaler) / (exampleGesture.Samples.Num());
362 if (d < minDist && d < FMath::Square(exampleGesture.GestureSettings.FullThreshold))
363 {
364 minDist = d;
365 OutGestureIndex = i;
366 }
367 }
368 }
369
370 /*if (exampleGesture.MirrorMode == EVRGestureMirrorMode::GES_MirrorBoth)
371 {
372 bMirrorGesture = true;
373
374 if (GetGestureDistance(inputGesture.Samples[0], exampleGesture.Samples[0], bMirrorGesture) < FMath::Square(exampleGesture.GestureSettings.firstThreshold))
375 {
376 float d = dtw(inputGesture, exampleGesture, bMirrorGesture) / (exampleGesture.Samples.Num());
377 if (d < minDist && d < FMath::Square(exampleGesture.GestureSettings.FullThreshold))
378 {
379 minDist = d;
380 OutGestureIndex = i;
381 }
382 }
383 }*/
384 }
385
386 if (/*minDist < FMath::Square(globalThreshold) && */OutGestureIndex != -1)
387 {
388 OnGestureDetected(GesturesDB->Gestures[OutGestureIndex].GestureType, /*minDist,*/ GesturesDB->Gestures[OutGestureIndex].Name, OutGestureIndex, GesturesDB);
389 OnGestureDetected_Bind.Broadcast(GesturesDB->Gestures[OutGestureIndex].GestureType, /*minDist,*/ GesturesDB->Gestures[OutGestureIndex].Name, OutGestureIndex, GesturesDB);
390 ClearRecording(); // Clear the recording out, we don't want to detect this gesture again with the same data
392 }
393}
394
395float UVRGestureComponent::dtw(FVRGesture seq1, FVRGesture seq2, bool bMirrorGesture, float Scaler)
396{
397
398 // #TODO: Skip copying the array and reversing it in the future, we only ever use the reversed value.
399 // So pre-reverse it and keep it stored like that on init. When we do the initial sample we can check off of the first index instead of last then
400
401 // Should also be able to get SizeSquared for values and compared to squared thresholds instead of doing the full SQRT calc.
402
403 // Getting number of average samples recorded over of a gesture (top down) may be able to achieve a basic % completed check
404 // to see how far into detecting a gesture we are, this would require ignoring the last position threshold though....
405
406 int RowCount = seq1.Samples.Num() + 1;
407 int ColumnCount = seq2.Samples.Num() + 1;
408
409 TArray<float> LookupTable;
410 LookupTable.AddZeroed(ColumnCount * RowCount);
411
412 TArray<int> SlopeI;
413 SlopeI.AddZeroed(ColumnCount * RowCount);
414 TArray<int> SlopeJ;
415 SlopeJ.AddZeroed(ColumnCount * RowCount);
416
417 for (int i = 1; i < (ColumnCount * RowCount); i++)
418 {
419 LookupTable[i] = MAX_FLT;
420 }
421 // Don't need to do this, it is already handled by add zeroed
422 //tab[0, 0] = 0;
423
424 int icol = 0, icolneg = 0;
425
426 // Dynamic computation of the DTW matrix.
427 for (int i = 1; i < RowCount; i++)
428 {
429 for (int j = 1; j < ColumnCount; j++)
430 {
431 icol = i * ColumnCount;
432 icolneg = icol - ColumnCount;// (i - 1) * ColumnCount;
433
434 if (
435 LookupTable[icol + (j - 1)] < LookupTable[icolneg + (j - 1)] &&
436 LookupTable[icol + (j - 1)] < LookupTable[icolneg + j] &&
437 SlopeI[icol + (j - 1)] < maxSlope)
438 {
439 LookupTable[icol + j] = GetGestureDistance(seq1.Samples[i - 1] * Scaler, seq2.Samples[j - 1], bMirrorGesture) + LookupTable[icol + j - 1];
440 SlopeI[icol + j] = SlopeJ[icol + j - 1] + 1;
441 SlopeJ[icol + j] = 0;
442 }
443 else if (
444 LookupTable[icolneg + j] < LookupTable[icolneg + j - 1] &&
445 LookupTable[icolneg + j] < LookupTable[icol + j - 1] &&
446 SlopeJ[icolneg + j] < maxSlope)
447 {
448 LookupTable[icol + j] = GetGestureDistance(seq1.Samples[i - 1] * Scaler, seq2.Samples[j - 1], bMirrorGesture) + LookupTable[icolneg + j];
449 SlopeI[icol + j] = 0;
450 SlopeJ[icol + j] = SlopeJ[icolneg + j] + 1;
451 }
452 else
453 {
454 LookupTable[icol + j] = GetGestureDistance(seq1.Samples[i - 1] * Scaler, seq2.Samples[j - 1], bMirrorGesture) + LookupTable[icolneg + j - 1];
455 SlopeI[icol + j] = 0;
456 SlopeJ[icol + j] = 0;
457 }
458 }
459 }
460
461 // Find best between seq2 and an ending (postfix) of seq1.
462 float bestMatch = FLT_MAX;
463
464 for (int i = 1; i < seq1.Samples.Num() + 1/* - seq2.Minimum_Gesture_Length*/; i++)
465 {
466 if (LookupTable[(i*ColumnCount) + seq2.Samples.Num()] < bestMatch)
467 bestMatch = LookupTable[(i*ColumnCount) + seq2.Samples.Num()];
468 }
469
470 return bestMatch;
471}
472
473void UVRGestureComponent::DrawDebugGesture(UObject* WorldContextObject, FTransform &StartTransform, FVRGesture GestureToDraw, FColor const& Color, bool bPersistentLines, uint8 DepthPriority, float LifeTime, float Thickness)
474{
475#if ENABLE_DRAW_DEBUG
476
477 UWorld* InWorld = GEngine->GetWorldFromContextObject(WorldContextObject, EGetWorldErrorMode::LogAndReturnNull);
478
479 if (InWorld != nullptr)
480 {
481 // no debug line drawing on dedicated server
482 if (GEngine->GetNetMode(InWorld) != NM_DedicatedServer && GestureToDraw.Samples.Num() > 1)
483 {
484 bool bMirrorGesture = (MirroringHand != EVRGestureMirrorMode::GES_NoMirror && MirroringHand == GestureToDraw.GestureSettings.MirrorMode);
485 FVector MirrorVector = FVector(1.f, -1.f, 1.f); // Only mirroring on Y axis to flip Left/Right
486
487 // this means foreground lines can't be persistent
488 ULineBatchComponent* const LineBatcher = (InWorld ? ((DepthPriority == SDPG_Foreground) ? InWorld->ForegroundLineBatcher : ((bPersistentLines || (LifeTime > 0.f)) ? InWorld->PersistentLineBatcher : InWorld->LineBatcher)) : NULL);
489
490 if (LineBatcher != NULL)
491 {
492 float const LineLifeTime = (LifeTime > 0.f) ? LifeTime : LineBatcher->DefaultLifeTime;
493
494 TArray<FBatchedLine> Lines;
495 FBatchedLine Line;
496 Line.Color = Color;
497 Line.Thickness = Thickness;
498 Line.RemainingLifeTime = LineLifeTime;
499 Line.DepthPriority = DepthPriority;
500
501 FVector FirstLoc = bMirrorGesture ? GestureToDraw.Samples[GestureToDraw.Samples.Num() - 1] * MirrorVector : GestureToDraw.Samples[GestureToDraw.Samples.Num() - 1];
502
503 for (int i = GestureToDraw.Samples.Num() - 2; i >= 0; --i)
504 {
505 Line.Start = bMirrorGesture ? GestureToDraw.Samples[i] * MirrorVector : GestureToDraw.Samples[i];
506
507 Line.End = FirstLoc;
508 FirstLoc = Line.Start;
509
510 Line.End = StartTransform.TransformPosition(Line.End);
511 Line.Start = StartTransform.TransformPosition(Line.Start);
512
513 Lines.Add(Line);
514 }
515
516 LineBatcher->DrawLines(Lines);
517 }
518 }
519 }
520#endif
521}
522
523void UGesturesDatabase::RecalculateGestures(bool bScaleToDatabase)
524{
525 for (int i = 0; i < Gestures.Num(); ++i)
526 {
527 Gestures[i].CalculateSizeOfGesture(bScaleToDatabase, TargetGestureScale);
528 }
529}
530
531bool UGesturesDatabase::ImportSplineAsGesture(USplineComponent * HostSplineComponent, FString GestureName, bool bKeepSplineCurves, float SegmentLen, bool bScaleToDatabase)
532{
533 FVRGesture NewGesture;
534
535 if (HostSplineComponent->GetNumberOfSplinePoints() < 2)
536 return false;
537
538 NewGesture.Name = GestureName;
539
540 FVector FirstPointPos = HostSplineComponent->GetLocationAtSplinePoint(0, ESplineCoordinateSpace::Local);
541
542 float LastDistance = 0.f;
543 float ThisDistance = 0.f;
544 FVector LastDistanceV;
545 FVector ThisDistanceV;
546 FVector DistNormal;
547 float DistAlongSegment = 0.f;
548
549 // Realign to xForward on the gesture, normally splines lay out as X to the right
550 FTransform Realignment = FTransform(FRotator(0.f, 90.f, 0.f), -FirstPointPos);
551
552 // Prefill the first point
553 NewGesture.Samples.Add(Realignment.TransformPosition(HostSplineComponent->GetLocationAtSplinePoint(HostSplineComponent->GetNumberOfSplinePoints() - 1, ESplineCoordinateSpace::Local)));
554
555 // Inserting in reverse order -2 so we start one down
556 for (int i = HostSplineComponent->GetNumberOfSplinePoints() - 2; i >= 0; --i)
557 {
558 if (bKeepSplineCurves)
559 {
560 LastDistance = HostSplineComponent->GetDistanceAlongSplineAtSplinePoint(i + 1);
561 ThisDistance = HostSplineComponent->GetDistanceAlongSplineAtSplinePoint(i);
562
563 DistAlongSegment = FMath::Abs(ThisDistance - LastDistance);
564 }
565 else
566 {
567 LastDistanceV = Realignment.TransformPosition(HostSplineComponent->GetLocationAtSplinePoint(i + 1, ESplineCoordinateSpace::Local));
568 ThisDistanceV = Realignment.TransformPosition(HostSplineComponent->GetLocationAtSplinePoint(i, ESplineCoordinateSpace::Local));
569
570 DistAlongSegment = FVector::Dist(ThisDistanceV, LastDistanceV);
571 DistNormal = ThisDistanceV - LastDistanceV;
572 DistNormal.Normalize();
573 }
574
575
576 float SegmentCount = FMath::FloorToFloat(DistAlongSegment / SegmentLen);
577 float OverFlow = FMath::Fmod(DistAlongSegment, SegmentLen);
578
579 if (SegmentCount < 1)
580 {
581 SegmentCount++;
582 }
583
584 float DistPerSegment = (DistAlongSegment / SegmentCount);
585
586 for (int j = 0; j < SegmentCount; j++)
587 {
588 if (j == SegmentCount - 1 && i > 0)
589 DistPerSegment += OverFlow;
590
591 if (bKeepSplineCurves)
592 {
593 LastDistance -= DistPerSegment;
594 if (j == SegmentCount - 1 && i > 0)
595 {
596 LastDistance = ThisDistance;
597 }
598 FVector loc = Realignment.TransformPosition(HostSplineComponent->GetLocationAtDistanceAlongSpline(LastDistance, ESplineCoordinateSpace::Local));
599
600 if (!loc.IsNearlyZero())
601 NewGesture.Samples.Add(loc);
602 }
603 else
604 {
605 LastDistanceV += DistPerSegment * DistNormal;
606
607 if (j == SegmentCount - 1 && i > 0)
608 {
609 LastDistanceV = ThisDistanceV;
610 }
611
612 if (!LastDistanceV.IsNearlyZero())
613 NewGesture.Samples.Add(LastDistanceV);
614 }
615 }
616 }
617
618 NewGesture.CalculateSizeOfGesture(bScaleToDatabase, this->TargetGestureScale);
619 Gestures.Add(NewGesture);
620 return true;
621}
622
624{
625 SplineComponent->RemoveSplinePoint(0, false);
626
627 if (SplineMeshes.Num() < NextIndexCleared + 1)
629
630 SplineMeshes[NextIndexCleared]->SetVisibility(false);
632}
633
635{
636 if (SplineComponent != nullptr)
637 SplineComponent->ClearSplinePoints(true);
638
639 for (int i = SplineMeshes.Num() - 1; i >= 0; --i)
640 {
641 if (SplineMeshes[i] != nullptr)
642 SplineMeshes[i]->SetVisibility(false);
643 else
644 SplineMeshes.RemoveAt(i);
645 }
646
647 LastIndexSet = 0;
649}
650
652{
653 for (int i = 0; i < SplineMeshes.Num(); ++i)
654 {
655 if (SplineMeshes[i] != nullptr && !SplineMeshes[i]->IsBeingDestroyed())
656 {
657 SplineMeshes[i]->Modify();
658 SplineMeshes[i]->DestroyComponent();
659 }
660 }
661 SplineMeshes.Empty();
662
663 if (SplineComponent != nullptr)
664 {
665 SplineComponent->DestroyComponent();
666 SplineComponent = nullptr;
667 }
668
669 LastIndexSet = 0;
671}
672
679
684
686{
687 Super::BeginDestroy();
689 if (TickGestureTimer_Handle.IsValid())
690 {
691 GetWorld()->GetTimerManager().ClearTimer(TickGestureTimer_Handle);
692 }
693}
694
696{
697 if (GestureDB != nullptr)
698 InputGesture.CalculateSizeOfGesture(true, GestureDB->TargetGestureScale);
699 else
700 InputGesture.CalculateSizeOfGesture(false);
701}
702
704{
705 if (TickGestureTimer_Handle.IsValid())
706 {
707 GetWorld()->GetTimerManager().ClearTimer(TickGestureTimer_Handle);
708 }
709
710 this->SetComponentTickEnabled(false);
712
713 // Reset the recording gesture
715
716 return GestureLog;
717}
718
723
724void UVRGestureComponent::SaveRecording(FVRGesture &Recording, FString RecordingName, bool bScaleRecordingToDatabase)
725{
726 if (GesturesDB)
727 {
728 Recording.CalculateSizeOfGesture(bScaleRecordingToDatabase, GesturesDB->TargetGestureScale);
729 Recording.Name = RecordingName;
730 GesturesDB->Gestures.Add(Recording);
731 }
732}
DECLARE_CYCLE_STAT(TEXT("TickGesture ~ TickingGesture"), STAT_TickGesture, STATGROUP_TickGesture)
FTransform OffsetComponentToWorld
UPROPERTY(BlueprintReadOnly, Transient, Category = "VRExpansionLibrary")
UCLASS(BlueprintType, Category = "VRGestures")
void RecalculateGestures(bool bScaleToDatabase=true)
UFUNCTION(BlueprintCallable, Category = "VRGestures")
bool ImportSplineAsGesture(USplineComponent *HostSplineComponent, FString GestureName, bool bKeepSplineCurves=true, float SegmentLen=10.0f, bool bScaleToDatabase=true)
UFUNCTION(BlueprintCallable, Category = "VRGestures")
void FillSplineWithGesture(UPARAM(ref) FVRGesture &Gesture, USplineComponent *SplineComponent, bool bCenterPointsOnSpline=true, bool bScaleToBounds=false, float OptionalBounds=0.0f, bool bUseCurvedPoints=true, bool bFillInSplineMeshComponents=true, UStaticMesh *Mesh=nullptr, UMaterial *MeshMat=nullptr)
UFUNCTION(BlueprintCallable, Category = "VRGestures")
TArray< FVRGesture > Gestures
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "VRGestures")
float TargetGestureScale
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "VRGestures")
UMaterialInterface * SplineMaterial
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "VRGestures")
FVRGesture GestureLog
UPROPERTY(BlueprintReadOnly, Category = "VRGestures")
UStaticMesh * SplineMesh
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "VRGestures")
AVRBaseCharacter * TargetCharacter
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "VRGestures")
void BeginRecording(bool bRunDetection, bool bFlattenGesture=true, bool bDrawGesture=true, bool bDrawAsSpline=false, int SamplingHTZ=30, int SampleBufferSize=60, float ClampingTolerance=0.01f)
UFUNCTION(BlueprintCallable, Category = "VRGestures")
void ClearRecording()
UFUNCTION(BlueprintCallable, Category = "VRGestures")
FVRGesture EndRecording()
UFUNCTION(BlueprintCallable, Category = "VRGestures")
EVRGestureState CurrentState
UPROPERTY(BlueprintReadOnly, Category = "VRGestures")
void RecognizeGesture(FVRGesture inputGesture)
float SameSampleTolerance
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "VRGestures")
UVRGestureComponent(const FObjectInitializer &ObjectInitializer)
float dtw(FVRGesture seq1, FVRGesture seq2, bool bMirrorGesture=false, float Scaler=1.f)
void DrawDebugGesture(UObject *WorldContextObject, UPARAM(ref) FTransform &StartTransform, FVRGesture GestureToDraw, FColor const &Color, bool bPersistentLines=false, uint8 DepthPriority=0, float LifeTime=-1.f, float Thickness=0.f)
UFUNCTION(BlueprintCallable, Category = "VRGestures", meta = (WorldContext = "WorldContextObject"))
FVRGestureSplineDraw RecordingGestureDraw
void RecalculateGestureSize(UPARAM(ref) FVRGesture &InputGesture, UGesturesDatabase *GestureDB)
UFUNCTION(BlueprintCallable, Category = "VRGestures")
int maxSlope
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "VRGestures")
FTimerHandle TickGestureTimer_Handle
FVRGestureDetectedSignature OnGestureDetected_Bind
UPROPERTY(BlueprintAssignable, Category = "VRGestures")
UGesturesDatabase * GesturesDB
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "VRGestures")
FVector2D SplineMeshScaler
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "VRGestures")
float GetGestureDistance(FVector Seq1, FVector Seq2, bool bMirrorGesture=false)
bool bGetGestureInWorldSpace
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "VRGestures")
EVRGestureMirrorMode MirroringHand
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "VRGestures")
void OnGestureDetected(uint8 GestureType, FString &DetectedGestureName, int &DetectedGestureIndex, UGesturesDatabase *GestureDatabase)
UFUNCTION(BlueprintImplementableEvent, Category = "BaseVRCharacter")
void SaveRecording(UPARAM(ref) FVRGesture &Recording, FString RecordingName, bool bScaleRecordingToDatabase=true)
UFUNCTION(BlueprintCallable, Category = "VRGestures")
bool bDrawSplinesCurved
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "VRGestures")
USTRUCT(BlueprintType, Category = "VRGestures")
FVRGestureSettings GestureSettings
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "VRGesture")
FBox GestureSize
UPROPERTY(BlueprintReadWrite, VisibleAnywhere, Category = "VRGesture")
TArray< FVector > Samples
UPROPERTY(BlueprintReadWrite, VisibleAnywhere, Category = "VRGesture")
void CalculateSizeOfGesture(bool bAllowResizing=false, float TargetExtentSize=1.f)
FString Name
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "VRGesture")
EVRGestureMirrorMode MirrorMode
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "VRGesture|Advanced")
float firstThreshold
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "VRGesture|Advanced")
bool bEnableScaling
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "VRGesture|Advanced")
float FullThreshold
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "VRGesture|Advanced")
int Minimum_Gesture_Length
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "VRGesture|Advanced")
bool bEnabled
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "VRGesture|Advanced")
USplineComponent * SplineComponent
UPROPERTY()
TArray< USplineMeshComponent * > SplineMeshes
UPROPERTY()