A Demo Project for the UnrealEngineSDK
Loading...
Searching...
No Matches
VRGestureComponent.h
Go to the documentation of this file.
1
2
3#pragma once
4
5#include "CoreMinimal.h"
6#include "Engine/Engine.h"
7#include "VRBPDatatypes.h"
8#include "Algo/Reverse.h"
9#include "Components/SplineMeshComponent.h"
10#include "Components/SplineComponent.h"
11#include "VRBaseCharacter.h"
12#include "Engine/DataAsset.h"
13#include "DrawDebugHelpers.h"
14#include "Components/LineBatchComponent.h"
15#include "Engine/EngineTypes.h"
16#include "Engine/EngineBaseTypes.h"
17#include "TimerManager.h"
18#include "VRGestureComponent.generated.h"
19
20DECLARE_STATS_GROUP(TEXT("TICKGesture"), STATGROUP_TickGesture, STATCAT_Advanced);
21
22
23UENUM(Blueprintable)
24enum class EVRGestureState : uint8
25{
29};
30
31
32UENUM(Blueprintable)
33enum class EVRGestureMirrorMode : uint8
34{
39};
40
41USTRUCT(BlueprintType, Category = "VRGestures")
42struct VREXPANSIONPLUGIN_API FVRGestureSettings
43{
44 GENERATED_BODY()
45public:
46
47 // Minimum length to start recognizing this gesture at
48 UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "VRGesture|Advanced")
49 int Minimum_Gesture_Length;
50
51 // Maximum distance between the last observations before throwing out this gesture, raise this to make it easier to start checking this gesture
52 UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "VRGesture|Advanced")
53 float firstThreshold;
54
55 // Full threshold before detecting the gesture, raise this to lower accuracy but make it easier to detect this gesture
56 UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "VRGesture|Advanced")
57 float FullThreshold;
58
59 // If set to left/right, will mirror the detected gesture if the gesture component is set to match that value
60 // If set to Both mode, the gesture will be checked both normal and mirrored and the best match will be chosen
61 UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "VRGesture|Advanced")
63
64 // If enabled this gesture will be checked when inside a DB
65 UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "VRGesture|Advanced")
66 bool bEnabled;
67
68 // If enabled this gesture will have sample data scaled to it when recognizing (if false you will want to record the gesture without scaling)
69 UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "VRGesture|Advanced")
70 bool bEnableScaling;
71
73 {
74 Minimum_Gesture_Length = 1;
75 firstThreshold = 20.0f;
76 FullThreshold = 20.0f;
78 bEnabled = true;
79 bEnableScaling = true;
80 }
81};
82
83USTRUCT(BlueprintType, Category = "VRGestures")
84struct VREXPANSIONPLUGIN_API FVRGesture
85{
86 GENERATED_BODY()
87public:
88
89 // Name of the recorded gesture
90 UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "VRGesture")
91 FString Name;
92
93 // Enum uint8 for end user use
94 UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "VRGesture")
95 uint8 GestureType;
96
97 // Samples in the recorded gesture
98 UPROPERTY(BlueprintReadWrite, VisibleAnywhere, Category = "VRGesture")
99 TArray<FVector> Samples;
100
101 UPROPERTY(BlueprintReadWrite, VisibleAnywhere, Category = "VRGesture")
102 FBox GestureSize;
104 UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "VRGesture")
105 FVRGestureSettings GestureSettings;
106
107 FVRGesture()
108 {
109 GestureType = 0;
110 GestureSize = FBox();
111 }
112
113 void CalculateSizeOfGesture(bool bAllowResizing = false, float TargetExtentSize = 1.f)
114 {
115 FVector NewSample;
116 for (int i = 0; i < Samples.Num(); ++i)
117 {
118 NewSample = Samples[i];
119 GestureSize.Max.X = FMath::Max(NewSample.X, GestureSize.Max.X);
120 GestureSize.Max.Y = FMath::Max(NewSample.Y, GestureSize.Max.Y);
121 GestureSize.Max.Z = FMath::Max(NewSample.Z, GestureSize.Max.Z);
123 GestureSize.Min.X = FMath::Min(NewSample.X, GestureSize.Min.X);
124 GestureSize.Min.Y = FMath::Min(NewSample.Y, GestureSize.Min.Y);
125 GestureSize.Min.Z = FMath::Min(NewSample.Z, GestureSize.Min.Z);
126 }
127
128 if (bAllowResizing)
129 {
130 FVector BoxSize = GestureSize.GetSize();
131 float Scaler = TargetExtentSize / BoxSize.GetMax();
132
133 for (int i = 0; i < Samples.Num(); ++i)
134 {
135 Samples[i] *= Scaler;
136 }
137
138 GestureSize.Min *= Scaler;
139 GestureSize.Max *= Scaler;
140 }
141 }
142};
143
147UCLASS(BlueprintType, Category = "VRGestures")
148class VREXPANSIONPLUGIN_API UGesturesDatabase : public UDataAsset
149{
150 GENERATED_BODY()
151public:
152
153 // Gestures in this database
154 UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "VRGestures")
155 TArray <FVRGesture> Gestures;
156
157 UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "VRGestures")
158 float TargetGestureScale;
159
162 TargetGestureScale = 100.0f;
164
165 // Recalculate size of gestures and re-scale them to the TargetGestureScale (if bScaleToDatabase is true)
166 UFUNCTION(BlueprintCallable, Category = "VRGestures")
167 void RecalculateGestures(bool bScaleToDatabase = true);
168
169 // Fills a spline component with a gesture, optionally also generates spline mesh components for it (uses ones already attached if possible)
170 UFUNCTION(BlueprintCallable, Category = "VRGestures")
171 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);
172
173 // Imports a spline as a gesture, Segment len is the max segment length (will break lines up into lengths of this size)
174 UFUNCTION(BlueprintCallable, Category = "VRGestures")
175 bool ImportSplineAsGesture(USplineComponent * HostSplineComponent, FString GestureName, bool bKeepSplineCurves = true, float SegmentLen = 10.0f, bool bScaleToDatabase = true);
176
177};
178
179
180USTRUCT(BlueprintType, Category = "VRGestures")
181struct VREXPANSIONPLUGIN_API FVRGestureSplineDraw
182{
183 GENERATED_BODY()
184public:
185
186 UPROPERTY()
187 USplineComponent* SplineComponent;
188
189 UPROPERTY()
190 TArray<USplineMeshComponent*> SplineMeshes;
191
192 int LastIndexSet;
193 int NextIndexCleared;
194
195 // Marches through the array and clears the last point
196 void ClearLastPoint();
197
198 // Hides all spline meshes and re-inits the spline component
199 void Reset();
200
201 void Clear();
202
204
206};
209DECLARE_DYNAMIC_MULTICAST_DELEGATE_FourParams(FVRGestureDetectedSignature, uint8, GestureType, FString, DetectedGestureName, int, DetectedGestureIndex, UGesturesDatabase *, GestureDataBase);
210
218UCLASS(Blueprintable, meta = (BlueprintSpawnableComponent), ClassGroup = (VRExpansionPlugin))
219class VREXPANSIONPLUGIN_API UVRGestureComponent : public USceneComponent
220{
221 GENERATED_BODY()
222
223public:
224 UVRGestureComponent(const FObjectInitializer& ObjectInitializer);
226
227 // Size of obeservations vectors.
228 //int dim; // Not needed, this is just dimensionality
229 // Can be used for arrays of samples (IE: multiple points), could add back in eventually
230 // if I decide to support three point tracked gestures or something at some point, but its a waste for single point.
231
232 UFUNCTION(BlueprintImplementableEvent, Category = "BaseVRCharacter")
233 void OnGestureDetected(uint8 GestureType, FString &DetectedGestureName, int & DetectedGestureIndex, UGesturesDatabase * GestureDatabase);
234
235 // Call to use an object
236 UPROPERTY(BlueprintAssignable, Category = "VRGestures")
237 FVRGestureDetectedSignature OnGestureDetected_Bind;
238
239 // Known sequences
240 UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "VRGestures")
241 UGesturesDatabase *GesturesDB;
242
243 // Tolerance within we throw out duplicate samples
244 UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "VRGestures")
245 float SameSampleTolerance;
246
247 // If a gesture is set to match this value then detection will mirror the gesture
248 UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "VRGestures")
249 EVRGestureMirrorMode MirroringHand;
250
251 // Tolerance within we throw out duplicate samples
252 UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "VRGestures")
253 AVRBaseCharacter * TargetCharacter;
254
255 FVRGestureSplineDraw RecordingGestureDraw;
256
257 // Should we draw splines curved or straight
258 UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "VRGestures")
259 bool bDrawSplinesCurved;
261 // If false will get the gesture in relative space instead
262 UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "VRGestures")
263 bool bGetGestureInWorldSpace;
264
265 // Mesh to use when drawing splines
266 UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "VRGestures")
267 UStaticMesh* SplineMesh;
269 // Scaler to apply to the spline mesh components
270 UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "VRGestures")
271 FVector2D SplineMeshScaler;
272
273 // Material to use when drawing splines
274 UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "VRGestures")
275 UMaterialInterface* SplineMaterial;
277 // HTZ to run recording at for detection and saving - now being used as a frame time instead of a HTZ
278 float RecordingDelta;
279
280 // Number of samples to keep in memory during detection
281 int RecordingBufferSize;
282
283 float RecordingClampingTolerance;
284 bool bRecordingFlattenGesture;
285 bool bDrawRecordingGesture;
286 bool bDrawRecordingGestureAsSpline;
287 bool bGestureChanged;
288
289 // Handle to our update timer
290 FTimerHandle TickGestureTimer_Handle;
291
292 // Maximum vertical or horizontal steps in a row in the lookup table before throwing out a gesture
293 UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "VRGestures")
294 int maxSlope;
295
296 UPROPERTY(BlueprintReadOnly, Category = "VRGestures")
297 EVRGestureState CurrentState;
298
299 // Currently recording gesture
300 UPROPERTY(BlueprintReadOnly, Category = "VRGestures")
301 FVRGesture GestureLog;
302
303 inline float GetGestureDistance(FVector Seq1, FVector Seq2, bool bMirrorGesture = false)
304 {
305 if (bMirrorGesture)
306 {
307 return FVector::DistSquared(Seq1, FVector(Seq2.X, -Seq2.Y, Seq2.Z));
308 }
309
310 return FVector::DistSquared(Seq1, Seq2);
311 }
312
313 void BeginDestroy() override;
314
315 // Recalculates a gestures size and re-scales it to the given database
316 UFUNCTION(BlueprintCallable, Category = "VRGestures")
317 void RecalculateGestureSize(UPARAM(ref) FVRGesture & InputGesture, UGesturesDatabase * GestureDB);
318
319 // Draw a gesture with a debug line batch
320 UFUNCTION(BlueprintCallable, Category = "VRGestures", meta = (WorldContext = "WorldContextObject"))
321 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);
323 FVector StartVector;
324 FTransform OriginatingTransform;
325
326 /* Function to begin recording a gesture for detection or saving
327 *
328 * bRunDetection: Should we detect gestures or only record them
329 * bFlattenGestue: Should we flatten the gesture into 2 dimensions (more stable detection and recording, less pretty visually)
330 * bDrawGesture: Should we draw the gesture during recording of it
331 * bDrawAsSpline: If true we will use spline meshes, if false we will draw as debug lines
332 * SamplingHTZ: How many times a second we will record a gesture point, recording is done with a timer now, i would steer away
333 * from htz > possible frames as that could cause double timer updates with how timers are implemented.
334 * SampleBufferSize: How many points we will store in history at a time
335 * ClampingTolerance: If larger than 0.0, we will clamp points to a grid of this size
336 */
337 UFUNCTION(BlueprintCallable, Category = "VRGestures")
338 void BeginRecording(bool bRunDetection, bool bFlattenGesture = true, bool bDrawGesture = true, bool bDrawAsSpline = false, int SamplingHTZ = 30, int SampleBufferSize = 60, float ClampingTolerance = 0.01f);
339
340 // Ends recording and returns the recorded gesture
341 UFUNCTION(BlueprintCallable, Category = "VRGestures")
342 FVRGesture EndRecording();
343
344 // Clears the current recording
345 UFUNCTION(BlueprintCallable, Category = "VRGestures")
346 void ClearRecording();
347
348 // Saves a VRGesture to the database, if Scale To Database is true then it will scale the data
349 UFUNCTION(BlueprintCallable, Category = "VRGestures")
350 void SaveRecording(UPARAM(ref) FVRGesture &Recording, FString RecordingName, bool bScaleRecordingToDatabase = true);
351
352 void CaptureGestureFrame();
354 // Ticks the logic from the gameplay timer.
355 void TickGesture();
356
357
358 // Recognize gesture in the given sequence.
359 // It will always assume that the gesture ends on the last observation of that sequence.
360 // If the distance between the last observations of each sequence is too great, or if the overall DTW distance between the two sequences is too great, no gesture will be recognized.
361 void RecognizeGesture(FVRGesture inputGesture);
362
364 // Compute the min DTW distance between seq2 and all possible endings of seq1.
365 float dtw(FVRGesture seq1, FVRGesture seq2, bool bMirrorGesture = false, float Scaler = 1.f);
366
367};
368
EVRGestureState
UENUM(Blueprintable)
EVRGestureMirrorMode
UENUM(Blueprintable)
DECLARE_STATS_GROUP(TEXT("TICKGesture"), STATGROUP_TickGesture, STATCAT_Advanced)
DECLARE_DYNAMIC_MULTICAST_DELEGATE_FourParams(FVRGestureDetectedSignature, uint8, GestureType, FString, DetectedGestureName, int, DetectedGestureIndex, UGesturesDatabase *, GestureDataBase)
UCLASS(BlueprintType, Category = "VRGestures")
TArray< FVRGesture > Gestures
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "VRGestures")
float TargetGestureScale
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "VRGestures")
UCLASS(Blueprintable, meta = (BlueprintSpawnableComponent), ClassGroup = (VRExpansionPlugin))
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")
EVRGestureState CurrentState
UPROPERTY(BlueprintReadOnly, Category = "VRGestures")
float SameSampleTolerance
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "VRGestures")
FVRGestureSplineDraw RecordingGestureDraw
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")
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")
uint8 GestureType
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "VRGesture")
void CalculateSizeOfGesture(bool bAllowResizing=false, float TargetExtentSize=1.f)
FString Name
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "VRGesture")
USTRUCT(BlueprintType, Category = "VRGestures")
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")
USTRUCT(BlueprintType, Category = "VRGestures")
USplineComponent * SplineComponent
UPROPERTY()
TArray< USplineMeshComponent * > SplineMeshes
UPROPERTY()