diff --git a/Config/Android/AndroidEngine.ini b/Config/Android/AndroidEngine.ini
new file mode 100644
index 0000000..2e9b81d
--- /dev/null
+++ b/Config/Android/AndroidEngine.ini
@@ -0,0 +1,6 @@
+[/Script/Engine.RendererSettings]
+r.DistanceFields=0
+
+; r.Mobile.AllowFramebufferFetch=1 reverts the mobile deferred renderer from multi-pass to single-pass/Vulkan subpasses, which was the default before UE 5.7.
+; Multi-pass deferred is disabled for VR Template for 5.7.1 due to rendering issues with mobile multi-view.
+r.Mobile.AllowFramebufferFetch=1
\ No newline at end of file
diff --git a/Config/DefaultEditor.ini b/Config/DefaultEditor.ini
new file mode 100644
index 0000000..d9cfc79
--- /dev/null
+++ b/Config/DefaultEditor.ini
@@ -0,0 +1,10 @@
+
+
+[/Script/UnrealEd.EditorPerformanceProjectSettings]
+RealtimeScreenPercentageMode=BasedOnDPIScale
+
+[/Script/AdvancedPreviewScene.SharedProfiles]
++Profiles=(ProfileName="Epic Headquarters",bSharedProfile=True,bIsEngineDefaultProfile=True,bUseSkyLighting=True,DirectionalLightIntensity=1.000000,DirectionalLightColor=(R=1.000000,G=1.000000,B=1.000000,A=1.000000),SkyLightIntensity=1.000000,bRotateLightingRig=False,bShowEnvironment=True,bShowFloor=True,bShowGrid=False,EnvironmentColor=(R=0.200000,G=0.200000,B=0.200000,A=1.000000),EnvironmentIntensity=1.000000,EnvironmentCubeMapPath="/Engine/EditorMaterials/AssetViewer/EpicQuadPanorama_CC+EV1.EpicQuadPanorama_CC+EV1",bPostProcessingEnabled=True,PostProcessingSettings=(bOverride_TemperatureType=False,bOverride_WhiteTemp=False,bOverride_WhiteTint=False,bOverride_ColorSaturation=False,bOverride_ColorContrast=False,bOverride_ColorGamma=False,bOverride_ColorGain=False,bOverride_ColorOffset=False,bOverride_ColorSaturationShadows=False,bOverride_ColorContrastShadows=False,bOverride_ColorGammaShadows=False,bOverride_ColorGainShadows=False,bOverride_ColorOffsetShadows=False,bOverride_ColorSaturationMidtones=False,bOverride_ColorContrastMidtones=False,bOverride_ColorGammaMidtones=False,bOverride_ColorGainMidtones=False,bOverride_ColorOffsetMidtones=False,bOverride_ColorSaturationHighlights=False,bOverride_ColorContrastHighlights=False,bOverride_ColorGammaHighlights=False,bOverride_ColorGainHighlights=False,bOverride_ColorOffsetHighlights=False,bOverride_ColorCorrectionShadowsMax=False,bOverride_ColorCorrectionHighlightsMin=False,bOverride_ColorCorrectionHighlightsMax=False,bOverride_BlueCorrection=False,bOverride_ExpandGamut=False,bOverride_ToneCurveAmount=False,bOverride_FilmSlope=False,bOverride_FilmToe=False,bOverride_FilmShoulder=False,bOverride_FilmBlackClip=False,bOverride_FilmWhiteClip=False,bOverride_SceneColorTint=False,bOverride_SceneFringeIntensity=False,bOverride_ChromaticAberrationStartOffset=False,bOverride_bMegaLights=False,bOverride_AmbientCubemapTint=False,bOverride_AmbientCubemapIntensity=False,bOverride_BloomMethod=False,bOverride_BloomIntensity=False,bOverride_BloomGaussianIntensity=False,bOverride_BloomThreshold=False,bOverride_Bloom1Tint=False,bOverride_Bloom1Size=False,bOverride_Bloom2Size=False,bOverride_Bloom2Tint=False,bOverride_Bloom3Tint=False,bOverride_Bloom3Size=False,bOverride_Bloom4Tint=False,bOverride_Bloom4Size=False,bOverride_Bloom5Tint=False,bOverride_Bloom5Size=False,bOverride_Bloom6Tint=False,bOverride_Bloom6Size=False,bOverride_BloomSizeScale=False,bOverride_BloomConvolutionIntensity=False,bOverride_BloomConvolutionTexture=False,bOverride_BloomConvolutionScatterDispersion=False,bOverride_BloomConvolutionSize=False,bOverride_BloomConvolutionCenterUV=False,bOverride_BloomConvolutionPreFilterMin=False,bOverride_BloomConvolutionPreFilterMax=False,bOverride_BloomConvolutionPreFilterMult=False,bOverride_BloomConvolutionBufferScale=False,bOverride_BloomDirtMaskIntensity=False,bOverride_BloomDirtMaskTint=False,bOverride_BloomDirtMask=False,bOverride_CameraShutterSpeed=False,bOverride_CameraISO=False,bOverride_AutoExposureMethod=False,bOverride_AutoExposureLowPercent=False,bOverride_AutoExposureHighPercent=False,bOverride_AutoExposureMinBrightness=False,bOverride_AutoExposureMaxBrightness=False,bOverride_AutoExposureSpeedUp=False,bOverride_AutoExposureSpeedDown=False,bOverride_AutoExposureBias=False,bOverride_AutoExposureBiasCurve=False,bOverride_AutoExposureMeterMask=False,bOverride_AutoExposureApplyPhysicalCameraExposure=False,bOverride_HistogramLogMin=False,bOverride_HistogramLogMax=False,bOverride_LocalExposureMethod=False,bOverride_LocalExposureHighlightContrastScale=False,bOverride_LocalExposureShadowContrastScale=False,bOverride_LocalExposureHighlightContrastCurve=False,bOverride_LocalExposureShadowContrastCurve=False,bOverride_LocalExposureHighlightThreshold=False,bOverride_LocalExposureShadowThreshold=False,bOverride_LocalExposureDetailStrength=False,bOverride_LocalExposureBlurredLuminanceBlend=False,bOverride_LocalExposureBlurredLuminanceKernelSizePercent=False,bOverride_LocalExposureHighlightThresholdStrength=False,bOverride_LocalExposureShadowThresholdStrength=False,bOverride_LocalExposureMiddleGreyBias=False,bOverride_LensFlareIntensity=False,bOverride_LensFlareTint=False,bOverride_LensFlareTints=False,bOverride_LensFlareBokehSize=False,bOverride_LensFlareBokehShape=False,bOverride_LensFlareThreshold=False,bOverride_VignetteIntensity=False,bOverride_Sharpen=False,bOverride_FilmGrainIntensity=False,bOverride_FilmGrainIntensityShadows=False,bOverride_FilmGrainIntensityMidtones=False,bOverride_FilmGrainIntensityHighlights=False,bOverride_FilmGrainShadowsMax=False,bOverride_FilmGrainHighlightsMin=False,bOverride_FilmGrainHighlightsMax=False,bOverride_FilmGrainTexelSize=False,bOverride_FilmGrainTexture=False,bOverride_AmbientOcclusionIntensity=False,bOverride_AmbientOcclusionStaticFraction=False,bOverride_AmbientOcclusionRadius=False,bOverride_AmbientOcclusionFadeDistance=False,bOverride_AmbientOcclusionFadeRadius=False,bOverride_AmbientOcclusionRadiusInWS=False,bOverride_AmbientOcclusionPower=False,bOverride_AmbientOcclusionBias=False,bOverride_AmbientOcclusionQuality=False,bOverride_AmbientOcclusionMipBlend=False,bOverride_AmbientOcclusionMipScale=False,bOverride_AmbientOcclusionMipThreshold=False,bOverride_AmbientOcclusionTemporalBlendWeight=False,bOverride_RayTracingAO=False,bOverride_RayTracingAOSamplesPerPixel=False,bOverride_RayTracingAOIntensity=False,bOverride_RayTracingAORadius=False,bOverride_IndirectLightingColor=False,bOverride_IndirectLightingIntensity=False,bOverride_ColorGradingIntensity=False,bOverride_ColorGradingLUT=False,bOverride_DepthOfFieldFocalDistance=False,bOverride_DepthOfFieldFstop=False,bOverride_DepthOfFieldMinFstop=False,bOverride_DepthOfFieldBladeCount=False,bOverride_DepthOfFieldSensorWidth=False,bOverride_DepthOfFieldSqueezeFactor=False,bOverride_DepthOfFieldDepthBlurRadius=False,bOverride_DepthOfFieldUseHairDepth=False,bOverride_DepthOfFieldPetzvalBokeh=False,bOverride_DepthOfFieldPetzvalBokehFalloff=False,bOverride_DepthOfFieldPetzvalExclusionBoxExtents=False,bOverride_DepthOfFieldPetzvalExclusionBoxRadius=False,bOverride_DepthOfFieldAspectRatioScalar=False,bOverride_DepthOfFieldMatteBoxFlags=False,bOverride_DepthOfFieldBarrelRadius=False,bOverride_DepthOfFieldBarrelLength=False,bOverride_DepthOfFieldDepthBlurAmount=False,bOverride_DepthOfFieldFocalRegion=False,bOverride_DepthOfFieldNearTransitionRegion=False,bOverride_DepthOfFieldFarTransitionRegion=False,bOverride_DepthOfFieldScale=False,bOverride_DepthOfFieldNearBlurSize=False,bOverride_DepthOfFieldFarBlurSize=False,bOverride_MobileHQGaussian=False,bOverride_DepthOfFieldOcclusion=False,bOverride_DepthOfFieldSkyFocusDistance=False,bOverride_DepthOfFieldVignetteSize=False,bOverride_MotionBlurAmount=False,bOverride_MotionBlurMax=False,bOverride_MotionBlurTargetFPS=False,bOverride_MotionBlurPerObjectSize=False,bOverride_ReflectionMethod=False,bOverride_LumenReflectionQuality=False,bOverride_ScreenSpaceReflectionIntensity=False,bOverride_ScreenSpaceReflectionQuality=False,bOverride_ScreenSpaceReflectionMaxRoughness=False,bOverride_ScreenSpaceReflectionRoughnessScale=False,bOverride_UserFlags=False,bOverride_RayTracingReflectionsMaxRoughness=False,bOverride_RayTracingReflectionsMaxBounces=False,bOverride_RayTracingReflectionsSamplesPerPixel=False,bOverride_RayTracingReflectionsShadows=False,bOverride_RayTracingReflectionsTranslucency=False,bOverride_TranslucencyType=False,bOverride_RayTracingTranslucencyMaxRoughness=False,bOverride_RayTracingTranslucencyRefractionRays=False,bOverride_RayTracingTranslucencySamplesPerPixel=False,bOverride_RayTracingTranslucencyShadows=False,bOverride_RayTracingTranslucencyRefraction=False,bOverride_RayTracingTranslucencyMaxPrimaryHitEvents=False,bOverride_RayTracingTranslucencyMaxSecondaryHitEvents=False,bOverride_RayTracingTranslucencyUseRayTracedRefraction=False,bOverride_DynamicGlobalIlluminationMethod=False,bOverride_LumenSceneLightingQuality=False,bOverride_LumenSceneDetail=False,bOverride_LumenSceneViewDistance=False,bOverride_LumenSceneLightingUpdateSpeed=False,bOverride_LumenFinalGatherQuality=False,bOverride_LumenFinalGatherLightingUpdateSpeed=False,bOverride_LumenFinalGatherScreenTraces=False,bOverride_LumenMaxTraceDistance=False,bOverride_LumenDiffuseColorBoost=False,bOverride_LumenSkylightLeaking=False,bOverride_LumenSkylightLeakingTint=False,bOverride_LumenFullSkylightLeakingDistance=False,bOverride_LumenRayLightingMode=False,bOverride_LumenReflectionsScreenTraces=False,bOverride_LumenFrontLayerTranslucencyReflections=False,bOverride_LumenMaxRoughnessToTraceReflections=False,bOverride_LumenMaxReflectionBounces=False,bOverride_LumenMaxRefractionBounces=False,bOverride_LumenSurfaceCacheResolution=False,bOverride_RayTracingGI=False,bOverride_RayTracingGIMaxBounces=False,bOverride_RayTracingGISamplesPerPixel=False,bOverride_PathTracingMaxBounces=False,bOverride_PathTracingSamplesPerPixel=False,bOverride_PathTracingMaxPathIntensity=False,bOverride_PathTracingEnableEmissiveMaterials=False,bOverride_PathTracingEnableReferenceDOF=False,bOverride_PathTracingEnableReferenceAtmosphere=False,bOverride_PathTracingEnableDenoiser=False,bOverride_PathTracingIncludeEmissive=False,bOverride_PathTracingIncludeDiffuse=False,bOverride_PathTracingIncludeIndirectDiffuse=False,bOverride_PathTracingIncludeSpecular=False,bOverride_PathTracingIncludeIndirectSpecular=False,bOverride_PathTracingIncludeVolume=False,bOverride_PathTracingIncludeIndirectVolume=False,bMobileHQGaussian=False,BloomMethod=BM_SOG,AutoExposureMethod=AEM_Histogram,TemperatureType=TEMP_WhiteBalance,WhiteTemp=6500.000000,WhiteTint=0.000000,ColorSaturation=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorContrast=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorGamma=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorGain=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorOffset=(X=0.000000,Y=0.000000,Z=0.000000,W=0.000000),ColorSaturationShadows=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorContrastShadows=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorGammaShadows=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorGainShadows=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorOffsetShadows=(X=0.000000,Y=0.000000,Z=0.000000,W=0.000000),ColorSaturationMidtones=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorContrastMidtones=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorGammaMidtones=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorGainMidtones=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorOffsetMidtones=(X=0.000000,Y=0.000000,Z=0.000000,W=0.000000),ColorSaturationHighlights=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorContrastHighlights=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorGammaHighlights=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorGainHighlights=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorOffsetHighlights=(X=0.000000,Y=0.000000,Z=0.000000,W=0.000000),ColorCorrectionHighlightsMin=0.500000,ColorCorrectionHighlightsMax=1.000000,ColorCorrectionShadowsMax=0.090000,BlueCorrection=0.600000,ExpandGamut=1.000000,ToneCurveAmount=1.000000,FilmSlope=0.880000,FilmToe=0.550000,FilmShoulder=0.260000,FilmBlackClip=0.000000,FilmWhiteClip=0.040000,SceneColorTint=(R=1.000000,G=1.000000,B=1.000000,A=1.000000),SceneFringeIntensity=0.000000,ChromaticAberrationStartOffset=0.000000,BloomIntensity=0.675000,BloomGaussianIntensity=1.000000,BloomThreshold=-1.000000,BloomSizeScale=4.000000,Bloom1Size=0.300000,Bloom2Size=1.000000,Bloom3Size=2.000000,Bloom4Size=10.000000,Bloom5Size=30.000000,Bloom6Size=64.000000,Bloom1Tint=(R=0.346500,G=0.346500,B=0.346500,A=1.000000),Bloom2Tint=(R=0.138000,G=0.138000,B=0.138000,A=1.000000),Bloom3Tint=(R=0.117600,G=0.117600,B=0.117600,A=1.000000),Bloom4Tint=(R=0.066000,G=0.066000,B=0.066000,A=1.000000),Bloom5Tint=(R=0.066000,G=0.066000,B=0.066000,A=1.000000),Bloom6Tint=(R=0.061000,G=0.061000,B=0.061000,A=1.000000),BloomConvolutionIntensity=1.000000,BloomConvolutionScatterDispersion=1.000000,BloomConvolutionSize=1.000000,BloomConvolutionTexture=None,BloomConvolutionCenterUV=(X=0.500000,Y=0.500000),BloomConvolutionPreFilterMin=7.000000,BloomConvolutionPreFilterMax=15000.000000,BloomConvolutionPreFilterMult=15.000000,BloomConvolutionBufferScale=0.133000,BloomDirtMask=None,BloomDirtMaskIntensity=0.000000,BloomDirtMaskTint=(R=0.500000,G=0.500000,B=0.500000,A=1.000000),DynamicGlobalIlluminationMethod=Lumen,IndirectLightingColor=(R=1.000000,G=1.000000,B=1.000000,A=1.000000),IndirectLightingIntensity=1.000000,LumenRayLightingMode=Default,LumenSceneLightingQuality=1.000000,LumenSceneDetail=1.000000,LumenSceneViewDistance=20000.000000,LumenSceneLightingUpdateSpeed=1.000000,LumenFinalGatherQuality=1.000000,LumenFinalGatherLightingUpdateSpeed=1.000000,LumenFinalGatherScreenTraces=True,LumenMaxTraceDistance=20000.000000,LumenDiffuseColorBoost=1.000000,LumenSkylightLeaking=0.000000,LumenSkylightLeakingTint=(R=1.000000,G=1.000000,B=1.000000,A=1.000000),LumenFullSkylightLeakingDistance=1000.000000,LumenSurfaceCacheResolution=1.000000,ReflectionMethod=Lumen,LumenReflectionQuality=1.000000,LumenReflectionsScreenTraces=True,LumenFrontLayerTranslucencyReflections=False,LumenMaxRoughnessToTraceReflections=0.400000,LumenMaxReflectionBounces=1,LumenMaxRefractionBounces=0,ScreenSpaceReflectionIntensity=100.000000,ScreenSpaceReflectionQuality=50.000000,ScreenSpaceReflectionMaxRoughness=0.600000,bMegaLights=True,AmbientCubemapTint=(R=1.000000,G=1.000000,B=1.000000,A=1.000000),AmbientCubemapIntensity=1.000000,AmbientCubemap=None,CameraShutterSpeed=60.000000,CameraISO=100.000000,DepthOfFieldFstop=4.000000,DepthOfFieldMinFstop=1.200000,DepthOfFieldBladeCount=5,AutoExposureBias=1.000000,AutoExposureBiasBackup=0.000000,bOverride_AutoExposureBiasBackup=False,AutoExposureApplyPhysicalCameraExposure=True,AutoExposureBiasCurve=None,AutoExposureMeterMask=None,AutoExposureLowPercent=10.000000,AutoExposureHighPercent=90.000000,AutoExposureMinBrightness=-10.000000,AutoExposureMaxBrightness=20.000000,AutoExposureSpeedUp=3.000000,AutoExposureSpeedDown=1.000000,HistogramLogMin=-10.000000,HistogramLogMax=20.000000,LocalExposureMethod=Bilateral,LocalExposureHighlightContrastScale=1.000000,LocalExposureShadowContrastScale=1.000000,LocalExposureHighlightContrastCurve=None,LocalExposureShadowContrastCurve=None,LocalExposureHighlightThreshold=0.000000,LocalExposureShadowThreshold=0.000000,LocalExposureDetailStrength=1.000000,LocalExposureBlurredLuminanceBlend=0.600000,LocalExposureBlurredLuminanceKernelSizePercent=50.000000,LocalExposureHighlightThresholdStrength=1.000000,LocalExposureShadowThresholdStrength=1.000000,LocalExposureMiddleGreyBias=0.000000,LensFlareIntensity=1.000000,LensFlareTint=(R=1.000000,G=1.000000,B=1.000000,A=1.000000),LensFlareBokehSize=3.000000,LensFlareThreshold=8.000000,LensFlareBokehShape=None,LensFlareTints[0]=(R=1.000000,G=0.800000,B=0.400000,A=0.600000),LensFlareTints[1]=(R=1.000000,G=1.000000,B=0.600000,A=0.530000),LensFlareTints[2]=(R=0.800000,G=0.800000,B=1.000000,A=0.460000),LensFlareTints[3]=(R=0.500000,G=1.000000,B=0.400000,A=0.390000),LensFlareTints[4]=(R=0.500000,G=0.800000,B=1.000000,A=0.310000),LensFlareTints[5]=(R=0.900000,G=1.000000,B=0.800000,A=0.270000),LensFlareTints[6]=(R=1.000000,G=0.800000,B=0.400000,A=0.220000),LensFlareTints[7]=(R=0.900000,G=0.700000,B=0.700000,A=0.150000),VignetteIntensity=0.400000,Sharpen=0.000000,FilmGrainIntensity=0.000000,FilmGrainIntensityShadows=1.000000,FilmGrainIntensityMidtones=1.000000,FilmGrainIntensityHighlights=1.000000,FilmGrainShadowsMax=0.090000,FilmGrainHighlightsMin=0.500000,FilmGrainHighlightsMax=1.000000,FilmGrainTexelSize=1.000000,FilmGrainTexture=None,AmbientOcclusionIntensity=0.500000,AmbientOcclusionStaticFraction=1.000000,AmbientOcclusionRadius=200.000000,AmbientOcclusionRadiusInWS=False,AmbientOcclusionFadeDistance=8000.000000,AmbientOcclusionFadeRadius=5000.000000,AmbientOcclusionPower=2.000000,AmbientOcclusionBias=3.000000,AmbientOcclusionQuality=50.000000,AmbientOcclusionMipBlend=0.600000,AmbientOcclusionMipScale=1.700000,AmbientOcclusionMipThreshold=0.010000,AmbientOcclusionTemporalBlendWeight=0.100000,RayTracingAO=False,RayTracingAOSamplesPerPixel=1,RayTracingAOIntensity=1.000000,RayTracingAORadius=200.000000,ColorGradingIntensity=1.000000,ColorGradingLUT=None,DepthOfFieldSensorWidth=24.576000,DepthOfFieldSqueezeFactor=1.000000,DepthOfFieldFocalDistance=0.000000,DepthOfFieldDepthBlurAmount=1.000000,DepthOfFieldDepthBlurRadius=0.000000,DepthOfFieldUseHairDepth=False,DepthOfFieldPetzvalBokeh=0.000000,DepthOfFieldPetzvalBokehFalloff=1.000000,DepthOfFieldPetzvalExclusionBoxExtents=(X=0.000000,Y=0.000000),DepthOfFieldPetzvalExclusionBoxRadius=0.000000,DepthOfFieldAspectRatioScalar=1.000000,DepthOfFieldBarrelRadius=5.000000,DepthOfFieldBarrelLength=0.000000,DepthOfFieldMatteBoxFlags[0]=(Pitch=0.000000,Roll=0.000000,Length=0.000000),DepthOfFieldMatteBoxFlags[1]=(Pitch=0.000000,Roll=0.000000,Length=0.000000),DepthOfFieldMatteBoxFlags[2]=(Pitch=0.000000,Roll=0.000000,Length=0.000000),DepthOfFieldFocalRegion=0.000000,DepthOfFieldNearTransitionRegion=300.000000,DepthOfFieldFarTransitionRegion=500.000000,DepthOfFieldScale=0.000000,DepthOfFieldNearBlurSize=15.000000,DepthOfFieldFarBlurSize=15.000000,DepthOfFieldOcclusion=0.400000,DepthOfFieldSkyFocusDistance=0.000000,DepthOfFieldVignetteSize=200.000000,MotionBlurAmount=0.500000,MotionBlurMax=5.000000,MotionBlurTargetFPS=30,MotionBlurPerObjectSize=0.000000,TranslucencyType=Raster,RayTracingTranslucencyMaxRoughness=0.600000,RayTracingTranslucencyRefractionRays=3,RayTracingTranslucencySamplesPerPixel=1,RayTracingTranslucencyMaxPrimaryHitEvents=4,RayTracingTranslucencyMaxSecondaryHitEvents=2,RayTracingTranslucencyShadows=Hard_shadows,RayTracingTranslucencyRefraction=True,RayTracingTranslucencyUseRayTracedRefraction=False,PathTracingMaxBounces=32,PathTracingSamplesPerPixel=2048,PathTracingMaxPathIntensity=24.000000,PathTracingEnableEmissiveMaterials=True,PathTracingEnableReferenceDOF=False,PathTracingEnableReferenceAtmosphere=False,PathTracingEnableDenoiser=True,PathTracingIncludeEmissive=True,PathTracingIncludeDiffuse=True,PathTracingIncludeIndirectDiffuse=True,PathTracingIncludeSpecular=True,PathTracingIncludeIndirectSpecular=True,PathTracingIncludeVolume=True,PathTracingIncludeIndirectVolume=True,UserFlags=0,WeightedBlendables=(Array=)),LightingRigRotation=0.000000,RotationSpeed=2.000000,DirectionalLightRotation=(Pitch=-40.000000,Yaw=-67.500000,Roll=0.000000),bEnableToneMapping=True,bShowMeshEdges=False)
++Profiles=(ProfileName="Grey Wireframe",bSharedProfile=True,bIsEngineDefaultProfile=True,bUseSkyLighting=True,DirectionalLightIntensity=1.000000,DirectionalLightColor=(R=1.000000,G=1.000000,B=1.000000,A=1.000000),SkyLightIntensity=1.000000,bRotateLightingRig=False,bShowEnvironment=False,bShowFloor=False,bShowGrid=True,EnvironmentColor=(R=0.039216,G=0.039216,B=0.039216,A=1.000000),EnvironmentIntensity=1.000000,EnvironmentCubeMapPath="/Engine/EditorMaterials/AssetViewer/EpicQuadPanorama_CC+EV1.EpicQuadPanorama_CC+EV1",bPostProcessingEnabled=False,PostProcessingSettings=(bOverride_TemperatureType=False,bOverride_WhiteTemp=False,bOverride_WhiteTint=False,bOverride_ColorSaturation=False,bOverride_ColorContrast=False,bOverride_ColorGamma=False,bOverride_ColorGain=False,bOverride_ColorOffset=False,bOverride_ColorSaturationShadows=False,bOverride_ColorContrastShadows=False,bOverride_ColorGammaShadows=False,bOverride_ColorGainShadows=False,bOverride_ColorOffsetShadows=False,bOverride_ColorSaturationMidtones=False,bOverride_ColorContrastMidtones=False,bOverride_ColorGammaMidtones=False,bOverride_ColorGainMidtones=False,bOverride_ColorOffsetMidtones=False,bOverride_ColorSaturationHighlights=False,bOverride_ColorContrastHighlights=False,bOverride_ColorGammaHighlights=False,bOverride_ColorGainHighlights=False,bOverride_ColorOffsetHighlights=False,bOverride_ColorCorrectionShadowsMax=False,bOverride_ColorCorrectionHighlightsMin=False,bOverride_ColorCorrectionHighlightsMax=False,bOverride_BlueCorrection=False,bOverride_ExpandGamut=False,bOverride_ToneCurveAmount=False,bOverride_FilmSlope=False,bOverride_FilmToe=False,bOverride_FilmShoulder=False,bOverride_FilmBlackClip=False,bOverride_FilmWhiteClip=False,bOverride_SceneColorTint=False,bOverride_SceneFringeIntensity=False,bOverride_ChromaticAberrationStartOffset=False,bOverride_bMegaLights=False,bOverride_AmbientCubemapTint=False,bOverride_AmbientCubemapIntensity=False,bOverride_BloomMethod=False,bOverride_BloomIntensity=False,bOverride_BloomGaussianIntensity=False,bOverride_BloomThreshold=False,bOverride_Bloom1Tint=False,bOverride_Bloom1Size=False,bOverride_Bloom2Size=False,bOverride_Bloom2Tint=False,bOverride_Bloom3Tint=False,bOverride_Bloom3Size=False,bOverride_Bloom4Tint=False,bOverride_Bloom4Size=False,bOverride_Bloom5Tint=False,bOverride_Bloom5Size=False,bOverride_Bloom6Tint=False,bOverride_Bloom6Size=False,bOverride_BloomSizeScale=False,bOverride_BloomConvolutionIntensity=False,bOverride_BloomConvolutionTexture=False,bOverride_BloomConvolutionScatterDispersion=False,bOverride_BloomConvolutionSize=False,bOverride_BloomConvolutionCenterUV=False,bOverride_BloomConvolutionPreFilterMin=False,bOverride_BloomConvolutionPreFilterMax=False,bOverride_BloomConvolutionPreFilterMult=False,bOverride_BloomConvolutionBufferScale=False,bOverride_BloomDirtMaskIntensity=False,bOverride_BloomDirtMaskTint=False,bOverride_BloomDirtMask=False,bOverride_CameraShutterSpeed=False,bOverride_CameraISO=False,bOverride_AutoExposureMethod=False,bOverride_AutoExposureLowPercent=False,bOverride_AutoExposureHighPercent=False,bOverride_AutoExposureMinBrightness=False,bOverride_AutoExposureMaxBrightness=False,bOverride_AutoExposureSpeedUp=False,bOverride_AutoExposureSpeedDown=False,bOverride_AutoExposureBias=False,bOverride_AutoExposureBiasCurve=False,bOverride_AutoExposureMeterMask=False,bOverride_AutoExposureApplyPhysicalCameraExposure=False,bOverride_HistogramLogMin=False,bOverride_HistogramLogMax=False,bOverride_LocalExposureMethod=False,bOverride_LocalExposureHighlightContrastScale=False,bOverride_LocalExposureShadowContrastScale=False,bOverride_LocalExposureHighlightContrastCurve=False,bOverride_LocalExposureShadowContrastCurve=False,bOverride_LocalExposureHighlightThreshold=False,bOverride_LocalExposureShadowThreshold=False,bOverride_LocalExposureDetailStrength=False,bOverride_LocalExposureBlurredLuminanceBlend=False,bOverride_LocalExposureBlurredLuminanceKernelSizePercent=False,bOverride_LocalExposureHighlightThresholdStrength=False,bOverride_LocalExposureShadowThresholdStrength=False,bOverride_LocalExposureMiddleGreyBias=False,bOverride_LensFlareIntensity=False,bOverride_LensFlareTint=False,bOverride_LensFlareTints=False,bOverride_LensFlareBokehSize=False,bOverride_LensFlareBokehShape=False,bOverride_LensFlareThreshold=False,bOverride_VignetteIntensity=False,bOverride_Sharpen=False,bOverride_FilmGrainIntensity=False,bOverride_FilmGrainIntensityShadows=False,bOverride_FilmGrainIntensityMidtones=False,bOverride_FilmGrainIntensityHighlights=False,bOverride_FilmGrainShadowsMax=False,bOverride_FilmGrainHighlightsMin=False,bOverride_FilmGrainHighlightsMax=False,bOverride_FilmGrainTexelSize=False,bOverride_FilmGrainTexture=False,bOverride_AmbientOcclusionIntensity=False,bOverride_AmbientOcclusionStaticFraction=False,bOverride_AmbientOcclusionRadius=False,bOverride_AmbientOcclusionFadeDistance=False,bOverride_AmbientOcclusionFadeRadius=False,bOverride_AmbientOcclusionRadiusInWS=False,bOverride_AmbientOcclusionPower=False,bOverride_AmbientOcclusionBias=False,bOverride_AmbientOcclusionQuality=False,bOverride_AmbientOcclusionMipBlend=False,bOverride_AmbientOcclusionMipScale=False,bOverride_AmbientOcclusionMipThreshold=False,bOverride_AmbientOcclusionTemporalBlendWeight=False,bOverride_RayTracingAO=False,bOverride_RayTracingAOSamplesPerPixel=False,bOverride_RayTracingAOIntensity=False,bOverride_RayTracingAORadius=False,bOverride_IndirectLightingColor=False,bOverride_IndirectLightingIntensity=False,bOverride_ColorGradingIntensity=False,bOverride_ColorGradingLUT=False,bOverride_DepthOfFieldFocalDistance=False,bOverride_DepthOfFieldFstop=False,bOverride_DepthOfFieldMinFstop=False,bOverride_DepthOfFieldBladeCount=False,bOverride_DepthOfFieldSensorWidth=False,bOverride_DepthOfFieldSqueezeFactor=False,bOverride_DepthOfFieldDepthBlurRadius=False,bOverride_DepthOfFieldUseHairDepth=False,bOverride_DepthOfFieldPetzvalBokeh=False,bOverride_DepthOfFieldPetzvalBokehFalloff=False,bOverride_DepthOfFieldPetzvalExclusionBoxExtents=False,bOverride_DepthOfFieldPetzvalExclusionBoxRadius=False,bOverride_DepthOfFieldAspectRatioScalar=False,bOverride_DepthOfFieldMatteBoxFlags=False,bOverride_DepthOfFieldBarrelRadius=False,bOverride_DepthOfFieldBarrelLength=False,bOverride_DepthOfFieldDepthBlurAmount=False,bOverride_DepthOfFieldFocalRegion=False,bOverride_DepthOfFieldNearTransitionRegion=False,bOverride_DepthOfFieldFarTransitionRegion=False,bOverride_DepthOfFieldScale=False,bOverride_DepthOfFieldNearBlurSize=False,bOverride_DepthOfFieldFarBlurSize=False,bOverride_MobileHQGaussian=False,bOverride_DepthOfFieldOcclusion=False,bOverride_DepthOfFieldSkyFocusDistance=False,bOverride_DepthOfFieldVignetteSize=False,bOverride_MotionBlurAmount=False,bOverride_MotionBlurMax=False,bOverride_MotionBlurTargetFPS=False,bOverride_MotionBlurPerObjectSize=False,bOverride_ReflectionMethod=False,bOverride_LumenReflectionQuality=False,bOverride_ScreenSpaceReflectionIntensity=False,bOverride_ScreenSpaceReflectionQuality=False,bOverride_ScreenSpaceReflectionMaxRoughness=False,bOverride_ScreenSpaceReflectionRoughnessScale=False,bOverride_UserFlags=False,bOverride_RayTracingReflectionsMaxRoughness=False,bOverride_RayTracingReflectionsMaxBounces=False,bOverride_RayTracingReflectionsSamplesPerPixel=False,bOverride_RayTracingReflectionsShadows=False,bOverride_RayTracingReflectionsTranslucency=False,bOverride_TranslucencyType=False,bOverride_RayTracingTranslucencyMaxRoughness=False,bOverride_RayTracingTranslucencyRefractionRays=False,bOverride_RayTracingTranslucencySamplesPerPixel=False,bOverride_RayTracingTranslucencyShadows=False,bOverride_RayTracingTranslucencyRefraction=False,bOverride_RayTracingTranslucencyMaxPrimaryHitEvents=False,bOverride_RayTracingTranslucencyMaxSecondaryHitEvents=False,bOverride_RayTracingTranslucencyUseRayTracedRefraction=False,bOverride_DynamicGlobalIlluminationMethod=False,bOverride_LumenSceneLightingQuality=False,bOverride_LumenSceneDetail=False,bOverride_LumenSceneViewDistance=False,bOverride_LumenSceneLightingUpdateSpeed=False,bOverride_LumenFinalGatherQuality=False,bOverride_LumenFinalGatherLightingUpdateSpeed=False,bOverride_LumenFinalGatherScreenTraces=False,bOverride_LumenMaxTraceDistance=False,bOverride_LumenDiffuseColorBoost=False,bOverride_LumenSkylightLeaking=False,bOverride_LumenSkylightLeakingTint=False,bOverride_LumenFullSkylightLeakingDistance=False,bOverride_LumenRayLightingMode=False,bOverride_LumenReflectionsScreenTraces=False,bOverride_LumenFrontLayerTranslucencyReflections=False,bOverride_LumenMaxRoughnessToTraceReflections=False,bOverride_LumenMaxReflectionBounces=False,bOverride_LumenMaxRefractionBounces=False,bOverride_LumenSurfaceCacheResolution=False,bOverride_RayTracingGI=False,bOverride_RayTracingGIMaxBounces=False,bOverride_RayTracingGISamplesPerPixel=False,bOverride_PathTracingMaxBounces=False,bOverride_PathTracingSamplesPerPixel=False,bOverride_PathTracingMaxPathIntensity=False,bOverride_PathTracingEnableEmissiveMaterials=False,bOverride_PathTracingEnableReferenceDOF=False,bOverride_PathTracingEnableReferenceAtmosphere=False,bOverride_PathTracingEnableDenoiser=False,bOverride_PathTracingIncludeEmissive=False,bOverride_PathTracingIncludeDiffuse=False,bOverride_PathTracingIncludeIndirectDiffuse=False,bOverride_PathTracingIncludeSpecular=False,bOverride_PathTracingIncludeIndirectSpecular=False,bOverride_PathTracingIncludeVolume=False,bOverride_PathTracingIncludeIndirectVolume=False,bMobileHQGaussian=False,BloomMethod=BM_SOG,AutoExposureMethod=AEM_Histogram,TemperatureType=TEMP_WhiteBalance,WhiteTemp=6500.000000,WhiteTint=0.000000,ColorSaturation=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorContrast=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorGamma=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorGain=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorOffset=(X=0.000000,Y=0.000000,Z=0.000000,W=0.000000),ColorSaturationShadows=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorContrastShadows=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorGammaShadows=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorGainShadows=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorOffsetShadows=(X=0.000000,Y=0.000000,Z=0.000000,W=0.000000),ColorSaturationMidtones=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorContrastMidtones=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorGammaMidtones=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorGainMidtones=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorOffsetMidtones=(X=0.000000,Y=0.000000,Z=0.000000,W=0.000000),ColorSaturationHighlights=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorContrastHighlights=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorGammaHighlights=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorGainHighlights=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorOffsetHighlights=(X=0.000000,Y=0.000000,Z=0.000000,W=0.000000),ColorCorrectionHighlightsMin=0.500000,ColorCorrectionHighlightsMax=1.000000,ColorCorrectionShadowsMax=0.090000,BlueCorrection=0.600000,ExpandGamut=1.000000,ToneCurveAmount=1.000000,FilmSlope=0.880000,FilmToe=0.550000,FilmShoulder=0.260000,FilmBlackClip=0.000000,FilmWhiteClip=0.040000,SceneColorTint=(R=1.000000,G=1.000000,B=1.000000,A=1.000000),SceneFringeIntensity=0.000000,ChromaticAberrationStartOffset=0.000000,BloomIntensity=0.675000,BloomGaussianIntensity=1.000000,BloomThreshold=-1.000000,BloomSizeScale=4.000000,Bloom1Size=0.300000,Bloom2Size=1.000000,Bloom3Size=2.000000,Bloom4Size=10.000000,Bloom5Size=30.000000,Bloom6Size=64.000000,Bloom1Tint=(R=0.346500,G=0.346500,B=0.346500,A=1.000000),Bloom2Tint=(R=0.138000,G=0.138000,B=0.138000,A=1.000000),Bloom3Tint=(R=0.117600,G=0.117600,B=0.117600,A=1.000000),Bloom4Tint=(R=0.066000,G=0.066000,B=0.066000,A=1.000000),Bloom5Tint=(R=0.066000,G=0.066000,B=0.066000,A=1.000000),Bloom6Tint=(R=0.061000,G=0.061000,B=0.061000,A=1.000000),BloomConvolutionIntensity=1.000000,BloomConvolutionScatterDispersion=1.000000,BloomConvolutionSize=1.000000,BloomConvolutionTexture=None,BloomConvolutionCenterUV=(X=0.500000,Y=0.500000),BloomConvolutionPreFilterMin=7.000000,BloomConvolutionPreFilterMax=15000.000000,BloomConvolutionPreFilterMult=15.000000,BloomConvolutionBufferScale=0.133000,BloomDirtMask=None,BloomDirtMaskIntensity=0.000000,BloomDirtMaskTint=(R=0.500000,G=0.500000,B=0.500000,A=1.000000),DynamicGlobalIlluminationMethod=Lumen,IndirectLightingColor=(R=1.000000,G=1.000000,B=1.000000,A=1.000000),IndirectLightingIntensity=1.000000,LumenRayLightingMode=Default,LumenSceneLightingQuality=1.000000,LumenSceneDetail=1.000000,LumenSceneViewDistance=20000.000000,LumenSceneLightingUpdateSpeed=1.000000,LumenFinalGatherQuality=1.000000,LumenFinalGatherLightingUpdateSpeed=1.000000,LumenFinalGatherScreenTraces=True,LumenMaxTraceDistance=20000.000000,LumenDiffuseColorBoost=1.000000,LumenSkylightLeaking=0.000000,LumenSkylightLeakingTint=(R=1.000000,G=1.000000,B=1.000000,A=1.000000),LumenFullSkylightLeakingDistance=1000.000000,LumenSurfaceCacheResolution=1.000000,ReflectionMethod=Lumen,LumenReflectionQuality=1.000000,LumenReflectionsScreenTraces=True,LumenFrontLayerTranslucencyReflections=False,LumenMaxRoughnessToTraceReflections=0.400000,LumenMaxReflectionBounces=1,LumenMaxRefractionBounces=0,ScreenSpaceReflectionIntensity=100.000000,ScreenSpaceReflectionQuality=50.000000,ScreenSpaceReflectionMaxRoughness=0.600000,bMegaLights=True,AmbientCubemapTint=(R=1.000000,G=1.000000,B=1.000000,A=1.000000),AmbientCubemapIntensity=1.000000,AmbientCubemap=None,CameraShutterSpeed=60.000000,CameraISO=100.000000,DepthOfFieldFstop=4.000000,DepthOfFieldMinFstop=1.200000,DepthOfFieldBladeCount=5,AutoExposureBias=1.000000,AutoExposureBiasBackup=0.000000,bOverride_AutoExposureBiasBackup=False,AutoExposureApplyPhysicalCameraExposure=True,AutoExposureBiasCurve=None,AutoExposureMeterMask=None,AutoExposureLowPercent=10.000000,AutoExposureHighPercent=90.000000,AutoExposureMinBrightness=-10.000000,AutoExposureMaxBrightness=20.000000,AutoExposureSpeedUp=3.000000,AutoExposureSpeedDown=1.000000,HistogramLogMin=-10.000000,HistogramLogMax=20.000000,LocalExposureMethod=Bilateral,LocalExposureHighlightContrastScale=1.000000,LocalExposureShadowContrastScale=1.000000,LocalExposureHighlightContrastCurve=None,LocalExposureShadowContrastCurve=None,LocalExposureHighlightThreshold=0.000000,LocalExposureShadowThreshold=0.000000,LocalExposureDetailStrength=1.000000,LocalExposureBlurredLuminanceBlend=0.600000,LocalExposureBlurredLuminanceKernelSizePercent=50.000000,LocalExposureHighlightThresholdStrength=1.000000,LocalExposureShadowThresholdStrength=1.000000,LocalExposureMiddleGreyBias=0.000000,LensFlareIntensity=1.000000,LensFlareTint=(R=1.000000,G=1.000000,B=1.000000,A=1.000000),LensFlareBokehSize=3.000000,LensFlareThreshold=8.000000,LensFlareBokehShape=None,LensFlareTints[0]=(R=1.000000,G=0.800000,B=0.400000,A=0.600000),LensFlareTints[1]=(R=1.000000,G=1.000000,B=0.600000,A=0.530000),LensFlareTints[2]=(R=0.800000,G=0.800000,B=1.000000,A=0.460000),LensFlareTints[3]=(R=0.500000,G=1.000000,B=0.400000,A=0.390000),LensFlareTints[4]=(R=0.500000,G=0.800000,B=1.000000,A=0.310000),LensFlareTints[5]=(R=0.900000,G=1.000000,B=0.800000,A=0.270000),LensFlareTints[6]=(R=1.000000,G=0.800000,B=0.400000,A=0.220000),LensFlareTints[7]=(R=0.900000,G=0.700000,B=0.700000,A=0.150000),VignetteIntensity=0.400000,Sharpen=0.000000,FilmGrainIntensity=0.000000,FilmGrainIntensityShadows=1.000000,FilmGrainIntensityMidtones=1.000000,FilmGrainIntensityHighlights=1.000000,FilmGrainShadowsMax=0.090000,FilmGrainHighlightsMin=0.500000,FilmGrainHighlightsMax=1.000000,FilmGrainTexelSize=1.000000,FilmGrainTexture=None,AmbientOcclusionIntensity=0.500000,AmbientOcclusionStaticFraction=1.000000,AmbientOcclusionRadius=200.000000,AmbientOcclusionRadiusInWS=False,AmbientOcclusionFadeDistance=8000.000000,AmbientOcclusionFadeRadius=5000.000000,AmbientOcclusionPower=2.000000,AmbientOcclusionBias=3.000000,AmbientOcclusionQuality=50.000000,AmbientOcclusionMipBlend=0.600000,AmbientOcclusionMipScale=1.700000,AmbientOcclusionMipThreshold=0.010000,AmbientOcclusionTemporalBlendWeight=0.100000,RayTracingAO=False,RayTracingAOSamplesPerPixel=1,RayTracingAOIntensity=1.000000,RayTracingAORadius=200.000000,ColorGradingIntensity=1.000000,ColorGradingLUT=None,DepthOfFieldSensorWidth=24.576000,DepthOfFieldSqueezeFactor=1.000000,DepthOfFieldFocalDistance=0.000000,DepthOfFieldDepthBlurAmount=1.000000,DepthOfFieldDepthBlurRadius=0.000000,DepthOfFieldUseHairDepth=False,DepthOfFieldPetzvalBokeh=0.000000,DepthOfFieldPetzvalBokehFalloff=1.000000,DepthOfFieldPetzvalExclusionBoxExtents=(X=0.000000,Y=0.000000),DepthOfFieldPetzvalExclusionBoxRadius=0.000000,DepthOfFieldAspectRatioScalar=1.000000,DepthOfFieldBarrelRadius=5.000000,DepthOfFieldBarrelLength=0.000000,DepthOfFieldMatteBoxFlags[0]=(Pitch=0.000000,Roll=0.000000,Length=0.000000),DepthOfFieldMatteBoxFlags[1]=(Pitch=0.000000,Roll=0.000000,Length=0.000000),DepthOfFieldMatteBoxFlags[2]=(Pitch=0.000000,Roll=0.000000,Length=0.000000),DepthOfFieldFocalRegion=0.000000,DepthOfFieldNearTransitionRegion=300.000000,DepthOfFieldFarTransitionRegion=500.000000,DepthOfFieldScale=0.000000,DepthOfFieldNearBlurSize=15.000000,DepthOfFieldFarBlurSize=15.000000,DepthOfFieldOcclusion=0.400000,DepthOfFieldSkyFocusDistance=0.000000,DepthOfFieldVignetteSize=200.000000,MotionBlurAmount=0.500000,MotionBlurMax=5.000000,MotionBlurTargetFPS=30,MotionBlurPerObjectSize=0.000000,TranslucencyType=Raster,RayTracingTranslucencyMaxRoughness=0.600000,RayTracingTranslucencyRefractionRays=3,RayTracingTranslucencySamplesPerPixel=1,RayTracingTranslucencyMaxPrimaryHitEvents=4,RayTracingTranslucencyMaxSecondaryHitEvents=2,RayTracingTranslucencyShadows=Hard_shadows,RayTracingTranslucencyRefraction=True,RayTracingTranslucencyUseRayTracedRefraction=False,PathTracingMaxBounces=32,PathTracingSamplesPerPixel=2048,PathTracingMaxPathIntensity=24.000000,PathTracingEnableEmissiveMaterials=True,PathTracingEnableReferenceDOF=False,PathTracingEnableReferenceAtmosphere=False,PathTracingEnableDenoiser=True,PathTracingIncludeEmissive=True,PathTracingIncludeDiffuse=True,PathTracingIncludeIndirectDiffuse=True,PathTracingIncludeSpecular=True,PathTracingIncludeIndirectSpecular=True,PathTracingIncludeVolume=True,PathTracingIncludeIndirectVolume=True,UserFlags=0,WeightedBlendables=(Array=)),LightingRigRotation=0.000000,RotationSpeed=2.000000,DirectionalLightRotation=(Pitch=-40.000000,Yaw=-67.500000,Roll=0.000000),bEnableToneMapping=False,bShowMeshEdges=True)
++Profiles=(ProfileName="Grey Ambient",bSharedProfile=True,bIsEngineDefaultProfile=True,bUseSkyLighting=True,DirectionalLightIntensity=4.000000,DirectionalLightColor=(R=1.000000,G=1.000000,B=1.000000,A=1.000000),SkyLightIntensity=2.000000,bRotateLightingRig=False,bShowEnvironment=True,bShowFloor=True,bShowGrid=True,EnvironmentColor=(R=0.200000,G=0.200000,B=0.200000,A=1.000000),EnvironmentIntensity=1.000000,EnvironmentCubeMapPath="/Engine/EditorMaterials/AssetViewer/T_GreyAmbient",bPostProcessingEnabled=False,PostProcessingSettings=(bOverride_TemperatureType=False,bOverride_WhiteTemp=False,bOverride_WhiteTint=False,bOverride_ColorSaturation=False,bOverride_ColorContrast=False,bOverride_ColorGamma=False,bOverride_ColorGain=False,bOverride_ColorOffset=False,bOverride_ColorSaturationShadows=False,bOverride_ColorContrastShadows=False,bOverride_ColorGammaShadows=False,bOverride_ColorGainShadows=False,bOverride_ColorOffsetShadows=False,bOverride_ColorSaturationMidtones=False,bOverride_ColorContrastMidtones=False,bOverride_ColorGammaMidtones=False,bOverride_ColorGainMidtones=False,bOverride_ColorOffsetMidtones=False,bOverride_ColorSaturationHighlights=False,bOverride_ColorContrastHighlights=False,bOverride_ColorGammaHighlights=False,bOverride_ColorGainHighlights=False,bOverride_ColorOffsetHighlights=False,bOverride_ColorCorrectionShadowsMax=False,bOverride_ColorCorrectionHighlightsMin=False,bOverride_ColorCorrectionHighlightsMax=False,bOverride_BlueCorrection=False,bOverride_ExpandGamut=False,bOverride_ToneCurveAmount=False,bOverride_FilmSlope=False,bOverride_FilmToe=False,bOverride_FilmShoulder=False,bOverride_FilmBlackClip=False,bOverride_FilmWhiteClip=False,bOverride_SceneColorTint=False,bOverride_SceneFringeIntensity=False,bOverride_ChromaticAberrationStartOffset=False,bOverride_bMegaLights=False,bOverride_AmbientCubemapTint=False,bOverride_AmbientCubemapIntensity=False,bOverride_BloomMethod=False,bOverride_BloomIntensity=False,bOverride_BloomGaussianIntensity=False,bOverride_BloomThreshold=False,bOverride_Bloom1Tint=False,bOverride_Bloom1Size=False,bOverride_Bloom2Size=False,bOverride_Bloom2Tint=False,bOverride_Bloom3Tint=False,bOverride_Bloom3Size=False,bOverride_Bloom4Tint=False,bOverride_Bloom4Size=False,bOverride_Bloom5Tint=False,bOverride_Bloom5Size=False,bOverride_Bloom6Tint=False,bOverride_Bloom6Size=False,bOverride_BloomSizeScale=False,bOverride_BloomConvolutionIntensity=False,bOverride_BloomConvolutionTexture=False,bOverride_BloomConvolutionScatterDispersion=False,bOverride_BloomConvolutionSize=False,bOverride_BloomConvolutionCenterUV=False,bOverride_BloomConvolutionPreFilterMin=False,bOverride_BloomConvolutionPreFilterMax=False,bOverride_BloomConvolutionPreFilterMult=False,bOverride_BloomConvolutionBufferScale=False,bOverride_BloomDirtMaskIntensity=False,bOverride_BloomDirtMaskTint=False,bOverride_BloomDirtMask=False,bOverride_CameraShutterSpeed=False,bOverride_CameraISO=False,bOverride_AutoExposureMethod=False,bOverride_AutoExposureLowPercent=False,bOverride_AutoExposureHighPercent=False,bOverride_AutoExposureMinBrightness=False,bOverride_AutoExposureMaxBrightness=False,bOverride_AutoExposureSpeedUp=False,bOverride_AutoExposureSpeedDown=False,bOverride_AutoExposureBias=False,bOverride_AutoExposureBiasCurve=False,bOverride_AutoExposureMeterMask=False,bOverride_AutoExposureApplyPhysicalCameraExposure=False,bOverride_HistogramLogMin=False,bOverride_HistogramLogMax=False,bOverride_LocalExposureMethod=False,bOverride_LocalExposureHighlightContrastScale=False,bOverride_LocalExposureShadowContrastScale=False,bOverride_LocalExposureHighlightContrastCurve=False,bOverride_LocalExposureShadowContrastCurve=False,bOverride_LocalExposureHighlightThreshold=False,bOverride_LocalExposureShadowThreshold=False,bOverride_LocalExposureDetailStrength=False,bOverride_LocalExposureBlurredLuminanceBlend=False,bOverride_LocalExposureBlurredLuminanceKernelSizePercent=False,bOverride_LocalExposureHighlightThresholdStrength=False,bOverride_LocalExposureShadowThresholdStrength=False,bOverride_LocalExposureMiddleGreyBias=False,bOverride_LensFlareIntensity=False,bOverride_LensFlareTint=False,bOverride_LensFlareTints=False,bOverride_LensFlareBokehSize=False,bOverride_LensFlareBokehShape=False,bOverride_LensFlareThreshold=False,bOverride_VignetteIntensity=False,bOverride_Sharpen=False,bOverride_FilmGrainIntensity=False,bOverride_FilmGrainIntensityShadows=False,bOverride_FilmGrainIntensityMidtones=False,bOverride_FilmGrainIntensityHighlights=False,bOverride_FilmGrainShadowsMax=False,bOverride_FilmGrainHighlightsMin=False,bOverride_FilmGrainHighlightsMax=False,bOverride_FilmGrainTexelSize=False,bOverride_FilmGrainTexture=False,bOverride_AmbientOcclusionIntensity=False,bOverride_AmbientOcclusionStaticFraction=False,bOverride_AmbientOcclusionRadius=False,bOverride_AmbientOcclusionFadeDistance=False,bOverride_AmbientOcclusionFadeRadius=False,bOverride_AmbientOcclusionRadiusInWS=False,bOverride_AmbientOcclusionPower=False,bOverride_AmbientOcclusionBias=False,bOverride_AmbientOcclusionQuality=False,bOverride_AmbientOcclusionMipBlend=False,bOverride_AmbientOcclusionMipScale=False,bOverride_AmbientOcclusionMipThreshold=False,bOverride_AmbientOcclusionTemporalBlendWeight=False,bOverride_RayTracingAO=False,bOverride_RayTracingAOSamplesPerPixel=False,bOverride_RayTracingAOIntensity=False,bOverride_RayTracingAORadius=False,bOverride_IndirectLightingColor=False,bOverride_IndirectLightingIntensity=False,bOverride_ColorGradingIntensity=False,bOverride_ColorGradingLUT=False,bOverride_DepthOfFieldFocalDistance=False,bOverride_DepthOfFieldFstop=False,bOverride_DepthOfFieldMinFstop=False,bOverride_DepthOfFieldBladeCount=False,bOverride_DepthOfFieldSensorWidth=False,bOverride_DepthOfFieldSqueezeFactor=False,bOverride_DepthOfFieldDepthBlurRadius=False,bOverride_DepthOfFieldUseHairDepth=False,bOverride_DepthOfFieldPetzvalBokeh=False,bOverride_DepthOfFieldPetzvalBokehFalloff=False,bOverride_DepthOfFieldPetzvalExclusionBoxExtents=False,bOverride_DepthOfFieldPetzvalExclusionBoxRadius=False,bOverride_DepthOfFieldAspectRatioScalar=False,bOverride_DepthOfFieldMatteBoxFlags=False,bOverride_DepthOfFieldBarrelRadius=False,bOverride_DepthOfFieldBarrelLength=False,bOverride_DepthOfFieldDepthBlurAmount=False,bOverride_DepthOfFieldFocalRegion=False,bOverride_DepthOfFieldNearTransitionRegion=False,bOverride_DepthOfFieldFarTransitionRegion=False,bOverride_DepthOfFieldScale=False,bOverride_DepthOfFieldNearBlurSize=False,bOverride_DepthOfFieldFarBlurSize=False,bOverride_MobileHQGaussian=False,bOverride_DepthOfFieldOcclusion=False,bOverride_DepthOfFieldSkyFocusDistance=False,bOverride_DepthOfFieldVignetteSize=False,bOverride_MotionBlurAmount=False,bOverride_MotionBlurMax=False,bOverride_MotionBlurTargetFPS=False,bOverride_MotionBlurPerObjectSize=False,bOverride_ReflectionMethod=False,bOverride_LumenReflectionQuality=False,bOverride_ScreenSpaceReflectionIntensity=False,bOverride_ScreenSpaceReflectionQuality=False,bOverride_ScreenSpaceReflectionMaxRoughness=False,bOverride_ScreenSpaceReflectionRoughnessScale=False,bOverride_UserFlags=False,bOverride_RayTracingReflectionsMaxRoughness=False,bOverride_RayTracingReflectionsMaxBounces=False,bOverride_RayTracingReflectionsSamplesPerPixel=False,bOverride_RayTracingReflectionsShadows=False,bOverride_RayTracingReflectionsTranslucency=False,bOverride_TranslucencyType=False,bOverride_RayTracingTranslucencyMaxRoughness=False,bOverride_RayTracingTranslucencyRefractionRays=False,bOverride_RayTracingTranslucencySamplesPerPixel=False,bOverride_RayTracingTranslucencyShadows=False,bOverride_RayTracingTranslucencyRefraction=False,bOverride_RayTracingTranslucencyMaxPrimaryHitEvents=False,bOverride_RayTracingTranslucencyMaxSecondaryHitEvents=False,bOverride_RayTracingTranslucencyUseRayTracedRefraction=False,bOverride_DynamicGlobalIlluminationMethod=False,bOverride_LumenSceneLightingQuality=False,bOverride_LumenSceneDetail=False,bOverride_LumenSceneViewDistance=False,bOverride_LumenSceneLightingUpdateSpeed=False,bOverride_LumenFinalGatherQuality=False,bOverride_LumenFinalGatherLightingUpdateSpeed=False,bOverride_LumenFinalGatherScreenTraces=False,bOverride_LumenMaxTraceDistance=False,bOverride_LumenDiffuseColorBoost=False,bOverride_LumenSkylightLeaking=False,bOverride_LumenSkylightLeakingTint=False,bOverride_LumenFullSkylightLeakingDistance=False,bOverride_LumenRayLightingMode=False,bOverride_LumenReflectionsScreenTraces=False,bOverride_LumenFrontLayerTranslucencyReflections=False,bOverride_LumenMaxRoughnessToTraceReflections=False,bOverride_LumenMaxReflectionBounces=False,bOverride_LumenMaxRefractionBounces=False,bOverride_LumenSurfaceCacheResolution=False,bOverride_RayTracingGI=False,bOverride_RayTracingGIMaxBounces=False,bOverride_RayTracingGISamplesPerPixel=False,bOverride_PathTracingMaxBounces=False,bOverride_PathTracingSamplesPerPixel=False,bOverride_PathTracingMaxPathIntensity=False,bOverride_PathTracingEnableEmissiveMaterials=False,bOverride_PathTracingEnableReferenceDOF=False,bOverride_PathTracingEnableReferenceAtmosphere=False,bOverride_PathTracingEnableDenoiser=False,bOverride_PathTracingIncludeEmissive=False,bOverride_PathTracingIncludeDiffuse=False,bOverride_PathTracingIncludeIndirectDiffuse=False,bOverride_PathTracingIncludeSpecular=False,bOverride_PathTracingIncludeIndirectSpecular=False,bOverride_PathTracingIncludeVolume=False,bOverride_PathTracingIncludeIndirectVolume=False,bMobileHQGaussian=False,BloomMethod=BM_SOG,AutoExposureMethod=AEM_Histogram,TemperatureType=TEMP_WhiteBalance,WhiteTemp=6500.000000,WhiteTint=0.000000,ColorSaturation=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorContrast=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorGamma=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorGain=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorOffset=(X=0.000000,Y=0.000000,Z=0.000000,W=0.000000),ColorSaturationShadows=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorContrastShadows=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorGammaShadows=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorGainShadows=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorOffsetShadows=(X=0.000000,Y=0.000000,Z=0.000000,W=0.000000),ColorSaturationMidtones=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorContrastMidtones=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorGammaMidtones=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorGainMidtones=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorOffsetMidtones=(X=0.000000,Y=0.000000,Z=0.000000,W=0.000000),ColorSaturationHighlights=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorContrastHighlights=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorGammaHighlights=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorGainHighlights=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorOffsetHighlights=(X=0.000000,Y=0.000000,Z=0.000000,W=0.000000),ColorCorrectionHighlightsMin=0.500000,ColorCorrectionHighlightsMax=1.000000,ColorCorrectionShadowsMax=0.090000,BlueCorrection=0.600000,ExpandGamut=1.000000,ToneCurveAmount=1.000000,FilmSlope=0.880000,FilmToe=0.550000,FilmShoulder=0.260000,FilmBlackClip=0.000000,FilmWhiteClip=0.040000,SceneColorTint=(R=1.000000,G=1.000000,B=1.000000,A=1.000000),SceneFringeIntensity=0.000000,ChromaticAberrationStartOffset=0.000000,BloomIntensity=0.675000,BloomGaussianIntensity=1.000000,BloomThreshold=-1.000000,BloomSizeScale=4.000000,Bloom1Size=0.300000,Bloom2Size=1.000000,Bloom3Size=2.000000,Bloom4Size=10.000000,Bloom5Size=30.000000,Bloom6Size=64.000000,Bloom1Tint=(R=0.346500,G=0.346500,B=0.346500,A=1.000000),Bloom2Tint=(R=0.138000,G=0.138000,B=0.138000,A=1.000000),Bloom3Tint=(R=0.117600,G=0.117600,B=0.117600,A=1.000000),Bloom4Tint=(R=0.066000,G=0.066000,B=0.066000,A=1.000000),Bloom5Tint=(R=0.066000,G=0.066000,B=0.066000,A=1.000000),Bloom6Tint=(R=0.061000,G=0.061000,B=0.061000,A=1.000000),BloomConvolutionIntensity=1.000000,BloomConvolutionScatterDispersion=1.000000,BloomConvolutionSize=1.000000,BloomConvolutionTexture=None,BloomConvolutionCenterUV=(X=0.500000,Y=0.500000),BloomConvolutionPreFilterMin=7.000000,BloomConvolutionPreFilterMax=15000.000000,BloomConvolutionPreFilterMult=15.000000,BloomConvolutionBufferScale=0.133000,BloomDirtMask=None,BloomDirtMaskIntensity=0.000000,BloomDirtMaskTint=(R=0.500000,G=0.500000,B=0.500000,A=1.000000),DynamicGlobalIlluminationMethod=Lumen,IndirectLightingColor=(R=1.000000,G=1.000000,B=1.000000,A=1.000000),IndirectLightingIntensity=1.000000,LumenRayLightingMode=Default,LumenSceneLightingQuality=1.000000,LumenSceneDetail=1.000000,LumenSceneViewDistance=20000.000000,LumenSceneLightingUpdateSpeed=1.000000,LumenFinalGatherQuality=1.000000,LumenFinalGatherLightingUpdateSpeed=1.000000,LumenFinalGatherScreenTraces=True,LumenMaxTraceDistance=20000.000000,LumenDiffuseColorBoost=1.000000,LumenSkylightLeaking=0.000000,LumenSkylightLeakingTint=(R=1.000000,G=1.000000,B=1.000000,A=1.000000),LumenFullSkylightLeakingDistance=1000.000000,LumenSurfaceCacheResolution=1.000000,ReflectionMethod=Lumen,LumenReflectionQuality=1.000000,LumenReflectionsScreenTraces=True,LumenFrontLayerTranslucencyReflections=False,LumenMaxRoughnessToTraceReflections=0.400000,LumenMaxReflectionBounces=1,LumenMaxRefractionBounces=0,ScreenSpaceReflectionIntensity=100.000000,ScreenSpaceReflectionQuality=50.000000,ScreenSpaceReflectionMaxRoughness=0.600000,bMegaLights=True,AmbientCubemapTint=(R=1.000000,G=1.000000,B=1.000000,A=1.000000),AmbientCubemapIntensity=1.000000,AmbientCubemap=None,CameraShutterSpeed=60.000000,CameraISO=100.000000,DepthOfFieldFstop=4.000000,DepthOfFieldMinFstop=1.200000,DepthOfFieldBladeCount=5,AutoExposureBias=1.000000,AutoExposureBiasBackup=0.000000,bOverride_AutoExposureBiasBackup=False,AutoExposureApplyPhysicalCameraExposure=True,AutoExposureBiasCurve=None,AutoExposureMeterMask=None,AutoExposureLowPercent=10.000000,AutoExposureHighPercent=90.000000,AutoExposureMinBrightness=-10.000000,AutoExposureMaxBrightness=20.000000,AutoExposureSpeedUp=3.000000,AutoExposureSpeedDown=1.000000,HistogramLogMin=-10.000000,HistogramLogMax=20.000000,LocalExposureMethod=Bilateral,LocalExposureHighlightContrastScale=1.000000,LocalExposureShadowContrastScale=1.000000,LocalExposureHighlightContrastCurve=None,LocalExposureShadowContrastCurve=None,LocalExposureHighlightThreshold=0.000000,LocalExposureShadowThreshold=0.000000,LocalExposureDetailStrength=1.000000,LocalExposureBlurredLuminanceBlend=0.600000,LocalExposureBlurredLuminanceKernelSizePercent=50.000000,LocalExposureHighlightThresholdStrength=1.000000,LocalExposureShadowThresholdStrength=1.000000,LocalExposureMiddleGreyBias=0.000000,LensFlareIntensity=1.000000,LensFlareTint=(R=1.000000,G=1.000000,B=1.000000,A=1.000000),LensFlareBokehSize=3.000000,LensFlareThreshold=8.000000,LensFlareBokehShape=None,LensFlareTints[0]=(R=1.000000,G=0.800000,B=0.400000,A=0.600000),LensFlareTints[1]=(R=1.000000,G=1.000000,B=0.600000,A=0.530000),LensFlareTints[2]=(R=0.800000,G=0.800000,B=1.000000,A=0.460000),LensFlareTints[3]=(R=0.500000,G=1.000000,B=0.400000,A=0.390000),LensFlareTints[4]=(R=0.500000,G=0.800000,B=1.000000,A=0.310000),LensFlareTints[5]=(R=0.900000,G=1.000000,B=0.800000,A=0.270000),LensFlareTints[6]=(R=1.000000,G=0.800000,B=0.400000,A=0.220000),LensFlareTints[7]=(R=0.900000,G=0.700000,B=0.700000,A=0.150000),VignetteIntensity=0.400000,Sharpen=0.000000,FilmGrainIntensity=0.000000,FilmGrainIntensityShadows=1.000000,FilmGrainIntensityMidtones=1.000000,FilmGrainIntensityHighlights=1.000000,FilmGrainShadowsMax=0.090000,FilmGrainHighlightsMin=0.500000,FilmGrainHighlightsMax=1.000000,FilmGrainTexelSize=1.000000,FilmGrainTexture=None,AmbientOcclusionIntensity=0.500000,AmbientOcclusionStaticFraction=1.000000,AmbientOcclusionRadius=200.000000,AmbientOcclusionRadiusInWS=False,AmbientOcclusionFadeDistance=8000.000000,AmbientOcclusionFadeRadius=5000.000000,AmbientOcclusionPower=2.000000,AmbientOcclusionBias=3.000000,AmbientOcclusionQuality=50.000000,AmbientOcclusionMipBlend=0.600000,AmbientOcclusionMipScale=1.700000,AmbientOcclusionMipThreshold=0.010000,AmbientOcclusionTemporalBlendWeight=0.100000,RayTracingAO=False,RayTracingAOSamplesPerPixel=1,RayTracingAOIntensity=1.000000,RayTracingAORadius=200.000000,ColorGradingIntensity=1.000000,ColorGradingLUT=None,DepthOfFieldSensorWidth=24.576000,DepthOfFieldSqueezeFactor=1.000000,DepthOfFieldFocalDistance=0.000000,DepthOfFieldDepthBlurAmount=1.000000,DepthOfFieldDepthBlurRadius=0.000000,DepthOfFieldUseHairDepth=False,DepthOfFieldPetzvalBokeh=0.000000,DepthOfFieldPetzvalBokehFalloff=1.000000,DepthOfFieldPetzvalExclusionBoxExtents=(X=0.000000,Y=0.000000),DepthOfFieldPetzvalExclusionBoxRadius=0.000000,DepthOfFieldAspectRatioScalar=1.000000,DepthOfFieldBarrelRadius=5.000000,DepthOfFieldBarrelLength=0.000000,DepthOfFieldMatteBoxFlags[0]=(Pitch=0.000000,Roll=0.000000,Length=0.000000),DepthOfFieldMatteBoxFlags[1]=(Pitch=0.000000,Roll=0.000000,Length=0.000000),DepthOfFieldMatteBoxFlags[2]=(Pitch=0.000000,Roll=0.000000,Length=0.000000),DepthOfFieldFocalRegion=0.000000,DepthOfFieldNearTransitionRegion=300.000000,DepthOfFieldFarTransitionRegion=500.000000,DepthOfFieldScale=0.000000,DepthOfFieldNearBlurSize=15.000000,DepthOfFieldFarBlurSize=15.000000,DepthOfFieldOcclusion=0.400000,DepthOfFieldSkyFocusDistance=0.000000,DepthOfFieldVignetteSize=200.000000,MotionBlurAmount=0.500000,MotionBlurMax=5.000000,MotionBlurTargetFPS=30,MotionBlurPerObjectSize=0.000000,TranslucencyType=Raster,RayTracingTranslucencyMaxRoughness=0.600000,RayTracingTranslucencyRefractionRays=3,RayTracingTranslucencySamplesPerPixel=1,RayTracingTranslucencyMaxPrimaryHitEvents=4,RayTracingTranslucencyMaxSecondaryHitEvents=2,RayTracingTranslucencyShadows=Hard_shadows,RayTracingTranslucencyRefraction=True,RayTracingTranslucencyUseRayTracedRefraction=False,PathTracingMaxBounces=32,PathTracingSamplesPerPixel=2048,PathTracingMaxPathIntensity=24.000000,PathTracingEnableEmissiveMaterials=True,PathTracingEnableReferenceDOF=False,PathTracingEnableReferenceAtmosphere=False,PathTracingEnableDenoiser=True,PathTracingIncludeEmissive=True,PathTracingIncludeDiffuse=True,PathTracingIncludeIndirectDiffuse=True,PathTracingIncludeSpecular=True,PathTracingIncludeIndirectSpecular=True,PathTracingIncludeVolume=True,PathTracingIncludeIndirectVolume=True,UserFlags=0,WeightedBlendables=(Array=)),LightingRigRotation=0.000000,RotationSpeed=2.000000,DirectionalLightRotation=(Pitch=-40.000000,Yaw=-67.500000,Roll=0.000000),bEnableToneMapping=False,bShowMeshEdges=False)
+
diff --git a/Config/DefaultEngine.ini b/Config/DefaultEngine.ini
new file mode 100644
index 0000000..59e0d17
--- /dev/null
+++ b/Config/DefaultEngine.ini
@@ -0,0 +1,174 @@
+[/Script/EngineSettings.GameMapsSettings]
+GlobalDefaultGameMode=/Game/XRFramework/Blueprints/BP_XRGameMode.BP_XRGameMode_C
+EditorStartupMap=/Game/VRTemplate/VRTemplateMap.VRTemplateMap
+GameDefaultMap=/Game/VRTemplate/VRTemplateMap.VRTemplateMap
+
+[/Script/Engine.RendererSettings]
+r.ForwardShading=True
+r.MobileHDR=False
+vr.MobileMultiView=True
+vr.InstancedStereo=True
+r.DefaultFeature.AutoExposure=False
+r.DefaultFeature.AmbientOcclusion=False
+r.DefaultFeature.AmbientOcclusionStaticFraction=False
+r.DefaultFeature.MotionBlur=False
+r.Mobile.UseHWsRGBEncoding=True
+
+r.Shadow.Virtual.Enable=1
+
+r.DefaultFeature.AutoExposure.ExtendDefaultLuminanceRange=True
+r.DefaultFeature.AutoExposure.ExtendDefaultLuminanceRange=True
+r.DefaultFeature.AutoExposure.ExtendDefaultLuminanceRange=True
+
+r.DefaultFeature.LocalExposure.HighlightContrastScale=0.8
+r.DefaultFeature.LocalExposure.HighlightContrastScale=0.8
+r.DefaultFeature.LocalExposure.HighlightContrastScale=0.8
+
+r.DefaultFeature.LocalExposure.ShadowContrastScale=0.8
+r.DefaultFeature.LocalExposure.ShadowContrastScale=0.8
+r.DefaultFeature.LocalExposure.ShadowContrastScale=0.8
+
+r.GenerateMeshDistanceFields=True
+
+r.DynamicGlobalIlluminationMethod=1
+
+r.ReflectionMethod=1
+r.Mobile.AntiAliasing=3
+r.AntiAliasingMethod=3
+r.AllowStaticLighting=True
+r.Mobile.PropagateAlpha=True
+r.Mobile.ShadingPath=0
+
+r.SkinCache.CompileShaders=True
+
+r.RayTracing=True
+
+r.RayTracing.RayTracingProxies.ProjectEnabled=True
+
+r.Substrate=True
+
+r.Substrate.ProjectGBufferFormat=0
+
+[/Script/AndroidRuntimeSettings.AndroidRuntimeSettings]
+bBuildForES31=False
+ExtraApplicationSettings=
+bPackageForMetaQuest=True
+MinSDKVersion=32
+TargetSDKVersion=32
+
+[/Script/WindowsTargetPlatform.WindowsTargetSettings]
+DefaultGraphicsRHI=DefaultGraphicsRHI_DX12
+DefaultGraphicsRHI=DefaultGraphicsRHI_Default
+-D3D12TargetedShaderFormats=PCD3D_SM5
++D3D12TargetedShaderFormats=PCD3D_SM6
+-D3D11TargetedShaderFormats=PCD3D_SM5
++D3D11TargetedShaderFormats=PCD3D_SM5
+Compiler=Default
+AudioSampleRate=48000
+AudioCallbackBufferFrameSize=1024
+AudioNumBuffersToEnqueue=1
+AudioMaxChannels=0
+AudioNumSourceWorkers=4
+SpatializationPlugin=
+SourceDataOverridePlugin=
+ReverbPlugin=
+OcclusionPlugin=
+CompressionOverrides=(bOverrideCompressionTimes=False,DurationThreshold=5.000000,MaxNumRandomBranches=0,SoundCueQualityIndex=0)
+CacheSizeKB=65536
+MaxChunkSizeOverrideKB=0
+bResampleForDevice=False
+MaxSampleRate=48000.000000
+HighSampleRate=32000.000000
+MedSampleRate=24000.000000
+LowSampleRate=12000.000000
+MinSampleRate=8000.000000
+CompressionQualityModifier=1.000000
+AutoStreamingThreshold=0.000000
+SoundCueCookQualityIndex=-1
+
+[/Script/NavigationSystem.NavigationSystemV1]
+bAllowClientSideNavigation=True
+
+[/Script/LinuxTargetPlatform.LinuxTargetSettings]
+-TargetedRHIs=SF_VULKAN_SM5
++TargetedRHIs=SF_VULKAN_SM6
+
+[/Script/Engine.CollisionProfile]
+-Profiles=(Name="NoCollision",CollisionEnabled=NoCollision,ObjectTypeName="WorldStatic",CustomResponses=((Channel="Visibility",Response=ECR_Ignore),(Channel="Camera",Response=ECR_Ignore)),HelpMessage="No collision",bCanModify=False)
+-Profiles=(Name="BlockAll",CollisionEnabled=QueryAndPhysics,ObjectTypeName="WorldStatic",CustomResponses=,HelpMessage="WorldStatic object that blocks all actors by default. All new custom channels will use its own default response. ",bCanModify=False)
+-Profiles=(Name="OverlapAll",CollisionEnabled=QueryOnly,ObjectTypeName="WorldStatic",CustomResponses=((Channel="WorldStatic",Response=ECR_Overlap),(Channel="Pawn",Response=ECR_Overlap),(Channel="Visibility",Response=ECR_Overlap),(Channel="WorldDynamic",Response=ECR_Overlap),(Channel="Camera",Response=ECR_Overlap),(Channel="PhysicsBody",Response=ECR_Overlap),(Channel="Vehicle",Response=ECR_Overlap),(Channel="Destructible",Response=ECR_Overlap)),HelpMessage="WorldStatic object that overlaps all actors by default. All new custom channels will use its own default response. ",bCanModify=False)
+-Profiles=(Name="BlockAllDynamic",CollisionEnabled=QueryAndPhysics,ObjectTypeName="WorldDynamic",CustomResponses=,HelpMessage="WorldDynamic object that blocks all actors by default. All new custom channels will use its own default response. ",bCanModify=False)
+-Profiles=(Name="OverlapAllDynamic",CollisionEnabled=QueryOnly,ObjectTypeName="WorldDynamic",CustomResponses=((Channel="WorldStatic",Response=ECR_Overlap),(Channel="Pawn",Response=ECR_Overlap),(Channel="Visibility",Response=ECR_Overlap),(Channel="WorldDynamic",Response=ECR_Overlap),(Channel="Camera",Response=ECR_Overlap),(Channel="PhysicsBody",Response=ECR_Overlap),(Channel="Vehicle",Response=ECR_Overlap),(Channel="Destructible",Response=ECR_Overlap)),HelpMessage="WorldDynamic object that overlaps all actors by default. All new custom channels will use its own default response. ",bCanModify=False)
+-Profiles=(Name="IgnoreOnlyPawn",CollisionEnabled=QueryOnly,ObjectTypeName="WorldDynamic",CustomResponses=((Channel="Pawn",Response=ECR_Ignore),(Channel="Vehicle",Response=ECR_Ignore)),HelpMessage="WorldDynamic object that ignores Pawn and Vehicle. All other channels will be set to default.",bCanModify=False)
+-Profiles=(Name="OverlapOnlyPawn",CollisionEnabled=QueryOnly,ObjectTypeName="WorldDynamic",CustomResponses=((Channel="Pawn",Response=ECR_Overlap),(Channel="Vehicle",Response=ECR_Overlap),(Channel="Camera",Response=ECR_Ignore)),HelpMessage="WorldDynamic object that overlaps Pawn, Camera, and Vehicle. All other channels will be set to default. ",bCanModify=False)
+-Profiles=(Name="Pawn",CollisionEnabled=QueryAndPhysics,ObjectTypeName="Pawn",CustomResponses=((Channel="Visibility",Response=ECR_Ignore)),HelpMessage="Pawn object. Can be used for capsule of any playerable character or AI. ",bCanModify=False)
+-Profiles=(Name="Spectator",CollisionEnabled=QueryOnly,ObjectTypeName="Pawn",CustomResponses=((Channel="WorldStatic",Response=ECR_Block),(Channel="Pawn",Response=ECR_Ignore),(Channel="Visibility",Response=ECR_Ignore),(Channel="WorldDynamic",Response=ECR_Ignore),(Channel="Camera",Response=ECR_Ignore),(Channel="PhysicsBody",Response=ECR_Ignore),(Channel="Vehicle",Response=ECR_Ignore),(Channel="Destructible",Response=ECR_Ignore)),HelpMessage="Pawn object that ignores all other actors except WorldStatic.",bCanModify=False)
+-Profiles=(Name="CharacterMesh",CollisionEnabled=QueryOnly,ObjectTypeName="Pawn",CustomResponses=((Channel="Pawn",Response=ECR_Ignore),(Channel="Vehicle",Response=ECR_Ignore),(Channel="Visibility",Response=ECR_Ignore)),HelpMessage="Pawn object that is used for Character Mesh. All other channels will be set to default.",bCanModify=False)
+-Profiles=(Name="PhysicsActor",CollisionEnabled=QueryAndPhysics,ObjectTypeName="PhysicsBody",CustomResponses=,HelpMessage="Simulating actors",bCanModify=False)
+-Profiles=(Name="Destructible",CollisionEnabled=QueryAndPhysics,ObjectTypeName="Destructible",CustomResponses=,HelpMessage="Destructible actors",bCanModify=False)
+-Profiles=(Name="InvisibleWall",CollisionEnabled=QueryAndPhysics,ObjectTypeName="WorldStatic",CustomResponses=((Channel="Visibility",Response=ECR_Ignore)),HelpMessage="WorldStatic object that is invisible.",bCanModify=False)
+-Profiles=(Name="InvisibleWallDynamic",CollisionEnabled=QueryAndPhysics,ObjectTypeName="WorldDynamic",CustomResponses=((Channel="Visibility",Response=ECR_Ignore)),HelpMessage="WorldDynamic object that is invisible.",bCanModify=False)
+-Profiles=(Name="Trigger",CollisionEnabled=QueryOnly,ObjectTypeName="WorldDynamic",CustomResponses=((Channel="WorldStatic",Response=ECR_Overlap),(Channel="Pawn",Response=ECR_Overlap),(Channel="Visibility",Response=ECR_Ignore),(Channel="WorldDynamic",Response=ECR_Overlap),(Channel="Camera",Response=ECR_Overlap),(Channel="PhysicsBody",Response=ECR_Overlap),(Channel="Vehicle",Response=ECR_Overlap),(Channel="Destructible",Response=ECR_Overlap)),HelpMessage="WorldDynamic object that is used for trigger. All other channels will be set to default.",bCanModify=False)
+-Profiles=(Name="Ragdoll",CollisionEnabled=QueryAndPhysics,ObjectTypeName="PhysicsBody",CustomResponses=((Channel="Pawn",Response=ECR_Ignore),(Channel="Visibility",Response=ECR_Ignore)),HelpMessage="Simulating Skeletal Mesh Component. All other channels will be set to default.",bCanModify=False)
+-Profiles=(Name="Vehicle",CollisionEnabled=QueryAndPhysics,ObjectTypeName="Vehicle",CustomResponses=,HelpMessage="Vehicle object that blocks Vehicle, WorldStatic, and WorldDynamic. All other channels will be set to default.",bCanModify=False)
+-Profiles=(Name="UI",CollisionEnabled=QueryOnly,ObjectTypeName="WorldDynamic",CustomResponses=((Channel="WorldStatic",Response=ECR_Overlap),(Channel="Pawn",Response=ECR_Overlap),(Channel="Visibility",Response=ECR_Block),(Channel="WorldDynamic",Response=ECR_Overlap),(Channel="Camera",Response=ECR_Overlap),(Channel="PhysicsBody",Response=ECR_Overlap),(Channel="Vehicle",Response=ECR_Overlap),(Channel="Destructible",Response=ECR_Overlap)),HelpMessage="WorldStatic object that overlaps all actors by default. All new custom channels will use its own default response. ",bCanModify=False)
++Profiles=(Name="NoCollision",CollisionEnabled=NoCollision,bCanModify=False,ObjectTypeName="WorldStatic",CustomResponses=((Channel="Visibility",Response=ECR_Ignore),(Channel="Camera",Response=ECR_Ignore)),HelpMessage="No collision")
++Profiles=(Name="BlockAll",CollisionEnabled=QueryAndPhysics,bCanModify=False,ObjectTypeName="WorldStatic",CustomResponses=,HelpMessage="WorldStatic object that blocks all actors by default. All new custom channels will use its own default response. ")
++Profiles=(Name="OverlapAll",CollisionEnabled=QueryOnly,bCanModify=False,ObjectTypeName="WorldStatic",CustomResponses=((Channel="WorldStatic",Response=ECR_Overlap),(Channel="Pawn",Response=ECR_Overlap),(Channel="Visibility",Response=ECR_Overlap),(Channel="WorldDynamic",Response=ECR_Overlap),(Channel="Camera",Response=ECR_Overlap),(Channel="PhysicsBody",Response=ECR_Overlap),(Channel="Vehicle",Response=ECR_Overlap),(Channel="Destructible",Response=ECR_Overlap)),HelpMessage="WorldStatic object that overlaps all actors by default. All new custom channels will use its own default response. ")
++Profiles=(Name="BlockAllDynamic",CollisionEnabled=QueryAndPhysics,bCanModify=False,ObjectTypeName="WorldDynamic",CustomResponses=,HelpMessage="WorldDynamic object that blocks all actors by default. All new custom channels will use its own default response. ")
++Profiles=(Name="OverlapAllDynamic",CollisionEnabled=QueryOnly,bCanModify=False,ObjectTypeName="WorldDynamic",CustomResponses=((Channel="WorldStatic",Response=ECR_Overlap),(Channel="Pawn",Response=ECR_Overlap),(Channel="Visibility",Response=ECR_Overlap),(Channel="WorldDynamic",Response=ECR_Overlap),(Channel="Camera",Response=ECR_Overlap),(Channel="PhysicsBody",Response=ECR_Overlap),(Channel="Vehicle",Response=ECR_Overlap),(Channel="Destructible",Response=ECR_Overlap)),HelpMessage="WorldDynamic object that overlaps all actors by default. All new custom channels will use its own default response. ")
++Profiles=(Name="IgnoreOnlyPawn",CollisionEnabled=QueryOnly,bCanModify=False,ObjectTypeName="WorldDynamic",CustomResponses=((Channel="Pawn",Response=ECR_Ignore),(Channel="Vehicle",Response=ECR_Ignore)),HelpMessage="WorldDynamic object that ignores Pawn and Vehicle. All other channels will be set to default.")
++Profiles=(Name="OverlapOnlyPawn",CollisionEnabled=QueryOnly,bCanModify=False,ObjectTypeName="WorldDynamic",CustomResponses=((Channel="Pawn",Response=ECR_Overlap),(Channel="Vehicle",Response=ECR_Overlap),(Channel="Camera",Response=ECR_Ignore)),HelpMessage="WorldDynamic object that overlaps Pawn, Camera, and Vehicle. All other channels will be set to default. ")
++Profiles=(Name="Pawn",CollisionEnabled=QueryAndPhysics,bCanModify=False,ObjectTypeName="Pawn",CustomResponses=((Channel="Visibility",Response=ECR_Ignore)),HelpMessage="Pawn object. Can be used for capsule of any playerable character or AI. ")
++Profiles=(Name="Spectator",CollisionEnabled=QueryOnly,bCanModify=False,ObjectTypeName="Pawn",CustomResponses=((Channel="WorldStatic"),(Channel="Pawn",Response=ECR_Ignore),(Channel="Visibility",Response=ECR_Ignore),(Channel="WorldDynamic",Response=ECR_Ignore),(Channel="Camera",Response=ECR_Ignore),(Channel="PhysicsBody",Response=ECR_Ignore),(Channel="Vehicle",Response=ECR_Ignore),(Channel="Destructible",Response=ECR_Ignore)),HelpMessage="Pawn object that ignores all other actors except WorldStatic.")
++Profiles=(Name="CharacterMesh",CollisionEnabled=QueryOnly,bCanModify=False,ObjectTypeName="Pawn",CustomResponses=((Channel="Pawn",Response=ECR_Ignore),(Channel="Vehicle",Response=ECR_Ignore),(Channel="Visibility",Response=ECR_Ignore)),HelpMessage="Pawn object that is used for Character Mesh. All other channels will be set to default.")
++Profiles=(Name="PhysicsActor",CollisionEnabled=QueryAndPhysics,bCanModify=False,ObjectTypeName="PhysicsBody",CustomResponses=,HelpMessage="Simulating actors")
++Profiles=(Name="Destructible",CollisionEnabled=QueryAndPhysics,bCanModify=False,ObjectTypeName="Destructible",CustomResponses=,HelpMessage="Destructible actors")
++Profiles=(Name="InvisibleWall",CollisionEnabled=QueryAndPhysics,bCanModify=False,ObjectTypeName="WorldStatic",CustomResponses=((Channel="Visibility",Response=ECR_Ignore)),HelpMessage="WorldStatic object that is invisible.")
++Profiles=(Name="InvisibleWallDynamic",CollisionEnabled=QueryAndPhysics,bCanModify=False,ObjectTypeName="WorldDynamic",CustomResponses=((Channel="Visibility",Response=ECR_Ignore)),HelpMessage="WorldDynamic object that is invisible.")
++Profiles=(Name="Trigger",CollisionEnabled=QueryOnly,bCanModify=False,ObjectTypeName="WorldDynamic",CustomResponses=((Channel="WorldStatic",Response=ECR_Overlap),(Channel="Pawn",Response=ECR_Overlap),(Channel="Visibility",Response=ECR_Ignore),(Channel="WorldDynamic",Response=ECR_Overlap),(Channel="Camera",Response=ECR_Overlap),(Channel="PhysicsBody",Response=ECR_Overlap),(Channel="Vehicle",Response=ECR_Overlap),(Channel="Destructible",Response=ECR_Overlap)),HelpMessage="WorldDynamic object that is used for trigger. All other channels will be set to default.")
++Profiles=(Name="Ragdoll",CollisionEnabled=QueryAndPhysics,bCanModify=False,ObjectTypeName="PhysicsBody",CustomResponses=((Channel="Pawn",Response=ECR_Ignore),(Channel="Visibility",Response=ECR_Ignore)),HelpMessage="Simulating Skeletal Mesh Component. All other channels will be set to default.")
++Profiles=(Name="Vehicle",CollisionEnabled=QueryAndPhysics,bCanModify=False,ObjectTypeName="Vehicle",CustomResponses=,HelpMessage="Vehicle object that blocks Vehicle, WorldStatic, and WorldDynamic. All other channels will be set to default.")
++Profiles=(Name="UI",CollisionEnabled=QueryOnly,bCanModify=False,ObjectTypeName="WorldDynamic",CustomResponses=((Channel="WorldStatic",Response=ECR_Overlap),(Channel="Pawn",Response=ECR_Overlap),(Channel="Visibility"),(Channel="WorldDynamic",Response=ECR_Overlap),(Channel="Camera",Response=ECR_Overlap),(Channel="PhysicsBody",Response=ECR_Overlap),(Channel="Vehicle",Response=ECR_Overlap),(Channel="Destructible",Response=ECR_Overlap)),HelpMessage="WorldStatic object that overlaps all actors by default. All new custom channels will use its own default response. ")
++DefaultChannelResponses=(Channel=ECC_GameTraceChannel1,DefaultResponse=ECR_Ignore,bTraceType=True,bStaticObject=False,Name="3DWidget")
+-ProfileRedirects=(OldName="BlockingVolume",NewName="InvisibleWall")
+-ProfileRedirects=(OldName="InterpActor",NewName="IgnoreOnlyPawn")
+-ProfileRedirects=(OldName="StaticMeshComponent",NewName="BlockAllDynamic")
+-ProfileRedirects=(OldName="SkeletalMeshActor",NewName="PhysicsActor")
+-ProfileRedirects=(OldName="InvisibleActor",NewName="InvisibleWallDynamic")
++ProfileRedirects=(OldName="BlockingVolume",NewName="InvisibleWall")
++ProfileRedirects=(OldName="InterpActor",NewName="IgnoreOnlyPawn")
++ProfileRedirects=(OldName="StaticMeshComponent",NewName="BlockAllDynamic")
++ProfileRedirects=(OldName="SkeletalMeshActor",NewName="PhysicsActor")
++ProfileRedirects=(OldName="InvisibleActor",NewName="InvisibleWallDynamic")
+-CollisionChannelRedirects=(OldName="Static",NewName="WorldStatic")
+-CollisionChannelRedirects=(OldName="Dynamic",NewName="WorldDynamic")
+-CollisionChannelRedirects=(OldName="VehicleMovement",NewName="Vehicle")
+-CollisionChannelRedirects=(OldName="PawnMovement",NewName="Pawn")
++CollisionChannelRedirects=(OldName="Static",NewName="WorldStatic")
++CollisionChannelRedirects=(OldName="Dynamic",NewName="WorldDynamic")
++CollisionChannelRedirects=(OldName="VehicleMovement",NewName="Vehicle")
++CollisionChannelRedirects=(OldName="PawnMovement",NewName="Pawn")
+
+[/Script/OpenXRHMD.OpenXRHMDSettings]
+xr.OpenXRInvertAlpha=True
+
+
+[/Script/Engine.Engine]
++ActiveGameNameRedirects=(OldGameName="TP_VirtualRealityBP",NewGameName="/Script/OXRHandIntxn")
++ActiveGameNameRedirects=(OldGameName="/Script/TP_VirtualRealityBP",NewGameName="/Script/OXRHandIntxn")
+
+[/Script/AndroidFileServerEditor.AndroidFileServerRuntimeSettings]
+bEnablePlugin=True
+bAllowNetworkConnection=True
+SecurityToken=332AFB814629DBFC77F7FAA59AA3C639
+bIncludeInShipping=False
+bAllowExternalStartInShipping=False
+bCompileAFSProject=False
+bUseCompression=False
+bLogFiles=False
+bReportStats=False
+ConnectionType=USBOnly
+bUseManualIPAddress=False
+ManualIPAddress=
+
diff --git a/Config/DefaultGame.ini b/Config/DefaultGame.ini
new file mode 100644
index 0000000..dea8592
--- /dev/null
+++ b/Config/DefaultGame.ini
@@ -0,0 +1,4 @@
+[/Script/EngineSettings.GeneralProjectSettings]
+ProjectID=4B38CC344E7EB6CB23B64C9DDE9F31B2
+bStartInVR=True
+
diff --git a/Config/DefaultInput.ini b/Config/DefaultInput.ini
new file mode 100644
index 0000000..b902e93
--- /dev/null
+++ b/Config/DefaultInput.ini
@@ -0,0 +1,128 @@
+[/Script/Engine.InputSettings]
+-AxisConfig=(AxisKeyName="Gamepad_LeftX",AxisProperties=(DeadZone=0.25,Exponent=1.f,Sensitivity=1.f))
+-AxisConfig=(AxisKeyName="Gamepad_LeftY",AxisProperties=(DeadZone=0.25,Exponent=1.f,Sensitivity=1.f))
+-AxisConfig=(AxisKeyName="Gamepad_RightX",AxisProperties=(DeadZone=0.25,Exponent=1.f,Sensitivity=1.f))
+-AxisConfig=(AxisKeyName="Gamepad_RightY",AxisProperties=(DeadZone=0.25,Exponent=1.f,Sensitivity=1.f))
+-AxisConfig=(AxisKeyName="MouseX",AxisProperties=(DeadZone=0.f,Exponent=1.f,Sensitivity=0.07f))
+-AxisConfig=(AxisKeyName="MouseY",AxisProperties=(DeadZone=0.f,Exponent=1.f,Sensitivity=0.07f))
+-AxisConfig=(AxisKeyName="Mouse2D",AxisProperties=(DeadZone=0.f,Exponent=1.f,Sensitivity=0.07f))
++AxisConfig=(AxisKeyName="Gamepad_LeftX",AxisProperties=(DeadZone=0.250000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="Gamepad_LeftY",AxisProperties=(DeadZone=0.250000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="Gamepad_RightX",AxisProperties=(DeadZone=0.250000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="Gamepad_RightY",AxisProperties=(DeadZone=0.250000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="MouseX",AxisProperties=(DeadZone=0.000000,Sensitivity=0.070000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="MouseY",AxisProperties=(DeadZone=0.000000,Sensitivity=0.070000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="Mouse2D",AxisProperties=(DeadZone=0.000000,Sensitivity=0.070000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="MouseWheelAxis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="Gamepad_LeftTriggerAxis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="Gamepad_RightTriggerAxis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="Gamepad_Special_Left_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="Gamepad_Special_Left_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="Vive_Left_Trigger_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="Vive_Left_Trackpad_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="Vive_Left_Trackpad_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="Vive_Right_Trigger_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="Vive_Right_Trackpad_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="Vive_Right_Trackpad_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="MixedReality_Left_Trigger_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="MixedReality_Left_Thumbstick_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="MixedReality_Left_Thumbstick_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="MixedReality_Left_Trackpad_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="MixedReality_Left_Trackpad_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="MixedReality_Right_Trigger_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="MixedReality_Right_Thumbstick_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="MixedReality_Right_Thumbstick_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="MixedReality_Right_Trackpad_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="MixedReality_Right_Trackpad_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="OculusTouch_Left_Grip_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="OculusTouch_Left_Trigger_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="OculusTouch_Left_Thumbstick_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="OculusTouch_Left_Thumbstick_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="OculusTouch_Right_Grip_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="OculusTouch_Right_Trigger_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="OculusTouch_Right_Thumbstick_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="OculusTouch_Right_Thumbstick_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="ValveIndex_Left_Grip_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="ValveIndex_Left_Grip_Force",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="ValveIndex_Left_Trigger_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="ValveIndex_Left_Thumbstick_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="ValveIndex_Left_Thumbstick_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="ValveIndex_Left_Trackpad_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="ValveIndex_Left_Trackpad_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="ValveIndex_Left_Trackpad_Force",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="ValveIndex_Left_Trackpad_Touch",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="ValveIndex_Right_Grip_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="ValveIndex_Right_Grip_Force",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="ValveIndex_Right_Trigger_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="ValveIndex_Right_Thumbstick_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="ValveIndex_Right_Thumbstick_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="ValveIndex_Right_Trackpad_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="ValveIndex_Right_Trackpad_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="ValveIndex_Right_Trackpad_Force",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="OculusTouch_Left_Thumbstick",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="OculusTouch_Left_FaceButton1",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="OculusTouch_Left_Trigger",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="OculusTouch_Left_FaceButton2",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="OculusTouch_Left_IndexPointing",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="OculusTouch_Left_ThumbUp",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="OculusTouch_Right_Thumbstick",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="OculusTouch_Right_FaceButton1",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="OculusTouch_Right_Trigger",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="OculusTouch_Right_FaceButton2",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="OculusTouch_Right_IndexPointing",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="OculusTouch_Right_ThumbUp",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="OculusHand_Left_ThumbPinchStrength",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="OculusHand_Left_IndexPinchStrength",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="OculusHand_Left_MiddlePinchStrength",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="OculusHand_Left_RingPinchStrength",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="OculusHand_Left_PinkPinchStrength",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="OculusHand_Right_ThumbPinchStrength",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="OculusHand_Right_IndexPinchStrength",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="OculusHand_Right_MiddlePinchStrength",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="OculusHand_Right_RingPinchStrength",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="OculusHand_Right_PinkPinchStrength",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="OculusTouch_Left_ThumbRest",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
++AxisConfig=(AxisKeyName="OculusTouch_Right_ThumbRest",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
+bAltEnterTogglesFullscreen=True
+bF11TogglesFullscreen=True
+bUseMouseForTouch=False
+bEnableMouseSmoothing=True
+bEnableFOVScaling=True
+bCaptureMouseOnLaunch=True
+bEnableLegacyInputScales=True
+bEnableMotionControls=True
+bFilterInputByPlatformUser=False
+bShouldFlushPressedKeysOnViewportFocusLost=True
+bAlwaysShowTouchInterface=False
+bShowConsoleOnFourFingerTap=True
+bEnableGestureRecognizer=False
+bUseAutocorrect=False
+DefaultViewportMouseCaptureMode=CapturePermanently_IncludingInitialMouseDown
+DefaultViewportMouseLockMode=LockOnCapture
+FOVScale=0.011110
+DoubleClickTime=0.200000
+DefaultPlayerInputClass=/Script/EnhancedInput.EnhancedPlayerInput
+DefaultInputComponentClass=/Script/EnhancedInput.EnhancedInputComponent
+DefaultTouchInterface=None
+-ConsoleKeys=Tilde
++ConsoleKeys=Tilde
+
+[/Script/EnhancedInput.EnhancedInputDeveloperSettings]
++DefaultMappingContexts=(InputMappingContext="/Game/XRFramework/Input/IMC_Default.IMC_Default",Priority=0,bAddImmediately=True,bRegisterWithUserSettings=False)
++DefaultMappingContexts=(InputMappingContext="/Game/XRFramework/Input/IMC_Hands.IMC_Hands",Priority=0,bAddImmediately=True,bRegisterWithUserSettings=False)
++DefaultMappingContexts=(InputMappingContext="/Game/XRFramework/Input/IMC_Weapon_Left.IMC_Weapon_Left",Priority=0,bAddImmediately=True,bRegisterWithUserSettings=False)
++DefaultMappingContexts=(InputMappingContext="/Game/XRFramework/Input/IMC_Weapon_Right.IMC_Weapon_Right",Priority=0,bAddImmediately=True,bRegisterWithUserSettings=False)
++DefaultMappingContexts=(InputMappingContext="/Game/XRFramework/Input/IMC_Menu.IMC_Menu",Priority=0,bAddImmediately=True,bRegisterWithUserSettings=False)
+UserSettingsClass=/Script/EnhancedInput.EnhancedInputUserSettings
+DefaultPlayerMappableKeyProfileClass=/Script/EnhancedInput.EnhancedPlayerMappableKeyProfile
+InputSettingsSaveSlotName=EnhancedInputUserSettings
+DefaultWorldInputClass=/Script/EnhancedInput.EnhancedPlayerInput
+bSendTriggeredEventsWhenInputIsFlushed=True
+bEnableUserSettings=False
+EnhancedInput.EnableDefaultMappingContexts=True
+EnhancedInput.OnlyTriggerLastActionInChord=True
+bEnableInputModeFiltering=True
+bEnableWorldSubsystem=False
+DefaultMappingContextInputModeQuery=(TokenStreamVersion=0,TagDictionary=((TagName="EnhancedInput.Modes.Default")),QueryTokenStream=(0,1,2,1,0),UserDescription="",AutoDescription="")
+DefaultInputMode=(GameplayTags=((TagName="EnhancedInput.Modes.Default")))
+
diff --git a/Config/DefaultSenseGloveSettings.ini b/Config/DefaultSenseGloveSettings.ini
new file mode 100644
index 0000000..77788a6
--- /dev/null
+++ b/Config/DefaultSenseGloveSettings.ini
@@ -0,0 +1,6 @@
+
+
+[/Script/SenseGloveSettings.SGSettings]
+TrackingSettings=(bFallbackToHandTrackingIfNoGloveDetected=True,GloveTrackingSettings=(DataRetrievalRefreshRate=60.000000),HandTrackingSettings=(bUseMoreSpecificMotionSourceNames=False,bSupportLegacyControllerMotionSources=True),HMDTrackingSettings=(ViveHMDDetectionPriority=FocusVision_XRElite_Focus3),WristTrackingSettings=(OpenXRPositionalTrackingProvider=OpenXR,TrackingHardware=Quest3Controller,TrackingHardwareLocationOffsetLeftHand=(X=0.000000,Y=0.000000,Z=0.000000),TrackingHardwareLocationOffsetRightHand=(X=0.000000,Y=0.000000,Z=0.000000),TrackingHardwareRotationOffsetLeftHand=(Pitch=0.000000,Yaw=0.000000,Roll=0.000000),TrackingHardwareRotationOffsetRightHand=(Pitch=0.000000,Yaw=0.000000,Roll=0.000000),LeftHandMotionSource="Left",RightHandMotionSource="Right",DebuggingSettings=(bDrawDebugWristTracker=False,DebugWristTrackerSettings=(Length=4.000000,XAxisColor=(B=0,G=0,R=255,A=255),YAxisColor=(B=0,G=255,R=0,A=255),ZAxisColor=(B=255,G=0,R=0,A=255),bPersistentLines=False,LifeTimeModifier=1.100000,DepthPriority=0,Thickness=0.400000))))
+VirtualHandSettings=(bVisibleWhenHandDataUnavailable=False,AnimationSettings=(AnimationBoneRotationCorrectionOffset=(Pitch=0.000000,Yaw=90.000000,Roll=90.000000),bShouldAnimationApplyBoneLocation=True),DebuggingSettings=(bDrawDebugVirtualHand=False,DrawingMode=None,DebugCubicHandSettings=(Extent=(X=0.500000,Y=0.500000,Z=0.500000),Color=(B=0,G=0,R=255,A=255),bPersistentLines=False,LifeTimeModifier=1.100000,DepthPriority=0,Thickness=0.200000),DebugGizmoHandSettings=(Length=1.000000,XAxisColor=(B=0,G=0,R=255,A=255),YAxisColor=(B=0,G=255,R=0,A=255),ZAxisColor=(B=255,G=0,R=0,A=255),bPersistentLines=False,LifeTimeModifier=1.100000,DepthPriority=0,Thickness=0.250000)),GrabSettings=(GrabAttachPointSocketName="GrabAttachPoint",GrabAttachPointSocketTransform=(Rotation=(X=0.000000,Y=0.000000,Z=0.000000,W=1.000000),Translation=(X=0.000000,Y=5.000000,Z=-6.615570),Scale3D=(X=1.000000,Y=1.000000,Z=1.000000)),DefaultColliderSize=(X=0.040000,Y=0.040000,Z=0.040000),ThumbColliderSocketName="GrabThumbCollider",IndexColliderSocketName="GrabIndexCollider",MiddleColliderSocketName="GrabMiddleCollider"),HapticsSettings=(bAutoStopAllHapticsOnEndPlay=True),MeshSettings=(LeftHandReferenceMesh="/Game/OpenXRMannequins/Meshes/SKM_QuinnXR_left.SKM_QuinnXR_left",RightHandReferenceMesh="/Game/OpenXRMannequins/Meshes/SKM_QuinnXR_right.SKM_QuinnXR_right",DistalPhalangesLengthSettings=(Thumb=2.400000,Index=2.400000,Middle=2.400000,Ring=2.400000,Little=2.400000),RootBoneRotationCorrection=(Pitch=0.000000,Yaw=-90.000000,Roll=0.000000),LeftHandDefaultReferenceBoneTransforms=(("hand_l", (Rotation=(X=-0.000000,Y=0.707107,Z=-0.707107,W=0.000000))),("palm_l", (Translation=(X=0.000000,Y=0.000000,Z=-6.615570))),("thumb_01_l", (Rotation=(X=-0.067039,Y=-0.369130,Z=0.505193,W=0.777193),Translation=(X=2.581536,Y=1.356605,Z=-1.992447))),("thumb_02_l", (Rotation=(X=0.201856,Y=0.010302,Z=0.033565,W=0.978786),Translation=(X=0.000000,Y=0.000000,Z=-4.378000))),("thumb_03_l", (Rotation=(X=0.087156,Y=0.000000,Z=0.000000,W=0.996195),Translation=(X=0.000000,Y=0.000000,Z=-3.086000))),("index_metacarpal_l", (Rotation=(X=-0.007109,Y=-0.063705,Z=0.028966,W=0.997523),Translation=(X=2.379324,Y=-0.384681,Z=-3.444506))),("index_01_l", (Rotation=(X=0.202557,Y=0.000000,Z=0.000000,W=0.979271),Translation=(X=-0.240876,Y=0.043182,Z=-5.877098))),("index_02_l", (Rotation=(X=0.129597,Y=0.000000,Z=0.000000,W=0.991567),Translation=(X=0.000000,Y=0.000000,Z=-4.080000))),("index_03_l", (Rotation=(X=0.109009,Y=0.000000,Z=0.000000,W=0.994041),Translation=(X=0.000000,Y=0.000000,Z=-2.595000))),("middle_metacarpal_l", (Rotation=(X=-0.020259,Y=0.000386,Z=-0.037291,W=0.999099),Translation=(X=0.182864,Y=-0.753571,Z=-3.375834))),("middle_01_l", (Rotation=(X=0.272051,Y=0.000000,Z=0.000000,W=0.962283),Translation=(X=0.000000,Y=0.000000,Z=-6.098209))),("middle_02_l", (Rotation=(X=0.180255,Y=0.000000,Z=0.000000,W=0.983620),Translation=(X=0.000000,Y=0.000000,Z=-5.169000))),("middle_03_l", (Rotation=(X=0.087156,Y=0.000000,Z=0.000000,W=0.996195),Translation=(X=0.000000,Y=0.000000,Z=-2.474000))),("ring_metacarpal_l", (Rotation=(X=-0.025660,Y=0.100569,Z=-0.116599,W=0.987741),Translation=(X=-1.091757,Y=-0.542517,Z=-3.374304))),("ring_01_l", (Rotation=(X=0.253540,Y=-0.013177,Z=0.054216,W=0.965715),Translation=(X=0.020689,Y=-0.041625,Z=-5.645517))),("ring_02_l", (Rotation=(X=0.164738,Y=0.000000,Z=0.000000,W=0.986337),Translation=(X=0.000000,Y=0.000000,Z=-4.977000))),("ring_03_l", (Rotation=(X=0.079921,Y=0.000000,Z=0.000000,W=0.996801),Translation=(X=0.000000,Y=0.000000,Z=-2.265000))),("pinky_metacarpal_l", (Rotation=(X=0.058286,Y=0.188166,Z=-0.218231,W=0.955809),Translation=(X=-2.391111,Y=-0.305917,Z=-3.314378))),("pinky_01_l", (Rotation=(X=0.128066,Y=-0.017016,Z=0.089984,W=0.987528),Translation=(X=0.198840,Y=-0.143120,Z=-4.957568))),("pinky_02_l", (Rotation=(X=0.184698,Y=0.000000,Z=0.000000,W=0.982795),Translation=(X=0.000000,Y=0.000000,Z=-3.816000))),("pinky_03_l", (Rotation=(X=0.042896,Y=0.000000,Z=0.000000,W=0.999080),Translation=(X=0.000000,Y=0.000000,Z=-2.040000)))),RightHandDefaultReferenceBoneTransforms=(("hand_r", (Rotation=(X=-0.000000,Y=0.707107,Z=-0.707107,W=0.000000))),("palm_r", (Translation=(X=0.000052,Y=-0.000629,Z=-6.615405))),("thumb_01_r", (Rotation=(X=-0.067039,Y=0.369130,Z=-0.505193,W=0.777193),Translation=(X=-2.581326,Y=1.356657,Z=-1.992833))),("thumb_02_r", (Rotation=(X=0.201856,Y=-0.010302,Z=-0.033565,W=0.978786),Translation=(X=0.000136,Y=-0.000427,Z=-4.377825))),("thumb_03_r", (Rotation=(X=0.087156,Y=0.000000,Z=0.000000,W=0.996195),Translation=(X=-0.000046,Y=0.000015,Z=-3.085952))),("index_metacarpal_r", (Rotation=(X=-0.007109,Y=0.063705,Z=-0.028966,W=0.997523),Translation=(X=-2.379309,Y=-0.385167,Z=-3.444514))),("index_01_r", (Rotation=(X=0.202557,Y=0.000000,Z=0.000000,W=0.979271),Translation=(X=0.240952,Y=0.043412,Z=-5.877205))),("index_02_r", (Rotation=(X=0.129597,Y=0.000000,Z=0.000000,W=0.991567),Translation=(X=-0.000032,Y=-0.000005,Z=-4.079933))),("index_03_r", (Rotation=(X=0.109009,Y=0.000000,Z=0.000000,W=0.994041),Translation=(X=0.000004,Y=-0.000040,Z=-2.595076))),("middle_metacarpal_r", (Rotation=(X=-0.020259,Y=-0.000386,Z=0.037291,W=0.999099),Translation=(X=-0.182806,Y=-0.754004,Z=-3.375800))),("middle_01_r", (Rotation=(X=0.272051,Y=0.000000,Z=0.000000,W=0.962283),Translation=(X=-0.000001,Y=0.000124,Z=-6.098366))),("middle_02_r", (Rotation=(X=0.180255,Y=0.000000,Z=0.000000,W=0.983620),Translation=(X=0.000037,Y=0.000077,Z=-5.168992))),("middle_03_r", (Rotation=(X=0.087156,Y=0.000000,Z=0.000000,W=0.996195),Translation=(X=-0.000034,Y=-0.000046,Z=-2.474047))),("ring_metacarpal_r", (Rotation=(X=-0.025660,Y=-0.100569,Z=0.116599,W=0.987741),Translation=(X=1.091782,Y=-0.542992,Z=-3.374208))),("ring_01_r", (Rotation=(X=0.253540,Y=0.013177,Z=-0.054216,W=0.965715),Translation=(X=-0.020675,Y=-0.041443,Z=-5.645712))),("ring_02_r", (Rotation=(X=0.164738,Y=0.000000,Z=0.000000,W=0.986337),Translation=(X=0.000024,Y=0.000021,Z=-4.977067))),("ring_03_r", (Rotation=(X=0.079921,Y=0.000000,Z=0.000000,W=0.996801),Translation=(X=0.000011,Y=-0.000050,Z=-2.264975))),("pinky_metacarpal_r", (Rotation=(X=0.058286,Y=-0.188166,Z=0.218231,W=0.955809),Translation=(X=2.391287,Y=-0.305935,Z=-3.314677))),("pinky_01_r", (Rotation=(X=0.128066,Y=0.017016,Z=-0.089984,W=0.987528),Translation=(X=-0.198920,Y=-0.143261,Z=-4.957301))),("pinky_02_r", (Rotation=(X=0.184698,Y=0.000000,Z=0.000000,W=0.982795),Translation=(X=-0.000033,Y=-0.000030,Z=-3.816036))),("pinky_03_r", (Rotation=(X=0.042896,Y=-0.000000,Z=-0.000000,W=0.999080),Translation=(X=0.000031,Y=0.000018,Z=-2.040000)))),LeftHandBoneNames=("palm_l","hand_l","thumb_01_l","thumb_02_l","thumb_03_l","","index_metacarpal_l","index_01_l","index_02_l","index_03_l","","middle_metacarpal_l","middle_01_l","middle_02_l","middle_03_l","","ring_metacarpal_l","ring_01_l","ring_02_l","ring_03_l","","pinky_metacarpal_l","pinky_01_l","pinky_02_l","pinky_03_l",""),RightHandBoneNames=("palm_r","hand_r","thumb_01_r","thumb_02_r","thumb_03_r","","index_metacarpal_r","index_01_r","index_02_r","index_03_r","","middle_metacarpal_r","middle_01_r","middle_02_r","middle_03_r","","ring_metacarpal_r","ring_01_r","ring_02_r","ring_03_r","","pinky_metacarpal_r","pinky_01_r","pinky_02_r","pinky_03_r",""),DefaultLeftHandMeshPath="SkeletalMesh\'/SenseGlove/Meshes/SK_SenseGlove_VirtualHand_Left.SK_SenseGlove_VirtualHand_Left\'",DefaultLeftHandMeshPathOnly="/SenseGlove/Meshes/SK_SenseGlove_VirtualHand_Left.SK_SenseGlove_VirtualHand_Left",DefaultRightHandMeshPath="SkeletalMesh\'/SenseGlove/Meshes/SK_SenseGlove_VirtualHand_Right.SK_SenseGlove_VirtualHand_Right\'",DefaultRightHandMeshPathOnly="/SenseGlove/Meshes/SK_SenseGlove_VirtualHand_Right.SK_SenseGlove_VirtualHand_Right"),TouchSettings=(DefaultColliderSize=(X=0.040000,Y=0.040000,Z=0.040000),ThumbColliderSocketName="TouchThumbCollider",IndexColliderSocketName="TouchIndexCollider",MiddleColliderSocketName="TouchMiddleCollider",RingColliderSocketName="TouchRingCollider",PinkyColliderSocketName="TouchPinkyCollider"))
+
diff --git a/Config/Mac/MacEngine.ini b/Config/Mac/MacEngine.ini
new file mode 100644
index 0000000..f1b23bb
--- /dev/null
+++ b/Config/Mac/MacEngine.ini
@@ -0,0 +1,4 @@
+
+[/Script/Engine.RendererSettings]
+vr.InstancedStereo=False
+vr.MobileMultiView=False
diff --git a/Config/VisionOS/VisionOSDeviceProfiles.ini b/Config/VisionOS/VisionOSDeviceProfiles.ini
new file mode 100644
index 0000000..d80311e
--- /dev/null
+++ b/Config/VisionOS/VisionOSDeviceProfiles.ini
@@ -0,0 +1,6 @@
+[VisionPro DeviceProfile]
+;MSAA
+;+CVars=r.Mobile.XRMSAAMode=2 ;Note: this is in BaseDeviceProfiles.ini, so you don't actually need to set it.
+
+;NOT MSAA
++CVars=r.Mobile.XRMSAAMode=0
diff --git a/Config/VisionOS/VisionOSEngine.ini b/Config/VisionOS/VisionOSEngine.ini
new file mode 100644
index 0000000..ed86c82
--- /dev/null
+++ b/Config/VisionOS/VisionOSEngine.ini
@@ -0,0 +1,30 @@
+[/Script/Engine.RendererSettings]
+;These settings are currently required for all visionOS projects.
+vr.InstancedStereo=False
+vr.MobileMultiView=False
+xr.OpenXRAcquireMode=1
+
+;MSAA ;Note these are the default settings, specified in DefaultEngine.ini and BaseDeviceProfiles.ini
+;r.Mobile.XRMSAAMode=2
+;r.Mobile.AntiAliasing=3
+;r.AntiAliasingMethod=3
+;MSAA END
+
+;FXAA
+;r.Mobile.XRMSAAMode=0 ; This is overridden in BaseDeviceProfile.ini for VisionPro. It can be changed in VisionOSDeviceProfiles.ini for this project.
+r.Mobile.AntiAliasing=1
+r.AntiAliasingMethod=1
+;FXAA END
+
+;MIXED IMMERSION Enable these settings for .mixed immesion mode, aka passthrough, AR.
+;Comment them all out and full immesion will be used.
+;Note: You would also probably need to delete the skybox from the level, etc, so that you are not covering up all of the real world.
+r.Mobile.PropagateAlpha=1
+r.PostProcessing.PropagateAlpha=1
+r.AlphaInvertPass=1
+
+;Note: this could also be done in Editor->ProjectSettings->Platforms->visionOS->Immersive Space->ImmsersiveStyle to 1.
+[/Script/VisionOSRuntimeSettings.VisionOSRuntimeSettings]
+ImmersiveStyle=1
+;MIXED IMMERSION END
+
diff --git a/Content/LevelPrototyping/Interactable/Door/BP_DoorFrame.uasset b/Content/LevelPrototyping/Interactable/Door/BP_DoorFrame.uasset
new file mode 100644
index 0000000..038f9e8
--- /dev/null
+++ b/Content/LevelPrototyping/Interactable/Door/BP_DoorFrame.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:4386d7d71b8d5241fd8150aa47900711eb0d8bd069c00de25fce07f011d698fa
+size 448616
diff --git a/Content/LevelPrototyping/Interactable/Door/Meshes/SM_Door.uasset b/Content/LevelPrototyping/Interactable/Door/Meshes/SM_Door.uasset
new file mode 100644
index 0000000..244ff77
--- /dev/null
+++ b/Content/LevelPrototyping/Interactable/Door/Meshes/SM_Door.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:fa98c4fc5083393e085d7b44d0b67a7e830922e2601a439167916616bbc29993
+size 59267
diff --git a/Content/LevelPrototyping/Interactable/Door/Meshes/SM_DoorFrame_Corner.uasset b/Content/LevelPrototyping/Interactable/Door/Meshes/SM_DoorFrame_Corner.uasset
new file mode 100644
index 0000000..68fec6c
--- /dev/null
+++ b/Content/LevelPrototyping/Interactable/Door/Meshes/SM_DoorFrame_Corner.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:383dea9555e76303d60d0011fae6651a4be07c24033c0aceeecd137c954ee3fc
+size 86885
diff --git a/Content/LevelPrototyping/Interactable/Door/Meshes/SM_DoorFrame_Edge.uasset b/Content/LevelPrototyping/Interactable/Door/Meshes/SM_DoorFrame_Edge.uasset
new file mode 100644
index 0000000..e15c698
--- /dev/null
+++ b/Content/LevelPrototyping/Interactable/Door/Meshes/SM_DoorFrame_Edge.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:26eb76a2ce36be334bf0770561475a9e06be928e9ca75b4a31f5df3debb36592
+size 69088
diff --git a/Content/LevelPrototyping/Interactable/JumpPad/Assets/Materials/MI_GlowNT.uasset b/Content/LevelPrototyping/Interactable/JumpPad/Assets/Materials/MI_GlowNT.uasset
new file mode 100644
index 0000000..413555f
--- /dev/null
+++ b/Content/LevelPrototyping/Interactable/JumpPad/Assets/Materials/MI_GlowNT.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:6fd40bc38bfea9418fa54fe365732a1219e615169542a0a4d2e1d518318c864b
+size 4145
diff --git a/Content/LevelPrototyping/Interactable/JumpPad/Assets/Materials/M_GradientGlow.uasset b/Content/LevelPrototyping/Interactable/JumpPad/Assets/Materials/M_GradientGlow.uasset
new file mode 100644
index 0000000..ecf223d
--- /dev/null
+++ b/Content/LevelPrototyping/Interactable/JumpPad/Assets/Materials/M_GradientGlow.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:c1b29ad29c2eac3dba6dd6a90aed9de8c6147a13b6288a42bd73fe14b78970a8
+size 17587
diff --git a/Content/LevelPrototyping/Interactable/JumpPad/Assets/Materials/M_SimpleGlow.uasset b/Content/LevelPrototyping/Interactable/JumpPad/Assets/Materials/M_SimpleGlow.uasset
new file mode 100644
index 0000000..203e88d
--- /dev/null
+++ b/Content/LevelPrototyping/Interactable/JumpPad/Assets/Materials/M_SimpleGlow.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:e6098242541b34be6aba19ea1fbfa35107f891e8f55d30d4061aa11b065b8bd8
+size 15213
diff --git a/Content/LevelPrototyping/Interactable/JumpPad/Assets/Meshes/SM_CircularBand.uasset b/Content/LevelPrototyping/Interactable/JumpPad/Assets/Meshes/SM_CircularBand.uasset
new file mode 100644
index 0000000..76776ce
--- /dev/null
+++ b/Content/LevelPrototyping/Interactable/JumpPad/Assets/Meshes/SM_CircularBand.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:97fbe3da2d907279dcbfbbf285e1b111ceb8d5f4a1c9d6bd48fcb8defc86842b
+size 70608
diff --git a/Content/LevelPrototyping/Interactable/JumpPad/Assets/Meshes/SM_CircularGlow.uasset b/Content/LevelPrototyping/Interactable/JumpPad/Assets/Meshes/SM_CircularGlow.uasset
new file mode 100644
index 0000000..6cb18b2
--- /dev/null
+++ b/Content/LevelPrototyping/Interactable/JumpPad/Assets/Meshes/SM_CircularGlow.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:6ddb27bf7020f4980d3f8435cb0928fb48b32c1ab3ea24bac7e253435081bf6b
+size 71705
diff --git a/Content/LevelPrototyping/Interactable/JumpPad/Assets/NS_JumpPad.uasset b/Content/LevelPrototyping/Interactable/JumpPad/Assets/NS_JumpPad.uasset
new file mode 100644
index 0000000..7510742
--- /dev/null
+++ b/Content/LevelPrototyping/Interactable/JumpPad/Assets/NS_JumpPad.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:2e76849cbb139160e5e73c226fe6aaa66a63fbf75cbd1213f871cca65e409817
+size 795908
diff --git a/Content/LevelPrototyping/Interactable/JumpPad/BP_JumpPad.uasset b/Content/LevelPrototyping/Interactable/JumpPad/BP_JumpPad.uasset
new file mode 100644
index 0000000..ff56bc2
--- /dev/null
+++ b/Content/LevelPrototyping/Interactable/JumpPad/BP_JumpPad.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:42ba8262f07355cdf36e33977f3bb415810390cf210b75573d766bd819cf8401
+size 92487
diff --git a/Content/LevelPrototyping/Interactable/Target/Assets/SM_TargetBaseMesh.uasset b/Content/LevelPrototyping/Interactable/Target/Assets/SM_TargetBaseMesh.uasset
new file mode 100644
index 0000000..a8140cd
--- /dev/null
+++ b/Content/LevelPrototyping/Interactable/Target/Assets/SM_TargetBaseMesh.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:378ace355b8f5e71a12e459d9f8cf4958626884142fec34be8630db6d8412cad
+size 283425
diff --git a/Content/LevelPrototyping/Interactable/Target/BP_WobbleTarget.uasset b/Content/LevelPrototyping/Interactable/Target/BP_WobbleTarget.uasset
new file mode 100644
index 0000000..8f7dc3b
--- /dev/null
+++ b/Content/LevelPrototyping/Interactable/Target/BP_WobbleTarget.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:8bd8784bf03cb945d63deb2c4c22261a9a9ea2af1f84766805ca92ec7b521ab3
+size 45551
diff --git a/Content/LevelPrototyping/Materials/MF_ProcGrid.uasset b/Content/LevelPrototyping/Materials/MF_ProcGrid.uasset
new file mode 100644
index 0000000..d998cb3
--- /dev/null
+++ b/Content/LevelPrototyping/Materials/MF_ProcGrid.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:e5ee13abdb241d9811f6c1bcc0a607d229fa7adc5f46bfd5a9d5fedbfa279683
+size 49820
diff --git a/Content/LevelPrototyping/Materials/MI_DefaultColorway.uasset b/Content/LevelPrototyping/Materials/MI_DefaultColorway.uasset
new file mode 100644
index 0000000..cc18613
--- /dev/null
+++ b/Content/LevelPrototyping/Materials/MI_DefaultColorway.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:786a4dfcaaa6f4daf459c77bda247c2c138140680a8246d521974d0cc4fc8a8e
+size 9260
diff --git a/Content/LevelPrototyping/Materials/MI_PrototypeGrid_Gray.uasset b/Content/LevelPrototyping/Materials/MI_PrototypeGrid_Gray.uasset
new file mode 100644
index 0000000..d844901
--- /dev/null
+++ b/Content/LevelPrototyping/Materials/MI_PrototypeGrid_Gray.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:084681bf680dad66b8c8ca803d927053cf46756f2f2e62d18fcc439e07de7263
+size 13121
diff --git a/Content/LevelPrototyping/Materials/MI_PrototypeGrid_Gray_02.uasset b/Content/LevelPrototyping/Materials/MI_PrototypeGrid_Gray_02.uasset
new file mode 100644
index 0000000..d727d35
--- /dev/null
+++ b/Content/LevelPrototyping/Materials/MI_PrototypeGrid_Gray_02.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:fa0f2a06a7d937127d7d737c13bacb3f8fe88c8e0479f5e6b7cda81fd225d077
+size 11922
diff --git a/Content/LevelPrototyping/Materials/MI_PrototypeGrid_Gray_Round.uasset b/Content/LevelPrototyping/Materials/MI_PrototypeGrid_Gray_Round.uasset
new file mode 100644
index 0000000..4778f66
--- /dev/null
+++ b/Content/LevelPrototyping/Materials/MI_PrototypeGrid_Gray_Round.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:901ec868e81e7a73c9f3ad4814e1205af27abc772949e5a30550a00f31ce4437
+size 11413
diff --git a/Content/LevelPrototyping/Materials/MI_PrototypeGrid_TopDark.uasset b/Content/LevelPrototyping/Materials/MI_PrototypeGrid_TopDark.uasset
new file mode 100644
index 0000000..1eae7f9
--- /dev/null
+++ b/Content/LevelPrototyping/Materials/MI_PrototypeGrid_TopDark.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:838034e5afc7ab3a70bfff6de2e1237aa02337fb423e95c1b536b3e30c61c982
+size 11957
diff --git a/Content/LevelPrototyping/Materials/M_FlatCol.uasset b/Content/LevelPrototyping/Materials/M_FlatCol.uasset
new file mode 100644
index 0000000..0be45d4
--- /dev/null
+++ b/Content/LevelPrototyping/Materials/M_FlatCol.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:41dbbda4b463a224c29ee01765e1f52a8ae63f2f94f1440ee267fe7b1a481585
+size 11771
diff --git a/Content/LevelPrototyping/Materials/M_PrototypeGrid.uasset b/Content/LevelPrototyping/Materials/M_PrototypeGrid.uasset
new file mode 100644
index 0000000..73c3bcf
--- /dev/null
+++ b/Content/LevelPrototyping/Materials/M_PrototypeGrid.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:bf8625ad5917cee705867038d8a590b25465e6b5d5818a738a584e515d7cbdfc
+size 79863
diff --git a/Content/LevelPrototyping/Meshes/SM_ChamferCube.uasset b/Content/LevelPrototyping/Meshes/SM_ChamferCube.uasset
new file mode 100644
index 0000000..dcc10b1
--- /dev/null
+++ b/Content/LevelPrototyping/Meshes/SM_ChamferCube.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:b20a3ae9668ec599db519af3f84b67952d0e31ac4427b80ee812232edde91f53
+size 128849
diff --git a/Content/LevelPrototyping/Meshes/SM_Cube.uasset b/Content/LevelPrototyping/Meshes/SM_Cube.uasset
new file mode 100644
index 0000000..fcb2e03
--- /dev/null
+++ b/Content/LevelPrototyping/Meshes/SM_Cube.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:4680317a8c87f983e077f6c0a6b93ffaa0ba4dbddbc810960d4553e6ad380a89
+size 18279
diff --git a/Content/LevelPrototyping/Meshes/SM_Cylinder.uasset b/Content/LevelPrototyping/Meshes/SM_Cylinder.uasset
new file mode 100644
index 0000000..ffaea18
--- /dev/null
+++ b/Content/LevelPrototyping/Meshes/SM_Cylinder.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:76aaa3c2b812ff790fb57ae7736b748e4e1556afbb13466fd787d4bacd2c7dfe
+size 193636
diff --git a/Content/LevelPrototyping/Meshes/SM_Plane.uasset b/Content/LevelPrototyping/Meshes/SM_Plane.uasset
new file mode 100644
index 0000000..8a6e0cd
--- /dev/null
+++ b/Content/LevelPrototyping/Meshes/SM_Plane.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:39ab83dc57a9a27a80476711610f72e81c0685a05d4aa95413b3e4e7f6c4a3f2
+size 15581
diff --git a/Content/LevelPrototyping/Meshes/SM_QuarterCylinder.uasset b/Content/LevelPrototyping/Meshes/SM_QuarterCylinder.uasset
new file mode 100644
index 0000000..e3e30cc
--- /dev/null
+++ b/Content/LevelPrototyping/Meshes/SM_QuarterCylinder.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:4087b3f85753d9ccbced7d00d42208dad4d21185f24a97b0c86db31be867b733
+size 135087
diff --git a/Content/LevelPrototyping/Meshes/SM_QuarterCylinderOuter.uasset b/Content/LevelPrototyping/Meshes/SM_QuarterCylinderOuter.uasset
new file mode 100644
index 0000000..108131a
--- /dev/null
+++ b/Content/LevelPrototyping/Meshes/SM_QuarterCylinderOuter.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:ab58a5224da57452f367c9d55ea4976466138fc2cb8ec9e2fa92748c86a07f11
+size 79741
diff --git a/Content/LevelPrototyping/Meshes/SM_Ramp.uasset b/Content/LevelPrototyping/Meshes/SM_Ramp.uasset
new file mode 100644
index 0000000..d176359
--- /dev/null
+++ b/Content/LevelPrototyping/Meshes/SM_Ramp.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:e5b472deb37a9956b9112dccee3d09498a456baeacc574db8c5d725463b73597
+size 18690
diff --git a/Content/LevelPrototyping/Textures/T_GridChecker_A.uasset b/Content/LevelPrototyping/Textures/T_GridChecker_A.uasset
new file mode 100644
index 0000000..37cf466
--- /dev/null
+++ b/Content/LevelPrototyping/Textures/T_GridChecker_A.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:bb8334767064b107ec4f4407ef1365e8b9e2dfeff5984e66d4a6232254ddd02a
+size 9600
diff --git a/Content/NonUsable/Blueprints/BP_OXRPawn.uasset b/Content/NonUsable/Blueprints/BP_OXRPawn.uasset
new file mode 100644
index 0000000..6d902e2
--- /dev/null
+++ b/Content/NonUsable/Blueprints/BP_OXRPawn.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:cd3ead833abbcfc50f01c529326019b15e5ff3c3b33c40d05ad849ea13479211
+size 2610650
diff --git a/Content/NonUsable/Blueprints/EGrabState.uasset b/Content/NonUsable/Blueprints/EGrabState.uasset
new file mode 100644
index 0000000..fc9aefc
--- /dev/null
+++ b/Content/NonUsable/Blueprints/EGrabState.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:ea7533d3ba1d692f027b462f7367b09de8156bfb14d51e1710d47ab60c822090
+size 2173
diff --git a/Content/NonUsable_Prototyping/Blueprints/BP_OXRGameModeBase.uasset b/Content/NonUsable_Prototyping/Blueprints/BP_OXRGameModeBase.uasset
new file mode 100644
index 0000000..d417f04
--- /dev/null
+++ b/Content/NonUsable_Prototyping/Blueprints/BP_OXRGameModeBase.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:dab8209a2a871b75c69fc5f89fdec7c70dfd5486753b1fecaed34d6c22380839
+size 21531
diff --git a/Content/NonUsable_Prototyping/Blueprints/BP_OXRHandInteractor.uasset b/Content/NonUsable_Prototyping/Blueprints/BP_OXRHandInteractor.uasset
new file mode 100644
index 0000000..a8bff7b
--- /dev/null
+++ b/Content/NonUsable_Prototyping/Blueprints/BP_OXRHandInteractor.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:36fee2672111792b52d3f44c522ffd7041bda981b3947013f722108518f21482
+size 35135
diff --git a/Content/NonUsable_Prototyping/Blueprints/BP_OXRPawn.uasset b/Content/NonUsable_Prototyping/Blueprints/BP_OXRPawn.uasset
new file mode 100644
index 0000000..6ac0e9f
--- /dev/null
+++ b/Content/NonUsable_Prototyping/Blueprints/BP_OXRPawn.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:dfba7ce6403cbbdee5fbc2d909a0165375b3e89f4c7ec8b7da1c46e34af8d01c
+size 2609506
diff --git a/Content/NonUsable_Prototyping/Blueprints/BP_OXRPlayerController.uasset b/Content/NonUsable_Prototyping/Blueprints/BP_OXRPlayerController.uasset
new file mode 100644
index 0000000..7969625
--- /dev/null
+++ b/Content/NonUsable_Prototyping/Blueprints/BP_OXRPlayerController.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:0a3f8e0b64734655b23718098e68cdd7e2a4e29227acea9a1c327f21e9df59b7
+size 20562
diff --git a/Content/NonUsable_Prototyping/Blueprints/EGrabState.uasset b/Content/NonUsable_Prototyping/Blueprints/EGrabState.uasset
new file mode 100644
index 0000000..3e71f43
--- /dev/null
+++ b/Content/NonUsable_Prototyping/Blueprints/EGrabState.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:2dfe9840a51f44b7caa82ac6ca57df7c7214c47cb14efdc3a6f12490a9c68f06
+size 2566
diff --git a/Content/NonUsable_Prototyping/Blueprints/TRIAL_OXRPawn.uasset b/Content/NonUsable_Prototyping/Blueprints/TRIAL_OXRPawn.uasset
new file mode 100644
index 0000000..2f2809c
--- /dev/null
+++ b/Content/NonUsable_Prototyping/Blueprints/TRIAL_OXRPawn.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:f89cc5cf4e0e947917045c019ceda1581f7cd85c61b9c0f737800b029ab047e1
+size 353497
diff --git a/Content/NonUsable_Prototyping/Objects/BP_Grabbable_Object.uasset b/Content/NonUsable_Prototyping/Objects/BP_Grabbable_Object.uasset
new file mode 100644
index 0000000..f4e21b7
--- /dev/null
+++ b/Content/NonUsable_Prototyping/Objects/BP_Grabbable_Object.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:169ca90a1fd1512d63e8f6e92b5bcd516e6f97136ca0b3d69686ab2def4299e0
+size 29330
diff --git a/Content/NonUsable_Prototyping/Objects/BP_Grabbable_SmallSphere.uasset b/Content/NonUsable_Prototyping/Objects/BP_Grabbable_SmallSphere.uasset
new file mode 100644
index 0000000..c8d1101
--- /dev/null
+++ b/Content/NonUsable_Prototyping/Objects/BP_Grabbable_SmallSphere.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:03f4c9553322e60045dd6de4d462b8d0fd55ff71ebf21498adb7486abc114dd3
+size 39026
diff --git a/Content/NonUsable_Prototyping/Objects/BP_Grabbable_Sphere.uasset b/Content/NonUsable_Prototyping/Objects/BP_Grabbable_Sphere.uasset
new file mode 100644
index 0000000..82011bf
--- /dev/null
+++ b/Content/NonUsable_Prototyping/Objects/BP_Grabbable_Sphere.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:0aa0f69fd162ad9b880ddec59ec71ef077910b267314cd74ab849d465bec714f
+size 38243
diff --git a/Content/OpenXRFramework/Blueprints/BP_Basket.uasset b/Content/OpenXRFramework/Blueprints/BP_Basket.uasset
new file mode 100644
index 0000000..4cff852
--- /dev/null
+++ b/Content/OpenXRFramework/Blueprints/BP_Basket.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:06f4687fffc71d44c50360980ed37f77494c03d85a4af2f7251055f309fa6ea7
+size 35635
diff --git a/Content/OpenXRFramework/Blueprints/BP_InstructionForDevelopers.uasset b/Content/OpenXRFramework/Blueprints/BP_InstructionForDevelopers.uasset
new file mode 100644
index 0000000..ef014a5
--- /dev/null
+++ b/Content/OpenXRFramework/Blueprints/BP_InstructionForDevelopers.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:e0f7cb7707c971b58b1ded0a9d6ee4e12e36e15a7d36ed34b260cd0990070d14
+size 36419
diff --git a/Content/OpenXRFramework/Blueprints/BP_InstructionForUsers.uasset b/Content/OpenXRFramework/Blueprints/BP_InstructionForUsers.uasset
new file mode 100644
index 0000000..08ce7a2
--- /dev/null
+++ b/Content/OpenXRFramework/Blueprints/BP_InstructionForUsers.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:cf4ec14e439ca7d765cb472ac67d4734789b97a4551fc3fc7cc85fdf4cd46355
+size 34408
diff --git a/Content/OpenXRFramework/Blueprints/BP_OXRPawn.uasset b/Content/OpenXRFramework/Blueprints/BP_OXRPawn.uasset
new file mode 100644
index 0000000..f28d155
--- /dev/null
+++ b/Content/OpenXRFramework/Blueprints/BP_OXRPawn.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:8c8b10f8964df470f02995727010c14cfbf64372eb0c042e1a81277c1f4b32ff
+size 2608500
diff --git a/Content/OpenXRFramework/Blueprints/EGrabState.uasset b/Content/OpenXRFramework/Blueprints/EGrabState.uasset
new file mode 100644
index 0000000..3ba717e
--- /dev/null
+++ b/Content/OpenXRFramework/Blueprints/EGrabState.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:9be46d2c96e2707321974c43a23f71b8f19343a67c3c3d14063d6941e70277d7
+size 2185
diff --git a/Content/OpenXRFramework/Materials/M_Basket.uasset b/Content/OpenXRFramework/Materials/M_Basket.uasset
new file mode 100644
index 0000000..de6887f
--- /dev/null
+++ b/Content/OpenXRFramework/Materials/M_Basket.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:afa4b5bdd920b539d613df6eb5569b30115461d8463ee5b5b90af6d637eea8cf
+size 11528
diff --git a/Content/OpenXRFramework/Materials/M_Floor.uasset b/Content/OpenXRFramework/Materials/M_Floor.uasset
new file mode 100644
index 0000000..8a126b5
--- /dev/null
+++ b/Content/OpenXRFramework/Materials/M_Floor.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:f912da5b0cd526c07bb1f63ce6af5f3b9de63e2c65d2d85e7247683d8b30aa94
+size 10831
diff --git a/Content/OpenXRFramework/Materials/M_Object.uasset b/Content/OpenXRFramework/Materials/M_Object.uasset
new file mode 100644
index 0000000..cb46819
--- /dev/null
+++ b/Content/OpenXRFramework/Materials/M_Object.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:70b01cd7e82b76804c1a0a55995658bfebc5b7d43bf1fa76a3d420f346a68881
+size 12361
diff --git a/Content/OpenXRFramework/Materials/M_Screen.uasset b/Content/OpenXRFramework/Materials/M_Screen.uasset
new file mode 100644
index 0000000..d5b307b
--- /dev/null
+++ b/Content/OpenXRFramework/Materials/M_Screen.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:88b623ef4d11d7609ff40fc6c1be3fa7b1999879cdde8aa83a1ae9b5a06bfbc2
+size 9215
diff --git a/Content/OpenXRFramework/Materials/M_Table.uasset b/Content/OpenXRFramework/Materials/M_Table.uasset
new file mode 100644
index 0000000..8cff9d9
--- /dev/null
+++ b/Content/OpenXRFramework/Materials/M_Table.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:4059d300a283aec7990d8302f1d4de3b1b26792d36eeacb5d24ff41da6d58c80
+size 10835
diff --git a/Content/OpenXRFramework/Materials/M_Wall.uasset b/Content/OpenXRFramework/Materials/M_Wall.uasset
new file mode 100644
index 0000000..9b41aed
--- /dev/null
+++ b/Content/OpenXRFramework/Materials/M_Wall.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:15ced81284bc3ce7ac273db3c049a5bbfacd7d89ebe0778e637726480e491268
+size 11133
diff --git a/Content/OpenXRMannequins/Meshes/SKM_QuinnXR_left.uasset b/Content/OpenXRMannequins/Meshes/SKM_QuinnXR_left.uasset
new file mode 100644
index 0000000..7ca2b4a
--- /dev/null
+++ b/Content/OpenXRMannequins/Meshes/SKM_QuinnXR_left.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:f13f8607e2690c378c1ebd08267aded5d38ac8f65363eabf0ee1d1dc0e951be4
+size 1358019
diff --git a/Content/OpenXRMannequins/Meshes/SKM_QuinnXR_left_PhysicsAsset.uasset b/Content/OpenXRMannequins/Meshes/SKM_QuinnXR_left_PhysicsAsset.uasset
new file mode 100644
index 0000000..9ece5ce
--- /dev/null
+++ b/Content/OpenXRMannequins/Meshes/SKM_QuinnXR_left_PhysicsAsset.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:9b9927ed6be31a044547982983bacac72632de78799202fbc6aa114afb50fe0c
+size 8780
diff --git a/Content/OpenXRMannequins/Meshes/SKM_QuinnXR_left_Skeleton.uasset b/Content/OpenXRMannequins/Meshes/SKM_QuinnXR_left_Skeleton.uasset
new file mode 100644
index 0000000..eec43f6
--- /dev/null
+++ b/Content/OpenXRMannequins/Meshes/SKM_QuinnXR_left_Skeleton.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:ccfd69a052acd69fd8f533ecc566bcab9a593e75607cf3a34069c9f7fd5d4235
+size 57366
diff --git a/Content/OpenXRMannequins/Meshes/SKM_QuinnXR_right.uasset b/Content/OpenXRMannequins/Meshes/SKM_QuinnXR_right.uasset
new file mode 100644
index 0000000..e6b85d2
--- /dev/null
+++ b/Content/OpenXRMannequins/Meshes/SKM_QuinnXR_right.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:6aa5e1fab55664f55a190dda26458c3ea5d8d01eb6b47b430bc0f8ae4e115c03
+size 1359089
diff --git a/Content/OpenXRMannequins/Meshes/SKM_QuinnXR_right_PhysicsAsset.uasset b/Content/OpenXRMannequins/Meshes/SKM_QuinnXR_right_PhysicsAsset.uasset
new file mode 100644
index 0000000..48a3353
--- /dev/null
+++ b/Content/OpenXRMannequins/Meshes/SKM_QuinnXR_right_PhysicsAsset.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:95240fdc0165a5a42c74bf9b1ef5a1529bcb3013595c178d1ef7e1accfb97545
+size 7487
diff --git a/Content/OpenXRMannequins/Meshes/SKM_QuinnXR_right_Skeleton.uasset b/Content/OpenXRMannequins/Meshes/SKM_QuinnXR_right_Skeleton.uasset
new file mode 100644
index 0000000..567f54b
--- /dev/null
+++ b/Content/OpenXRMannequins/Meshes/SKM_QuinnXR_right_Skeleton.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:0685d744b1441a33acb16e73fd9fa52898d9a60153ad9c231f1663461cbfb4b5
+size 57328
diff --git a/Content/TemplateXRMannequins/Animations/A_MannequinsXR_Grasp_Right.uasset b/Content/TemplateXRMannequins/Animations/A_MannequinsXR_Grasp_Right.uasset
new file mode 100644
index 0000000..88a7972
--- /dev/null
+++ b/Content/TemplateXRMannequins/Animations/A_MannequinsXR_Grasp_Right.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:a3fd8e1fb929cfb9254d83d0cbc38a8264f4b63d55f6aaf4ec27b097bd497026
+size 81776
diff --git a/Content/TemplateXRMannequins/Animations/A_MannequinsXR_Idle_Right.uasset b/Content/TemplateXRMannequins/Animations/A_MannequinsXR_Idle_Right.uasset
new file mode 100644
index 0000000..928c2c3
--- /dev/null
+++ b/Content/TemplateXRMannequins/Animations/A_MannequinsXR_Idle_Right.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:1313477640f6bc31981085b9d6ada66cb60611de39e32a4acd79e142eb2763c6
+size 66795
diff --git a/Content/TemplateXRMannequins/Animations/A_MannequinsXR_IndexCurl_Right.uasset b/Content/TemplateXRMannequins/Animations/A_MannequinsXR_IndexCurl_Right.uasset
new file mode 100644
index 0000000..741d07d
--- /dev/null
+++ b/Content/TemplateXRMannequins/Animations/A_MannequinsXR_IndexCurl_Right.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:ea8e95d99267f1d63d8b6e0415a40c89637740cecc3b472d3ed0ea5901b0177a
+size 82071
diff --git a/Content/TemplateXRMannequins/Animations/A_MannequinsXR_Point_Right.uasset b/Content/TemplateXRMannequins/Animations/A_MannequinsXR_Point_Right.uasset
new file mode 100644
index 0000000..b2b2a43
--- /dev/null
+++ b/Content/TemplateXRMannequins/Animations/A_MannequinsXR_Point_Right.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:c4f2467c0af16f38a0b95ee6d05b7b481eefe00b99bc22bc01240cd1c23859e2
+size 82018
diff --git a/Content/TemplateXRMannequins/Animations/A_MannequinsXR_ThumbUp_Right.uasset b/Content/TemplateXRMannequins/Animations/A_MannequinsXR_ThumbUp_Right.uasset
new file mode 100644
index 0000000..ded845d
--- /dev/null
+++ b/Content/TemplateXRMannequins/Animations/A_MannequinsXR_ThumbUp_Right.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:bea60924ca2b948dc2a63e9734b959908fb81e24d06c5372d754dec0820142c2
+size 82132
diff --git a/Content/TemplateXRMannequins/Animations/MDT_MannequinsXR.uasset b/Content/TemplateXRMannequins/Animations/MDT_MannequinsXR.uasset
new file mode 100644
index 0000000..85d8517
--- /dev/null
+++ b/Content/TemplateXRMannequins/Animations/MDT_MannequinsXR.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:c24dbe5ab20b0ddd0a1ce81f58660a8b118594dca473ceb4d8023d4ff70477e9
+size 13538
diff --git a/Content/TemplateXRMannequins/BP_MannequinsXR.uasset b/Content/TemplateXRMannequins/BP_MannequinsXR.uasset
new file mode 100644
index 0000000..506b6de
--- /dev/null
+++ b/Content/TemplateXRMannequins/BP_MannequinsXR.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:ca90922250a8f1e32dd3aff3e757018f1a30c0196daa81638a150ac6ed7ea517
+size 14555
diff --git a/Content/TemplateXRMannequins/Materials/Functions/CA_Mannequin.uasset b/Content/TemplateXRMannequins/Materials/Functions/CA_Mannequin.uasset
new file mode 100644
index 0000000..598e5ab
--- /dev/null
+++ b/Content/TemplateXRMannequins/Materials/Functions/CA_Mannequin.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:a3cc29a80726dc15b98981ec091509d89c9a6739e25892dfc29ae9b012f2d33c
+size 5248
diff --git a/Content/TemplateXRMannequins/Materials/Functions/ChromaticCurve.uasset b/Content/TemplateXRMannequins/Materials/Functions/ChromaticCurve.uasset
new file mode 100644
index 0000000..2e0ecd0
--- /dev/null
+++ b/Content/TemplateXRMannequins/Materials/Functions/ChromaticCurve.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:1cc3085bedd6c978e53e33971cb00ada8ac52b1a4ae456dbc0604a902779f0fd
+size 6182
diff --git a/Content/TemplateXRMannequins/Materials/Functions/MF_Diffraction.uasset b/Content/TemplateXRMannequins/Materials/Functions/MF_Diffraction.uasset
new file mode 100644
index 0000000..43decb1
--- /dev/null
+++ b/Content/TemplateXRMannequins/Materials/Functions/MF_Diffraction.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:b8f155b2645752c043e43390eb5f77b4ccd55e76d937d19a5e69a61f8196dae3
+size 32250
diff --git a/Content/TemplateXRMannequins/Materials/Functions/MF_logo3layers.uasset b/Content/TemplateXRMannequins/Materials/Functions/MF_logo3layers.uasset
new file mode 100644
index 0000000..84bb7e9
--- /dev/null
+++ b/Content/TemplateXRMannequins/Materials/Functions/MF_logo3layers.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:e8157a91a96cd8dc30fcf7781c96b666f75486db3430be21179913ae545ee552
+size 56826
diff --git a/Content/TemplateXRMannequins/Materials/Functions/ML_BaseColorFallOff.uasset b/Content/TemplateXRMannequins/Materials/Functions/ML_BaseColorFallOff.uasset
new file mode 100644
index 0000000..451d063
--- /dev/null
+++ b/Content/TemplateXRMannequins/Materials/Functions/ML_BaseColorFallOff.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:4d0bdb0ca8f217b61bb00a4be779a22bc2c45ca17ec8201375e2ee0d64e28e18
+size 14390
diff --git a/Content/TemplateXRMannequins/Materials/Instances/Manny/MI_Manny_01.uasset b/Content/TemplateXRMannequins/Materials/Instances/Manny/MI_Manny_01.uasset
new file mode 100644
index 0000000..2d14a88
--- /dev/null
+++ b/Content/TemplateXRMannequins/Materials/Instances/Manny/MI_Manny_01.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:af503afeac7c4cfb1166acdafe9889e2b359ece04cbefa3a9c57ad1f4a2df3bf
+size 19429
diff --git a/Content/TemplateXRMannequins/Materials/Instances/Manny/MI_Manny_02.uasset b/Content/TemplateXRMannequins/Materials/Instances/Manny/MI_Manny_02.uasset
new file mode 100644
index 0000000..aa39241
--- /dev/null
+++ b/Content/TemplateXRMannequins/Materials/Instances/Manny/MI_Manny_02.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:8b46d93d3b1b1ffe0cc62f4c27a1fb805532f86402dd51833cf87a07a97dec1e
+size 19802
diff --git a/Content/TemplateXRMannequins/Materials/Instances/Quinn/MI_Quinn_01.uasset b/Content/TemplateXRMannequins/Materials/Instances/Quinn/MI_Quinn_01.uasset
new file mode 100644
index 0000000..2b68ebd
--- /dev/null
+++ b/Content/TemplateXRMannequins/Materials/Instances/Quinn/MI_Quinn_01.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:68ad9b793fc7f88c0dc932ad3133db6913753813636311ad81c923af7ec04c1c
+size 17536
diff --git a/Content/TemplateXRMannequins/Materials/Instances/Quinn/MI_Quinn_02.uasset b/Content/TemplateXRMannequins/Materials/Instances/Quinn/MI_Quinn_02.uasset
new file mode 100644
index 0000000..b49551f
--- /dev/null
+++ b/Content/TemplateXRMannequins/Materials/Instances/Quinn/MI_Quinn_02.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:6e14ae72514cc7a49ca3fd130b26989f8ad71d28093599428a0222f42c58b409
+size 19867
diff --git a/Content/TemplateXRMannequins/Materials/M_Mannequin.uasset b/Content/TemplateXRMannequins/Materials/M_Mannequin.uasset
new file mode 100644
index 0000000..77d025a
--- /dev/null
+++ b/Content/TemplateXRMannequins/Materials/M_Mannequin.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:fd6861db9b30ceedf9b39bc5c9999a7e7ba85a2756fad0c8dab7665730f474c3
+size 83632
diff --git a/Content/TemplateXRMannequins/Meshes/ABP_MannequinsXR.uasset b/Content/TemplateXRMannequins/Meshes/ABP_MannequinsXR.uasset
new file mode 100644
index 0000000..ace5235
--- /dev/null
+++ b/Content/TemplateXRMannequins/Meshes/ABP_MannequinsXR.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:8f624ac1db8b1c02206d0e168f09d87b6ad2b04376bc188addee53e6683c58ac
+size 215941
diff --git a/Content/TemplateXRMannequins/Meshes/SKM_MannyXR_left.uasset b/Content/TemplateXRMannequins/Meshes/SKM_MannyXR_left.uasset
new file mode 100644
index 0000000..9e5e4be
--- /dev/null
+++ b/Content/TemplateXRMannequins/Meshes/SKM_MannyXR_left.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:eb1c439647cf11e8e53cc844c320e740c3c159cd73b9b883474a412f39ccd28f
+size 1347093
diff --git a/Content/TemplateXRMannequins/Meshes/SKM_MannyXR_right.uasset b/Content/TemplateXRMannequins/Meshes/SKM_MannyXR_right.uasset
new file mode 100644
index 0000000..4f717dd
--- /dev/null
+++ b/Content/TemplateXRMannequins/Meshes/SKM_MannyXR_right.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:ca42ed8b79acbd05ce6a0a96020c300f0a6bcb3d51433bfd38b676d7b39e8728
+size 1346139
diff --git a/Content/TemplateXRMannequins/Meshes/SKM_QuinnXR_left.uasset b/Content/TemplateXRMannequins/Meshes/SKM_QuinnXR_left.uasset
new file mode 100644
index 0000000..6b6791a
--- /dev/null
+++ b/Content/TemplateXRMannequins/Meshes/SKM_QuinnXR_left.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:b67aa04fcbc8a527db6dd5966d2098353cba6e65944012349e1e46472d426ef1
+size 1416856
diff --git a/Content/TemplateXRMannequins/Meshes/SKM_QuinnXR_right.uasset b/Content/TemplateXRMannequins/Meshes/SKM_QuinnXR_right.uasset
new file mode 100644
index 0000000..7b16480
--- /dev/null
+++ b/Content/TemplateXRMannequins/Meshes/SKM_QuinnXR_right.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:9aa8f72b0fd19f4b530814ace0761083705176750bee89bb3f9d02338c32d95b
+size 1414861
diff --git a/Content/TemplateXRMannequins/Meshes/SK_MannequinsXR.uasset b/Content/TemplateXRMannequins/Meshes/SK_MannequinsXR.uasset
new file mode 100644
index 0000000..6460402
--- /dev/null
+++ b/Content/TemplateXRMannequins/Meshes/SK_MannequinsXR.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:98cdafa11e43d7a748afb675fc14ee2ce923e474d97a80a2c838445fcea53fda
+size 58148
diff --git a/Content/TemplateXRMannequins/Meshes/SkeletalMeshLODSettings_MannequinsXR.uasset b/Content/TemplateXRMannequins/Meshes/SkeletalMeshLODSettings_MannequinsXR.uasset
new file mode 100644
index 0000000..e240d88
--- /dev/null
+++ b/Content/TemplateXRMannequins/Meshes/SkeletalMeshLODSettings_MannequinsXR.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:ab98c7a7b80ba9a10883cecac78342078cdc1f21c95e7d054ffc7d7ccbb36241
+size 8238
diff --git a/Content/TemplateXRMannequins/Textures/Manny/T_Manny_01_ASAOPMASK_MSK.uasset b/Content/TemplateXRMannequins/Textures/Manny/T_Manny_01_ASAOPMASK_MSK.uasset
new file mode 100644
index 0000000..6e50715
--- /dev/null
+++ b/Content/TemplateXRMannequins/Textures/Manny/T_Manny_01_ASAOPMASK_MSK.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:c41224f93901591593826641fa79bcffcc8d5d20853fa8427e3675dc0b7ed17a
+size 464936
diff --git a/Content/TemplateXRMannequins/Textures/Manny/T_Manny_01_BN.uasset b/Content/TemplateXRMannequins/Textures/Manny/T_Manny_01_BN.uasset
new file mode 100644
index 0000000..94ba2a9
--- /dev/null
+++ b/Content/TemplateXRMannequins/Textures/Manny/T_Manny_01_BN.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:7182a4975d044f23ad68a57265740a547716035c16d09a660c922d124d3c83bb
+size 1226719
diff --git a/Content/TemplateXRMannequins/Textures/Manny/T_Manny_01_CCRCCPlastic_MSK.uasset b/Content/TemplateXRMannequins/Textures/Manny/T_Manny_01_CCRCCPlastic_MSK.uasset
new file mode 100644
index 0000000..ee629b4
--- /dev/null
+++ b/Content/TemplateXRMannequins/Textures/Manny/T_Manny_01_CCRCCPlastic_MSK.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:0bc1ee56a90b5c9c7a3f5cdfb4df0c71ac99b5c4a8c255e56dd23f9410e7d59c
+size 740524
diff --git a/Content/TemplateXRMannequins/Textures/Manny/T_Manny_01_D.uasset b/Content/TemplateXRMannequins/Textures/Manny/T_Manny_01_D.uasset
new file mode 100644
index 0000000..dac258e
--- /dev/null
+++ b/Content/TemplateXRMannequins/Textures/Manny/T_Manny_01_D.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:77dcee1485cab91f418254eeb85dad4d2f211373f4c13ed530d0f4f32bcc434f
+size 709973
diff --git a/Content/TemplateXRMannequins/Textures/Manny/T_Manny_01_MSR_MSK.uasset b/Content/TemplateXRMannequins/Textures/Manny/T_Manny_01_MSR_MSK.uasset
new file mode 100644
index 0000000..e3fb5f4
--- /dev/null
+++ b/Content/TemplateXRMannequins/Textures/Manny/T_Manny_01_MSR_MSK.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:9757557b0d02ec50eb026cc3baa8b9b31c09e239968033d2259278407d168aee
+size 804968
diff --git a/Content/TemplateXRMannequins/Textures/Manny/T_Manny_01_N.uasset b/Content/TemplateXRMannequins/Textures/Manny/T_Manny_01_N.uasset
new file mode 100644
index 0000000..a95a688
--- /dev/null
+++ b/Content/TemplateXRMannequins/Textures/Manny/T_Manny_01_N.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:38d78dcfae714867e7d62366849cbf2c4cb81b8cc74b27eef90275899ecaedc3
+size 674336
diff --git a/Content/TemplateXRMannequins/Textures/Manny/T_Manny_01_Tan.uasset b/Content/TemplateXRMannequins/Textures/Manny/T_Manny_01_Tan.uasset
new file mode 100644
index 0000000..e1e16cc
--- /dev/null
+++ b/Content/TemplateXRMannequins/Textures/Manny/T_Manny_01_Tan.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:454ca4b6a6493102ecc91bb3233eacd81c667d3dff8ebdbc2a6b4a0ba5bfdec6
+size 182368
diff --git a/Content/TemplateXRMannequins/Textures/Manny/T_Manny_02_ASAOPMASK_MSK.uasset b/Content/TemplateXRMannequins/Textures/Manny/T_Manny_02_ASAOPMASK_MSK.uasset
new file mode 100644
index 0000000..257c677
--- /dev/null
+++ b/Content/TemplateXRMannequins/Textures/Manny/T_Manny_02_ASAOPMASK_MSK.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:41d5279324c8b57e2de71b0d8231ec0314ee15d11023744d21dbde34cbb2d733
+size 752506
diff --git a/Content/TemplateXRMannequins/Textures/Manny/T_Manny_02_BN.uasset b/Content/TemplateXRMannequins/Textures/Manny/T_Manny_02_BN.uasset
new file mode 100644
index 0000000..72bd31a
--- /dev/null
+++ b/Content/TemplateXRMannequins/Textures/Manny/T_Manny_02_BN.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:91430b61e964ad56a8f1e2e8e31b81a4fdc2560c33914dde41a1805437afd386
+size 1513459
diff --git a/Content/TemplateXRMannequins/Textures/Manny/T_Manny_02_CCRCCPlastic_MSK.uasset b/Content/TemplateXRMannequins/Textures/Manny/T_Manny_02_CCRCCPlastic_MSK.uasset
new file mode 100644
index 0000000..e41886f
--- /dev/null
+++ b/Content/TemplateXRMannequins/Textures/Manny/T_Manny_02_CCRCCPlastic_MSK.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:b86a538e4e1d4e77f37312eeb8c7a9f572b8ca308768bee3d8c9edc503c80328
+size 1030503
diff --git a/Content/TemplateXRMannequins/Textures/Manny/T_Manny_02_D.uasset b/Content/TemplateXRMannequins/Textures/Manny/T_Manny_02_D.uasset
new file mode 100644
index 0000000..23c62b7
--- /dev/null
+++ b/Content/TemplateXRMannequins/Textures/Manny/T_Manny_02_D.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:d538855569f14f7cf95466ac0099735895f9829b65e172ea1079eacfd0d0ebe6
+size 1021402
diff --git a/Content/TemplateXRMannequins/Textures/Manny/T_Manny_02_MSR_MSK.uasset b/Content/TemplateXRMannequins/Textures/Manny/T_Manny_02_MSR_MSK.uasset
new file mode 100644
index 0000000..586e669
--- /dev/null
+++ b/Content/TemplateXRMannequins/Textures/Manny/T_Manny_02_MSR_MSK.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:92c6ad4b3ac2811187cf1f862e550856684165b3b8858413009209accedaa672
+size 1038471
diff --git a/Content/TemplateXRMannequins/Textures/Manny/T_Manny_02_N.uasset b/Content/TemplateXRMannequins/Textures/Manny/T_Manny_02_N.uasset
new file mode 100644
index 0000000..0d70662
--- /dev/null
+++ b/Content/TemplateXRMannequins/Textures/Manny/T_Manny_02_N.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:0a823b311f08685e097d1c5d4814802918875df18105af44e1f0767780f7b00a
+size 671894
diff --git a/Content/TemplateXRMannequins/Textures/Manny/T_Manny_02_Tan.uasset b/Content/TemplateXRMannequins/Textures/Manny/T_Manny_02_Tan.uasset
new file mode 100644
index 0000000..6ac0f4d
--- /dev/null
+++ b/Content/TemplateXRMannequins/Textures/Manny/T_Manny_02_Tan.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:c602a1590736258ab79eac3b371688c08fd4ca316fcec658d62fc091d558815e
+size 307100
diff --git a/Content/TemplateXRMannequins/Textures/Quinn/T_Quinn_01ID_BN.uasset b/Content/TemplateXRMannequins/Textures/Quinn/T_Quinn_01ID_BN.uasset
new file mode 100644
index 0000000..fa460e7
--- /dev/null
+++ b/Content/TemplateXRMannequins/Textures/Quinn/T_Quinn_01ID_BN.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:70b7f20ff2e3e13b7e372f830f7b0e80b0c55060df83a9c6e5abf212ff9b82eb
+size 1087323
diff --git a/Content/TemplateXRMannequins/Textures/Quinn/T_Quinn_01ID_D.uasset b/Content/TemplateXRMannequins/Textures/Quinn/T_Quinn_01ID_D.uasset
new file mode 100644
index 0000000..31f3c66
--- /dev/null
+++ b/Content/TemplateXRMannequins/Textures/Quinn/T_Quinn_01ID_D.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:07bba80114621eed5f8d66091efe2f7c0b9381ad38bbe2e57434cb2f6c0f0136
+size 711778
diff --git a/Content/TemplateXRMannequins/Textures/Quinn/T_Quinn_01ID_MSR_MSK.uasset b/Content/TemplateXRMannequins/Textures/Quinn/T_Quinn_01ID_MSR_MSK.uasset
new file mode 100644
index 0000000..cbc787d
--- /dev/null
+++ b/Content/TemplateXRMannequins/Textures/Quinn/T_Quinn_01ID_MSR_MSK.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:62ffab09d644a3559c813b7fae499113006e004ce1f3482409d4f2107027d8c9
+size 699838
diff --git a/Content/TemplateXRMannequins/Textures/Quinn/T_Quinn_01ID_N.uasset b/Content/TemplateXRMannequins/Textures/Quinn/T_Quinn_01ID_N.uasset
new file mode 100644
index 0000000..0e85973
--- /dev/null
+++ b/Content/TemplateXRMannequins/Textures/Quinn/T_Quinn_01ID_N.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:c4a7afff9c98492c68004a2125400f2116538b0c03b5933c74efb3648ac69ba2
+size 539472
diff --git a/Content/TemplateXRMannequins/Textures/Quinn/T_Quinn_01ID_Tan.uasset b/Content/TemplateXRMannequins/Textures/Quinn/T_Quinn_01ID_Tan.uasset
new file mode 100644
index 0000000..19c6686
--- /dev/null
+++ b/Content/TemplateXRMannequins/Textures/Quinn/T_Quinn_01ID_Tan.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:0cfdcc98f059e700cc91dca4f090445fd754a9d2fdd8ec530bad4c17b431cdeb
+size 128217
diff --git a/Content/TemplateXRMannequins/Textures/Quinn/T_Quinn_01_ASAOMASK_MSK.uasset b/Content/TemplateXRMannequins/Textures/Quinn/T_Quinn_01_ASAOMASK_MSK.uasset
new file mode 100644
index 0000000..306491f
--- /dev/null
+++ b/Content/TemplateXRMannequins/Textures/Quinn/T_Quinn_01_ASAOMASK_MSK.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:d558d81e3a64f3a4d566d026c86bdca3c36ccda515ea19246b38068663356740
+size 466596
diff --git a/Content/TemplateXRMannequins/Textures/Quinn/T_Quinn_01_CCRCCPlastic_MSK.uasset b/Content/TemplateXRMannequins/Textures/Quinn/T_Quinn_01_CCRCCPlastic_MSK.uasset
new file mode 100644
index 0000000..69ca3f4
--- /dev/null
+++ b/Content/TemplateXRMannequins/Textures/Quinn/T_Quinn_01_CCRCCPlastic_MSK.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:b7f4556102c75311e7dd61a44a1eacffefcef1bc77b291f2f8f5c4c9a52161c7
+size 708119
diff --git a/Content/TemplateXRMannequins/Textures/Quinn/T_Quinn_02ID_BN.uasset b/Content/TemplateXRMannequins/Textures/Quinn/T_Quinn_02ID_BN.uasset
new file mode 100644
index 0000000..bc83755
--- /dev/null
+++ b/Content/TemplateXRMannequins/Textures/Quinn/T_Quinn_02ID_BN.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:72055c416f0aeda993bdab043118e444507b8d362ec979af9683c1f78b18b688
+size 1369142
diff --git a/Content/TemplateXRMannequins/Textures/Quinn/T_Quinn_02ID_D.uasset b/Content/TemplateXRMannequins/Textures/Quinn/T_Quinn_02ID_D.uasset
new file mode 100644
index 0000000..9a75b67
--- /dev/null
+++ b/Content/TemplateXRMannequins/Textures/Quinn/T_Quinn_02ID_D.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:0f571a3e3625403afd907e8b461757f275cf995aae8a4e5e3ea618c8a8609514
+size 949879
diff --git a/Content/TemplateXRMannequins/Textures/Quinn/T_Quinn_02ID_MSR_MSK.uasset b/Content/TemplateXRMannequins/Textures/Quinn/T_Quinn_02ID_MSR_MSK.uasset
new file mode 100644
index 0000000..9756766
--- /dev/null
+++ b/Content/TemplateXRMannequins/Textures/Quinn/T_Quinn_02ID_MSR_MSK.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:2cddd2361f4f3b0881edc5cb75b79e13794faaa0a152a5cb48fd998b568b8075
+size 924030
diff --git a/Content/TemplateXRMannequins/Textures/Quinn/T_Quinn_02ID_N.uasset b/Content/TemplateXRMannequins/Textures/Quinn/T_Quinn_02ID_N.uasset
new file mode 100644
index 0000000..30f0499
--- /dev/null
+++ b/Content/TemplateXRMannequins/Textures/Quinn/T_Quinn_02ID_N.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:f88859d8d58b2bb62a4326e2c2e7613542c7ead93a3f1efce0abcfb8deadbf16
+size 539472
diff --git a/Content/TemplateXRMannequins/Textures/Quinn/T_Quinn_02ID_Tan.uasset b/Content/TemplateXRMannequins/Textures/Quinn/T_Quinn_02ID_Tan.uasset
new file mode 100644
index 0000000..5be74bc
--- /dev/null
+++ b/Content/TemplateXRMannequins/Textures/Quinn/T_Quinn_02ID_Tan.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:508c42791d7cd9a3d06ca4aec5d84804b29b65ad847c12e0b90b243147f8b15c
+size 234040
diff --git a/Content/TemplateXRMannequins/Textures/Quinn/T_Quinn_02_ASAOMASK_MSK.uasset b/Content/TemplateXRMannequins/Textures/Quinn/T_Quinn_02_ASAOMASK_MSK.uasset
new file mode 100644
index 0000000..b9369a6
--- /dev/null
+++ b/Content/TemplateXRMannequins/Textures/Quinn/T_Quinn_02_ASAOMASK_MSK.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:508fbe4ca48161339f779b22c72e145395372d06a01f0202ae157ff1cca924d0
+size 646963
diff --git a/Content/TemplateXRMannequins/Textures/Quinn/T_Quinn_02_CCRCCPlastic_MSK.uasset b/Content/TemplateXRMannequins/Textures/Quinn/T_Quinn_02_CCRCCPlastic_MSK.uasset
new file mode 100644
index 0000000..9735ccb
--- /dev/null
+++ b/Content/TemplateXRMannequins/Textures/Quinn/T_Quinn_02_CCRCCPlastic_MSK.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:90524ee187bb716acc6a0969e17af2d3a85c223a1c600f785f56ed8ecb56c06d
+size 878248
diff --git a/Content/TemplateXRMannequins/Textures/Shared/T_UE_Logo_M.uasset b/Content/TemplateXRMannequins/Textures/Shared/T_UE_Logo_M.uasset
new file mode 100644
index 0000000..eca2807
--- /dev/null
+++ b/Content/TemplateXRMannequins/Textures/Shared/T_UE_Logo_M.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:58d5dcf56f208a2249579a6ef821f454f1a889fecfa3e041e5d328dc5c47550c
+size 40398
diff --git a/Content/VRSpectator/EVRSpectatorMode.uasset b/Content/VRSpectator/EVRSpectatorMode.uasset
new file mode 100644
index 0000000..690b949
--- /dev/null
+++ b/Content/VRSpectator/EVRSpectatorMode.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:aa6df298d25cd834f0dbf61085f8a575acec266f223c3f5046be099b243ba799
+size 2258
diff --git a/Content/VRSpectator/Input/Actions/IA_VRSpectator_FOV.uasset b/Content/VRSpectator/Input/Actions/IA_VRSpectator_FOV.uasset
new file mode 100644
index 0000000..8a2369f
--- /dev/null
+++ b/Content/VRSpectator/Input/Actions/IA_VRSpectator_FOV.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:beffb8ed9644112c6201fa115440b210999f3db66a99c845473d5a0ef7e7cbd0
+size 1823
diff --git a/Content/VRSpectator/Input/Actions/IA_VRSpectator_FOV_Reset.uasset b/Content/VRSpectator/Input/Actions/IA_VRSpectator_FOV_Reset.uasset
new file mode 100644
index 0000000..92b2a98
--- /dev/null
+++ b/Content/VRSpectator/Input/Actions/IA_VRSpectator_FOV_Reset.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:5d757a1a22d3d258c97587b011f2298d921fcd4b3192864b1142924881462e78
+size 1335
diff --git a/Content/VRSpectator/Input/Actions/IA_VRSpectator_FadeInOut.uasset b/Content/VRSpectator/Input/Actions/IA_VRSpectator_FadeInOut.uasset
new file mode 100644
index 0000000..fb4a310
--- /dev/null
+++ b/Content/VRSpectator/Input/Actions/IA_VRSpectator_FadeInOut.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:dc3e8f32dec0548eaa07d802244e83c74734df1941300508cfeeef413f1f6af2
+size 1335
diff --git a/Content/VRSpectator/Input/Actions/IA_VRSpectator_Look.uasset b/Content/VRSpectator/Input/Actions/IA_VRSpectator_Look.uasset
new file mode 100644
index 0000000..4546cef
--- /dev/null
+++ b/Content/VRSpectator/Input/Actions/IA_VRSpectator_Look.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:d877954be45775869a06a3f6bd4736ff154e244419aa3ecbab151b5b48668c38
+size 1393
diff --git a/Content/VRSpectator/Input/Actions/IA_VRSpectator_Move.uasset b/Content/VRSpectator/Input/Actions/IA_VRSpectator_Move.uasset
new file mode 100644
index 0000000..254f572
--- /dev/null
+++ b/Content/VRSpectator/Input/Actions/IA_VRSpectator_Move.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:57056afa83714794315c94fc21a406d49399eee95a3dad4a832c1e4ac9a32fa1
+size 1393
diff --git a/Content/VRSpectator/Input/Actions/IA_VRSpectator_ResetRotation.uasset b/Content/VRSpectator/Input/Actions/IA_VRSpectator_ResetRotation.uasset
new file mode 100644
index 0000000..703ddf6
--- /dev/null
+++ b/Content/VRSpectator/Input/Actions/IA_VRSpectator_ResetRotation.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:f4795719d2268e50dff3c06a3818e7e92a461352203a280d2be4d7c980373511
+size 1351
diff --git a/Content/VRSpectator/Input/Actions/IA_VRSpectator_Toggle.uasset b/Content/VRSpectator/Input/Actions/IA_VRSpectator_Toggle.uasset
new file mode 100644
index 0000000..3218c81
--- /dev/null
+++ b/Content/VRSpectator/Input/Actions/IA_VRSpectator_Toggle.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:a940acc6d5ea65d7bf60f4d59560e67902c52bff0b396ebbf9d405e833a0caa7
+size 1323
diff --git a/Content/VRSpectator/Input/IMC_VRSpectator.uasset b/Content/VRSpectator/Input/IMC_VRSpectator.uasset
new file mode 100644
index 0000000..c52a5c3
--- /dev/null
+++ b/Content/VRSpectator/Input/IMC_VRSpectator.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:da5f583933d9a6001fc3e1ac1791a28471274b08a6b3345a9de385e9bad6c771
+size 16331
diff --git a/Content/VRSpectator/RT_VRSpectator.uasset b/Content/VRSpectator/RT_VRSpectator.uasset
new file mode 100644
index 0000000..27e393e
--- /dev/null
+++ b/Content/VRSpectator/RT_VRSpectator.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:7817529fc5665f53c0fed68b6dcb2915dff8fbe0cab4f55e5ae42e98f85b64fd
+size 4077
diff --git a/Content/VRSpectator/VRSpectator.uasset b/Content/VRSpectator/VRSpectator.uasset
new file mode 100644
index 0000000..d71c2ff
--- /dev/null
+++ b/Content/VRSpectator/VRSpectator.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:3d9a064c38fc863bcfabbcbaa3d2b3530d9eae7137a37d0f9943d7354767d020
+size 622191
diff --git a/Content/VRTemplate/VRTemplateMap.umap b/Content/VRTemplate/VRTemplateMap.umap
new file mode 100644
index 0000000..29bed7b
--- /dev/null
+++ b/Content/VRTemplate/VRTemplateMap.umap
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:b7f525b8d27e91fd134017358cd68a52a427453b9519dbf5d31bfcf9658341c7
+size 158356
diff --git a/Content/VRTemplate/VRTemplateMap_BuiltData.uasset b/Content/VRTemplate/VRTemplateMap_BuiltData.uasset
new file mode 100644
index 0000000..cd7112a
--- /dev/null
+++ b/Content/VRTemplate/VRTemplateMap_BuiltData.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:cf6bd54cf1ec301a77662b3dc2f81fb7b1ecdd0bbd3024db813febcc153736b7
+size 3682015
diff --git a/Content/Weapons/GrenadeLauncher/Audio/FirstPersonTemplateWeaponFire02.uasset b/Content/Weapons/GrenadeLauncher/Audio/FirstPersonTemplateWeaponFire02.uasset
new file mode 100644
index 0000000..2571c5d
--- /dev/null
+++ b/Content/Weapons/GrenadeLauncher/Audio/FirstPersonTemplateWeaponFire02.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:89a195929a0ead34f4b3b3901270350b2d3854ad9a98df75f3b7c74a849b3ff8
+size 308458
diff --git a/Content/Weapons/GrenadeLauncher/Materials/M_GrenadeLauncher.uasset b/Content/Weapons/GrenadeLauncher/Materials/M_GrenadeLauncher.uasset
new file mode 100644
index 0000000..9710e1b
--- /dev/null
+++ b/Content/Weapons/GrenadeLauncher/Materials/M_GrenadeLauncher.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:0efea7f92b8d05461c4dbf6fdb23e5519d48795680658575138a4b097cb46b6c
+size 27126
diff --git a/Content/Weapons/GrenadeLauncher/Materials/M_ProjectileBullet.uasset b/Content/Weapons/GrenadeLauncher/Materials/M_ProjectileBullet.uasset
new file mode 100644
index 0000000..70e32cc
--- /dev/null
+++ b/Content/Weapons/GrenadeLauncher/Materials/M_ProjectileBullet.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:a632681a46fef4cc87f4a9933eb7e19d73a9efbd856b55335bd883309b92530a
+size 14888
diff --git a/Content/Weapons/GrenadeLauncher/Meshes/FirstPersonProjectileMesh.uasset b/Content/Weapons/GrenadeLauncher/Meshes/FirstPersonProjectileMesh.uasset
new file mode 100644
index 0000000..c4b6c41
--- /dev/null
+++ b/Content/Weapons/GrenadeLauncher/Meshes/FirstPersonProjectileMesh.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:1ceea86d9723b80c01e88d55e4277cccbf6f71da3ce209562dd9c35743342a82
+size 33630
diff --git a/Content/Weapons/GrenadeLauncher/Meshes/PA_GrenadeLauncher.uasset b/Content/Weapons/GrenadeLauncher/Meshes/PA_GrenadeLauncher.uasset
new file mode 100644
index 0000000..e91b892
--- /dev/null
+++ b/Content/Weapons/GrenadeLauncher/Meshes/PA_GrenadeLauncher.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:8917535392ac6df3f7f29c8a675a2357d02d2abf6034215106a4c34cba0b9f34
+size 10166
diff --git a/Content/Weapons/GrenadeLauncher/Meshes/SKM_GrenadeLauncher.uasset b/Content/Weapons/GrenadeLauncher/Meshes/SKM_GrenadeLauncher.uasset
new file mode 100644
index 0000000..87a0a9a
--- /dev/null
+++ b/Content/Weapons/GrenadeLauncher/Meshes/SKM_GrenadeLauncher.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:cad142505e8354c5db09683074624b7fe89dbc8a450dcfcf3476ce70d0e0941d
+size 2698295
diff --git a/Content/Weapons/GrenadeLauncher/Meshes/SK_GrenadeLauncher.uasset b/Content/Weapons/GrenadeLauncher/Meshes/SK_GrenadeLauncher.uasset
new file mode 100644
index 0000000..63b89b7
--- /dev/null
+++ b/Content/Weapons/GrenadeLauncher/Meshes/SK_GrenadeLauncher.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:13a244b07acf7288f3d1da0dcf954c53b4c29f0007cd00ce1b4d5f8fc531c15b
+size 9165
diff --git a/Content/Weapons/GrenadeLauncher/Meshes/SM_GrenadeLauncher.uasset b/Content/Weapons/GrenadeLauncher/Meshes/SM_GrenadeLauncher.uasset
new file mode 100644
index 0000000..79a3794
--- /dev/null
+++ b/Content/Weapons/GrenadeLauncher/Meshes/SM_GrenadeLauncher.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:2d10839f109baf5e7424759d3e5f7d8c7588f080a28a4a1d93b0b4eefc73a378
+size 512976
diff --git a/Content/Weapons/GrenadeLauncher/Textures/T_FPGun_M.uasset b/Content/Weapons/GrenadeLauncher/Textures/T_FPGun_M.uasset
new file mode 100644
index 0000000..6d6c0f0
--- /dev/null
+++ b/Content/Weapons/GrenadeLauncher/Textures/T_FPGun_M.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:87b4ef72e628f6140ffe677d2b64082b10acbd0e03940fc5e0627ccccd091887
+size 156597
diff --git a/Content/Weapons/GrenadeLauncher/Textures/T_FPGun_N.uasset b/Content/Weapons/GrenadeLauncher/Textures/T_FPGun_N.uasset
new file mode 100644
index 0000000..8cadd22
--- /dev/null
+++ b/Content/Weapons/GrenadeLauncher/Textures/T_FPGun_N.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:223e0f814cbd8bae88b719b654380cb60755c768217ab0ccdd83c462a913aa7d
+size 1734086
diff --git a/Content/Weapons/Pistol/Materials/MI_Weapon_Pistol.uasset b/Content/Weapons/Pistol/Materials/MI_Weapon_Pistol.uasset
new file mode 100644
index 0000000..273bb3c
--- /dev/null
+++ b/Content/Weapons/Pistol/Materials/MI_Weapon_Pistol.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:9213d13b5529f4c4fa84877099127d9dbc0a779a8afcca48ea1ceabb8bf07634
+size 17359
diff --git a/Content/Weapons/Pistol/Meshes/PA_Pistol.uasset b/Content/Weapons/Pistol/Meshes/PA_Pistol.uasset
new file mode 100644
index 0000000..e9a85cc
--- /dev/null
+++ b/Content/Weapons/Pistol/Meshes/PA_Pistol.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:312403ecb1c0f9c735345928619f366cb16027aa7ee22c9142f807a6511b2512
+size 8807
diff --git a/Content/Weapons/Pistol/Meshes/SKM_Pistol.uasset b/Content/Weapons/Pistol/Meshes/SKM_Pistol.uasset
new file mode 100644
index 0000000..fda4700
--- /dev/null
+++ b/Content/Weapons/Pistol/Meshes/SKM_Pistol.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:cdfe37753e21c8c88c32caa1b7919918fb53c4adeda7c7fd472e9bc6730eb694
+size 999935
diff --git a/Content/Weapons/Pistol/Meshes/SK_Pistol.uasset b/Content/Weapons/Pistol/Meshes/SK_Pistol.uasset
new file mode 100644
index 0000000..32d9141
--- /dev/null
+++ b/Content/Weapons/Pistol/Meshes/SK_Pistol.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:c6aeb605a2b969ab8a6d48a56c35bb44d96651727e9bfdd7f7fa38a71606e918
+size 9704
diff --git a/Content/Weapons/Pistol/Meshes/SM_Pistol.uasset b/Content/Weapons/Pistol/Meshes/SM_Pistol.uasset
new file mode 100644
index 0000000..39056dd
--- /dev/null
+++ b/Content/Weapons/Pistol/Meshes/SM_Pistol.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:07b6066bce4cafc04232614f1e24fd44ea0b5c6a3f314dbdfb102356e59c7f56
+size 183837
diff --git a/Content/Weapons/Pistol/Textures/T_Pistol_AORM.uasset b/Content/Weapons/Pistol/Textures/T_Pistol_AORM.uasset
new file mode 100644
index 0000000..ecec1f5
--- /dev/null
+++ b/Content/Weapons/Pistol/Textures/T_Pistol_AORM.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:65789a079408010ddc777e9291aecbcda28ea550832429b2c05c48f8dbe3c708
+size 1263989
diff --git a/Content/Weapons/Pistol/Textures/T_Pistol_D.uasset b/Content/Weapons/Pistol/Textures/T_Pistol_D.uasset
new file mode 100644
index 0000000..fcacac4
--- /dev/null
+++ b/Content/Weapons/Pistol/Textures/T_Pistol_D.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:9014359ad0a79b762fccd472fa5dc5b1087a5407564952c0982d7c907dc9a588
+size 791319
diff --git a/Content/Weapons/Pistol/Textures/T_Pistol_Masks.uasset b/Content/Weapons/Pistol/Textures/T_Pistol_Masks.uasset
new file mode 100644
index 0000000..29f52a1
--- /dev/null
+++ b/Content/Weapons/Pistol/Textures/T_Pistol_Masks.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:506d1878e24b3dffb04e9df90d287117a0e12445e5e46d1732a093b96e2a43e3
+size 54818
diff --git a/Content/Weapons/Pistol/Textures/T_Pistol_N.uasset b/Content/Weapons/Pistol/Textures/T_Pistol_N.uasset
new file mode 100644
index 0000000..660616b
--- /dev/null
+++ b/Content/Weapons/Pistol/Textures/T_Pistol_N.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:7a3cabf7783371d113c47b6fe82d2b5cc478c09f1155f54cf08e857e472f54f5
+size 1094269
diff --git a/Content/Weapons/Rifle/Materials/M_Rifle.uasset b/Content/Weapons/Rifle/Materials/M_Rifle.uasset
new file mode 100644
index 0000000..1fa592b
--- /dev/null
+++ b/Content/Weapons/Rifle/Materials/M_Rifle.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:a4825031b9d0c4607f541b778b6e29baf5d3da3aa0a8ae511742eb93ebc429a3
+size 12404
diff --git a/Content/Weapons/Rifle/Materials/M_Weapon.uasset b/Content/Weapons/Rifle/Materials/M_Weapon.uasset
new file mode 100644
index 0000000..2157891
--- /dev/null
+++ b/Content/Weapons/Rifle/Materials/M_Weapon.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:304ab2ae0a6ba53d20b4d45ce3761e39c7d28597027bcd0f17d67618bc13c175
+size 29581
diff --git a/Content/Weapons/Rifle/Meshes/PA_Rifle.uasset b/Content/Weapons/Rifle/Meshes/PA_Rifle.uasset
new file mode 100644
index 0000000..9a1612b
--- /dev/null
+++ b/Content/Weapons/Rifle/Meshes/PA_Rifle.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:b33b03102c2ff60f6c6f4856a8293d3160293a1582219fdbd39ff0527ac16b75
+size 7677
diff --git a/Content/Weapons/Rifle/Meshes/SKM_Rifle.uasset b/Content/Weapons/Rifle/Meshes/SKM_Rifle.uasset
new file mode 100644
index 0000000..e90a3e2
--- /dev/null
+++ b/Content/Weapons/Rifle/Meshes/SKM_Rifle.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:349e47d396d21d908cf4763dfc759e9fd49da776b09dc699799e1b19591c0506
+size 2584805
diff --git a/Content/Weapons/Rifle/Meshes/SK_Rifle.uasset b/Content/Weapons/Rifle/Meshes/SK_Rifle.uasset
new file mode 100644
index 0000000..02e5c94
--- /dev/null
+++ b/Content/Weapons/Rifle/Meshes/SK_Rifle.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:bc21d41a9a220446bcb9b7d021bee25d8811cfc6a01b8b381000f22e586c8e17
+size 7939
diff --git a/Content/Weapons/Rifle/Meshes/SM_Rifle.uasset b/Content/Weapons/Rifle/Meshes/SM_Rifle.uasset
new file mode 100644
index 0000000..4f2e05e
--- /dev/null
+++ b/Content/Weapons/Rifle/Meshes/SM_Rifle.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:f32e41a07638d165957caee2ba550aac591503b69b8c897e7bd3b284b8d775e2
+size 450116
diff --git a/Content/Weapons/Rifle/Textures/T_Rifle_BC.uasset b/Content/Weapons/Rifle/Textures/T_Rifle_BC.uasset
new file mode 100644
index 0000000..a613c42
--- /dev/null
+++ b/Content/Weapons/Rifle/Textures/T_Rifle_BC.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:a186121704cce5edc611c79a0476a935387c5d2c627a219dcdfe75205be8b979
+size 429100
diff --git a/Content/Weapons/Rifle/Textures/T_Rifle_N.uasset b/Content/Weapons/Rifle/Textures/T_Rifle_N.uasset
new file mode 100644
index 0000000..5be981c
--- /dev/null
+++ b/Content/Weapons/Rifle/Textures/T_Rifle_N.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:df596e6ab2c7c94bddf9eb71717896c6d2c1b7ccf14b804b59b5e54f29d39a16
+size 1115236
diff --git a/Content/XRFramework/Audio/Fire01.uasset b/Content/XRFramework/Audio/Fire01.uasset
new file mode 100644
index 0000000..0b173b9
--- /dev/null
+++ b/Content/XRFramework/Audio/Fire01.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:11575b78135959575a45e156c6b30838c35f7cbcc9e5ff92b577ff7ce6fb8878
+size 553301
diff --git a/Content/XRFramework/Audio/Fire_Cue.uasset b/Content/XRFramework/Audio/Fire_Cue.uasset
new file mode 100644
index 0000000..42c602e
--- /dev/null
+++ b/Content/XRFramework/Audio/Fire_Cue.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:701f343660e1495d7fe3d81dbb568086de821856880b1a54e5fd614402fdda0d
+size 4945
diff --git a/Content/XRFramework/Blueprints/BPI_PawnAnim.uasset b/Content/XRFramework/Blueprints/BPI_PawnAnim.uasset
new file mode 100644
index 0000000..3d96122
--- /dev/null
+++ b/Content/XRFramework/Blueprints/BPI_PawnAnim.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:ca764a748e0a999387e87f1d5eafc38ba95604ba5a4032629cb0b38a7edcca93
+size 10316
diff --git a/Content/XRFramework/Blueprints/BP_AssetGuideline.uasset b/Content/XRFramework/Blueprints/BP_AssetGuideline.uasset
new file mode 100644
index 0000000..90ae88d
--- /dev/null
+++ b/Content/XRFramework/Blueprints/BP_AssetGuideline.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:d270d55435de4516ac338e82b0fa9e31efce439420ed01cc547d1ddd52b7d4ac
+size 7950
diff --git a/Content/XRFramework/Blueprints/BP_GrabComponent.uasset b/Content/XRFramework/Blueprints/BP_GrabComponent.uasset
new file mode 100644
index 0000000..c156efc
--- /dev/null
+++ b/Content/XRFramework/Blueprints/BP_GrabComponent.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:469043a3458f91439b1fc0f03bc3e2f2085737ed024077ac9f866d515d7ae1bb
+size 261010
diff --git a/Content/XRFramework/Blueprints/BP_Grabbable_SmallCube.uasset b/Content/XRFramework/Blueprints/BP_Grabbable_SmallCube.uasset
new file mode 100644
index 0000000..97bad4c
--- /dev/null
+++ b/Content/XRFramework/Blueprints/BP_Grabbable_SmallCube.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:d23219274a9777b230623a24fa8d0fc42775c21feaf3982f0d9304e59a1feefc
+size 30104
diff --git a/Content/XRFramework/Blueprints/BP_Grabbable_SmallSphere.uasset b/Content/XRFramework/Blueprints/BP_Grabbable_SmallSphere.uasset
new file mode 100644
index 0000000..2adae4d
--- /dev/null
+++ b/Content/XRFramework/Blueprints/BP_Grabbable_SmallSphere.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:463af588bcab716b5e180ced501e58bdf16127c3416a379ff552ed454d21861e
+size 2590
diff --git a/Content/XRFramework/Blueprints/BP_Menu.uasset b/Content/XRFramework/Blueprints/BP_Menu.uasset
new file mode 100644
index 0000000..ae3461d
--- /dev/null
+++ b/Content/XRFramework/Blueprints/BP_Menu.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:c8b7a52071399da863d152b978d2dafc906691a48eee675dc2ad20bdacfdf084
+size 528845
diff --git a/Content/XRFramework/Blueprints/BP_Passthrough.uasset b/Content/XRFramework/Blueprints/BP_Passthrough.uasset
new file mode 100644
index 0000000..09c96ff
--- /dev/null
+++ b/Content/XRFramework/Blueprints/BP_Passthrough.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:5df569844792cccc8d63404473feb1edd1c89af4d51c96cf3a21c1b84b8ee86d
+size 123685
diff --git a/Content/XRFramework/Blueprints/BP_Pistol.uasset b/Content/XRFramework/Blueprints/BP_Pistol.uasset
new file mode 100644
index 0000000..0aea387
--- /dev/null
+++ b/Content/XRFramework/Blueprints/BP_Pistol.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:64ef2376114ab0a20c7facb6e7ad863095c641ac2866b6b713c9f3a137f0c12b
+size 148086
diff --git a/Content/XRFramework/Blueprints/BP_Projectile.uasset b/Content/XRFramework/Blueprints/BP_Projectile.uasset
new file mode 100644
index 0000000..69b0142
--- /dev/null
+++ b/Content/XRFramework/Blueprints/BP_Projectile.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:c920786fff12da70144056c42ff0281d488b7b490f69d1e19b6000ae278b57ef
+size 96725
diff --git a/Content/XRFramework/Blueprints/BP_TeleportVisualizer.uasset b/Content/XRFramework/Blueprints/BP_TeleportVisualizer.uasset
new file mode 100644
index 0000000..ed9a941
--- /dev/null
+++ b/Content/XRFramework/Blueprints/BP_TeleportVisualizer.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:e8cae996aedc4aa3e3e351029e8b413bc57ae9e8c0429a8dd3b42ccd8504e95e
+size 78876
diff --git a/Content/XRFramework/Blueprints/BP_XRGameMode.uasset b/Content/XRFramework/Blueprints/BP_XRGameMode.uasset
new file mode 100644
index 0000000..00472a2
--- /dev/null
+++ b/Content/XRFramework/Blueprints/BP_XRGameMode.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:9e35fd1fb205be76109ddd26498f61332738052d23e7df9d8481b60de1d2ab17
+size 21668
diff --git a/Content/XRFramework/Blueprints/BP_XRPawn.uasset b/Content/XRFramework/Blueprints/BP_XRPawn.uasset
new file mode 100644
index 0000000..f03b5ae
--- /dev/null
+++ b/Content/XRFramework/Blueprints/BP_XRPawn.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:d5c850a3373bd0cf1d992953e01efdcf1ddb585b1ae7e81234d6de3d7c0dab2d
+size 945124
diff --git a/Content/XRFramework/Blueprints/E_GrabType.uasset b/Content/XRFramework/Blueprints/E_GrabType.uasset
new file mode 100644
index 0000000..7f50632
--- /dev/null
+++ b/Content/XRFramework/Blueprints/E_GrabType.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:f10a3cf38bc90768b98934a11ebaa6eb660a3ec60769f6c6676b1b95efa9087d
+size 2856
diff --git a/Content/XRFramework/Blueprints/WBP_Menu.uasset b/Content/XRFramework/Blueprints/WBP_Menu.uasset
new file mode 100644
index 0000000..00d9932
--- /dev/null
+++ b/Content/XRFramework/Blueprints/WBP_Menu.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:c74438c0e7f8a6563fc1cdf3c1245c34a4556dcd9b9c48f609b709461ccda44f
+size 117862
diff --git a/Content/XRFramework/Haptics/GrabHapticEffect.uasset b/Content/XRFramework/Haptics/GrabHapticEffect.uasset
new file mode 100644
index 0000000..92d6c25
--- /dev/null
+++ b/Content/XRFramework/Haptics/GrabHapticEffect.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:4aa9bdfdac2145d2cdb139d905e683308d344cb49e28137607dceed9ed32cc2e
+size 1893
diff --git a/Content/XRFramework/Haptics/PistolFireHapticEffect.uasset b/Content/XRFramework/Haptics/PistolFireHapticEffect.uasset
new file mode 100644
index 0000000..e8034a3
--- /dev/null
+++ b/Content/XRFramework/Haptics/PistolFireHapticEffect.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:0aca10dc99e9728514bbd4bdaef2ade825f663331c5818bdbb44b2b51e1aeeeb
+size 1923
diff --git a/Content/XRFramework/Input/Actions/Hands/IA_Hand_Grasp_Left.uasset b/Content/XRFramework/Input/Actions/Hands/IA_Hand_Grasp_Left.uasset
new file mode 100644
index 0000000..7cc3869
--- /dev/null
+++ b/Content/XRFramework/Input/Actions/Hands/IA_Hand_Grasp_Left.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:45a0c32a9f18eacec89db6dc1e06e66aca85e6e2651acf14b15bd53fcbf24a7f
+size 1840
diff --git a/Content/XRFramework/Input/Actions/Hands/IA_Hand_Grasp_Right.uasset b/Content/XRFramework/Input/Actions/Hands/IA_Hand_Grasp_Right.uasset
new file mode 100644
index 0000000..4ccd308
--- /dev/null
+++ b/Content/XRFramework/Input/Actions/Hands/IA_Hand_Grasp_Right.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:bdc2af73d20e3d86544ce2db064a87c05800771963d29655611f05447da2d4a1
+size 1849
diff --git a/Content/XRFramework/Input/Actions/Hands/IA_Hand_IndexCurl_Left.uasset b/Content/XRFramework/Input/Actions/Hands/IA_Hand_IndexCurl_Left.uasset
new file mode 100644
index 0000000..03773f2
--- /dev/null
+++ b/Content/XRFramework/Input/Actions/Hands/IA_Hand_IndexCurl_Left.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:87d71151b277ec0f747ce55a804595a32e5e6d0197cd897b082715d123baa71e
+size 1878
diff --git a/Content/XRFramework/Input/Actions/Hands/IA_Hand_IndexCurl_Right.uasset b/Content/XRFramework/Input/Actions/Hands/IA_Hand_IndexCurl_Right.uasset
new file mode 100644
index 0000000..f334f14
--- /dev/null
+++ b/Content/XRFramework/Input/Actions/Hands/IA_Hand_IndexCurl_Right.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:4e1340686328894b3f012aefc896fd95363f63bdbe120654d5bca6e6cf4ab6ff
+size 1887
diff --git a/Content/XRFramework/Input/Actions/Hands/IA_Hand_Point_Left.uasset b/Content/XRFramework/Input/Actions/Hands/IA_Hand_Point_Left.uasset
new file mode 100644
index 0000000..3ca07a4
--- /dev/null
+++ b/Content/XRFramework/Input/Actions/Hands/IA_Hand_Point_Left.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:8e2301cccec7da79d80a1e28d22ba13f585a4fc0c59ee840cea0a50421e5d804
+size 1840
diff --git a/Content/XRFramework/Input/Actions/Hands/IA_Hand_Point_Right.uasset b/Content/XRFramework/Input/Actions/Hands/IA_Hand_Point_Right.uasset
new file mode 100644
index 0000000..c8d6be2
--- /dev/null
+++ b/Content/XRFramework/Input/Actions/Hands/IA_Hand_Point_Right.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:bae11eb510f3c450b1075c4f23961891a9f2fcd2e20a8d35d51263faaf4b51b1
+size 1849
diff --git a/Content/XRFramework/Input/Actions/Hands/IA_Hand_ThumbUp_Left.uasset b/Content/XRFramework/Input/Actions/Hands/IA_Hand_ThumbUp_Left.uasset
new file mode 100644
index 0000000..25b59bb
--- /dev/null
+++ b/Content/XRFramework/Input/Actions/Hands/IA_Hand_ThumbUp_Left.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:bc77f49a865a3e41509cb301378d5ccda780613735eed5fa1db8b99385dabe7b
+size 1860
diff --git a/Content/XRFramework/Input/Actions/Hands/IA_Hand_ThumbUp_Right.uasset b/Content/XRFramework/Input/Actions/Hands/IA_Hand_ThumbUp_Right.uasset
new file mode 100644
index 0000000..5ffcf8a
--- /dev/null
+++ b/Content/XRFramework/Input/Actions/Hands/IA_Hand_ThumbUp_Right.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:3b501e68a42fcf6530ce17fd63b27d5b08244ad15878dc4454402a6d70c2d2d6
+size 1869
diff --git a/Content/XRFramework/Input/Actions/IA_Grab_Left_Pressed.uasset b/Content/XRFramework/Input/Actions/IA_Grab_Left_Pressed.uasset
new file mode 100644
index 0000000..081c469
--- /dev/null
+++ b/Content/XRFramework/Input/Actions/IA_Grab_Left_Pressed.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:b4783b28ecd7f66a39a64c23b340bb87f57858a60cf59da22172c1a1288ac0fb
+size 2338
diff --git a/Content/XRFramework/Input/Actions/IA_Grab_Left_Released.uasset b/Content/XRFramework/Input/Actions/IA_Grab_Left_Released.uasset
new file mode 100644
index 0000000..9a39f54
--- /dev/null
+++ b/Content/XRFramework/Input/Actions/IA_Grab_Left_Released.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:03fb746fd9615e47157a7c3518d82d88147e3e8a8d793b1d3cbee468d43781ff
+size 2164
diff --git a/Content/XRFramework/Input/Actions/IA_Grab_Right_Pressed.uasset b/Content/XRFramework/Input/Actions/IA_Grab_Right_Pressed.uasset
new file mode 100644
index 0000000..651b928
--- /dev/null
+++ b/Content/XRFramework/Input/Actions/IA_Grab_Right_Pressed.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:08363d7f6189af64246554ef97fa1d1ebcb0d42f0c5007e20d9477269edd8e66
+size 2347
diff --git a/Content/XRFramework/Input/Actions/IA_Grab_Right_Released.uasset b/Content/XRFramework/Input/Actions/IA_Grab_Right_Released.uasset
new file mode 100644
index 0000000..c281678
--- /dev/null
+++ b/Content/XRFramework/Input/Actions/IA_Grab_Right_Released.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:cb160d0d99cf85bbd0cf7084488b995a732f88a49737c37990930d8205063cbe
+size 2173
diff --git a/Content/XRFramework/Input/Actions/IA_Menu_Cursor_Left.uasset b/Content/XRFramework/Input/Actions/IA_Menu_Cursor_Left.uasset
new file mode 100644
index 0000000..39df400
--- /dev/null
+++ b/Content/XRFramework/Input/Actions/IA_Menu_Cursor_Left.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:d01e4a0083fb8187c88243a16e4d3af42761044e50722f8a1393d13a0b3979fe
+size 2070
diff --git a/Content/XRFramework/Input/Actions/IA_Menu_Cursor_Right.uasset b/Content/XRFramework/Input/Actions/IA_Menu_Cursor_Right.uasset
new file mode 100644
index 0000000..74764dc
--- /dev/null
+++ b/Content/XRFramework/Input/Actions/IA_Menu_Cursor_Right.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:6f67928e46481523a4b7983136a8679876d636e0bec42b78904196aa66dba271
+size 2079
diff --git a/Content/XRFramework/Input/Actions/IA_Menu_Interact_Left_Pressed.uasset b/Content/XRFramework/Input/Actions/IA_Menu_Interact_Left_Pressed.uasset
new file mode 100644
index 0000000..dc3f110
--- /dev/null
+++ b/Content/XRFramework/Input/Actions/IA_Menu_Interact_Left_Pressed.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:b5c6cfafd43a478fb754719d2f2458cdea6f21d6969362f95924f809f79fbbaf
+size 1961
diff --git a/Content/XRFramework/Input/Actions/IA_Menu_Interact_Left_Released.uasset b/Content/XRFramework/Input/Actions/IA_Menu_Interact_Left_Released.uasset
new file mode 100644
index 0000000..97e7ed2
--- /dev/null
+++ b/Content/XRFramework/Input/Actions/IA_Menu_Interact_Left_Released.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:9f28a1ad295c440d01b8ca8fa60ab6237cf5abaf46f714ab774aa9f68b3d70a6
+size 1971
diff --git a/Content/XRFramework/Input/Actions/IA_Menu_Interact_Right_Pressed.uasset b/Content/XRFramework/Input/Actions/IA_Menu_Interact_Right_Pressed.uasset
new file mode 100644
index 0000000..74649e9
--- /dev/null
+++ b/Content/XRFramework/Input/Actions/IA_Menu_Interact_Right_Pressed.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:54d9b03fceb06061dfc6d0db1b300adde9d3550fff5ed06e9093964d99e0246f
+size 1970
diff --git a/Content/XRFramework/Input/Actions/IA_Menu_Interact_Right_Released.uasset b/Content/XRFramework/Input/Actions/IA_Menu_Interact_Right_Released.uasset
new file mode 100644
index 0000000..76c0fd2
--- /dev/null
+++ b/Content/XRFramework/Input/Actions/IA_Menu_Interact_Right_Released.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:5f84444024f35c1d09baeaf7a900560dc386cec15b6dffd7458eeb58a0ef58b0
+size 1979
diff --git a/Content/XRFramework/Input/Actions/IA_Menu_Toggle_Left.uasset b/Content/XRFramework/Input/Actions/IA_Menu_Toggle_Left.uasset
new file mode 100644
index 0000000..71eb6a5
--- /dev/null
+++ b/Content/XRFramework/Input/Actions/IA_Menu_Toggle_Left.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:12ecd23defd720b56c4843507042d4b8aaf9f7a9c86ca944be7740144aca20c8
+size 1871
diff --git a/Content/XRFramework/Input/Actions/IA_Menu_Toggle_Right.uasset b/Content/XRFramework/Input/Actions/IA_Menu_Toggle_Right.uasset
new file mode 100644
index 0000000..4a90876
--- /dev/null
+++ b/Content/XRFramework/Input/Actions/IA_Menu_Toggle_Right.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:eb00b3bbe6e9de661048523593679a41b1166c26ebaf4768b4ee8b17509ba682
+size 1880
diff --git a/Content/XRFramework/Input/Actions/IA_Move.uasset b/Content/XRFramework/Input/Actions/IA_Move.uasset
new file mode 100644
index 0000000..b41153c
--- /dev/null
+++ b/Content/XRFramework/Input/Actions/IA_Move.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:ea961c9a25cac6a0bdab41a5dd336bc308f2ce80c7eccfdbd4d1d1ef3611ab64
+size 2416
diff --git a/Content/XRFramework/Input/Actions/IA_Shoot_Left.uasset b/Content/XRFramework/Input/Actions/IA_Shoot_Left.uasset
new file mode 100644
index 0000000..665d647
--- /dev/null
+++ b/Content/XRFramework/Input/Actions/IA_Shoot_Left.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:16d3cc2e4a6d66f792f4164c78f32ce42a2a78d0d3114d6fc0e1d62cebef0f3e
+size 1817
diff --git a/Content/XRFramework/Input/Actions/IA_Shoot_Right.uasset b/Content/XRFramework/Input/Actions/IA_Shoot_Right.uasset
new file mode 100644
index 0000000..f6d8054
--- /dev/null
+++ b/Content/XRFramework/Input/Actions/IA_Shoot_Right.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:99b627720c2babbb05ad6bfd60570bb7a8fa21c1848fe980a838165e3b795141
+size 1826
diff --git a/Content/XRFramework/Input/Actions/IA_Turn.uasset b/Content/XRFramework/Input/Actions/IA_Turn.uasset
new file mode 100644
index 0000000..44c0ded
--- /dev/null
+++ b/Content/XRFramework/Input/Actions/IA_Turn.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:fdf2bdf02850e37f8c5b2654410d41b2b04e894c1719569398df5d38ff30afd2
+size 2295
diff --git a/Content/XRFramework/Input/BP_InputModifier_XAxisPositiveOnly.uasset b/Content/XRFramework/Input/BP_InputModifier_XAxisPositiveOnly.uasset
new file mode 100644
index 0000000..bcc97b5
--- /dev/null
+++ b/Content/XRFramework/Input/BP_InputModifier_XAxisPositiveOnly.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:6cf5f39509ca36896fe3b3144003e2aaf6203e4f2bf79048737ebd4303ee296f
+size 39719
diff --git a/Content/XRFramework/Input/IMC_Default.uasset b/Content/XRFramework/Input/IMC_Default.uasset
new file mode 100644
index 0000000..810014d
--- /dev/null
+++ b/Content/XRFramework/Input/IMC_Default.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:639156684e5a7712c146af0ba623e22775f804452ef996ea6172ddd2a17a1c5f
+size 15034
diff --git a/Content/XRFramework/Input/IMC_Hands.uasset b/Content/XRFramework/Input/IMC_Hands.uasset
new file mode 100644
index 0000000..1aafd8a
--- /dev/null
+++ b/Content/XRFramework/Input/IMC_Hands.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:477c8592066ca9ede11b7770562d6b21d4e99ee4fc700720045ab74f8cb0260b
+size 16626
diff --git a/Content/XRFramework/Input/IMC_Menu.uasset b/Content/XRFramework/Input/IMC_Menu.uasset
new file mode 100644
index 0000000..d1c3675
--- /dev/null
+++ b/Content/XRFramework/Input/IMC_Menu.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:9200a31eb20689b74340a5f0eb7de9618965e15f8eb2dec80018032adfa7088c
+size 8378
diff --git a/Content/XRFramework/Input/IMC_Weapon_Left.uasset b/Content/XRFramework/Input/IMC_Weapon_Left.uasset
new file mode 100644
index 0000000..53b5107
--- /dev/null
+++ b/Content/XRFramework/Input/IMC_Weapon_Left.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:24865958f25da645237a5bfbe3f4de603a6c3e270428b56de9f9b869f3b6b733
+size 3754
diff --git a/Content/XRFramework/Input/IMC_Weapon_Right.uasset b/Content/XRFramework/Input/IMC_Weapon_Right.uasset
new file mode 100644
index 0000000..908d674
--- /dev/null
+++ b/Content/XRFramework/Input/IMC_Weapon_Right.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:2220ebd2ea2a57c27b33c82b0b3276a373b8aa50da8e85da68994d338a1dd5ea
+size 3769
diff --git a/Content/XRFramework/Materials/DefaultRibbonMaterial_NoGradient.uasset b/Content/XRFramework/Materials/DefaultRibbonMaterial_NoGradient.uasset
new file mode 100644
index 0000000..a492fe0
--- /dev/null
+++ b/Content/XRFramework/Materials/DefaultRibbonMaterial_NoGradient.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:f4796ef8a0d49690f10a61eaa9756d2cda663f5e27137db6a144ceebe2435447
+size 67436
diff --git a/Content/XRFramework/Materials/Functions/MF_OccludedPixels.uasset b/Content/XRFramework/Materials/Functions/MF_OccludedPixels.uasset
new file mode 100644
index 0000000..f696419
--- /dev/null
+++ b/Content/XRFramework/Materials/Functions/MF_OccludedPixels.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:fe9c3eb506fff90f176d2a88f5b1807cacd491fa2c6a9fa4f2a9e2036d69b84e
+size 116119
diff --git a/Content/XRFramework/Materials/MI_Grid_Accent.uasset b/Content/XRFramework/Materials/MI_Grid_Accent.uasset
new file mode 100644
index 0000000..686a632
--- /dev/null
+++ b/Content/XRFramework/Materials/MI_Grid_Accent.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:81c3f0b900043c4be003f8e239b21e3b92414b6aa5e1f8b748761530e9372aa9
+size 114254
diff --git a/Content/XRFramework/Materials/MI_Grid_Default.uasset b/Content/XRFramework/Materials/MI_Grid_Default.uasset
new file mode 100644
index 0000000..5fa651d
--- /dev/null
+++ b/Content/XRFramework/Materials/MI_Grid_Default.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:9c5ef9073b231688b5754c097e37a0ea6d9d99d67570418bb75df0ebcc85e2c8
+size 119057
diff --git a/Content/XRFramework/Materials/MI_Projectile.uasset b/Content/XRFramework/Materials/MI_Projectile.uasset
new file mode 100644
index 0000000..ec568ed
--- /dev/null
+++ b/Content/XRFramework/Materials/MI_Projectile.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:3b2976faa5a5eb4672270546b543e2156f21a6c08ba275e3ad0f692809b37d5e
+size 101581
diff --git a/Content/XRFramework/Materials/MI_VRColorway.uasset b/Content/XRFramework/Materials/MI_VRColorway.uasset
new file mode 100644
index 0000000..0120490
--- /dev/null
+++ b/Content/XRFramework/Materials/MI_VRColorway.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:f664e6a6a5f11fb319b2df21fc908da7c2bb3e6dfb15103fe6018240cdfc5f4a
+size 8878
diff --git a/Content/XRFramework/Materials/M_GridRotation.uasset b/Content/XRFramework/Materials/M_GridRotation.uasset
new file mode 100644
index 0000000..e37eda7
--- /dev/null
+++ b/Content/XRFramework/Materials/M_GridRotation.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:4c7a77d2a94f3d8253f3525b0aa6f5f22c767b55f4c9d9432e0ddce8d217c783
+size 129893
diff --git a/Content/XRFramework/Materials/M_LeftEyeGreen_RightEyeRed.uasset b/Content/XRFramework/Materials/M_LeftEyeGreen_RightEyeRed.uasset
new file mode 100644
index 0000000..6ac90bf
--- /dev/null
+++ b/Content/XRFramework/Materials/M_LeftEyeGreen_RightEyeRed.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:10da0f64f947af67755ba08f39b33a37e7c3aae33346c2b58d0249f3b23fb183
+size 12118
diff --git a/Content/XRFramework/Materials/M_Projectile.uasset b/Content/XRFramework/Materials/M_Projectile.uasset
new file mode 100644
index 0000000..e424298
--- /dev/null
+++ b/Content/XRFramework/Materials/M_Projectile.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:46fcf7c8d8fb61b2e6ae8ac27dcb3654f48d08e330a6604e8527d397fc51f5d0
+size 106534
diff --git a/Content/XRFramework/Materials/M_TeleportCylinder.uasset b/Content/XRFramework/Materials/M_TeleportCylinder.uasset
new file mode 100644
index 0000000..75f7069
--- /dev/null
+++ b/Content/XRFramework/Materials/M_TeleportCylinder.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:e5f5c5914b67b93b11a3f7ded26ca9a6805bf147ddb168025dfb56799ef50074
+size 83829
diff --git a/Content/XRFramework/Materials/M_TeleportNoise.uasset b/Content/XRFramework/Materials/M_TeleportNoise.uasset
new file mode 100644
index 0000000..0daa71c
--- /dev/null
+++ b/Content/XRFramework/Materials/M_TeleportNoise.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:06f16735ac8b2d555134e8051b6848518c018e3ad72c0e31f94870b0121a1dfd
+size 73169
diff --git a/Content/XRFramework/Materials/M_VRCursor.uasset b/Content/XRFramework/Materials/M_VRCursor.uasset
new file mode 100644
index 0000000..834f010
--- /dev/null
+++ b/Content/XRFramework/Materials/M_VRCursor.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:a02584982d3201d37d2953256524adb9ba27b7b5205c7ba0fc65faa2d79bdcb5
+size 56045
diff --git a/Content/XRFramework/Textures/T_Grid.uasset b/Content/XRFramework/Textures/T_Grid.uasset
new file mode 100644
index 0000000..edb10d2
--- /dev/null
+++ b/Content/XRFramework/Textures/T_Grid.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:44c1d931a292273693ff4394d9504966d1a8f36431dafd8e8b4c9c3c5b6c824f
+size 36448
diff --git a/Content/XRFramework/VFX/NPC_VRTemplate.uasset b/Content/XRFramework/VFX/NPC_VRTemplate.uasset
new file mode 100644
index 0000000..095303f
--- /dev/null
+++ b/Content/XRFramework/VFX/NPC_VRTemplate.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:ef474d9c26cba1fb1270952fb1bb3320d8acdeaaed3451c12af6ce13686ef88b
+size 2746
diff --git a/Content/XRFramework/VFX/NS_MenuLaser.uasset b/Content/XRFramework/VFX/NS_MenuLaser.uasset
new file mode 100644
index 0000000..7011b2e
--- /dev/null
+++ b/Content/XRFramework/VFX/NS_MenuLaser.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:e4f1966c980d572857887598d9cc81eeb8598de05a740ba412515f97ad1809a9
+size 282875
diff --git a/Content/XRFramework/VFX/NS_PlayAreaBounds.uasset b/Content/XRFramework/VFX/NS_PlayAreaBounds.uasset
new file mode 100644
index 0000000..43dbef9
--- /dev/null
+++ b/Content/XRFramework/VFX/NS_PlayAreaBounds.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:35ccc4483508403163493111e5ad353b5de05416b4ecf3865a714b79c171dbcf
+size 341574
diff --git a/Content/XRFramework/VFX/NS_TeleportRing.uasset b/Content/XRFramework/VFX/NS_TeleportRing.uasset
new file mode 100644
index 0000000..cbea83b
--- /dev/null
+++ b/Content/XRFramework/VFX/NS_TeleportRing.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:43f39953414e69e9250e61b6b65d9977ec9e4b7dd92b72eeb4a8d0c6d7b3f23f
+size 422501
diff --git a/Content/XRFramework/VFX/NS_TeleportTrace.uasset b/Content/XRFramework/VFX/NS_TeleportTrace.uasset
new file mode 100644
index 0000000..38b344f
--- /dev/null
+++ b/Content/XRFramework/VFX/NS_TeleportTrace.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:ccd994d3715a32c95af05ff8f332253ceac911d7ff63d6b54d3d0a6aad76a2c4
+size 342337
diff --git a/Content/XRMannequins/Animations/A_MannequinsXR_Grasp_Right.uasset b/Content/XRMannequins/Animations/A_MannequinsXR_Grasp_Right.uasset
new file mode 100644
index 0000000..d0e8967
--- /dev/null
+++ b/Content/XRMannequins/Animations/A_MannequinsXR_Grasp_Right.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:85aa88651a9839e95426057de6b8d8cf0abf7e68e3fbbc6cfaf981c950651f05
+size 81728
diff --git a/Content/XRMannequins/Animations/A_MannequinsXR_Idle_Right.uasset b/Content/XRMannequins/Animations/A_MannequinsXR_Idle_Right.uasset
new file mode 100644
index 0000000..bd2b2a0
--- /dev/null
+++ b/Content/XRMannequins/Animations/A_MannequinsXR_Idle_Right.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:d0e134e2a8a58343a1a71a037af1adbeb36d837472766179fa20a0b1ec29a2e4
+size 66747
diff --git a/Content/XRMannequins/Animations/A_MannequinsXR_IndexCurl_Right.uasset b/Content/XRMannequins/Animations/A_MannequinsXR_IndexCurl_Right.uasset
new file mode 100644
index 0000000..b0e42bc
--- /dev/null
+++ b/Content/XRMannequins/Animations/A_MannequinsXR_IndexCurl_Right.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:7feab0fcd6cd8bb1c23acc6947a0f0cd1571e3d7885d6f16df0aaa5a51ae76bb
+size 82023
diff --git a/Content/XRMannequins/Animations/A_MannequinsXR_Point_Right.uasset b/Content/XRMannequins/Animations/A_MannequinsXR_Point_Right.uasset
new file mode 100644
index 0000000..b848646
--- /dev/null
+++ b/Content/XRMannequins/Animations/A_MannequinsXR_Point_Right.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:9d41054dc51ebd87ea0d6d76cf19d9a9282863dc1f88b2ba342dfd62b63f1793
+size 81970
diff --git a/Content/XRMannequins/Animations/A_MannequinsXR_ThumbUp_Right.uasset b/Content/XRMannequins/Animations/A_MannequinsXR_ThumbUp_Right.uasset
new file mode 100644
index 0000000..dd7ed35
--- /dev/null
+++ b/Content/XRMannequins/Animations/A_MannequinsXR_ThumbUp_Right.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:726a3e36cbb7973b85f7f259b959ba7c82888c2b77a7ccc036f6978b3ab0bc16
+size 82084
diff --git a/Content/XRMannequins/Animations/MDT_MannequinsXR.uasset b/Content/XRMannequins/Animations/MDT_MannequinsXR.uasset
new file mode 100644
index 0000000..e7968ad
--- /dev/null
+++ b/Content/XRMannequins/Animations/MDT_MannequinsXR.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:8b5e257560910542340b49c07a4cd88d350177dc99cfc07e1a0f56a0b3da3d10
+size 13445
diff --git a/Content/XRMannequins/BP_MannequinsXR.uasset b/Content/XRMannequins/BP_MannequinsXR.uasset
new file mode 100644
index 0000000..2923792
--- /dev/null
+++ b/Content/XRMannequins/BP_MannequinsXR.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:87280917c4dec6c5723802fe0af8d836260bfe7c14431a3a2720e751ac1393ec
+size 14387
diff --git a/Content/XRMannequins/Materials/Functions/CA_Mannequin.uasset b/Content/XRMannequins/Materials/Functions/CA_Mannequin.uasset
new file mode 100644
index 0000000..4868ff6
--- /dev/null
+++ b/Content/XRMannequins/Materials/Functions/CA_Mannequin.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:d1d617ad3c490faa15b5c04571bb629ec226038df4a752530389dc17b8ed1acc
+size 5224
diff --git a/Content/XRMannequins/Materials/Functions/ChromaticCurve.uasset b/Content/XRMannequins/Materials/Functions/ChromaticCurve.uasset
new file mode 100644
index 0000000..b90c0aa
--- /dev/null
+++ b/Content/XRMannequins/Materials/Functions/ChromaticCurve.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:77518f2b4f885f89204a9ed021e2d9569512539e4e913d3ecbde9e9739a0923e
+size 6166
diff --git a/Content/XRMannequins/Materials/Functions/MF_Diffraction.uasset b/Content/XRMannequins/Materials/Functions/MF_Diffraction.uasset
new file mode 100644
index 0000000..d00d3c3
--- /dev/null
+++ b/Content/XRMannequins/Materials/Functions/MF_Diffraction.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:8e1768e5d8826d5d86ee8d9a4e622dc0807a7ba92c75a38573fe6f85b4ff8a98
+size 32218
diff --git a/Content/XRMannequins/Materials/Functions/MF_logo3layers.uasset b/Content/XRMannequins/Materials/Functions/MF_logo3layers.uasset
new file mode 100644
index 0000000..4318e98
--- /dev/null
+++ b/Content/XRMannequins/Materials/Functions/MF_logo3layers.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:c6f5148bfd6fe342f7a41942d947b1e3e9fdd0ae303e2e04d2be6482d3b15182
+size 56802
diff --git a/Content/XRMannequins/Materials/Functions/ML_BaseColorFallOff.uasset b/Content/XRMannequins/Materials/Functions/ML_BaseColorFallOff.uasset
new file mode 100644
index 0000000..d031c7b
--- /dev/null
+++ b/Content/XRMannequins/Materials/Functions/ML_BaseColorFallOff.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:866fdffd4fbb0787ecf57b397a415d4a13ab8d8ca888b4bf53e128f3b379bb29
+size 14374
diff --git a/Content/XRMannequins/Materials/Instances/Manny/MI_Manny_01.uasset b/Content/XRMannequins/Materials/Instances/Manny/MI_Manny_01.uasset
new file mode 100644
index 0000000..8ec2568
--- /dev/null
+++ b/Content/XRMannequins/Materials/Instances/Manny/MI_Manny_01.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:0a31bf5325e8d5aa27e541ba3ef0f5218cf6f61ab1d1cd9a949fe8fd2c020291
+size 19341
diff --git a/Content/XRMannequins/Materials/Instances/Manny/MI_Manny_02.uasset b/Content/XRMannequins/Materials/Instances/Manny/MI_Manny_02.uasset
new file mode 100644
index 0000000..469fea3
--- /dev/null
+++ b/Content/XRMannequins/Materials/Instances/Manny/MI_Manny_02.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:8b768c1d3124cbde26b3b35ec815eaedfc7810763e620418f3ac007e412038b2
+size 19706
diff --git a/Content/XRMannequins/Materials/Instances/Quinn/MI_Quinn_01.uasset b/Content/XRMannequins/Materials/Instances/Quinn/MI_Quinn_01.uasset
new file mode 100644
index 0000000..98a8368
--- /dev/null
+++ b/Content/XRMannequins/Materials/Instances/Quinn/MI_Quinn_01.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:4fe280b5d4e8e19b74080ab41a9126b34e1dfdc46b04dbb49bebc0579631c34c
+size 17448
diff --git a/Content/XRMannequins/Materials/Instances/Quinn/MI_Quinn_02.uasset b/Content/XRMannequins/Materials/Instances/Quinn/MI_Quinn_02.uasset
new file mode 100644
index 0000000..bf6f13f
--- /dev/null
+++ b/Content/XRMannequins/Materials/Instances/Quinn/MI_Quinn_02.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:f845fdaf8a5ad058e2c2b6fb3fd21513b0f843d68a7074fe0663b13158c031d8
+size 19771
diff --git a/Content/XRMannequins/Materials/M_Mannequin.uasset b/Content/XRMannequins/Materials/M_Mannequin.uasset
new file mode 100644
index 0000000..926fbf0
--- /dev/null
+++ b/Content/XRMannequins/Materials/M_Mannequin.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:dc25d4e8759b0230991da67e8912f991cc77f985c25854d2163879e4de3a80bf
+size 83448
diff --git a/Content/XRMannequins/Meshes/ABP_MannequinsXR.uasset b/Content/XRMannequins/Meshes/ABP_MannequinsXR.uasset
new file mode 100644
index 0000000..eefc000
--- /dev/null
+++ b/Content/XRMannequins/Meshes/ABP_MannequinsXR.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:97f03fd306dd463a3d8092b137a948d38f849d9f51811d1702d897ad8287963a
+size 215702
diff --git a/Content/XRMannequins/Meshes/SKM_MannyXR_left.uasset b/Content/XRMannequins/Meshes/SKM_MannyXR_left.uasset
new file mode 100644
index 0000000..0641775
--- /dev/null
+++ b/Content/XRMannequins/Meshes/SKM_MannyXR_left.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:5f96f17ec7c1a31a9dedc436bc625cc3b32d5c2beff8cb6fa8922b36c84fd282
+size 1347037
diff --git a/Content/XRMannequins/Meshes/SKM_MannyXR_right.uasset b/Content/XRMannequins/Meshes/SKM_MannyXR_right.uasset
new file mode 100644
index 0000000..fa28771
--- /dev/null
+++ b/Content/XRMannequins/Meshes/SKM_MannyXR_right.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:6f0e05254596f9ceb5888fd08618a6ebee3469b9dc174d01dc95764fac566ce1
+size 1346194
diff --git a/Content/XRMannequins/Meshes/SKM_QuinnXR_left.uasset b/Content/XRMannequins/Meshes/SKM_QuinnXR_left.uasset
new file mode 100644
index 0000000..c9534dc
--- /dev/null
+++ b/Content/XRMannequins/Meshes/SKM_QuinnXR_left.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:720a0e8920aa48924c850a6c734b6d786c1549600582707417fbd13d41b4f9e4
+size 1416800
diff --git a/Content/XRMannequins/Meshes/SKM_QuinnXR_right.uasset b/Content/XRMannequins/Meshes/SKM_QuinnXR_right.uasset
new file mode 100644
index 0000000..7fec1dd
--- /dev/null
+++ b/Content/XRMannequins/Meshes/SKM_QuinnXR_right.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:ecabc844ba6daf530357849053ed230416886c97683d510fb3f04ebcf5e403fa
+size 1414716
diff --git a/Content/XRMannequins/Meshes/SK_MannequinsXR.uasset b/Content/XRMannequins/Meshes/SK_MannequinsXR.uasset
new file mode 100644
index 0000000..5424751
--- /dev/null
+++ b/Content/XRMannequins/Meshes/SK_MannequinsXR.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:a59f41fd3ea9c2509b1bff0f1b9db3c477e5aa5e3e30bb02e11a55167dbd7609
+size 54437
diff --git a/Content/XRMannequins/Meshes/SkeletalMeshLODSettings_MannequinsXR.uasset b/Content/XRMannequins/Meshes/SkeletalMeshLODSettings_MannequinsXR.uasset
new file mode 100644
index 0000000..b90b263
--- /dev/null
+++ b/Content/XRMannequins/Meshes/SkeletalMeshLODSettings_MannequinsXR.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:e9d8e45d24fc858a02e326e269339f723e694195f6d94c829a6a569a4b67c72a
+size 8133
diff --git a/Content/XRMannequins/Textures/Manny/T_Manny_01_ASAOPMASK_MSK.uasset b/Content/XRMannequins/Textures/Manny/T_Manny_01_ASAOPMASK_MSK.uasset
new file mode 100644
index 0000000..99a5495
--- /dev/null
+++ b/Content/XRMannequins/Textures/Manny/T_Manny_01_ASAOPMASK_MSK.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:b105c97f736cfa8769656368ee0b05d70c16dfb6b1c1ed34efaf37f8cfe1db4b
+size 464920
diff --git a/Content/XRMannequins/Textures/Manny/T_Manny_01_BN.uasset b/Content/XRMannequins/Textures/Manny/T_Manny_01_BN.uasset
new file mode 100644
index 0000000..f670307
--- /dev/null
+++ b/Content/XRMannequins/Textures/Manny/T_Manny_01_BN.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:f96cebf8654531935ef18d4f6860a39b6abcab1b856d6d7239f4a907ff42d87f
+size 1226703
diff --git a/Content/XRMannequins/Textures/Manny/T_Manny_01_CCRCCPlastic_MSK.uasset b/Content/XRMannequins/Textures/Manny/T_Manny_01_CCRCCPlastic_MSK.uasset
new file mode 100644
index 0000000..f3fc544
--- /dev/null
+++ b/Content/XRMannequins/Textures/Manny/T_Manny_01_CCRCCPlastic_MSK.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:80c4a696338594410906c486d9d2230831e1e85259bd19624be0ede152245463
+size 740508
diff --git a/Content/XRMannequins/Textures/Manny/T_Manny_01_D.uasset b/Content/XRMannequins/Textures/Manny/T_Manny_01_D.uasset
new file mode 100644
index 0000000..7589c58
--- /dev/null
+++ b/Content/XRMannequins/Textures/Manny/T_Manny_01_D.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:49231c4d5fff91bde6e367bccb56fcb0ecad2cfd3bd948b8a2337d18187a3e10
+size 709957
diff --git a/Content/XRMannequins/Textures/Manny/T_Manny_01_MSR_MSK.uasset b/Content/XRMannequins/Textures/Manny/T_Manny_01_MSR_MSK.uasset
new file mode 100644
index 0000000..6195b60
--- /dev/null
+++ b/Content/XRMannequins/Textures/Manny/T_Manny_01_MSR_MSK.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:b3a0b07dbbea0d4499030f5a4f6e6e6ce0eadc37c4083a989d4783da498baddb
+size 804952
diff --git a/Content/XRMannequins/Textures/Manny/T_Manny_01_N.uasset b/Content/XRMannequins/Textures/Manny/T_Manny_01_N.uasset
new file mode 100644
index 0000000..2ddc2f1
--- /dev/null
+++ b/Content/XRMannequins/Textures/Manny/T_Manny_01_N.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:4e3160c4dc0442e80cd27102297f2bf839b09bc1a3e2a737c614ba85e4fd75a7
+size 674320
diff --git a/Content/XRMannequins/Textures/Manny/T_Manny_01_Tan.uasset b/Content/XRMannequins/Textures/Manny/T_Manny_01_Tan.uasset
new file mode 100644
index 0000000..d600443
--- /dev/null
+++ b/Content/XRMannequins/Textures/Manny/T_Manny_01_Tan.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:31cdcd3779814b7c34da8d577a1b3dfdc3030c918e5cf4616c3a7ebdaea1bf1a
+size 182352
diff --git a/Content/XRMannequins/Textures/Manny/T_Manny_02_ASAOPMASK_MSK.uasset b/Content/XRMannequins/Textures/Manny/T_Manny_02_ASAOPMASK_MSK.uasset
new file mode 100644
index 0000000..2952f85
--- /dev/null
+++ b/Content/XRMannequins/Textures/Manny/T_Manny_02_ASAOPMASK_MSK.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:53a34df436ae63521139cf38a84f37e3fbff8955fec86b7ea71055936e9b8888
+size 752490
diff --git a/Content/XRMannequins/Textures/Manny/T_Manny_02_BN.uasset b/Content/XRMannequins/Textures/Manny/T_Manny_02_BN.uasset
new file mode 100644
index 0000000..a6104e1
--- /dev/null
+++ b/Content/XRMannequins/Textures/Manny/T_Manny_02_BN.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:6cfc9a23534b3588b759e42d0d14e33b6b78dfdc990ed2c5622a11c68d01d1b3
+size 1513443
diff --git a/Content/XRMannequins/Textures/Manny/T_Manny_02_CCRCCPlastic_MSK.uasset b/Content/XRMannequins/Textures/Manny/T_Manny_02_CCRCCPlastic_MSK.uasset
new file mode 100644
index 0000000..38b79d1
--- /dev/null
+++ b/Content/XRMannequins/Textures/Manny/T_Manny_02_CCRCCPlastic_MSK.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:60ff3314a4244948372bc638ba88c51d8015196df21c89df84a7dc372a8d8354
+size 1030487
diff --git a/Content/XRMannequins/Textures/Manny/T_Manny_02_D.uasset b/Content/XRMannequins/Textures/Manny/T_Manny_02_D.uasset
new file mode 100644
index 0000000..460f082
--- /dev/null
+++ b/Content/XRMannequins/Textures/Manny/T_Manny_02_D.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:2d87001ca22340f0990aac98a0feb60393faa62490a1ce7bbc7126cab489cea4
+size 1021386
diff --git a/Content/XRMannequins/Textures/Manny/T_Manny_02_MSR_MSK.uasset b/Content/XRMannequins/Textures/Manny/T_Manny_02_MSR_MSK.uasset
new file mode 100644
index 0000000..4378950
--- /dev/null
+++ b/Content/XRMannequins/Textures/Manny/T_Manny_02_MSR_MSK.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:6ef3890133cbd40dd5a92fb4a93069abfc5ba2a3139504457279709efabd3b38
+size 1038455
diff --git a/Content/XRMannequins/Textures/Manny/T_Manny_02_N.uasset b/Content/XRMannequins/Textures/Manny/T_Manny_02_N.uasset
new file mode 100644
index 0000000..a847fc3
--- /dev/null
+++ b/Content/XRMannequins/Textures/Manny/T_Manny_02_N.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:9433f649a62cb7060b997aed55306857d5bd3edaf1647d250a9c44a56d6e0fe5
+size 671878
diff --git a/Content/XRMannequins/Textures/Manny/T_Manny_02_Tan.uasset b/Content/XRMannequins/Textures/Manny/T_Manny_02_Tan.uasset
new file mode 100644
index 0000000..5f3c977
--- /dev/null
+++ b/Content/XRMannequins/Textures/Manny/T_Manny_02_Tan.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:48187c7f67f9991cccd7603833ff5f3c435289e88987ca93528fb27497af7f85
+size 307084
diff --git a/Content/XRMannequins/Textures/Quinn/T_Quinn_01ID_BN.uasset b/Content/XRMannequins/Textures/Quinn/T_Quinn_01ID_BN.uasset
new file mode 100644
index 0000000..4051e86
--- /dev/null
+++ b/Content/XRMannequins/Textures/Quinn/T_Quinn_01ID_BN.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:2fb881a5310d6bbb3044fc69683d2b1dd1a31f006aa266d2b22946a29ae5644c
+size 1087307
diff --git a/Content/XRMannequins/Textures/Quinn/T_Quinn_01ID_D.uasset b/Content/XRMannequins/Textures/Quinn/T_Quinn_01ID_D.uasset
new file mode 100644
index 0000000..7312951
--- /dev/null
+++ b/Content/XRMannequins/Textures/Quinn/T_Quinn_01ID_D.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:ac9e4864aea0618ce031c2ad477bfa6e8df8407f9e71bae6a354f4a24099ba58
+size 711762
diff --git a/Content/XRMannequins/Textures/Quinn/T_Quinn_01ID_MSR_MSK.uasset b/Content/XRMannequins/Textures/Quinn/T_Quinn_01ID_MSR_MSK.uasset
new file mode 100644
index 0000000..9fea3a0
--- /dev/null
+++ b/Content/XRMannequins/Textures/Quinn/T_Quinn_01ID_MSR_MSK.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:949fb0f7bc5ae9d1d3a6036a06ff761b894f3ffd48d37c713ca679204c3ec9a3
+size 699822
diff --git a/Content/XRMannequins/Textures/Quinn/T_Quinn_01ID_N.uasset b/Content/XRMannequins/Textures/Quinn/T_Quinn_01ID_N.uasset
new file mode 100644
index 0000000..3356731
--- /dev/null
+++ b/Content/XRMannequins/Textures/Quinn/T_Quinn_01ID_N.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:6a7cf98000b127030d1564b07014370a55012e1f3562c99d9691a68ac6210155
+size 539456
diff --git a/Content/XRMannequins/Textures/Quinn/T_Quinn_01ID_Tan.uasset b/Content/XRMannequins/Textures/Quinn/T_Quinn_01ID_Tan.uasset
new file mode 100644
index 0000000..271f1fa
--- /dev/null
+++ b/Content/XRMannequins/Textures/Quinn/T_Quinn_01ID_Tan.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:dc77cc7ffb5fa514db7d3c096bfd600ee77c7368ad6af40ace52b8435af30363
+size 128201
diff --git a/Content/XRMannequins/Textures/Quinn/T_Quinn_01_ASAOMASK_MSK.uasset b/Content/XRMannequins/Textures/Quinn/T_Quinn_01_ASAOMASK_MSK.uasset
new file mode 100644
index 0000000..465c008
--- /dev/null
+++ b/Content/XRMannequins/Textures/Quinn/T_Quinn_01_ASAOMASK_MSK.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:87d72f28d1f8715bab4acd9059667c841b42feb38f2c634c2368b237060701a3
+size 466580
diff --git a/Content/XRMannequins/Textures/Quinn/T_Quinn_01_CCRCCPlastic_MSK.uasset b/Content/XRMannequins/Textures/Quinn/T_Quinn_01_CCRCCPlastic_MSK.uasset
new file mode 100644
index 0000000..4b3ad81
--- /dev/null
+++ b/Content/XRMannequins/Textures/Quinn/T_Quinn_01_CCRCCPlastic_MSK.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:7e382153a1d79af6adb0a7626b8935e6111e82ba67a89c004b97db33a50a94a1
+size 708103
diff --git a/Content/XRMannequins/Textures/Quinn/T_Quinn_02ID_BN.uasset b/Content/XRMannequins/Textures/Quinn/T_Quinn_02ID_BN.uasset
new file mode 100644
index 0000000..1772c30
--- /dev/null
+++ b/Content/XRMannequins/Textures/Quinn/T_Quinn_02ID_BN.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:ffd08e14956b5c1d47fe96df3002d129e937917fbb83d051f3fa9ef7217b0ba2
+size 1369126
diff --git a/Content/XRMannequins/Textures/Quinn/T_Quinn_02ID_D.uasset b/Content/XRMannequins/Textures/Quinn/T_Quinn_02ID_D.uasset
new file mode 100644
index 0000000..f8d7eb7
--- /dev/null
+++ b/Content/XRMannequins/Textures/Quinn/T_Quinn_02ID_D.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:7ce98db1ef002599832d13c917b4a5dd3609c194c31e666781bdae9d31ec00c5
+size 949863
diff --git a/Content/XRMannequins/Textures/Quinn/T_Quinn_02ID_MSR_MSK.uasset b/Content/XRMannequins/Textures/Quinn/T_Quinn_02ID_MSR_MSK.uasset
new file mode 100644
index 0000000..f178851
--- /dev/null
+++ b/Content/XRMannequins/Textures/Quinn/T_Quinn_02ID_MSR_MSK.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:df541622450f3b4c68f824ee371414e099d27359fb5592c51f3a0756adec4f37
+size 924014
diff --git a/Content/XRMannequins/Textures/Quinn/T_Quinn_02ID_N.uasset b/Content/XRMannequins/Textures/Quinn/T_Quinn_02ID_N.uasset
new file mode 100644
index 0000000..dcd135d
--- /dev/null
+++ b/Content/XRMannequins/Textures/Quinn/T_Quinn_02ID_N.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:d8dcf71e827099941563156686feca0e65fec33bbd0b08abdef9662027574910
+size 539456
diff --git a/Content/XRMannequins/Textures/Quinn/T_Quinn_02ID_Tan.uasset b/Content/XRMannequins/Textures/Quinn/T_Quinn_02ID_Tan.uasset
new file mode 100644
index 0000000..b24204d
--- /dev/null
+++ b/Content/XRMannequins/Textures/Quinn/T_Quinn_02ID_Tan.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:8ed5901802cbdb712b16c4be732261a568d75b016255a39198d202b34f21ac00
+size 234024
diff --git a/Content/XRMannequins/Textures/Quinn/T_Quinn_02_ASAOMASK_MSK.uasset b/Content/XRMannequins/Textures/Quinn/T_Quinn_02_ASAOMASK_MSK.uasset
new file mode 100644
index 0000000..fb2ba32
--- /dev/null
+++ b/Content/XRMannequins/Textures/Quinn/T_Quinn_02_ASAOMASK_MSK.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:b0f1e19581242c0221e06b03a2f4217b04be739ea133a42321fe91309281abc3
+size 646947
diff --git a/Content/XRMannequins/Textures/Quinn/T_Quinn_02_CCRCCPlastic_MSK.uasset b/Content/XRMannequins/Textures/Quinn/T_Quinn_02_CCRCCPlastic_MSK.uasset
new file mode 100644
index 0000000..8d97205
--- /dev/null
+++ b/Content/XRMannequins/Textures/Quinn/T_Quinn_02_CCRCCPlastic_MSK.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:eda5d50bf61d9196cec21bf0065a7b4b4f1026b56f301843dea7456a960b52b1
+size 878232
diff --git a/Content/XRMannequins/Textures/Shared/T_UE_Logo_M.uasset b/Content/XRMannequins/Textures/Shared/T_UE_Logo_M.uasset
new file mode 100644
index 0000000..cc1702c
--- /dev/null
+++ b/Content/XRMannequins/Textures/Shared/T_UE_Logo_M.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:0661aef80718dec776743f8965ea29bbf8dcf7f20315d92ac675c6db6ca13ed7
+size 40382
diff --git a/OXRHandIntxn.uproject b/OXRHandIntxn.uproject
new file mode 100644
index 0000000..5cb0992
--- /dev/null
+++ b/OXRHandIntxn.uproject
@@ -0,0 +1,40 @@
+{
+ "FileVersion": 3,
+ "EngineAssociation": "5.7",
+ "Category": "",
+ "Description": "",
+ "Plugins": [
+ {
+ "Name": "OpenXR",
+ "Enabled": true
+ },
+ {
+ "Name": "OpenXREyeTracker",
+ "Enabled": true
+ },
+ {
+ "Name": "SenseGlove",
+ "Enabled": true,
+ "MarketplaceURL": "com.epicgames.launcher://ue/marketplace/product/5e1d408f517b4966a269ec1035de0b9e",
+ "SupportedTargetPlatforms": [
+ "Android",
+ "Linux",
+ "Win64"
+ ]
+ }
+ ],
+ "TargetPlatforms": [
+ "Android",
+ "IOS",
+ "Linux",
+ "Mac",
+ "PS4",
+ "PS5",
+ "Windows",
+ "WindowsNoEditor",
+ "Switch",
+ "XboxOne",
+ "XSX",
+ "VisionOS"
+ ]
+}
\ No newline at end of file
diff --git a/Plugins/SenseGlove/.gitattributes b/Plugins/SenseGlove/.gitattributes
new file mode 100644
index 0000000..28ca956
--- /dev/null
+++ b/Plugins/SenseGlove/.gitattributes
@@ -0,0 +1,58 @@
+# Keep CRLF out of the repository
+* text=auto
+
+# In case the git binary/text auto-detection algorithm fails, in order to avoid
+# line-ending normalization, treat these files as binary no matter what
+*.{uasset,umap,3ds,fbx,flac,gif,jif,jiff,jpe,jpeg,jpg,m4a,mp3,mp4,ogg,png,psd,ttf,wav,webp,xcf} binary
+
+# LFS: UE4 file types
+*.uasset filter=lfs diff=lfs merge=lfs -text
+*.umap filter=lfs diff=lfs merge=lfs -text
+
+# LFS: raw content types
+*.3ds filter=lfs diff=lfs merge=lfs -text
+*.fbx filter=lfs diff=lfs merge=lfs -text
+*.flac filter=lfs diff=lfs merge=lfs -text
+*.gif filter=lfs diff=lfs merge=lfs -text
+*.jif filter=lfs diff=lfs merge=lfs -text
+*.jiff filter=lfs diff=lfs merge=lfs -text
+*.jpe filter=lfs diff=lfs merge=lfs -text
+*.jpeg filter=lfs diff=lfs merge=lfs -text
+*.jpg filter=lfs diff=lfs merge=lfs -text
+*.m4a filter=lfs diff=lfs merge=lfs -text
+*.mp3 filter=lfs diff=lfs merge=lfs -text
+*.mp4 filter=lfs diff=lfs merge=lfs -text
+*.ogg filter=lfs diff=lfs merge=lfs -text
+*.png filter=lfs diff=lfs merge=lfs -text
+*.psd filter=lfs diff=lfs merge=lfs -text
+*.ttf filter=lfs diff=lfs merge=lfs -text
+*.wav filter=lfs diff=lfs merge=lfs -text
+*.webp filter=lfs diff=lfs merge=lfs -text
+*.xcf filter=lfs diff=lfs merge=lfs -text
+
+# LFS: everything under Content/
+Content/**/* filter=lfs diff=lfs merge=lfs -text
+
+# LFS: everything under Resources/
+Resources/**/* filter=lfs diff=lfs merge=lfs -text
+
+# LFS: everything under Source/ThirdParty/
+Source/ThirdParty/**/* filter=lfs diff=lfs merge=lfs -text
+# Except
+Source/ThirdParty/**/*.Build.cs filter= diff= merge= text
+
+# LFS: everything under Handbook/dist/
+Handbook/dist/**/* filter=lfs diff=lfs merge=lfs -text
+# Except
+.gitignore filter= diff= merge= text
+.gitattributes filter= diff= merge= text
+
+# LFS: sgum-packager Linux and Windows binaries
+Packager/sgum-packager filter=lfs diff=lfs merge=lfs -text
+Packager/sgum-packager.exe filter=lfs diff=lfs merge=lfs -text
+
+# LFS: sgfakeshm-connect-client abd sgfakeshm-core-client Linux and Windows binaries
+Tuning/sgfakeshm-connect-client filter=lfs diff=lfs merge=lfs -text
+Tuning/sgfakeshm-connect-client.exe filter=lfs diff=lfs merge=lfs -text
+Tuning/sgfakeshm-core-client filter=lfs diff=lfs merge=lfs -text
+Tuning/sgfakeshm-core-client.exe filter=lfs diff=lfs merge=lfs -text
\ No newline at end of file
diff --git a/Plugins/SenseGlove/.gitignore b/Plugins/SenseGlove/.gitignore
new file mode 100644
index 0000000..e7ba676
--- /dev/null
+++ b/Plugins/SenseGlove/.gitignore
@@ -0,0 +1,135 @@
+# Explcitly ignore annoying Windows files, regardless of where they are
+[Dd]esktop.ini
+Thumbs.db
+
+# Explcitly ignore annoying macOS files, regardless of where they are
+.DS_Store
+
+# Makefile; only in the project root
+/Makefile
+
+# CMake; only in the project root
+/CMakeLists.txt
+
+# QMake / Qt Creator
+*.pro
+*Config.pri
+*Defines.pri
+*Header.pri
+*Includes.pri
+*Source.pri
+.qmake.stash
+*.autosave
+*.pro.user
+*.pro.user.*
+CMakeLists.txt.user
+CMakeLists.txt.user.*
+
+# Visual Studio
+.vs/
+*.sln
+*.suo
+*.opensdf
+*.sdf
+*.VC.db
+*.VC.opendb
+
+# Visual Studio Code
+.vscode/
+*.code-workspace
+
+# Code::Blocks
+*.workspace
+
+# CodeLite
+*CodeLitePreProcessor.txt
+
+# KDevelop
+.kdev4/
+*.kdev4
+
+# CLion
+*CodeCompletionFolders.txt
+
+# IntelliJ IDE
+.idea/
+
+# Xcode
+*.xcodeproj
+*.xcworkspace
+
+# Compiled Object files
+*.slo
+*.lo
+*.o
+*.obj
+
+# Precompiled Headers
+*.gch
+*.pch
+
+# Compiled Dynamic libraries
+*.so
+*.dylib
+*.dll
+
+# Fortran module files
+*.mod
+
+# Compiled Static libraries
+*.lai
+*.la
+*.a
+*.lib
+
+# Executables
+*.exe
+*.out
+*.app
+*.ipa
+
+# Precompiled Assets
+SourceArt/**/*.png
+SourceArt/**/*.tga
+
+# Binary Files
+Binaries/*
+Plugins/*/Binaries/*
+
+# Builds
+Build/*
+
+# Whitelist PakBlacklist-.txt files
+!Build/*/
+Build/*/**
+!Build/*/PakBlacklist*.txt
+
+# Don't ignore icon files in Build
+!Build/**/*.ico
+
+# Built data for maps
+*_BuiltData.uasset
+
+# Configuration files generated by the Editor
+Saved/*
+
+# Compiled source files for the engine to use
+Intermediate/*
+Plugins/*/Intermediate/*
+
+# Cache files for the editor to use
+DerivedDataCache/*
+
+# Misc
+.ignore
+
+# Do not ignore anything inside the ThirdParty directory at all
+!Source/ThirdParty/**/*
+
+# The Handbook
+Documentation/
+Handbook/book/
+
+# sgum-packager
+# Do not ignore the sgum-packager Windows binary
+!Packager/sgum-packager.exe
\ No newline at end of file
diff --git a/Plugins/SenseGlove/Config/FilterPlugin.ini b/Plugins/SenseGlove/Config/FilterPlugin.ini
new file mode 100644
index 0000000..b45ca7a
--- /dev/null
+++ b/Plugins/SenseGlove/Config/FilterPlugin.ini
@@ -0,0 +1,5 @@
+[FilterPlugin]
+/Config/FilterPlugin.ini
+/Documentation/the-senseglove-unreal-engine-handbook.epub
+/Documentation/the-senseglove-unreal-engine-handbook.pdf
+/Documentation/the-senseglove-unreal-engine-handbook.zip
diff --git a/Plugins/SenseGlove/Content/Materials/M_SenseGlove_VirtualHand.uasset b/Plugins/SenseGlove/Content/Materials/M_SenseGlove_VirtualHand.uasset
new file mode 100644
index 0000000..960a80c
--- /dev/null
+++ b/Plugins/SenseGlove/Content/Materials/M_SenseGlove_VirtualHand.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:ffc5ec37eff81f3298922c7cc4807144181289129485e3e7bbd0cd10b1186c52
+size 9219
diff --git a/Plugins/SenseGlove/Resources/Icon128.png b/Plugins/SenseGlove/Resources/Icon128.png
new file mode 100644
index 0000000..59687cb
--- /dev/null
+++ b/Plugins/SenseGlove/Resources/Icon128.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:21dd03aae60812bdd01f6afe261accd4d3152be594e097c30f5abf0b5afa5d75
+size 11743
diff --git a/Plugins/SenseGlove/SenseGlove.uplugin b/Plugins/SenseGlove/SenseGlove.uplugin
new file mode 100644
index 0000000..adfbb20
--- /dev/null
+++ b/Plugins/SenseGlove/SenseGlove.uplugin
@@ -0,0 +1,292 @@
+{
+ "FileVersion": 3,
+ "Version": 1,
+ "VersionName": "2.8.0",
+ "FriendlyName": "SenseGlove",
+ "Description": "Integrating the SenseGlove haptic controller into Unreal Engine",
+ "Category": "Virtual Reality",
+ "CreatedBy": "SenseGlove",
+ "CreatedByURL": "https://www.senseglove.com/",
+ "DocsURL": "https://unreal.docs.senseglove.com/",
+ "MarketplaceURL": "com.epicgames.launcher://ue/marketplace/product/5e1d408f517b4966a269ec1035de0b9e",
+ "SupportURL": "https://www.senseglove.com/support/",
+ "EngineVersion": "5.7.0",
+ "CanContainContent": true,
+ "IsBetaVersion": false,
+ "IsExperimentalVersion": false,
+ "Installed": false,
+ "SupportedTargetPlatforms": [
+ "Android",
+ "Linux",
+ "Win64"
+ ],
+ "Modules": [
+ {
+ "Name": "SenseGlove",
+ "Type": "Runtime",
+ "LoadingPhase": "Default",
+ "WhitelistPlatforms": [
+ "Android",
+ "Linux",
+ "LinuxArm64",
+ "Win64"
+ ]
+ },
+ {
+ "Name": "SenseGloveAndroid",
+ "Type": "Runtime",
+ "LoadingPhase": "PreDefault",
+ "WhitelistPlatforms": [
+ "Android",
+ "Linux",
+ "LinuxArm64",
+ "Win64"
+ ]
+ },
+ {
+ "Name": "SenseGloveBackend",
+ "Type": "Runtime",
+ "LoadingPhase": "PreDefault",
+ "WhitelistPlatforms": [
+ "Android",
+ "Linux",
+ "LinuxArm64",
+ "Win64"
+ ]
+ },
+ {
+ "Name": "SenseGloveBackendKismet",
+ "Type": "Runtime",
+ "LoadingPhase": "PreDefault",
+ "WhitelistPlatforms": [
+ "Android",
+ "Linux",
+ "LinuxArm64",
+ "Win64"
+ ]
+ },
+ {
+ "Name": "SenseGloveBuildHacks",
+ "Type": "Runtime",
+ "LoadingPhase": "PreDefault",
+ "WhitelistPlatforms": [
+ "Android",
+ "Linux",
+ "LinuxArm64",
+ "Win64"
+ ]
+ },
+ {
+ "Name": "SenseGloveConnect",
+ "Type": "Runtime",
+ "LoadingPhase": "PreDefault",
+ "WhitelistPlatforms": [
+ "Android",
+ "Linux",
+ "LinuxArm64",
+ "Win64"
+ ]
+ },
+ {
+ "Name": "SenseGloveConnectImpl",
+ "Type": "Runtime",
+ "LoadingPhase": "PreDefault",
+ "WhitelistPlatforms": [
+ "Android",
+ "Linux",
+ "LinuxArm64",
+ "Win64"
+ ]
+ },
+ {
+ "Name": "SenseGloveConnectKismet",
+ "Type": "Runtime",
+ "LoadingPhase": "PreDefault",
+ "WhitelistPlatforms": [
+ "Android",
+ "Linux",
+ "LinuxArm64",
+ "Win64"
+ ]
+ },
+ {
+ "Name": "SenseGloveCore",
+ "Type": "Runtime",
+ "LoadingPhase": "PreDefault",
+ "WhitelistPlatforms": [
+ "Android",
+ "Linux",
+ "LinuxArm64",
+ "Win64"
+ ]
+ },
+ {
+ "Name": "SenseGloveCoreImpl",
+ "Type": "Runtime",
+ "LoadingPhase": "PreDefault",
+ "WhitelistPlatforms": [
+ "Android",
+ "Linux",
+ "LinuxArm64",
+ "Win64"
+ ]
+ },
+ {
+ "Name": "SenseGloveCoreKismet",
+ "Type": "Runtime",
+ "LoadingPhase": "PreDefault",
+ "WhitelistPlatforms": [
+ "Android",
+ "Linux",
+ "LinuxArm64",
+ "Win64"
+ ]
+ },
+ {
+ "Name": "SenseGloveDebug",
+ "Type": "Runtime",
+ "LoadingPhase": "PreDefault",
+ "WhitelistPlatforms": [
+ "Android",
+ "Linux",
+ "LinuxArm64",
+ "Win64"
+ ]
+ },
+ {
+ "Name": "SenseGloveDebugKismet",
+ "Type": "Runtime",
+ "LoadingPhase": "PreDefault",
+ "WhitelistPlatforms": [
+ "Android",
+ "Linux",
+ "LinuxArm64",
+ "Win64"
+ ]
+ },
+ {
+ "Name": "SenseGloveEditor",
+ "Type": "Editor",
+ "LoadingPhase": "PostEngineInit",
+ "WhitelistPlatforms": [
+ "Linux",
+ "LinuxArm64",
+ "Win64"
+ ]
+ },
+ {
+ "Name": "SenseGloveInterop",
+ "Type": "Runtime",
+ "LoadingPhase": "PreDefault",
+ "WhitelistPlatforms": [
+ "Android",
+ "Linux",
+ "LinuxArm64",
+ "Win64"
+ ]
+ },
+ {
+ "Name": "SenseGloveKismet",
+ "Type": "Runtime",
+ "LoadingPhase": "PreDefault",
+ "WhitelistPlatforms": [
+ "Android",
+ "Linux",
+ "LinuxArm64",
+ "Win64"
+ ]
+ },
+ {
+ "Name": "SenseGloveLog",
+ "Type": "Runtime",
+ "LoadingPhase": "PreDefault",
+ "WhitelistPlatforms": [
+ "Android",
+ "Linux",
+ "LinuxArm64",
+ "Win64"
+ ]
+ },
+ {
+ "Name": "SenseGloveSettings",
+ "Type": "Runtime",
+ "LoadingPhase": "PreDefault",
+ "WhitelistPlatforms": [
+ "Android",
+ "Linux",
+ "LinuxArm64",
+ "Win64"
+ ]
+ },
+ {
+ "Name": "SenseGloveSettingsKismet",
+ "Type": "Runtime",
+ "LoadingPhase": "PreDefault",
+ "WhitelistPlatforms": [
+ "Android",
+ "Linux",
+ "LinuxArm64",
+ "Win64"
+ ]
+ },
+ {
+ "Name": "SenseGloveTracking",
+ "Type": "Runtime",
+ "LoadingPhase": "PostConfigInit",
+ "WhitelistPlatforms": [
+ "Android",
+ "Linux",
+ "LinuxArm64",
+ "Win64"
+ ]
+ },
+ {
+ "Name": "SenseGloveTrackingKismet",
+ "Type": "Runtime",
+ "LoadingPhase": "PostConfigInit",
+ "WhitelistPlatforms": [
+ "Android",
+ "Linux",
+ "LinuxArm64",
+ "Win64"
+ ]
+ },
+ {
+ "Name": "SenseGloveTypes",
+ "Type": "Runtime",
+ "LoadingPhase": "PreDefault",
+ "WhitelistPlatforms": [
+ "Android",
+ "Linux",
+ "LinuxArm64",
+ "Win64"
+ ]
+ },
+ {
+ "Name": "SenseGloveUtils",
+ "Type": "Runtime",
+ "LoadingPhase": "PreDefault",
+ "WhitelistPlatforms": [
+ "Android",
+ "Linux",
+ "LinuxArm64",
+ "Win64"
+ ]
+ }
+ ],
+ "Plugins": [
+ {
+ "Name": "AndroidPermission",
+ "Enabled": true
+ },
+ {
+ "Name": "OpenXR",
+ "Enabled": true,
+ "PlatformAllowList": [
+ "Android",
+ "Linux",
+ "Win64"
+ ]
+ }
+ ]
+}
diff --git a/Plugins/SenseGlove/Source/SenseGlove/Private/SenseGlove.cpp b/Plugins/SenseGlove/Source/SenseGlove/Private/SenseGlove.cpp
new file mode 100644
index 0000000..ac13303
--- /dev/null
+++ b/Plugins/SenseGlove/Source/SenseGlove/Private/SenseGlove.cpp
@@ -0,0 +1,40 @@
+/**
+ * @file
+ *
+ * @author Mamadou Babaei
+ *
+ * @section LICENSE
+ *
+ * (The MIT License)
+ *
+ * Copyright (c) 2020 - 2026 SenseGlove
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * @section DESCRIPTION
+ *
+ *
+ */
+
+
+#include "SenseGlove.h"
+#include "Modules/ModuleManager.h"
+#include "SenseGloveModule.h"
+
+IMPLEMENT_MODULE(FSenseGloveModule, SenseGlove)
diff --git a/Plugins/SenseGlove/Source/SenseGlove/Private/SenseGlove.h b/Plugins/SenseGlove/Source/SenseGlove/Private/SenseGlove.h
new file mode 100644
index 0000000..334f908
--- /dev/null
+++ b/Plugins/SenseGlove/Source/SenseGlove/Private/SenseGlove.h
@@ -0,0 +1,38 @@
+/**
+ * @file
+ *
+ * @author Mamadou Babaei
+ *
+ * @section LICENSE
+ *
+ * (The MIT License)
+ *
+ * Copyright (c) 2020 - 2026 SenseGlove
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * @section DESCRIPTION
+ *
+ *
+ */
+
+
+#pragma once
+
+#include "CoreMinimal.h"
diff --git a/Plugins/SenseGlove/Source/SenseGlove/Private/SenseGlove/Animation/SGVirtualHandAnimInstance.cpp b/Plugins/SenseGlove/Source/SenseGlove/Private/SenseGlove/Animation/SGVirtualHandAnimInstance.cpp
new file mode 100644
index 0000000..a2664fd
--- /dev/null
+++ b/Plugins/SenseGlove/Source/SenseGlove/Private/SenseGlove/Animation/SGVirtualHandAnimInstance.cpp
@@ -0,0 +1,46 @@
+/**
+ * @file
+ *
+ * @author Mamadou Babaei
+ *
+ * @section LICENSE
+ *
+ * (The MIT License)
+ *
+ * Copyright (c) 2020 - 2026 SenseGlove
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * @section DESCRIPTION
+ *
+ *
+ */
+
+
+#include "SenseGlove/Animation/SGVirtualHandAnimInstance.h"
+
+USGVirtualHandAnimInstance::USGVirtualHandAnimInstance(const FObjectInitializer& ObjectInitializer)
+ : Super(ObjectInitializer)
+{
+}
+
+FSGVirtualHandAnimInstanceProxy* USGVirtualHandAnimInstance::CreateAnimInstanceProxy()
+{
+ return new FSGVirtualHandAnimInstanceProxy();
+}
\ No newline at end of file
diff --git a/Plugins/SenseGlove/Source/SenseGlove/Private/SenseGlove/Animation/SGVirtualHandAnimInstanceProxy.cpp b/Plugins/SenseGlove/Source/SenseGlove/Private/SenseGlove/Animation/SGVirtualHandAnimInstanceProxy.cpp
new file mode 100644
index 0000000..eebcc3c
--- /dev/null
+++ b/Plugins/SenseGlove/Source/SenseGlove/Private/SenseGlove/Animation/SGVirtualHandAnimInstanceProxy.cpp
@@ -0,0 +1,231 @@
+/**
+ * @file
+ *
+ * @author Mamadou Babaei
+ *
+ * @section LICENSE
+ *
+ * (The MIT License)
+ *
+ * Copyright (c) 2020 - 2026 SenseGlove
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * @section DESCRIPTION
+ *
+ *
+ */
+
+
+#include "SenseGlove/Animation/SGVirtualHandAnimInstanceProxy.h"
+
+#include "Animation/AnimInstance.h"
+#include "Animation/Skeleton.h"
+#include "BoneContainer.h"
+#include "BoneControllers/AnimNode_ModifyBone.h"
+#include "BonePose.h"
+#include "Containers/Array.h"
+#include "Containers/Map.h"
+#include "Containers/UnrealString.h"
+#include "Engine/SkeletalMesh.h"
+#include "Math/Rotator.h"
+
+#include "SenseGlove/Components/SGVirtualHandComponent.h"
+
+FSGVirtualHandAnimInstanceProxy::FSGVirtualHandAnimInstanceProxy()
+ : FAnimInstanceProxy{}
+{
+ VirtualHand = nullptr;
+ HandMeshAsset = nullptr;
+ HandSkeleton = nullptr;
+
+ bVirtualHandVisible = false;
+ bMarkRenderStateDirty = false;
+
+ bShouldAnimationApplyBoneLocation = false;
+}
+
+FSGVirtualHandAnimInstanceProxy::FSGVirtualHandAnimInstanceProxy(UAnimInstance* Instance)
+ : FAnimInstanceProxy{Instance}
+{
+ VirtualHand = nullptr;
+ HandMeshAsset = nullptr;
+ HandSkeleton = nullptr;
+
+ bVirtualHandVisible = false;
+ bMarkRenderStateDirty = false;
+
+ bShouldAnimationApplyBoneLocation = false;
+}
+
+void FSGVirtualHandAnimInstanceProxy::PreEvaluateAnimation(UAnimInstance* InAnimInstance)
+{
+ FAnimInstanceProxy::PreEvaluateAnimation(InAnimInstance);
+
+ if (!IsValid(InAnimInstance))
+ {
+ return;
+ }
+
+ VirtualHand = Cast(GetSkelMeshComponent());
+ if (!IsValid(VirtualHand))
+ {
+ return;
+ }
+
+ HandMeshAsset = VirtualHand->GetSkeletalMeshAsset();
+ if (!IsValid(HandMeshAsset))
+ {
+ return;
+ }
+
+ HandSkeleton = HandMeshAsset->GetSkeleton();
+ if (!IsValid(HandSkeleton))
+ {
+ return;
+ }
+
+ bVirtualHandVisible = VirtualHand->IsVisible() && VirtualHand->GetVisibleFlag();
+ if (!bVirtualHandVisible)
+ {
+ return;
+ }
+
+ HandTrackingState = VirtualHand->GetHandTrackingState();
+ if (!HandTrackingState.bValid)
+ {
+ return;
+ }
+ ensureAlwaysMsgf(HandTrackingState.HandKeyLocations.Num() == HandTrackingState.HandKeyRotations.Num(),
+ TEXT("Mismatched hand tracking state locations and rotations!"));
+
+ BoneNamesMap.Empty();
+ for (TArray::SizeType HandKeyIndex = 0;
+ HandKeyIndex < HandTrackingState.HandKeyRotations.Num();
+ ++HandKeyIndex)
+ {
+ const FName& BoneName{VirtualHand->GetHandBoneName(HandKeyIndex)};
+ BoneNamesMap.Add(HandKeyIndex, BoneName);
+ }
+
+ const FSGVirtualHandAnimationSettings AnimationSettings{VirtualHand->GetVirtualHandSettings().AnimationSettings};
+ AnimationBoneCorrectionOffsetTransform = FTransform{AnimationSettings.AnimationBoneRotationCorrectionOffset};
+ bShouldAnimationApplyBoneLocation = AnimationSettings.bShouldAnimationApplyBoneLocation;
+}
+
+bool FSGVirtualHandAnimInstanceProxy::Evaluate(FPoseContext& Output)
+{
+ if (!bVirtualHandVisible)
+ {
+ return FAnimInstanceProxy::Evaluate(Output);
+ }
+
+ if (!HandTrackingState.bValid)
+ {
+ return FAnimInstanceProxy::Evaluate(Output);
+ }
+
+ FComponentSpacePoseContext PoseContext{Output.AnimInstanceProxy};
+ PoseContext.Pose.InitPose(Output.Pose);
+
+ for (const TTuple::SizeType, FName>& Bone: BoneNamesMap)
+ {
+ const TArray::SizeType HandKeyIndex = Bone.Key;
+ const FName& BoneName{Bone.Value};
+ if (BoneName == NAME_None)
+ {
+ continue;
+ }
+
+ const int32 BoneIndex = GetRequiredBones().GetPoseBoneIndexForBoneName(BoneName);
+ if (BoneIndex == INDEX_NONE)
+ {
+ continue;
+ }
+
+ FBoneReference BoneReference;
+ BoneReference.BoneName = BoneName;
+ BoneReference.BoneIndex = BoneIndex;
+ BoneReference.bUseSkeletonIndex = true;
+
+ const FVector& HandKeyLocation{HandTrackingState.HandKeyLocations[HandKeyIndex]};
+ const FQuat& HandKeyRotation{HandTrackingState.HandKeyRotations[HandKeyIndex]};
+ const FTransform HandKeyTransform{HandKeyRotation, HandKeyLocation};
+
+ const FTransform BoneTransform{AnimationBoneCorrectionOffsetTransform * HandKeyTransform};
+ FRotator BoneRotation{BoneTransform.GetRotation().Rotator()};
+
+ FAnimNode_ModifyBone BoneController;
+ BoneController.BoneToModify = MoveTemp(BoneReference);
+ BoneController.Rotation = MoveTemp(BoneRotation);
+ BoneController.RotationMode = EBoneModificationMode::BMM_Replace;
+ BoneController.RotationSpace = EBoneControlSpace::BCS_WorldSpace;
+ BoneController.ScaleMode = EBoneModificationMode::BMM_Ignore;
+
+ if (bShouldAnimationApplyBoneLocation)
+ {
+ FVector BoneTranslation{BoneTransform.GetLocation()};
+ BoneController.TranslationMode = EBoneModificationMode::BMM_Replace;
+ BoneController.TranslationSpace = EBoneControlSpace::BCS_WorldSpace;
+ BoneController.Translation = MoveTemp(BoneTranslation);
+ }
+ else
+ {
+ BoneController.TranslationMode = EBoneModificationMode::BMM_Ignore;
+ }
+
+ const bool bValidToEvaluate =
+ BoneController.IsValidToEvaluate(HandSkeleton, Output.Pose.GetBoneContainer());
+ if (!bValidToEvaluate)
+ {
+ continue;
+ }
+
+ TArray BoneTransforms;
+ BoneController.EvaluateSkeletalControl_AnyThread(PoseContext, BoneTransforms);
+
+ const TArray::SizeType BoneTransformsNum = BoneTransforms.Num();
+ if (BoneTransformsNum > 0)
+ {
+ PoseContext.Pose.LocalBlendCSBoneTransforms(BoneTransforms, 1.0f);
+ PoseContext.Pose.ConvertComponentPosesToLocalPoses(PoseContext.Pose, Output.Pose);
+
+ bMarkRenderStateDirty = true;
+ }
+ }
+
+ return true;
+}
+
+void FSGVirtualHandAnimInstanceProxy::PostEvaluate(UAnimInstance* InAnimInstance)
+{
+ FAnimInstanceProxy::PostEvaluate(InAnimInstance);
+
+ if (!bMarkRenderStateDirty)
+ {
+ return;
+ }
+
+ if (!IsValid(VirtualHand))
+ {
+ return;
+ }
+
+ VirtualHand->MarkRenderStateDirty();
+}
\ No newline at end of file
diff --git a/Plugins/SenseGlove/Source/SenseGlove/Private/SenseGlove/Components/SGGrabComponent.cpp b/Plugins/SenseGlove/Source/SenseGlove/Private/SenseGlove/Components/SGGrabComponent.cpp
new file mode 100644
index 0000000..7a91a65
--- /dev/null
+++ b/Plugins/SenseGlove/Source/SenseGlove/Private/SenseGlove/Components/SGGrabComponent.cpp
@@ -0,0 +1,177 @@
+/**
+ * @file
+ *
+ * @author Mamadou Babaei
+ *
+ * @section LICENSE
+ *
+ * (The MIT License)
+ *
+ * Copyright (c) 2020 - 2026 SenseGlove
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * @section DESCRIPTION
+ *
+ *
+ */
+
+
+#include "SenseGlove/Components/SGGrabComponent.h"
+
+#include "Components/PrimitiveComponent.h"
+#include "GameFramework/Actor.h"
+
+#include "SGSettings/SGSettings.h"
+
+struct USGGrabComponent::FImpl
+{
+ /************************
+ * Static methods
+ ************************/
+
+ FORCEINLINE static const FName& GetDefaultGrabAttachPointSocketName()
+ {
+ const USGSettings* Settings{USGSettings::GetInstance()};
+ const FName& SocketName{Settings->GetVirtualHandSettings().GrabSettings.GrabAttachPointSocketName};
+ return SocketName;
+ }
+
+ /************************
+ * Owner object
+ ************************/
+
+ USGGrabComponent* Owner;
+
+ /************************
+ * Constructor / Destructor
+ ************************/
+
+ explicit FImpl(USGGrabComponent* InOwner);
+ ~FImpl();
+
+ /************************
+ * Default copy constructor & copy assignment operator
+ ************************/
+
+ FImpl(const FImpl& Rhs) = default;
+ FImpl& operator=(const FImpl& Rhs) = default;
+
+ /************************
+ * Methods
+ ************************/
+};
+
+USGGrabComponent* USGGrabComponent::GetGrabComponent(const AActor* Actor)
+{
+ if (!ensureAlwaysMsgf(IsValid(Actor), TEXT("%s"), TEXT("ERROR: invalid actor!")))
+ {
+ return nullptr;
+ }
+
+ USGGrabComponent* GrabComponent = Cast(
+ Actor->GetComponentByClass(USGGrabComponent::StaticClass()));
+ return GrabComponent;
+}
+
+bool USGGrabComponent::IsGrabbable(const AActor* Actor)
+{
+ if (!ensureAlwaysMsgf(IsValid(Actor), TEXT("%s"), TEXT("ERROR: invalid actor!")))
+ {
+ return false;
+ }
+
+ const USGGrabComponent* GrabComponent = Cast(
+ Actor->GetComponentByClass(USGGrabComponent::StaticClass()));
+ if (!IsValid(GrabComponent))
+ {
+ return false;
+ }
+
+ return true;
+}
+
+USGGrabComponent::USGGrabComponent(const FObjectInitializer& ObjectInitializer)
+ : Super(ObjectInitializer),
+ Pimpl(TUniquePtr(new FImpl{this}, PimplDeleter))
+{
+ AttachmentLocationRule = EAttachmentRule::SnapToTarget;
+ AttachmentRotationRule = EAttachmentRule::KeepRelative;
+ AttachmentScaleRule = EAttachmentRule::KeepWorld;
+ bAttachmentWeldSimulatedBodies = true;
+
+ AttachmentSocketName = FImpl::GetDefaultGrabAttachPointSocketName();
+
+ DetachmentLocationRule = EDetachmentRule::KeepWorld;
+ DetachmentRotationRule = EDetachmentRule::KeepWorld;
+ DetachmentScaleRule = EDetachmentRule::KeepWorld;
+ bDetachmentCallModify = false;
+
+ bAffectPhysicsState = true;
+}
+
+USGGrabComponent::FImpl::FImpl(USGGrabComponent* InOwner)
+ : Owner(InOwner)
+{
+}
+
+USGGrabComponent::FImpl::~FImpl() = default;
+
+void USGGrabComponent::FImplDeleter::operator()(const FImpl* P) const
+{
+ delete P;
+}
+
+void USGGrabComponent::SimulatePhysics(const bool bSimulatePhysics) const
+{
+ const AActor* OwnerActor{GetOwner()};
+ if (!ensureAlwaysMsgf(IsValid(OwnerActor), TEXT("Invalid OwnerActor!")))
+ {
+ return;
+ }
+
+ UPrimitiveComponent* ActorRootComponent{Cast(OwnerActor->GetRootComponent())};
+ if (!ensureAlwaysMsgf(IsValid(ActorRootComponent),
+ TEXT("RootComponent is not a valid UPrimativeComponent!")))
+ {
+ return;
+ }
+
+ ActorRootComponent->SetEnableGravity(bSimulatePhysics);
+ ActorRootComponent->SetSimulatePhysics(bSimulatePhysics);
+
+ if (bSimulatePhysics)
+ {
+ ActorRootComponent->WakeRigidBody();
+ }
+ else
+ {
+ ActorRootComponent->PutRigidBodyToSleep();
+ }
+}
+
+void USGGrabComponent::BeginPlay()
+{
+ Super::BeginPlay();
+
+ if (bAffectPhysicsState)
+ {
+ SimulatePhysics(true);
+ }
+}
\ No newline at end of file
diff --git a/Plugins/SenseGlove/Source/SenseGlove/Private/SenseGlove/Components/SGHandTrackerComponent.cpp b/Plugins/SenseGlove/Source/SenseGlove/Private/SenseGlove/Components/SGHandTrackerComponent.cpp
new file mode 100644
index 0000000..ed889d5
--- /dev/null
+++ b/Plugins/SenseGlove/Source/SenseGlove/Private/SenseGlove/Components/SGHandTrackerComponent.cpp
@@ -0,0 +1,181 @@
+/**
+ * @file
+ *
+ * @author Mamadou Babaei
+ *
+ * @section LICENSE
+ *
+ * (The MIT License)
+ *
+ * Copyright (c) 2020 - 2026 SenseGlove
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * @section DESCRIPTION
+ *
+ *
+ */
+
+
+#include "SenseGlove/Components/SGHandTrackerComponent.h"
+
+#include "SGDebug/SGDebugVirtualHand.h"
+#include "SGTracking/SGXRTracker.h"
+
+struct USGHandTrackerComponent::FImpl
+{
+ /************************
+ * Static methods
+ ************************/
+
+ /************************
+ * Member variables
+ ************************/
+
+ FXRHandTrackingState HandTrackingState;
+
+ /************************
+ * Owner object
+ ************************/
+
+ USGHandTrackerComponent* Owner;
+
+ /************************
+ * Constructor / Destructor
+ ************************/
+
+ explicit FImpl(USGHandTrackerComponent* InOwner);
+ ~FImpl();
+
+ /************************
+ * Default copy constructor & copy assignment operator
+ ************************/
+
+ FImpl(const FImpl& Rhs) = default;
+ FImpl& operator=(const FImpl& Rhs) = default;
+
+ /************************
+ * Methods
+ ************************/
+
+ void UpdateXRData();
+
+ void Visualize() const;
+};
+
+USGHandTrackerComponent::USGHandTrackerComponent(const FObjectInitializer& ObjectInitializer)
+ : Super(ObjectInitializer),
+ Pimpl(TUniquePtr(new FImpl{this}, PimplDeleter))
+{
+ PrimaryComponentTick.bTickEvenWhenPaused = false;
+ PrimaryComponentTick.bCanEverTick = true;
+ PrimaryComponentTick.bStartWithTickEnabled = true;
+ bTickInEditor = false;
+ bNeverNeedsRenderUpdate = true;
+ bAllowConcurrentTick = true;
+
+ bRight = true;
+
+ bVisualize = false;
+ VisualizationSettings.Length = 1.0f;
+ VisualizationSettings.XAxisColor = FColor::Red;
+ VisualizationSettings.YAxisColor = FColor::Green;
+ VisualizationSettings.ZAxisColor = FColor::Blue;
+ VisualizationSettings.bPersistentLines = false;
+ VisualizationSettings.LifeTimeModifier = 1.1f;
+ VisualizationSettings.DepthPriority = 0;
+ VisualizationSettings.Thickness = 0.15f;
+}
+
+void USGHandTrackerComponent::SetRight(const bool bInRight)
+{
+ bRight = bInRight;
+
+ Pimpl->UpdateXRData();
+}
+
+void USGHandTrackerComponent::InitializeComponent()
+{
+ Super::InitializeComponent();
+
+ Pimpl->UpdateXRData();
+}
+
+void USGHandTrackerComponent::BeginPlay()
+{
+ Super::BeginPlay();
+
+ Pimpl->UpdateXRData();
+}
+
+void USGHandTrackerComponent::TickComponent(
+ const float DeltaTime, const ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction)
+{
+ Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
+
+ Pimpl->UpdateXRData();
+
+ if (IsVisualized())
+ {
+ Pimpl->Visualize();
+ }
+}
+
+const FXRHandTrackingState& USGHandTrackerComponent::GetHandTrackingState() const
+{
+ return Pimpl->HandTrackingState;
+}
+
+USGHandTrackerComponent::FImpl::FImpl(USGHandTrackerComponent* InOwner)
+ : Owner(InOwner)
+{
+}
+
+USGHandTrackerComponent::FImpl::~FImpl() = default;
+
+void USGHandTrackerComponent::FImpl::UpdateXRData()
+{
+ const EControllerHand Hand = Owner->IsRight() ? EControllerHand::Right : EControllerHand::Left;
+
+ FXRHandTrackingState State;
+ State.bValid = false;
+ const bool bGotHandTrackingState =
+ FSGXRTracker::GetHandTrackingState(
+ Owner, EXRSpaceType::UnrealWorldSpace, Hand, State);
+
+ (void) bGotHandTrackingState;
+ HandTrackingState = MoveTemp(State);
+}
+
+void USGHandTrackerComponent::FImpl::Visualize() const
+{
+ const UWorld* World{Owner->GetWorld()};
+
+ if (!HandTrackingState.bValid)
+ {
+ return;
+ }
+
+ FSGDebugVirtualHand::Draw(World, HandTrackingState, Owner->VisualizationSettings);
+}
+
+void USGHandTrackerComponent::FImplDeleter::operator()(const FImpl* P) const
+{
+ delete P;
+}
\ No newline at end of file
diff --git a/Plugins/SenseGlove/Source/SenseGlove/Private/SenseGlove/Components/SGHapticsComponent.cpp b/Plugins/SenseGlove/Source/SenseGlove/Private/SenseGlove/Components/SGHapticsComponent.cpp
new file mode 100644
index 0000000..8d09fa3
--- /dev/null
+++ b/Plugins/SenseGlove/Source/SenseGlove/Private/SenseGlove/Components/SGHapticsComponent.cpp
@@ -0,0 +1,276 @@
+/**
+ * @file
+ *
+ * @author Mamadou Babaei
+ *
+ * @section LICENSE
+ *
+ * (The MIT License)
+ *
+ * Copyright (c) 2020 - 2026 SenseGlove
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * @section DESCRIPTION
+ *
+ *
+ */
+
+
+#include "SenseGlove/Components/SGHapticsComponent.h"
+
+#include "SGCore/SGCustomWaveform.h"
+#include "SGCore/SGHapticGlove.h"
+#include "SGCore/SGHandLayer.h"
+#include "SGTracking/SGGloveTracker.h"
+
+struct USGHapticsComponent::FImpl
+{
+ /************************
+ * Static methods
+ ************************/
+
+ /************************
+ * Owner object
+ ************************/
+
+ USGHapticsComponent* Owner;
+
+ /************************
+ * Constructor / Destructor
+ ************************/
+
+ explicit FImpl(USGHapticsComponent* InOwner);
+ ~FImpl();
+
+ /************************
+ * Default copy constructor & copy assignment operator
+ ************************/
+
+ FImpl(const FImpl& Rhs) = default;
+ FImpl& operator=(const FImpl& Rhs) = default;
+
+ /************************
+ * Methods
+ ************************/
+
+ FORCEINLINE USGHapticGlove* GetGlove() const
+ {
+ const USGGloveTracker* GloveTracker{USGGloveTracker::GetInstance(Owner->GetOuter())};
+ USGHapticGlove* Glove{IsValid(GloveTracker) ? GloveTracker->GetGlove(Owner->IsRight()) : nullptr};
+ return Glove;
+ }
+
+ void AutoStopAllHaptics() const;
+};
+
+USGHapticsComponent::USGHapticsComponent(const FObjectInitializer& ObjectInitializer)
+ : Super(ObjectInitializer),
+ Pimpl(TUniquePtr(new FImpl{this}, PimplDeleter))
+{
+ bRight = true;
+ bAutoStopAllHaptics = true;
+}
+
+void USGHapticsComponent::SetRight(const bool bInRight)
+{
+ bRight = bInRight;
+
+ if (bAutoStopAllHaptics)
+ {
+ Pimpl->AutoStopAllHaptics();
+ }
+}
+
+void USGHapticsComponent::UninitializeComponent()
+{
+ Super::UninitializeComponent();
+
+ if (bAutoStopAllHaptics)
+ {
+ Pimpl->AutoStopAllHaptics();
+ }
+}
+
+void USGHapticsComponent::EndPlay(const EEndPlayReason::Type EndPlayReason)
+{
+ Super::EndPlay(EndPlayReason);
+
+ if (bAutoStopAllHaptics)
+ {
+ Pimpl->AutoStopAllHaptics();
+ }
+}
+
+void USGHapticsComponent::StopHaptics()
+{
+ USGHapticGlove* Glove{Pimpl->GetGlove()};
+ if (IsValid(Glove) && Glove->IsConnected())
+ {
+ Glove->StopHaptics();
+ }
+}
+
+void USGHapticsComponent::StopVibrations()
+{
+ USGHapticGlove* Glove{Pimpl->GetGlove()};
+ if (IsValid(Glove) && Glove->IsConnected())
+ {
+ Glove->StopVibrations();
+ }
+}
+
+bool USGHapticsComponent::SendHaptics()
+{
+ USGHapticGlove* Glove{Pimpl->GetGlove()};
+ if (IsValid(Glove) && Glove->IsConnected())
+ {
+ return Glove->SendHaptics();
+ }
+
+ return false;
+}
+
+bool USGHapticsComponent::SupportsCustomWaveform(const ESGHapticLocation AtLocation) const
+{
+ USGHapticGlove* Glove{Pimpl->GetGlove()};
+ if (IsValid(Glove) && Glove->IsConnected())
+ {
+ return Glove->SupportsCustomWaveform(AtLocation);
+ }
+
+ return false;
+}
+
+bool USGHapticsComponent::SendCustomWaveform(
+ USGCustomWaveform* OutWaveform, const ESGHapticLocation Location)
+{
+ USGHapticGlove* Glove{Pimpl->GetGlove()};
+ if (IsValid(Glove) && Glove->IsConnected())
+ {
+ return Glove->SendCustomWaveform(OutWaveform, Location);
+ }
+
+ return false;
+}
+
+bool USGHapticsComponent::SendCustomWaveform(
+ const float Amplitude, const float Duration, const ESGHapticLocation Location)
+{
+ USGHapticGlove* Glove{Pimpl->GetGlove()};
+ if (IsValid(Glove) && Glove->IsConnected())
+ {
+ USGCustomWaveform* Waveform{
+ USGCustomWaveform::NewCustomWaveform(GetOuter(), Amplitude, Duration)
+ };
+ return Glove->SendCustomWaveform(MoveTemp(Waveform), Location);
+ }
+
+ return false;
+}
+
+bool USGHapticsComponent::SendCustomWaveform(
+ const float Amplitude, const float Duration, const float Frequency, const ESGHapticLocation Location)
+{
+ USGHapticGlove* Glove{Pimpl->GetGlove()};
+ if (IsValid(Glove) && Glove->IsConnected())
+ {
+ USGCustomWaveform* Waveform{
+ USGCustomWaveform::NewCustomWaveform(GetOuter(), Amplitude, Duration, Frequency)
+ };
+ return Glove->SendCustomWaveform(MoveTemp(Waveform), Location);
+ }
+
+ return false;
+}
+
+bool USGHapticsComponent::QueueForceFeedbackLevels(const TArray& Levels01)
+{
+ USGHapticGlove* Glove{Pimpl->GetGlove()};
+ if (IsValid(Glove) && Glove->IsConnected())
+ {
+ return Glove->QueueForceFeedbackLevels(Levels01);
+ }
+
+ return false;
+}
+
+bool USGHapticsComponent::QueueForceFeedbackLevel(const int32 Finger, const float Level01)
+{
+ USGHapticGlove* Glove{Pimpl->GetGlove()};
+ if (IsValid(Glove) && Glove->IsConnected())
+ {
+ return Glove->QueueForceFeedbackLevel(Finger, Level01);
+ }
+
+ return false;
+}
+
+bool USGHapticsComponent::QueueVibroLevels(const TArray& Levels01)
+{
+ USGHapticGlove* Glove{Pimpl->GetGlove()};
+ if (IsValid(Glove) && Glove->IsConnected())
+ {
+ return Glove->QueueVibroLevels(Levels01);
+ }
+
+ return false;
+}
+
+bool USGHapticsComponent::QueueVibroLevel(const ESGHapticLocation Location, const float Level01)
+{
+ USGHapticGlove* Glove{Pimpl->GetGlove()};
+ if (IsValid(Glove) && Glove->IsConnected())
+ {
+ return Glove->QueueVibroLevel(Location, Level01);
+ }
+
+ return false;
+}
+
+bool USGHapticsComponent::SupportsWristSqueeze() const
+{
+ return FSGHandLayer::SupportsWristSqueeze(IsRight());
+}
+
+bool USGHapticsComponent::QueueWristSqueeze(const float SqueezeLevel01, const bool bSendImmediate)
+{
+ return FSGHandLayer::QueueCommand_WristSqueeze(IsRight(), SqueezeLevel01, bSendImmediate);
+}
+
+USGHapticsComponent::FImpl::FImpl(USGHapticsComponent* InOwner)
+ : Owner(InOwner)
+{
+}
+
+USGHapticsComponent::FImpl::~FImpl() = default;
+
+void USGHapticsComponent::FImpl::AutoStopAllHaptics() const
+{
+ USGHapticGlove* Glove{GetGlove()};
+ if (IsValid(Glove) && Glove->IsConnected() && Owner->bAutoStopAllHaptics)
+ {
+ Glove->StopHaptics();
+ }
+}
+
+void USGHapticsComponent::FImplDeleter::operator()(const FImpl* P) const
+{
+ delete P;
+}
\ No newline at end of file
diff --git a/Plugins/SenseGlove/Source/SenseGlove/Private/SenseGlove/Components/SGTouchComponent.cpp b/Plugins/SenseGlove/Source/SenseGlove/Private/SenseGlove/Components/SGTouchComponent.cpp
new file mode 100644
index 0000000..281e437
--- /dev/null
+++ b/Plugins/SenseGlove/Source/SenseGlove/Private/SenseGlove/Components/SGTouchComponent.cpp
@@ -0,0 +1,121 @@
+/**
+ * @file
+ *
+ * @author Mamadou Babaei
+ *
+ * @section LICENSE
+ *
+ * (The MIT License)
+ *
+ * Copyright (c) 2020 - 2026 SenseGlove
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * @section DESCRIPTION
+ *
+ *
+ */
+
+
+#include "SenseGlove/Components/SGTouchComponent.h"
+
+#include "GameFramework/Actor.h"
+
+struct USGTouchComponent::FImpl
+{
+ /************************
+ * Static methods
+ ************************/
+
+ /************************
+ * Owner object
+ ************************/
+
+ USGTouchComponent* Owner;
+
+ /************************
+ * Constructor / Destructor
+ ************************/
+
+ explicit FImpl(USGTouchComponent* InOwner);
+ ~FImpl();
+
+ /************************
+ * Default copy constructor & copy assignment operator
+ ************************/
+
+ FImpl(const FImpl& Rhs) = default;
+ FImpl& operator=(const FImpl& Rhs) = default;
+
+ /************************
+ * Methods
+ ************************/
+};
+
+USGTouchComponent* USGTouchComponent::GetTouchComponent(const AActor* Actor)
+{
+ if (!ensureAlwaysMsgf(IsValid(Actor), TEXT("%s"), TEXT("ERROR: invalid actor!")))
+ {
+ return nullptr;
+ }
+
+ USGTouchComponent* TouchComponent = Cast(
+ Actor->GetComponentByClass(USGTouchComponent::StaticClass()));
+ return TouchComponent;
+}
+
+bool USGTouchComponent::IsTouchable(const AActor* Actor)
+{
+ if (!ensureAlwaysMsgf(IsValid(Actor), TEXT("%s"), TEXT("ERROR: invalid actor!")))
+ {
+ return false;
+ }
+
+ const USGTouchComponent* TouchComponent = Cast(
+ Actor->GetComponentByClass(USGTouchComponent::StaticClass()));
+ if (!IsValid(TouchComponent))
+ {
+ return false;
+ }
+
+ return true;
+}
+
+USGTouchComponent::USGTouchComponent(const FObjectInitializer& ObjectInitializer)
+ : Super(ObjectInitializer),
+ Pimpl(TUniquePtr(new FImpl{this}, PimplDeleter))
+{
+ ForceFeedbackLevel = 1.0f;
+
+ VibrotactileAmplitude = 1.0f;
+ VibrotactileDuration = 1.0f;
+ VibrotactileFrequency= 180.0f;
+}
+
+USGTouchComponent::FImpl::FImpl(USGTouchComponent* InOwner)
+ : Owner(InOwner)
+{
+}
+
+USGTouchComponent::FImpl::~FImpl() = default;
+
+void USGTouchComponent::FImplDeleter::operator()(const FImpl* P) const
+{
+ delete P;
+}
\ No newline at end of file
diff --git a/Plugins/SenseGlove/Source/SenseGlove/Private/SenseGlove/Components/SGVirtualHandComponent.cpp b/Plugins/SenseGlove/Source/SenseGlove/Private/SenseGlove/Components/SGVirtualHandComponent.cpp
new file mode 100644
index 0000000..8297e4b
--- /dev/null
+++ b/Plugins/SenseGlove/Source/SenseGlove/Private/SenseGlove/Components/SGVirtualHandComponent.cpp
@@ -0,0 +1,850 @@
+/**
+ * @file
+ *
+ * @author Mamadou Babaei
+ *
+ * @section LICENSE
+ *
+ * (The MIT License)
+ *
+ * Copyright (c) 2020 - 2026 SenseGlove
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * @section DESCRIPTION
+ *
+ *
+ */
+
+
+#include "SenseGlove/Components/SGVirtualHandComponent.h"
+
+#include "HeadMountedDisplayTypes.h"
+#include "Animation/Skeleton.h"
+#include "Components/SkeletalMeshComponent.h"
+#include "Engine/Engine.h"
+#include "Engine/EngineTypes.h"
+#include "Engine/SkeletalMesh.h"
+#include "InputCoreTypes.h"
+#include "Templates/UnrealTypeTraits.h"
+#include "UObject/ConstructorHelpers.h"
+
+#include "SenseGlove/Animation/SGVirtualHandAnimInstance.h"
+#include "SenseGlove/Components/SGWristTrackerComponent.h"
+#include "SGCore/SGHapticGlove.h"
+#include "SGDebug/SGDebugVirtualHand.h"
+#include "SGLog/SGLog.h"
+#include "SGSettings/SGSettings.h"
+#include "SGTracking/SGGloveTracker.h"
+#include "SGTracking/SGXRTracker.h"
+
+struct USGVirtualHandComponent::FImpl
+{
+ /************************
+ * Static methods
+ ************************/
+
+ static FName ParseVirtualHandSettingsOverridesPropertyName(const FName& PropertyName);
+
+ FORCEINLINE static const FSGVirtualHandSettings& GetPluginVirtualHandSettings()
+ {
+ const USGSettings* Settings{USGSettings::GetInstance()};
+ ensureAlwaysMsgf(Settings, TEXT("The settings subsystem has not been initialized!"));
+ return Settings->GetVirtualHandSettings();
+ }
+
+ FORCEINLINE static const FString& GetDefaultLeftHandMeshPath()
+ {
+ return GetPluginVirtualHandSettings().MeshSettings.DefaultLeftHandMeshPath;
+ }
+
+ FORCEINLINE static const FString& GetDefaultLeftHandMeshPathOnly()
+ {
+ return GetPluginVirtualHandSettings().MeshSettings.DefaultLeftHandMeshPathOnly;
+ }
+
+ FORCEINLINE static const FString& GetDefaultRightHandMeshPath()
+ {
+ return GetPluginVirtualHandSettings().MeshSettings.DefaultRightHandMeshPath;
+ }
+
+ FORCEINLINE static const FString& GetDefaultRightHandMeshPathOnly()
+ {
+ return GetPluginVirtualHandSettings().MeshSettings.DefaultRightHandMeshPathOnly;
+ }
+
+ /************************
+ * Member variables
+ ************************/
+
+ bool bOverridePluginSettingsPropertyAlreadyModified;
+
+ bool bCachedGenerateOverlapEvents;
+ ECollisionEnabled::Type CachedCollisionEnabled;
+ FCollisionResponseContainer CachedCollisionResponse;
+
+ FXRHandTrackingState HandTrackingState;
+
+ /************************
+ * Owner object
+ ************************/
+
+ USGVirtualHandComponent* Owner;
+
+ /************************
+ * Constructor / Destructor
+ ************************/
+
+ explicit FImpl(USGVirtualHandComponent* InOwner);
+ ~FImpl();
+
+ /************************
+ * Default copy constructor & copy assignment operator
+ ************************/
+
+ FImpl(const FImpl& Rhs) = default;
+ FImpl& operator=(const FImpl& Rhs) = default;
+
+ /************************
+ * Methods
+ ************************/
+
+ bool IsEditor() const;
+
+ void RightPropertyChanged();
+ void OverridePluginSettingsPropertyChanged();
+
+ FString GetDefaultMeshPath() const;
+ bool IsUsingDefaultMesh() const;
+ bool IsUsingUserMesh() const;
+ USkeletalMesh* LoadDefaultMesh() const;
+ void SetDefaultMesh() const;
+
+ USkeletalMesh* GetMesh() const;
+ USkeleton* GetSkeleton() const;
+ const FReferenceSkeleton& GetRefSkeleton() const;
+
+ void DisableCollisionResponse();
+ void RestoreCollisionResponse() const;
+
+ void UpdateXRData();
+
+ void SetVisibility(const bool bInVisible);
+ void UpdateVisibility();
+
+ FORCEINLINE bool ShouldDrawDebugVirtualHand() const
+ {
+ const FSGVirtualHandDebuggingSettings& Settings{Owner->GetVirtualHandSettings().DebuggingSettings};
+ const bool bDraw = Settings.bDrawDebugVirtualHand
+ && Settings.DrawingMode != ESGDebugVirtualHandDrawingMode::None;
+ return bDraw;
+ }
+
+ void DrawDebugVirtualHand() const;
+
+ void TriggerHandVisibilityChangedEvent(bool bNewVisibility) const;
+
+ void AutoStopAllHaptics() const;
+};
+
+FName USGVirtualHandComponent::FImpl::ParseVirtualHandSettingsOverridesPropertyName(const FName& PropertyName)
+{
+ static const UStruct* Struct{FSGVirtualHandSettingsOverrides::StaticStruct()};
+ if (!ensureAlwaysMsgf(Struct, TEXT("Invalid Struct!")))
+ {
+ return NAME_None;
+ }
+
+ // +3 because of ::
+ static const int32 StructNameLength = Struct->GetFName().ToString().Len() + 3;
+
+ const FString PropertyString{PropertyName.ToString()};
+ const int32 PropertyStringLength = PropertyString.Len();
+ const int32 PropertyNameLength = PropertyStringLength - StructNameLength;
+
+ const FName ParsedName{*PropertyString.Right(PropertyNameLength)};
+ return ParsedName;
+}
+
+const TArray& USGVirtualHandComponent::GetLeftHandBoneNames()
+{
+ return FImpl::GetPluginVirtualHandSettings().MeshSettings.LeftHandBoneNames;
+}
+
+const TArray& USGVirtualHandComponent::GetRightHandBoneNames()
+{
+ return FImpl::GetPluginVirtualHandSettings().MeshSettings.RightHandBoneNames;
+}
+
+const FName& USGVirtualHandComponent::GetLeftHandBoneName(const int32 Joint)
+{
+ const TArray& BoneNames{GetLeftHandBoneNames()};
+
+ ensureAlwaysMsgf(Joint < BoneNames.Num(), TEXT("Joint index is out of bound!"));
+
+ const FName& BoneName{BoneNames[Joint]};
+ return BoneName;
+}
+
+const FName& USGVirtualHandComponent::GetRightHandBoneName(const int32 Joint)
+{
+ const TArray& BoneNames{GetRightHandBoneNames()};
+
+ ensureAlwaysMsgf(Joint < BoneNames.Num(), TEXT("Joint index is out of bound!"));
+
+ const FName& BoneName{BoneNames[Joint]};
+ return BoneName;
+}
+
+USGVirtualHandComponent::USGVirtualHandComponent(const FObjectInitializer& ObjectInitializer)
+ : Super(ObjectInitializer),
+ Pimpl(TUniquePtr(new FImpl{this}, PimplDeleter))
+{
+ PrimaryComponentTick.bTickEvenWhenPaused = false;
+ PrimaryComponentTick.bCanEverTick = true;
+ PrimaryComponentTick.bStartWithTickEnabled = true;
+ bTickInEditor = true;
+ bNeverNeedsRenderUpdate = false;
+ bAllowConcurrentTick = true;
+ SetUpdateAnimationInEditor(true);
+
+ SetCastShadow(true);
+ SetCastHiddenShadow(false);
+ bCastDynamicShadow = true;
+ SetOnlyOwnerSee(false);
+ SetReceivesDecals(true);
+ SetCanEverAffectNavigation(false);
+ Super::SetCollisionObjectType(ECC_WorldDynamic);
+ Super::SetCollisionEnabled(ECollisionEnabled::QueryAndPhysics);
+ Super::SetCollisionResponseToAllChannels(ECR_Overlap);
+ Super::SetCollisionResponseToChannel(ECC_WorldStatic, ECR_Block);
+ Super::SetCollisionResponseToChannel(ECC_WorldDynamic, ECR_Block);
+ Super::SetCollisionResponseToChannel(ECC_Pawn, ECR_Block);
+ Super::SetCollisionResponseToChannel(ECC_Visibility, ECR_Block);
+ Super::SetCollisionResponseToChannel(ECC_PhysicsBody, ECR_Block);
+ Super::SetCollisionResponseToChannel(ECC_Vehicle, ECR_Block);
+ Super::SetCollisionResponseToChannel(ECC_Destructible, ECR_Block);
+
+ Super::SetAnimInstanceClass(USGVirtualHandAnimInstance::StaticClass());
+
+ bRight = true;
+
+ Super::SetSkeletalMesh(nullptr, true);
+
+ WristTracker = nullptr;
+}
+
+void USGVirtualHandComponent::OnHandVisibilityChanged_Implementation(bool bNewVisibility)
+{
+}
+
+void USGVirtualHandComponent::SetRight(const bool bInRight)
+{
+ bRight = bInRight;
+
+ const bool bAutoStopHaptics = GetVirtualHandSettings().HapticsSettings.bAutoStopAllHapticsOnEndPlay;
+ if (bAutoStopHaptics)
+ {
+ Pimpl->AutoStopAllHaptics();
+ }
+
+ Pimpl->UpdateXRData();
+
+ if (!Pimpl->IsUsingUserMesh())
+ {
+ Pimpl->SetDefaultMesh();
+ }
+}
+
+void USGVirtualHandComponent::SetVirtualHandSettingsOverrides(
+ const FSGVirtualHandSettingsOverrides& InVirtualHandSettingsOverrides)
+{
+ VirtualHandSettingsOverrides = InVirtualHandSettingsOverrides;
+}
+
+FSGVirtualHandSettings USGVirtualHandComponent::GetVirtualHandSettings() const
+{
+ const USGSettings* Settings{USGSettings::GetInstance()};
+ if (!IsValid(Settings))
+ {
+ return FSGVirtualHandSettings{};
+ }
+
+ const FSGVirtualHandSettings& PluginVirtualHandSettings{Settings->GetVirtualHandSettings()};
+ if (!OverridesPluginVirtualHandSettings())
+ {
+ return PluginVirtualHandSettings;
+ }
+
+ const FSGVirtualHandSettings VirtualHandSettings{
+ VirtualHandSettingsOverrides.bVisibleWhenHandDataUnavailable,
+ VirtualHandSettingsOverrides.AnimationSettings,
+ VirtualHandSettingsOverrides.DebuggingSettings,
+ VirtualHandSettingsOverrides.GrabSettings,
+ VirtualHandSettingsOverrides.HapticsSettings,
+ PluginVirtualHandSettings.MeshSettings,
+ VirtualHandSettingsOverrides.TouchSettings
+ };
+ return VirtualHandSettings;
+}
+
+void USGVirtualHandComponent::SetAnimInstanceClass(UClass* NewClass)
+{
+ ensureAlwaysMsgf((TIsDerivedFrom::IsDerived),
+ TEXT("The AnimInstance class must be a subclass of USGVirtualHandAnimInstance!"));
+
+ Super::SetAnimInstanceClass(NewClass);
+}
+
+void USGVirtualHandComponent::PostInitProperties()
+{
+ Super::PostInitProperties();
+
+ if (!Pimpl->IsUsingUserMesh())
+ {
+ Pimpl->SetDefaultMesh();
+ }
+}
+
+#if WITH_EDITOR
+void USGVirtualHandComponent::PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent)
+{
+ Super::PostEditChangeProperty(PropertyChangedEvent);
+
+ const FName PropertyName{
+ PropertyChangedEvent.Property
+ ? PropertyChangedEvent.Property->GetFName()
+ : NAME_None
+ };
+
+ static const FName RightName{
+ GET_MEMBER_NAME_CHECKED(USGVirtualHandComponent, bRight)
+ };
+
+ static const FName OverridePluginSettingsName{
+ FImpl::ParseVirtualHandSettingsOverridesPropertyName(
+ GET_MEMBER_NAME_CHECKED(FSGVirtualHandSettingsOverrides,
+ FSGVirtualHandSettingsOverrides::bOverridePluginSettings))
+ };
+
+ if (PropertyName == RightName)
+ {
+ Pimpl->RightPropertyChanged();
+ return;
+ }
+
+ if (PropertyName == OverridePluginSettingsName)
+ {
+ Pimpl->OverridePluginSettingsPropertyChanged();
+ return;
+ }
+}
+#endif /* WITH_EDITOR */
+
+void USGVirtualHandComponent::InitializeComponent()
+{
+ Super::InitializeComponent();
+
+ Pimpl->UpdateXRData();
+
+ if (!Pimpl->IsUsingUserMesh())
+ {
+ Pimpl->SetDefaultMesh();
+ }
+}
+
+void USGVirtualHandComponent::UninitializeComponent()
+{
+ Super::UninitializeComponent();
+
+ const bool bAutoStopHaptics = GetVirtualHandSettings().HapticsSettings.bAutoStopAllHapticsOnEndPlay;
+ if (bAutoStopHaptics)
+ {
+ Pimpl->AutoStopAllHaptics();
+ }
+}
+
+void USGVirtualHandComponent::BeginPlay()
+{
+ Super::BeginPlay();
+
+ Pimpl->UpdateXRData();
+}
+
+void USGVirtualHandComponent::EndPlay(const EEndPlayReason::Type EndPlayReason)
+{
+ Super::EndPlay(EndPlayReason);
+
+ const bool bAutoStopHaptics = GetVirtualHandSettings().HapticsSettings.bAutoStopAllHapticsOnEndPlay;
+ if (bAutoStopHaptics)
+ {
+ Pimpl->AutoStopAllHaptics();
+ }
+}
+
+void USGVirtualHandComponent::TickComponent(
+ const float DeltaTime, const ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction)
+{
+#if WITH_EDITOR
+ if (Pimpl->IsEditor())
+ {
+ EditorTick(DeltaTime, TickType, ThisTickFunction);
+ return;
+ }
+#endif /* WITH_EDITOR */
+
+ Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
+
+ Pimpl->UpdateXRData();
+
+ if (Pimpl->ShouldDrawDebugVirtualHand())
+ {
+ Pimpl->DrawDebugVirtualHand();
+ }
+}
+
+void USGVirtualHandComponent::SetSkeletalMesh(USkeletalMesh* NewMesh, const bool bReinitPose)
+{
+ if (IsValid(NewMesh))
+ {
+ Super::SetSkeletalMesh(NewMesh, bReinitPose);
+ }
+ else
+ {
+ Super::SetSkeletalMesh(Pimpl->LoadDefaultMesh(), bReinitPose);
+ }
+}
+
+void USGVirtualHandComponent::EditorTick(
+ const float DeltaTime, const ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction)
+{
+ Pimpl->UpdateXRData();
+
+ if (Pimpl->ShouldDrawDebugVirtualHand())
+ {
+ Pimpl->DrawDebugVirtualHand();
+ }
+}
+
+bool USGVirtualHandComponent::IsGloveConnected() const
+{
+ const USGGloveTracker* GloveTracker{USGGloveTracker::GetInstance(GetOuter())};
+ const USGHapticGlove* Glove{IsValid(GloveTracker) ? GloveTracker->GetGlove(IsRight()) : nullptr};
+ return IsValid(Glove);
+}
+
+USGHapticGlove* USGVirtualHandComponent::GetConnectedGlove() const
+{
+ const USGGloveTracker* GloveTracker{USGGloveTracker::GetInstance(GetOuter())};
+ USGHapticGlove* Glove{IsValid(GloveTracker) ? GloveTracker->GetGlove(IsRight()) : nullptr};
+ return Glove;
+}
+
+const FXRHandTrackingState& USGVirtualHandComponent::GetHandTrackingState() const
+{
+ return Pimpl->HandTrackingState;
+}
+
+const TArray& USGVirtualHandComponent::GetHandBoneNames() const
+{
+ const TArray& Names{
+ IsRight()
+ ? GetRightHandBoneNames()
+ : GetLeftHandBoneNames()
+ };
+ return Names;
+}
+
+const FName& USGVirtualHandComponent::GetHandBoneName(const int32 Joint) const
+{
+ const FName& Name{
+ IsRight()
+ ? GetRightHandBoneName(Joint)
+ : GetLeftHandBoneName(Joint)
+ };
+ return Name;
+}
+
+const FTransform& USGVirtualHandComponent::GetHandBoneRefTransform(const FName& BoneName) const
+{
+ const FReferenceSkeleton& ReferenceSkeleton{Pimpl->GetRefSkeleton()};
+ const int32 BoneIndex = ReferenceSkeleton.FindBoneIndex(BoneName);
+
+ if (BoneIndex == INDEX_NONE)
+ {
+ SGLOG_WARNING(FString::Printf(TEXT("Could not find a bone index for the '%s' joint!"),
+ *BoneName.ToString()));
+ return FTransform::Identity;
+ }
+
+ const FTransform& BoneTransform{ReferenceSkeleton.GetRefBonePose()[BoneIndex]};
+ return BoneTransform;
+}
+
+const FTransform& USGVirtualHandComponent::GetHandRootBoneRefTransform() const
+{
+ static constexpr int32 WristKeypointIndex = static_cast(EHandKeypoint::Wrist);
+ const FTransform& BoneTransform{GetHandBoneRefTransform(WristKeypointIndex)};
+ return BoneTransform;
+}
+
+const FTransform& USGVirtualHandComponent::GetHandBoneRefTransform(const int32 Joint) const
+{
+ const FName& BoneName{GetHandBoneName(Joint)};
+ return GetHandBoneRefTransform(BoneName);
+}
+
+FRotator USGVirtualHandComponent::GetHandBoneRefRotation(const FName& BoneName) const
+{
+ const FTransform BoneTransform{GetHandBoneRefTransform(BoneName)};
+ const FRotator BoneRotation{BoneTransform.GetRotation().Rotator()};
+ return BoneRotation;
+}
+
+FRotator USGVirtualHandComponent::GetHandBoneRefRotation(const int32 Joint) const
+{
+ const FTransform BoneTransform{GetHandBoneRefTransform(Joint)};
+ const FRotator BoneRotation{BoneTransform.GetRotation().Rotator()};
+ return BoneRotation;
+}
+
+USGVirtualHandComponent::FImpl::FImpl(USGVirtualHandComponent* InOwner)
+ : bOverridePluginSettingsPropertyAlreadyModified(false),
+ bCachedGenerateOverlapEvents(InOwner->GetGenerateOverlapEvents()),
+ CachedCollisionEnabled(InOwner->GetCollisionEnabled()),
+ Owner(InOwner)
+
+{
+}
+
+USGVirtualHandComponent::FImpl::~FImpl() = default;
+
+bool USGVirtualHandComponent::FImpl::IsEditor() const
+{
+ const UWorld* World{Owner->GetWorld()};
+ if (World && (World->WorldType == EWorldType::Editor || World->WorldType == EWorldType::EditorPreview))
+ {
+ return true;
+ }
+
+ return false;
+}
+
+void USGVirtualHandComponent::FImpl::RightPropertyChanged()
+{
+ if (!IsUsingUserMesh())
+ {
+ SetDefaultMesh();
+ }
+}
+
+void USGVirtualHandComponent::FImpl::OverridePluginSettingsPropertyChanged()
+{
+ if (bOverridePluginSettingsPropertyAlreadyModified)
+ {
+ return;
+ }
+
+ bOverridePluginSettingsPropertyAlreadyModified = true;
+
+ if (!Owner->VirtualHandSettingsOverrides.bOverridePluginSettings)
+ {
+ return;
+ }
+
+ const USGSettings* Settings{USGSettings::GetInstance()};
+ if (!ensureAlwaysMsgf(IsValid(Settings), TEXT("The settings subsystem has not been initialized!")))
+ {
+ return;
+ }
+
+ const FSGVirtualHandSettings& PluginVirtualHandSettings{Settings->GetVirtualHandSettings()};
+ Owner->VirtualHandSettingsOverrides.bVisibleWhenHandDataUnavailable =
+ PluginVirtualHandSettings.bVisibleWhenHandDataUnavailable;
+ Owner->VirtualHandSettingsOverrides.AnimationSettings = PluginVirtualHandSettings.AnimationSettings;
+ Owner->VirtualHandSettingsOverrides.DebuggingSettings = PluginVirtualHandSettings.DebuggingSettings;
+ Owner->VirtualHandSettingsOverrides.GrabSettings = PluginVirtualHandSettings.GrabSettings;
+ Owner->VirtualHandSettingsOverrides.HapticsSettings = PluginVirtualHandSettings.HapticsSettings;
+ Owner->VirtualHandSettingsOverrides.TouchSettings = PluginVirtualHandSettings.TouchSettings;
+}
+
+FString USGVirtualHandComponent::FImpl::GetDefaultMeshPath() const
+{
+ FString SkeletalMeshPath;
+
+ if (Owner->IsRight())
+ {
+ SkeletalMeshPath = GetDefaultRightHandMeshPath();
+ }
+ else
+ {
+ SkeletalMeshPath = GetDefaultLeftHandMeshPath();
+ }
+
+ return SkeletalMeshPath;
+}
+
+bool USGVirtualHandComponent::FImpl::IsUsingDefaultMesh() const
+{
+ const USkeletalMesh* CurrentMesh{GetMesh()};
+ if (!IsValid(CurrentMesh))
+ {
+ return false;
+ }
+
+ const FString CurrentMeshPath{CurrentMesh->GetPathName()};
+ const FString LeftHandDefaultMeshPath{GetDefaultLeftHandMeshPath()};
+ const FString LeftHandDefaultMeshPathOnly{GetDefaultLeftHandMeshPathOnly()};
+ const FString RightHandDefaultMeshPath{GetDefaultRightHandMeshPath()};
+ const FString RightHandDefaultMeshPathOnly{GetDefaultRightHandMeshPathOnly()};
+ if (CurrentMeshPath == LeftHandDefaultMeshPath
+ || CurrentMeshPath == LeftHandDefaultMeshPathOnly
+ || CurrentMeshPath == RightHandDefaultMeshPath
+ || CurrentMeshPath == RightHandDefaultMeshPathOnly)
+ {
+ return true;
+ }
+
+ return false;
+}
+
+bool USGVirtualHandComponent::FImpl::IsUsingUserMesh() const
+{
+ const USkeletalMesh* CurrentMesh{GetMesh()};
+ if (!IsValid(CurrentMesh))
+ {
+ return false;
+ }
+
+ const FString CurrentMeshPath{CurrentMesh->GetPathName()};
+ const FString LeftHandDefaultMeshPath{GetDefaultLeftHandMeshPath()};
+ const FString LeftHandDefaultMeshPathOnly{GetDefaultLeftHandMeshPathOnly()};
+ const FString RightHandDefaultMeshPath{GetDefaultRightHandMeshPath()};
+ const FString RightHandDefaultMeshPathOnly{GetDefaultRightHandMeshPathOnly()};
+ if (CurrentMeshPath == LeftHandDefaultMeshPath
+ || CurrentMeshPath == LeftHandDefaultMeshPathOnly
+ || CurrentMeshPath == RightHandDefaultMeshPath
+ || CurrentMeshPath == RightHandDefaultMeshPathOnly)
+ {
+ return false;
+ }
+
+ return true;
+}
+
+USkeletalMesh* USGVirtualHandComponent::FImpl::LoadDefaultMesh() const
+{
+ // FIXME
+ // Disable this warning altogether as it results in polluting the packaging logs, at least until we ship a set of
+ // virtual hand meshes again.
+ // const FString DefaultMeshPath{GetDefaultMeshPath()};
+ // USkeletalMesh* DefaultMesh{
+ // Cast(StaticLoadObject(USkeletalMesh::StaticClass(), nullptr, *DefaultMeshPath))
+ // };
+
+ // FIXME
+ // Disable this warning altogether as it results in polluting the packaging logs, at least until we ship a set of
+ // virtual hand meshes again.
+ // if (!IsValid(DefaultMesh))
+ // {
+ // SGLOG_WARNING(FString::Printf(TEXT("Failed to load the default skeletal mesh: '%s'"), *DefaultMeshPath));
+ // }
+
+ // FIXME
+ //return DefaultMesh;
+
+ return nullptr;
+}
+
+void USGVirtualHandComponent::FImpl::SetDefaultMesh() const
+{
+ // FIXME
+ // Since we don't ship a default virtual hand mesh at the moment, calling SetDefaultMesh causes the following
+ // failed assertion due the fact that the engine won't be able to find the asset at the default location:
+ // Assertion failed: IsInGameThread()
+ // Unable to load /SenseGlove/Meshes/SK_SenseGlove_VirtualHand_*. Objects and Packages can only be loaded from the
+ // game thread with the currently active loader '%s'.
+ // One workaround would be disabling the async loading or as an alternative simply disabling this portion of the
+ // code should be a good-enough fix until we address this issue in its own time.
+ //Owner->SetSkeletalMesh(LoadDefaultMesh(), true);
+ // Temporary hack to make it work inside the Blueprint Editor.
+#if WITH_EDITOR
+ // FIXME
+ //Owner->SetSkeletalMesh(LoadDefaultMesh(), true);
+ // Instead
+ Owner->SetSkeletalMesh(nullptr, true);
+#endif /* WITH_EDITOR */
+}
+
+USkeletalMesh* USGVirtualHandComponent::FImpl::GetMesh() const
+{
+ return Owner->GetSkeletalMeshAsset();
+}
+
+USkeleton* USGVirtualHandComponent::FImpl::GetSkeleton() const
+{
+ USkeletalMesh* Mesh{GetMesh()};
+ ensureAlwaysMsgf(IsValid(Mesh), TEXT("Invalid SkeletalMesh object!"));
+ return Mesh->GetSkeleton();
+}
+
+const FReferenceSkeleton& USGVirtualHandComponent::FImpl::GetRefSkeleton() const
+{
+ const USkeletalMesh* Mesh{GetMesh()};
+ ensureAlwaysMsgf(IsValid(Mesh), TEXT("Invalid SkeletalMesh object!"));
+ return Mesh->GetRefSkeleton();
+}
+
+void USGVirtualHandComponent::FImpl::DisableCollisionResponse()
+{
+ bCachedGenerateOverlapEvents = Owner->GetGenerateOverlapEvents();
+ CachedCollisionEnabled = Owner->GetCollisionEnabled();
+ CachedCollisionResponse = Owner->GetCollisionResponseToChannels();
+ Owner->SetGenerateOverlapEvents(false);
+ Owner->SetCollisionEnabled(ECollisionEnabled::NoCollision);
+ Owner->SetCollisionResponseToAllChannels(ECR_Ignore);
+}
+
+void USGVirtualHandComponent::FImpl::RestoreCollisionResponse() const
+{
+ Owner->SetGenerateOverlapEvents(bCachedGenerateOverlapEvents);
+ Owner->SetCollisionEnabled(CachedCollisionEnabled);
+ Owner->SetCollisionResponseToChannels(CachedCollisionResponse);
+}
+
+void USGVirtualHandComponent::FImpl::UpdateXRData()
+{
+ const EControllerHand Hand = Owner->IsRight() ? EControllerHand::Right : EControllerHand::Left;
+
+ FXRHandTrackingState State;
+ State.bValid = false;
+ const bool bGotHandTrackingState =
+ FSGXRTracker::GetHandTrackingState(
+ Owner, EXRSpaceType::UnrealWorldSpace, Hand, State);
+
+ (void) bGotHandTrackingState;
+ HandTrackingState = MoveTemp(State);
+}
+
+void USGVirtualHandComponent::FImpl::SetVisibility(const bool bInVisible)
+{
+ Owner->SetHiddenInGame(!bInVisible, false);
+
+ if (bInVisible)
+ {
+ RestoreCollisionResponse();
+ }
+ else
+ {
+ DisableCollisionResponse();
+ }
+
+ TriggerHandVisibilityChangedEvent(bInVisible);
+}
+
+void USGVirtualHandComponent::FImpl::UpdateVisibility()
+{
+ const bool bHandDataAvailable = HandTrackingState.bValid;
+ const bool bHandVisible = Owner->IsVisible();
+ const bool bShouldBeVisibleWhenHandDataUnavailable =
+ Owner->GetVirtualHandSettings().bVisibleWhenHandDataUnavailable;
+
+ bool bSetNewVisibility = false;
+
+ if (bHandDataAvailable)
+ {
+ if (bHandVisible)
+ {
+ bSetNewVisibility = false;
+ }
+ else
+ {
+ bSetNewVisibility = true;
+ }
+ }
+ else
+ {
+ if (bHandVisible)
+ {
+ if (bShouldBeVisibleWhenHandDataUnavailable)
+ {
+ bSetNewVisibility = false;
+ }
+ else
+ {
+ bSetNewVisibility = true;
+ }
+ }
+ else
+ {
+ if (bShouldBeVisibleWhenHandDataUnavailable)
+ {
+ bSetNewVisibility = true;
+ }
+ else
+ {
+ bSetNewVisibility = false;
+ }
+ }
+ }
+
+ if (bSetNewVisibility)
+ {
+ SetVisibility(bHandDataAvailable);
+ }
+}
+
+void USGVirtualHandComponent::FImpl::DrawDebugVirtualHand() const
+{
+ const UWorld* World{Owner->GetWorld()};
+
+ if (!IsValid(Owner->WristTracker))
+ {
+ return;
+ }
+
+ if (!HandTrackingState.bValid)
+ {
+ return;
+ }
+
+ FSGDebugVirtualHand::Draw(World, HandTrackingState, Owner->GetVirtualHandSettings().DebuggingSettings);
+}
+
+void USGVirtualHandComponent::FImpl::TriggerHandVisibilityChangedEvent(const bool bNewVisibility) const
+{
+ Owner->HandVisibilityChangedEvent.Broadcast(bNewVisibility);
+ Owner->OnHandVisibilityChanged(bNewVisibility);
+}
+
+void USGVirtualHandComponent::FImpl::AutoStopAllHaptics() const
+{
+ const USGGloveTracker* GloveTracker{USGGloveTracker::GetInstance(Owner->GetOuter())};
+ USGHapticGlove* Glove{IsValid(GloveTracker) ? GloveTracker->GetGlove(Owner->IsRight()) : nullptr};
+ const bool bAutoStopHaptics = Owner->GetVirtualHandSettings().HapticsSettings.bAutoStopAllHapticsOnEndPlay;
+ if (IsValid(Glove) && Glove->IsConnected() && bAutoStopHaptics)
+ {
+ Glove->StopHaptics();
+ }
+}
+
+void USGVirtualHandComponent::FImplDeleter::operator()(const FImpl* P) const
+{
+ delete P;
+}
\ No newline at end of file
diff --git a/Plugins/SenseGlove/Source/SenseGlove/Private/SenseGlove/Components/SGWristTrackerComponent.cpp b/Plugins/SenseGlove/Source/SenseGlove/Private/SenseGlove/Components/SGWristTrackerComponent.cpp
new file mode 100644
index 0000000..9a034d2
--- /dev/null
+++ b/Plugins/SenseGlove/Source/SenseGlove/Private/SenseGlove/Components/SGWristTrackerComponent.cpp
@@ -0,0 +1,376 @@
+/**
+ * @file
+ *
+ * @author Mamadou Babaei
+ *
+ * @section LICENSE
+ *
+ * (The MIT License)
+ *
+ * Copyright (c) 2020 - 2026 SenseGlove
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * @section DESCRIPTION
+ *
+ *
+ */
+
+
+#include "SenseGlove/Components/SGWristTrackerComponent.h"
+
+#include "IXRTrackingSystem.h"
+
+#include "SGDebug/SGDebugGizmo.h"
+#include "SGLog/SGLog.h"
+#include "SGSettings/SGSettings.h"
+#include "SGTracking/SGXRTracker.h"
+
+struct USGWristTrackerComponent::FImpl
+{
+ /************************
+ * Static methods
+ ************************/
+
+ static FName ParseWristTrackingSettingsOverridesPropertyName(const FName& PropertyName);
+
+ /************************
+ * Member variables
+ ************************/
+
+ bool bOverridePluginSettingsPropertyAlreadyModified;
+
+ FXRHandTrackingState HandTrackingState;
+
+ /************************
+ * Owner object
+ ************************/
+
+ USGWristTrackerComponent* Owner;
+
+ /************************
+ * Constructor / Destructor
+ ************************/
+
+ explicit FImpl(USGWristTrackerComponent* InOwner);
+ ~FImpl();
+
+ /************************
+ * Default copy constructor & copy assignment operator
+ ************************/
+
+ FImpl(const FImpl& Rhs) = default;
+ FImpl& operator=(const FImpl& Rhs) = default;
+
+ /************************
+ * Methods
+ ************************/
+
+ bool IsEditor() const;
+
+ void OverridePluginSettingsPropertyChanged();
+
+ void UpdateMotionSource() const;
+ void UpdateXRData();
+ void UpdateWristTrackingData() const;
+
+ void DrawDebugWristTracker() const;
+};
+
+USGWristTrackerComponent::USGWristTrackerComponent(const FObjectInitializer& ObjectInitializer)
+ : Super(ObjectInitializer),
+ Pimpl(TUniquePtr(new FImpl{this}, PimplDeleter))
+{
+ PrimaryComponentTick.bTickEvenWhenPaused = false;
+ PrimaryComponentTick.bCanEverTick = true;
+ PrimaryComponentTick.bStartWithTickEnabled = true;
+ bTickInEditor = true;
+ bNeverNeedsRenderUpdate = true;
+ bAllowConcurrentTick = true;
+
+ SetCastShadow(false);
+ SetCastHiddenShadow(false);
+ bCastDynamicShadow = false;
+ SetOnlyOwnerSee(false);
+ SetReceivesDecals(false);
+ SetCanEverAffectNavigation(false);
+
+ bRight = true;
+
+ WristLocation = FVector::ZeroVector;
+ WristRotation = FRotator::ZeroRotator;
+}
+
+FName USGWristTrackerComponent::FImpl::ParseWristTrackingSettingsOverridesPropertyName(const FName& PropertyName)
+{
+ static const UStruct* Struct{FSGWristTrackingSettingsOverrides::StaticStruct()};
+ if (!ensureAlwaysMsgf(Struct, TEXT("Invalid Struct!")))
+ {
+ return NAME_None;
+ }
+
+ // +3 because of ::
+ static const int32 StructNameLength = Struct->GetFName().ToString().Len() + 3;
+
+ const FString PropertyString{PropertyName.ToString()};
+ const int32 PropertyStringLength = PropertyString.Len();
+ const int32 PropertyNameLength = PropertyStringLength - StructNameLength;
+
+ const FName ParsedName{*PropertyString.Right(PropertyNameLength)};
+ return ParsedName;
+}
+
+void USGWristTrackerComponent::SetRight(const bool bInRight)
+{
+ bRight = bInRight;
+
+ Pimpl->UpdateMotionSource();
+ Pimpl->UpdateXRData();
+ Pimpl->UpdateWristTrackingData();
+}
+
+void USGWristTrackerComponent::SetWristTrackingSettingsOverrides(
+ const FSGWristTrackingSettingsOverrides& InWristTrackingSettingsOverrides)
+{
+ WristTrackingSettingsOverrides = InWristTrackingSettingsOverrides;
+}
+
+FSGWristTrackingSettings USGWristTrackerComponent::GetWristTrackingSettings() const
+{
+ const USGSettings* Settings{USGSettings::GetInstance()};
+ if (!IsValid(Settings))
+ {
+ return FSGWristTrackingSettings{};
+ }
+
+ const FSGWristTrackingSettings& PluginWristTrackingSettings{Settings->GetTrackingSettings().WristTrackingSettings};
+ if (!OverridesPluginWristTrackingSettings())
+ {
+ return PluginWristTrackingSettings;
+ }
+
+ const FSGWristTrackingSettings WristTrackingSettings{
+ PluginWristTrackingSettings.OpenXRPositionalTrackingProvider,
+ PluginWristTrackingSettings.TrackingHardware,
+ PluginWristTrackingSettings.TrackingHardwareLocationOffsetLeftHand,
+ PluginWristTrackingSettings.TrackingHardwareLocationOffsetRightHand,
+ PluginWristTrackingSettings.TrackingHardwareRotationOffsetLeftHand,
+ PluginWristTrackingSettings.TrackingHardwareRotationOffsetRightHand,
+ PluginWristTrackingSettings.LeftHandMotionSource,
+ PluginWristTrackingSettings.RightHandMotionSource,
+ WristTrackingSettingsOverrides.DebuggingSettings,
+ };
+ return WristTrackingSettings;
+}
+
+#if WITH_EDITOR
+void USGWristTrackerComponent::PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent)
+{
+ Super::PostEditChangeProperty(PropertyChangedEvent);
+
+ const FName PropertyName{
+ PropertyChangedEvent.Property
+ ? PropertyChangedEvent.Property->GetFName()
+ : NAME_None
+ };
+
+ static const FName OverridePluginSettingsName{
+ FImpl::ParseWristTrackingSettingsOverridesPropertyName(
+ GET_MEMBER_NAME_CHECKED(FSGWristTrackingSettingsOverrides,
+ FSGWristTrackingSettingsOverrides::bOverridePluginSettings))
+ };
+
+ if (PropertyName == OverridePluginSettingsName)
+ {
+ Pimpl->OverridePluginSettingsPropertyChanged();
+ return;
+ }
+}
+#endif /* WITH_EDITOR */
+
+void USGWristTrackerComponent::InitializeComponent()
+{
+ Super::InitializeComponent();
+
+ Pimpl->UpdateMotionSource();
+ Pimpl->UpdateXRData();
+ Pimpl->UpdateWristTrackingData();
+}
+
+void USGWristTrackerComponent::BeginPlay()
+{
+ Super::BeginPlay();
+
+ Pimpl->UpdateMotionSource();
+ Pimpl->UpdateXRData();
+ Pimpl->UpdateWristTrackingData();
+}
+
+void USGWristTrackerComponent::TickComponent(
+ const float DeltaTime, const ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction)
+{
+#if WITH_EDITOR
+ if (Pimpl->IsEditor())
+ {
+ EditorTick(DeltaTime, TickType, ThisTickFunction);
+ return;
+ }
+#endif /* WITH_EDITOR */
+
+ Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
+
+ Pimpl->UpdateXRData();
+ Pimpl->UpdateWristTrackingData();
+
+ if (GetWristTrackingSettings().DebuggingSettings.bDrawDebugWristTracker)
+ {
+ Pimpl->DrawDebugWristTracker();
+ }
+}
+
+void USGWristTrackerComponent::EditorTick(
+ const float DeltaTime, const ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction)
+{
+ Pimpl->UpdateXRData();
+ Pimpl->UpdateWristTrackingData();
+}
+
+const FXRHandTrackingState& USGWristTrackerComponent::GetHandTrackingState() const
+{
+ return Pimpl->HandTrackingState;
+}
+
+USGWristTrackerComponent::FImpl::FImpl(USGWristTrackerComponent* InOwner)
+ : bOverridePluginSettingsPropertyAlreadyModified(false),
+ Owner(InOwner)
+{
+}
+
+USGWristTrackerComponent::FImpl::~FImpl() = default;
+
+bool USGWristTrackerComponent::FImpl::IsEditor() const
+{
+ const UWorld* World{Owner->GetWorld()};
+ if (World && (World->WorldType == EWorldType::Editor || World->WorldType == EWorldType::EditorPreview))
+ {
+ return true;
+ }
+
+ return false;
+}
+
+void USGWristTrackerComponent::FImpl::OverridePluginSettingsPropertyChanged()
+{
+ if (bOverridePluginSettingsPropertyAlreadyModified)
+ {
+ return;
+ }
+
+ bOverridePluginSettingsPropertyAlreadyModified = true;
+
+ if (!Owner->WristTrackingSettingsOverrides.bOverridePluginSettings)
+ {
+ return;
+ }
+
+ const USGSettings* Settings{USGSettings::GetInstance()};
+ if (!ensureAlwaysMsgf(IsValid(Settings), TEXT("The settings subsystem has not been initialized!")))
+ {
+ return;
+ }
+
+ const FSGWristTrackingSettings& PluginWristTrackingSettings{Settings->GetTrackingSettings().WristTrackingSettings};
+ Owner->WristTrackingSettingsOverrides.DebuggingSettings = PluginWristTrackingSettings.DebuggingSettings;
+}
+
+void USGWristTrackerComponent::FImpl::UpdateMotionSource() const
+{
+ if (!GEngine)
+ {
+ return;
+ }
+
+ const FName& TrackingMotionSource{
+ Owner->IsRight()
+ ? Owner->GetWristTrackingSettings().RightHandMotionSource
+ : Owner->GetWristTrackingSettings().LeftHandMotionSource
+ };
+ Owner->SetTrackingMotionSource(TrackingMotionSource);
+}
+
+void USGWristTrackerComponent::FImpl::UpdateXRData()
+{
+ const EControllerHand Hand = Owner->IsRight() ? EControllerHand::Right : EControllerHand::Left;
+
+ FXRHandTrackingState State;
+ State.bValid = false;
+ const bool bGotHandTrackingState =
+ FSGXRTracker::GetHandTrackingState(
+ Owner, EXRSpaceType::UnrealWorldSpace, Hand, State);
+
+ (void) bGotHandTrackingState;
+ HandTrackingState = MoveTemp(State);
+}
+
+void USGWristTrackerComponent::FImpl::UpdateWristTrackingData() const
+{
+ FVector NewWristLocation{Owner->GetComponentLocation()};
+ FRotator NewWristRotation{Owner->GetComponentRotation()};
+
+ const bool bLocationUpdated = !Owner->WristLocation.Equals(NewWristLocation, SMALL_NUMBER);
+ const bool bRotationUpdated = !Owner->WristRotation.Equals(NewWristRotation, SMALL_NUMBER);
+
+ if (bLocationUpdated)
+ {
+ Owner->WristLocation = MoveTemp(NewWristLocation);
+ }
+
+ if (bRotationUpdated)
+ {
+ Owner->WristRotation = MoveTemp(NewWristRotation);
+ }
+
+ if (bLocationUpdated && Owner->WristLocationChangedEvent.IsBound())
+ {
+ Owner->WristLocationChangedEvent.Broadcast(Owner->WristLocation);
+ }
+
+ if (bRotationUpdated && Owner->WristRotationChangedEvent.IsBound())
+ {
+ Owner->WristRotationChangedEvent.Broadcast(Owner->WristRotation);
+ }
+
+ if ((bLocationUpdated && bRotationUpdated) && Owner->WristLocationAndRotationChangedEvent.IsBound())
+ {
+ Owner->WristLocationAndRotationChangedEvent.Broadcast(Owner->WristLocation, Owner->WristRotation);
+ }
+}
+
+void USGWristTrackerComponent::FImpl::DrawDebugWristTracker() const
+{
+ const UWorld* World{Owner->GetWorld()};
+ const FSGWristTrackingDebuggingSettings& DebuggingSettings{Owner->GetWristTrackingSettings().DebuggingSettings};
+
+ FSGDebugGizmo::Draw(World, Owner->WristLocation, Owner->WristRotation, DebuggingSettings.DebugWristTrackerSettings);
+}
+
+void USGWristTrackerComponent::FImplDeleter::operator()(const FImpl* P) const
+{
+ delete P;
+}
\ No newline at end of file
diff --git a/Plugins/SenseGlove/Source/SenseGlove/Private/SenseGlove/Engine/SGGameInstance.cpp b/Plugins/SenseGlove/Source/SenseGlove/Private/SenseGlove/Engine/SGGameInstance.cpp
new file mode 100644
index 0000000..a8f54d7
--- /dev/null
+++ b/Plugins/SenseGlove/Source/SenseGlove/Private/SenseGlove/Engine/SGGameInstance.cpp
@@ -0,0 +1,97 @@
+/**
+ * @file
+ *
+ * @author Mamadou Babaei
+ *
+ * @section LICENSE
+ *
+ * (The MIT License)
+ *
+ * Copyright (c) 2020 - 2026 SenseGlove
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * @section DESCRIPTION
+ *
+ *
+ */
+
+
+#include "SenseGlove/Engine/SGGameInstance.h"
+
+#include "Engine/Engine.h"
+
+#include "SenseGlove/GameFramework/SGGameUserSettings.h"
+#include "SGLog/SGLog.h"
+
+void USGGameInstance::SG_GetEngineScalabilitySettings()
+{
+ const USGGameUserSettings* GameUserSettings{USGGameUserSettings::GetInstance()};
+ ensureAlwaysMsgf(GameUserSettings, TEXT("Invalid GameUserSettings!"));
+
+ static UEnum* EnumPtr{StaticEnum()};
+ ensureAlwaysMsgf(EnumPtr, TEXT("Failed to find the ESGEngineScalabilitySettings enum!"));
+
+ const ESGEngineScalabilitySettings Scalability = GameUserSettings->GetEngineScalabilitySettings();
+ const FName ScalabilityName{
+ EnumPtr->GetNameByValue(static_cast(Scalability))
+ };
+
+ SGLOG("Get Engine Scalability Settings", ScalabilityName);
+}
+
+void USGGameInstance::SG_SetEngineScalabilitySettings(const ESGEngineScalabilitySettings Scalability)
+{
+ USGGameUserSettings* GameUserSettings{USGGameUserSettings::GetInstance()};
+ ensureAlwaysMsgf(GameUserSettings, TEXT("Invalid GameUserSettings!"));
+
+ static UEnum* EnumPtr{StaticEnum()};
+ ensureAlwaysMsgf(EnumPtr, TEXT("Failed to find the ESGEngineScalabilitySettings enum!"));
+
+ GameUserSettings->SetEngineScalabilitySettings(Scalability);
+
+ const FName ScalabilityName{
+ EnumPtr->GetNameByValue(static_cast(Scalability))
+ };
+
+ SGLOG("Set Engine Scalability Settings", ScalabilityName);
+}
+
+USGGameInstance::USGGameInstance(const FObjectInitializer& ObjectInitializer)
+ : Super(ObjectInitializer)
+{
+}
+
+void USGGameInstance::Init()
+{
+ Super::Init();
+
+ SGLOG("Initializing the SenseGlove GameInstance...")
+
+ SGLOG("The SenseGlove GameInstance has been initialized successfully!")
+}
+
+void USGGameInstance::Shutdown()
+{
+ Super::Shutdown();
+
+ SGLOG("Shutting down the SenseGlove GameInstance...")
+
+ SGLOG("The SenseGlove GameInstance has been shutdown successfully!")
+}
\ No newline at end of file
diff --git a/Plugins/SenseGlove/Source/SenseGlove/Private/SenseGlove/GameFramework/SGGameModeBase.cpp b/Plugins/SenseGlove/Source/SenseGlove/Private/SenseGlove/GameFramework/SGGameModeBase.cpp
new file mode 100644
index 0000000..13372f6
--- /dev/null
+++ b/Plugins/SenseGlove/Source/SenseGlove/Private/SenseGlove/GameFramework/SGGameModeBase.cpp
@@ -0,0 +1,46 @@
+/**
+ * @file
+ *
+ * @author Mamadou Babaei
+ *
+ * @section LICENSE
+ *
+ * (The MIT License)
+ *
+ * Copyright (c) 2020 - 2026 SenseGlove
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * @section DESCRIPTION
+ *
+ *
+ */
+
+
+#include "SenseGlove/GameFramework/SGGameModeBase.h"
+
+#include "SenseGlove/GameFramework/SGPawn.h"
+#include "SenseGlove/GameFramework/SGPlayerController.h"
+
+ASGGameModeBase::ASGGameModeBase(const FObjectInitializer& ObjectInitializer)
+ : Super(ObjectInitializer)
+{
+ DefaultPawnClass = ASGPawn::StaticClass();
+ PlayerControllerClass = ASGPlayerController::StaticClass();
+}
\ No newline at end of file
diff --git a/Plugins/SenseGlove/Source/SenseGlove/Private/SenseGlove/GameFramework/SGGameUserSettings.cpp b/Plugins/SenseGlove/Source/SenseGlove/Private/SenseGlove/GameFramework/SGGameUserSettings.cpp
new file mode 100644
index 0000000..57c5bdb
--- /dev/null
+++ b/Plugins/SenseGlove/Source/SenseGlove/Private/SenseGlove/GameFramework/SGGameUserSettings.cpp
@@ -0,0 +1,300 @@
+/**
+ * @file
+ *
+ * @author Mamadou Babaei
+ *
+ * @section LICENSE
+ *
+ * (The MIT License)
+ *
+ * Copyright (c) 2020 - 2026 SenseGlove
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * @section DESCRIPTION
+ *
+ *
+ */
+
+
+#include "SenseGlove/GameFramework/SGGameUserSettings.h"
+
+#include "Engine/Engine.h"
+#include "HAL/IConsoleManager.h"
+#include "Misc/ConfigCacheIni.h"
+#include "Runtime/Launch/Resources/Version.h"
+#include "Scalability.h"
+
+#include "SGSettings/SGSettings.h"
+
+struct USGGameUserSettings::FImpl
+{
+ /************************
+ * Static methods
+ ************************/
+
+ static float GetRenderScaleLevelFromQualityLevel(
+ int32 InQualityLevel,
+ Scalability::EQualityLevelBehavior Behavior = Scalability::EQualityLevelBehavior::EAbsolute);
+
+ /************************
+ * Owner object
+ ************************/
+
+ USGGameUserSettings* Owner;
+
+ /************************
+ * Constructor / Destructor
+ ************************/
+
+ explicit FImpl(USGGameUserSettings* InOwner);
+ ~FImpl();
+
+ /************************
+ * Default copy constructor & copy assignment operator
+ ************************/
+
+ FImpl(const FImpl& Rhs) = default;
+ FImpl& operator=(const FImpl& Rhs) = default;
+
+ /************************
+ * Methods
+ ************************/
+
+ bool DoesCurrentSettingsMatch(ESGEngineScalabilitySettings Scalability) const;
+};
+
+float USGGameUserSettings::FImpl::GetRenderScaleLevelFromQualityLevel(
+ const int32 InQualityLevel, const Scalability::EQualityLevelBehavior Behavior)
+{
+ if (!ensureAlwaysMsgf(GConfig, TEXT("Invalid GConfig!")))
+ {
+ return 0.0f;
+ }
+
+ TArray ResolutionValueStrings;
+ GConfig->GetSingleLineArray(TEXT("ScalabilitySettings"), TEXT("PerfIndexValues_ResolutionQuality"),
+ ResolutionValueStrings, GScalabilityIni);
+
+ if (ResolutionValueStrings.Num() == 0)
+ {
+ UE_LOG(LogConsoleResponse, Display,
+ TEXT("Failed to find resolution value strings in scalability ini. Falling back to the default."));
+ return 100.0f;
+ }
+
+ // No negative levels!
+ int32 QualityLevel = FMath::Max(0, InQualityLevel);
+ if (Behavior == Scalability::EQualityLevelBehavior::ERelativeToMax)
+ {
+ QualityLevel = FMath::Max(ResolutionValueStrings.Num() - 1 - QualityLevel, 0);
+ }
+ else
+ {
+ QualityLevel = FMath::Clamp(QualityLevel, 0, ResolutionValueStrings.Num() - 1);
+ }
+
+ return FCString::Atof(*ResolutionValueStrings[QualityLevel]);
+}
+
+USGGameUserSettings::USGGameUserSettings(const FObjectInitializer& ObjectInitializer)
+ : Super(ObjectInitializer),
+ Pimpl(TUniquePtr(new FImpl{this}, PimplDeleter))
+{
+}
+
+USGGameUserSettings* USGGameUserSettings::GetInstance()
+{
+ USGGameUserSettings* Instance{GEngine ? Cast(GEngine->GetGameUserSettings()) : nullptr};
+ ensureAlwaysMsgf(Instance, TEXT("Invalid GameUserSettings!"));
+ return Instance;
+}
+
+ESGEngineScalabilitySettings USGGameUserSettings::GetEngineScalabilitySettings() const
+{
+ const bool bDoesCurrentSettingsMatchLow = Pimpl->DoesCurrentSettingsMatch(ESGEngineScalabilitySettings::Low);
+ if (bDoesCurrentSettingsMatchLow)
+ {
+ return ESGEngineScalabilitySettings::Low;
+ }
+
+ const bool bDoesCurrentSettingsMatchMedium = Pimpl->DoesCurrentSettingsMatch(ESGEngineScalabilitySettings::Medium);
+ if (bDoesCurrentSettingsMatchMedium)
+ {
+ return ESGEngineScalabilitySettings::Medium;
+ }
+
+ const bool bDoesCurrentSettingsMatchHigh = Pimpl->DoesCurrentSettingsMatch(ESGEngineScalabilitySettings::High);
+ if (bDoesCurrentSettingsMatchHigh)
+ {
+ return ESGEngineScalabilitySettings::High;
+ }
+
+ const bool bDoesCurrentSettingsMatchEpic = Pimpl->DoesCurrentSettingsMatch(ESGEngineScalabilitySettings::Epic);
+ if (bDoesCurrentSettingsMatchEpic)
+ {
+ return ESGEngineScalabilitySettings::Epic;
+ }
+
+ const bool bDoesCurrentSettingsMatchCinematic = Pimpl->DoesCurrentSettingsMatch(
+ ESGEngineScalabilitySettings::Cinematic);
+ if (bDoesCurrentSettingsMatchCinematic)
+ {
+ return ESGEngineScalabilitySettings::Cinematic;
+ }
+
+ return ESGEngineScalabilitySettings::Custom;
+}
+
+void USGGameUserSettings::SetEngineScalabilitySettings(ESGEngineScalabilitySettings Scalability)
+{
+ if (!ensureAlwaysMsgf(Scalability != ESGEngineScalabilitySettings::Custom,
+ TEXT("Cannot set the engine scalability directly to custom!"
+ " It should be either: Low, Medium, High, Epic, Cinematic,"
+ " or Auto (determined by hardware benchmarking)!")))
+ {
+ return;
+ }
+
+ const FSGHardwareBenchmarkingSettings& Settings{
+ USGSettings::GetInstance()->GetGameUserSettings().HardwareBenchmarkingSettings
+ };
+
+ if (IsDirty())
+ {
+ ValidateSettings();
+ ConfirmVideoMode();
+ }
+
+ if (Scalability != ESGEngineScalabilitySettings::Auto)
+ {
+ const int32 ScalabilityValue = static_cast(Scalability);
+
+ const float ResolutionScaleNormalized = FImpl::GetRenderScaleLevelFromQualityLevel(
+ ScalabilityValue, Scalability::EQualityLevelBehavior::EAbsolute);
+ SetResolutionScaleNormalized(ResolutionScaleNormalized);
+
+ ScalabilityQuality.SetViewDistanceQuality(ScalabilityValue);
+ ScalabilityQuality.SetAntiAliasingQuality(ScalabilityValue);
+ ScalabilityQuality.SetShadowQuality(ScalabilityValue);
+ ScalabilityQuality.SetGlobalIlluminationQuality(ScalabilityValue);
+ ScalabilityQuality.SetReflectionQuality(ScalabilityValue);
+ ScalabilityQuality.SetPostProcessQuality(ScalabilityValue);
+ ScalabilityQuality.SetTextureQuality(ScalabilityValue);
+ ScalabilityQuality.SetEffectsQuality(ScalabilityValue);
+ ScalabilityQuality.SetFoliageQuality(ScalabilityValue);
+ ScalabilityQuality.SetShadingQuality(ScalabilityValue);
+ ScalabilityQuality.SetLandscapeQuality(ScalabilityValue);
+ }
+ else
+ {
+ RunHardwareBenchmark(
+ Settings.WorkScale,
+ Settings.CPUMultiplier,
+ Settings.GPUMultiplier);
+ ApplyHardwareBenchmarkResults();
+ }
+
+ ConfirmVideoMode();
+ ApplySettings(false);
+}
+
+USGGameUserSettings::FImpl::FImpl(USGGameUserSettings* InOwner)
+ : Owner(InOwner)
+{
+}
+
+USGGameUserSettings::FImpl::~FImpl() = default;
+
+bool USGGameUserSettings::FImpl::DoesCurrentSettingsMatch(const ESGEngineScalabilitySettings Scalability) const
+{
+ static const float ResolutionScaleNormalizedLow = FImpl::GetRenderScaleLevelFromQualityLevel(
+ static_cast(ESGEngineScalabilitySettings::Low));
+ static const float ResolutionScaleNormalizedMedium = FImpl::GetRenderScaleLevelFromQualityLevel(
+ static_cast(ESGEngineScalabilitySettings::Medium));
+ static const float ResolutionScaleNormalizedHigh = FImpl::GetRenderScaleLevelFromQualityLevel(
+ static_cast(ESGEngineScalabilitySettings::High));
+ static const float ResolutionScaleNormalizedEpic = FImpl::GetRenderScaleLevelFromQualityLevel(
+ static_cast(ESGEngineScalabilitySettings::Epic));
+ static const float ResolutionScaleNormalizedCinematic = FImpl::GetRenderScaleLevelFromQualityLevel(
+ static_cast(ESGEngineScalabilitySettings::Cinematic));
+
+ float ResolutionScaleNormalized = 0.0f;
+ switch (Scalability)
+ {
+ case ESGEngineScalabilitySettings::Low:
+ ResolutionScaleNormalized = ResolutionScaleNormalizedLow;
+ break;
+ case ESGEngineScalabilitySettings::Medium:
+ ResolutionScaleNormalized = ResolutionScaleNormalizedMedium;
+ break;
+ case ESGEngineScalabilitySettings::High:
+ ResolutionScaleNormalized = ResolutionScaleNormalizedHigh;
+ break;
+ case ESGEngineScalabilitySettings::Epic:
+ ResolutionScaleNormalized = ResolutionScaleNormalizedEpic;
+ break;
+ case ESGEngineScalabilitySettings::Cinematic:
+ ResolutionScaleNormalized = ResolutionScaleNormalizedCinematic;
+ break;
+ default:
+ ensureAlwaysMsgf(false, TEXT("Login Error: the execution flow should never reach here!"));
+ break;
+ }
+
+ const int32 ScalabilityValue = static_cast(Scalability);
+
+ const float CurrentResolutionScaleNormalized = FImpl::GetRenderScaleLevelFromQualityLevel(
+ ScalabilityValue, Scalability::EQualityLevelBehavior::EAbsolute);
+
+ const int32 CurrentViewDistanceQuality = Owner->ScalabilityQuality.ViewDistanceQuality;
+ const int32 CurrentAntiAliasingQuality = Owner->ScalabilityQuality.AntiAliasingQuality;
+ const int32 CurrentShadowQuality = Owner->ScalabilityQuality.ShadowQuality;
+ const int32 CurrentGlobalIlluminationQuality = Owner->ScalabilityQuality.GlobalIlluminationQuality;
+ const int32 CurrentReflectionQuality = Owner->ScalabilityQuality.ReflectionQuality;
+ const int32 CurrentPostProcessingQuality = Owner->ScalabilityQuality.PostProcessQuality;
+ const int32 CurrentTextureQuality = Owner->ScalabilityQuality.TextureQuality;
+ const int32 CurrentEffectQuality = Owner->ScalabilityQuality.EffectsQuality;
+ const int32 CurrentFoliageQuality = Owner->ScalabilityQuality.FoliageQuality;
+ const int32 CurrentShadingQuality = Owner->ScalabilityQuality.ShadingQuality;
+ const int32 CurrentLandscapeQuality = Owner->ScalabilityQuality.LandscapeQuality;
+
+ if (FMath::IsNearlyEqual(CurrentResolutionScaleNormalized, ResolutionScaleNormalized)
+ && CurrentViewDistanceQuality == ScalabilityValue
+ && CurrentAntiAliasingQuality == ScalabilityValue
+ && CurrentShadowQuality == ScalabilityValue
+ && CurrentGlobalIlluminationQuality == ScalabilityValue
+ && CurrentReflectionQuality == ScalabilityValue
+ && CurrentPostProcessingQuality == ScalabilityValue
+ && CurrentTextureQuality == ScalabilityValue
+ && CurrentEffectQuality == ScalabilityValue
+ && CurrentFoliageQuality == ScalabilityValue
+ && CurrentShadingQuality == ScalabilityValue
+ && CurrentLandscapeQuality == ScalabilityValue)
+ {
+ return true;
+ }
+
+ return false;
+}
+
+void USGGameUserSettings::FImplDeleter::operator()(const FImpl* P) const
+{
+ delete P;
+}
\ No newline at end of file
diff --git a/Plugins/SenseGlove/Source/SenseGlove/Private/SenseGlove/GameFramework/SGPawn.cpp b/Plugins/SenseGlove/Source/SenseGlove/Private/SenseGlove/GameFramework/SGPawn.cpp
new file mode 100644
index 0000000..7dacfd3
--- /dev/null
+++ b/Plugins/SenseGlove/Source/SenseGlove/Private/SenseGlove/GameFramework/SGPawn.cpp
@@ -0,0 +1,2279 @@
+/**
+ * @file
+ *
+ * @author Mamadou Babaei
+ *
+ * @section LICENSE
+ *
+ * (The MIT License)
+ *
+ * Copyright (c) 2020 - 2026 SenseGlove
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * @section DESCRIPTION
+ *
+ *
+ */
+
+
+#include "SenseGlove/GameFramework/SGPawn.h"
+
+#include "Camera/CameraComponent.h"
+#include "Components/PrimitiveComponent.h"
+#include "Components/SphereComponent.h"
+#include "Kismet/KismetMathLibrary.h"
+#include "Runtime/Launch/Resources/Version.h"
+#include "UObject/UObjectGlobals.h"
+#include "XRDeviceVisualizationComponent.h"
+
+#include "SenseGlove/Components/SGGrabComponent.h"
+#include "SenseGlove/Components/SGTouchComponent.h"
+#include "SenseGlove/Components/SGVirtualHandComponent.h"
+#include "SenseGlove/Components/SGWristTrackerComponent.h"
+#include "SGSettings/SGSettings.h"
+#include "SGLog/SGLog.h"
+
+struct ASGPawn::FImpl
+{
+ /************************
+ * Static methods
+ ************************/
+
+ /************************
+ * Owner object
+ ************************/
+
+ ASGPawn* Owner;
+
+ /************************
+ * Constructor / Destructor
+ ************************/
+
+ explicit FImpl(ASGPawn* InOwner);
+ ~FImpl();
+
+ /************************
+ * Default copy constructor & copy assignment operator
+ ************************/
+
+ FImpl(const FImpl& Rhs) = default;
+ FImpl& operator=(const FImpl& Rhs) = default;
+
+ /************************
+ * Methods
+ ************************/
+
+ void BindRealHandsOverlapEvents();
+ void UnbindRealHandsOverlapEvents();
+
+ void BindFingerGrabOverlapEvents();
+ void UnbindFingerGrabOverlapEvents();
+
+ void BindFingerTouchOverlapEvents();
+ void UnbindFingerTouchOverlapEvents();
+
+ void SetVisibility(USceneComponent* SceneComponent, const bool bVisibility) const;
+
+ void UpdateHandLocation(const bool bRight, const FVector& Location, const bool bUpdateVirtualHand) const;
+ void UpdateLeftHandLocation(const FVector& Location, const bool bUpdateVirtualHand) const;
+ void UpdateRightHandLocation(const FVector& Location, const bool bUpdateVirtualHand) const;
+
+ void UpdateHandRotation(const bool bRight, const FRotator& Rotation, const bool bUpdateVirtualHand);
+ void UpdateLeftHandRotation(const FRotator& Rotation, const bool bUpdateVirtualHand);
+ void UpdateRightHandRotation(const FRotator& Rotation, const bool bUpdateVirtualHand);
+
+ void UpdateHandLocationAndRotation(
+ const bool bRight, const FVector& Location, const FRotator& Rotation, const bool bUpdateVirtualHand);
+ void UpdateLeftHandLocationAndRotation(
+ const FVector& Location, const FRotator& Rotation, const bool bUpdateVirtualHand);
+ void UpdateRightHandLocationAndRotation(
+ const FVector& Location, const FRotator& Rotation, const bool bUpdateVirtualHand);
+
+ void SetFingerColliderCollisionEnabled(USphereComponent* Collider, bool bEnabled) const;
+ void SetAllFingersColliderCollisionEnabled(bool bRight, bool bEnabled) const;
+
+ FORCEINLINE void RegisterLeftHandOverlappedActor(const AActor* Actor) const
+ {
+ Owner->LeftHandOverlappedActors.AddUnique(Actor);
+ }
+
+ FORCEINLINE void UnregisterLeftHandOverlappedActor(const AActor* Actor) const
+ {
+ Owner->LeftHandOverlappedActors.Remove(Actor);
+ }
+
+ FORCEINLINE void RegisterRightHandOverlappedActor(const AActor* Actor) const
+ {
+ Owner->RightHandOverlappedActors.AddUnique(Actor);
+ }
+
+ FORCEINLINE void UnregisterRightHandOverlappedActor(const AActor* Actor) const
+ {
+ Owner->RightHandOverlappedActors.Remove(Actor);
+ }
+
+ FORCEINLINE bool IsLeftHandOverlappingWithAnyActor() const
+ {
+ return Owner->LeftHandOverlappedActors.Num() > 0;
+ }
+
+ FORCEINLINE bool IsRightHandOverlappingWithAnyActor() const
+ {
+ return Owner->RightHandOverlappedActors.Num() > 0;
+ }
+
+ void ApplyHandVelocityToGrabbedActor(const FSGGrabState& GrabState) const;
+
+ void SetGrabbedActor(FSGGrabState& GrabState, AActor* Actor) const;
+ void SetLeftHandGrabbedActor(AActor* Actor) const;
+ void SetRightHandGrabbedActor(AActor* Actor) const;
+
+ void RecordHandVelocity(FSGGrabState& GrabState, const float DeltaSeconds) const;
+
+ void Release(FSGGrabState& GrabState) const;
+
+ void TriggerGrabStateUpdatedEvent(const FSGGrabState& GrabState) const;
+ void TriggerTouchStateUpdatedEvent(const FSGTouchState& TouchState) const;
+ void TriggerActorGrabbedEvent(const USGVirtualHandComponent* Hand, const AActor* Actor) const;
+ void TriggerActorReleasedEvent(const USGVirtualHandComponent* Hand, const AActor* Actor) const;
+ void TriggerBeginTouch(const USGVirtualHandComponent* Hand, const AActor* Actor) const;
+ void TriggerEndTouch(const USGVirtualHandComponent* Hand, const AActor* Actor) const;
+};
+
+ASGPawn::ASGPawn(const FObjectInitializer& ObjectInitializer)
+ : Super(ObjectInitializer),
+ Pimpl(TUniquePtr(new FImpl{this}, PimplDeleter))
+{
+ PrimaryActorTick.bCanEverTick = true;
+
+ SceneRoot = ObjectInitializer.CreateDefaultSubobject(this, TEXT("SceneRoot"));
+ SetRootComponent(SceneRoot);
+
+ Camera = ObjectInitializer.CreateDefaultSubobject(this, TEXT("Camera"));
+ Camera->SetupAttachment(GetRootComponent());
+ Camera->SetCanEverAffectNavigation(false);
+
+ WristTrackerLeft = ObjectInitializer.CreateDefaultSubobject(
+ this, TEXT("WristTrackerLeft"));
+ WristTrackerLeft->SetupAttachment(GetRootComponent());
+ WristTrackerLeft->SetRight(false);
+
+ ControllerVisualizerLeft = ObjectInitializer.CreateDefaultSubobject(
+ this, TEXT("ControllerVisualizerLeft"));
+ ControllerVisualizerLeft->SetupAttachment(WristTrackerLeft);
+ ControllerVisualizerLeft->SetDisplayModelSource(FName("OpenXR"));
+
+ HandLeft = ObjectInitializer.CreateDefaultSubobject(
+ this, TEXT("HandLeft"));
+ HandLeft->SetupAttachment(GetRootComponent());
+ HandLeft->SetCanEverAffectNavigation(false);
+ HandLeft->SetGenerateOverlapEvents(true);
+ HandLeft->CanCharacterStepUpOn = ECanBeCharacterBase::ECB_Owner;
+ HandLeft->SetCollisionObjectType(ECC_Pawn);
+ HandLeft->SetCollisionEnabled(ECollisionEnabled::QueryAndPhysics);
+ HandLeft->SetCollisionResponseToAllChannels(ECR_Block);
+ HandLeft->SetCollisionResponseToChannel(ECC_WorldStatic, ECR_Block);
+ HandLeft->SetCollisionResponseToChannel(ECC_WorldDynamic, ECR_Block);
+ HandLeft->SetCollisionResponseToChannel(ECC_Pawn, ECR_Block);
+ HandLeft->SetCollisionResponseToChannel(ECC_Visibility, ECR_Block);
+ HandLeft->SetCollisionResponseToChannel(ECC_PhysicsBody, ECR_Block);
+ HandLeft->SetCollisionResponseToChannel(ECC_Vehicle, ECR_Block);
+ HandLeft->SetCollisionResponseToChannel(ECC_Destructible, ECR_Block);
+ HandLeft->SetRight(false);
+
+ const FSGVirtualHandSettings LeftHandSettings{HandLeft->GetVirtualHandSettings()};
+ HandLeft->SetRelativeRotation(LeftHandSettings.MeshSettings.RootBoneRotationCorrection,
+ false, nullptr, ETeleportType::None);
+ HandLeft->SetRelativeLocation(FVector{30.0f, -20.0f, -10.0f},
+ false, nullptr, ETeleportType::None);
+ HandLeft->SetWristTracker(WristTrackerLeft);
+
+ RealHandLeft = ObjectInitializer.CreateDefaultSubobject(
+ this, TEXT("RealHandLeft"));
+ RealHandLeft->SetupAttachment(GetRootComponent());
+ RealHandLeft->SetCanEverAffectNavigation(false);
+ RealHandLeft->SetGenerateOverlapEvents(true);
+ RealHandLeft->CanCharacterStepUpOn = ECanBeCharacterBase::ECB_Owner;
+ RealHandLeft->SetCollisionObjectType(ECC_Pawn);
+ RealHandLeft->SetCollisionEnabled(ECollisionEnabled::QueryOnly);
+ RealHandLeft->SetCollisionResponseToAllChannels(ECR_Overlap);
+ RealHandLeft->SetCollisionResponseToChannel(ECC_WorldStatic, ECR_Overlap);
+ RealHandLeft->SetCollisionResponseToChannel(ECC_WorldDynamic, ECR_Overlap);
+ RealHandLeft->SetCollisionResponseToChannel(ECC_Pawn, ECR_Overlap);
+ RealHandLeft->SetCollisionResponseToChannel(ECC_Visibility, ECR_Overlap);
+ RealHandLeft->SetCollisionResponseToChannel(ECC_PhysicsBody, ECR_Overlap);
+ RealHandLeft->SetCollisionResponseToChannel(ECC_Vehicle, ECR_Overlap);
+ RealHandLeft->SetCollisionResponseToChannel(ECC_Destructible, ECR_Overlap);
+ RealHandLeft->SetRight(false);
+ Pimpl->SetVisibility(RealHandLeft, false);
+
+ const FSGVirtualHandMeshSettings RealLeftHandMeshSettings{HandLeft->GetVirtualHandSettings().MeshSettings};
+ RealHandLeft->SetRelativeRotation(RealLeftHandMeshSettings.RootBoneRotationCorrection,
+ false, nullptr, ETeleportType::None);
+ RealHandLeft->SetRelativeLocation(FVector{30.0f, -20.0f, -10.0f},
+ false, nullptr, ETeleportType::None);
+ RealHandLeft->SetWristTracker(WristTrackerLeft);
+
+ LeftThumbFingertipGrabCollider = ObjectInitializer.CreateDefaultSubobject(
+ this, TEXT("LeftThumbFingertipGrabCollider"));
+ LeftThumbFingertipGrabCollider->SetRelativeScale3D(LeftHandSettings.GrabSettings.DefaultColliderSize);
+ LeftThumbFingertipGrabCollider->SetupAttachment(HandLeft, LeftHandSettings.GrabSettings.ThumbColliderSocketName);
+ LeftThumbFingertipGrabCollider->SetCastShadow(false);
+ LeftThumbFingertipGrabCollider->bCastDynamicShadow = false;
+ LeftThumbFingertipGrabCollider->SetReceivesDecals(true);
+ LeftThumbFingertipGrabCollider->SetCanEverAffectNavigation(false);
+ LeftThumbFingertipGrabCollider->CanCharacterStepUpOn = ECanBeCharacterBase::ECB_Owner;
+ LeftThumbFingertipGrabCollider->SetCollisionObjectType(ECC_Pawn);
+ Pimpl->SetFingerColliderCollisionEnabled(LeftThumbFingertipGrabCollider, true);
+
+ LeftIndexFingertipGrabCollider = ObjectInitializer.CreateDefaultSubobject(
+ this, TEXT("LeftIndexFingertipGrabCollider"));
+ LeftIndexFingertipGrabCollider->SetRelativeScale3D(LeftHandSettings.GrabSettings.DefaultColliderSize);
+ LeftIndexFingertipGrabCollider->SetupAttachment(HandLeft, LeftHandSettings.GrabSettings.IndexColliderSocketName);
+ LeftIndexFingertipGrabCollider->SetCastShadow(false);
+ LeftIndexFingertipGrabCollider->bCastDynamicShadow = false;
+ LeftIndexFingertipGrabCollider->SetReceivesDecals(true);
+ LeftIndexFingertipGrabCollider->SetCanEverAffectNavigation(false);
+ LeftIndexFingertipGrabCollider->CanCharacterStepUpOn = ECanBeCharacterBase::ECB_Owner;
+ LeftIndexFingertipGrabCollider->SetCollisionObjectType(ECC_Pawn);
+ Pimpl->SetFingerColliderCollisionEnabled(LeftIndexFingertipGrabCollider, true);
+
+ LeftMiddleFingertipGrabCollider = ObjectInitializer.CreateDefaultSubobject(
+ this, TEXT("LeftMiddleFingertipGrabCollider"));
+ LeftMiddleFingertipGrabCollider->SetRelativeScale3D(LeftHandSettings.GrabSettings.DefaultColliderSize);
+ LeftMiddleFingertipGrabCollider->SetupAttachment(HandLeft, LeftHandSettings.GrabSettings.MiddleColliderSocketName);
+ LeftMiddleFingertipGrabCollider->SetCastShadow(false);
+ LeftMiddleFingertipGrabCollider->bCastDynamicShadow = false;
+ LeftMiddleFingertipGrabCollider->SetReceivesDecals(true);
+ LeftMiddleFingertipGrabCollider->SetCanEverAffectNavigation(false);
+ LeftMiddleFingertipGrabCollider->CanCharacterStepUpOn = ECanBeCharacterBase::ECB_Owner;
+ LeftMiddleFingertipGrabCollider->SetCollisionObjectType(ECC_Pawn);
+ Pimpl->SetFingerColliderCollisionEnabled(LeftMiddleFingertipGrabCollider, true);
+
+ LeftThumbFingertipTouchCollider = ObjectInitializer.CreateDefaultSubobject(
+ this, TEXT("LeftThumbFingertipTouchCollider"));
+ LeftThumbFingertipTouchCollider->SetRelativeScale3D(LeftHandSettings.TouchSettings.DefaultColliderSize);
+ LeftThumbFingertipTouchCollider->SetupAttachment(HandLeft, LeftHandSettings.TouchSettings.ThumbColliderSocketName);
+ LeftThumbFingertipTouchCollider->SetCastShadow(false);
+ LeftThumbFingertipTouchCollider->bCastDynamicShadow = false;
+ LeftThumbFingertipTouchCollider->SetReceivesDecals(true);
+ LeftThumbFingertipTouchCollider->SetCanEverAffectNavigation(false);
+ LeftThumbFingertipTouchCollider->CanCharacterStepUpOn = ECanBeCharacterBase::ECB_Owner;
+ LeftThumbFingertipTouchCollider->SetCollisionObjectType(ECC_Pawn);
+ Pimpl->SetFingerColliderCollisionEnabled(LeftThumbFingertipTouchCollider, true);
+
+ LeftIndexFingertipTouchCollider = ObjectInitializer.CreateDefaultSubobject(
+ this, TEXT("LeftIndexFingertipTouchCollider"));
+ LeftIndexFingertipTouchCollider->SetRelativeScale3D(LeftHandSettings.TouchSettings.DefaultColliderSize);
+ LeftIndexFingertipTouchCollider->SetupAttachment(HandLeft, LeftHandSettings.TouchSettings.IndexColliderSocketName);
+ LeftIndexFingertipTouchCollider->SetCastShadow(false);
+ LeftIndexFingertipTouchCollider->bCastDynamicShadow = false;
+ LeftIndexFingertipTouchCollider->SetReceivesDecals(true);
+ LeftIndexFingertipTouchCollider->SetCanEverAffectNavigation(false);
+ LeftIndexFingertipTouchCollider->CanCharacterStepUpOn = ECanBeCharacterBase::ECB_Owner;
+ LeftIndexFingertipTouchCollider->SetCollisionObjectType(ECC_Pawn);
+ Pimpl->SetFingerColliderCollisionEnabled(LeftIndexFingertipTouchCollider, true);
+
+ LeftMiddleFingertipTouchCollider = ObjectInitializer.CreateDefaultSubobject(
+ this, TEXT("LeftMiddleFingertipTouchCollider"));
+ LeftMiddleFingertipTouchCollider->SetRelativeScale3D(LeftHandSettings.TouchSettings.DefaultColliderSize);
+ LeftMiddleFingertipTouchCollider->SetupAttachment(
+ HandLeft, LeftHandSettings.TouchSettings.MiddleColliderSocketName);
+ LeftMiddleFingertipTouchCollider->SetCastShadow(false);
+ LeftMiddleFingertipTouchCollider->bCastDynamicShadow = false;
+ LeftMiddleFingertipTouchCollider->SetReceivesDecals(true);
+ LeftMiddleFingertipTouchCollider->SetCanEverAffectNavigation(false);
+ LeftMiddleFingertipTouchCollider->CanCharacterStepUpOn = ECanBeCharacterBase::ECB_Owner;
+ LeftMiddleFingertipTouchCollider->SetCollisionObjectType(ECC_Pawn);
+ LeftMiddleFingertipTouchCollider->SetCollisionEnabled(ECollisionEnabled::QueryOnly);
+ Pimpl->SetFingerColliderCollisionEnabled(LeftMiddleFingertipTouchCollider, true);
+
+ LeftRingFingertipTouchCollider = ObjectInitializer.CreateDefaultSubobject(
+ this, TEXT("LeftRingFingertipTouchCollider"));
+ LeftRingFingertipTouchCollider->SetRelativeScale3D(LeftHandSettings.TouchSettings.DefaultColliderSize);
+ LeftRingFingertipTouchCollider->SetupAttachment(HandLeft, LeftHandSettings.TouchSettings.RingColliderSocketName);
+ LeftRingFingertipTouchCollider->SetCastShadow(false);
+ LeftRingFingertipTouchCollider->bCastDynamicShadow = false;
+ LeftRingFingertipTouchCollider->SetReceivesDecals(true);
+ LeftRingFingertipTouchCollider->SetCanEverAffectNavigation(false);
+ LeftRingFingertipTouchCollider->CanCharacterStepUpOn = ECanBeCharacterBase::ECB_Owner;
+ LeftRingFingertipTouchCollider->SetCollisionObjectType(ECC_Pawn);
+ Pimpl->SetFingerColliderCollisionEnabled(LeftRingFingertipTouchCollider, true);
+
+ LeftPinkyFingertipTouchCollider = ObjectInitializer.CreateDefaultSubobject(
+ this, TEXT("LeftPinkyFingertipTouchCollider"));
+ LeftPinkyFingertipTouchCollider->SetRelativeScale3D(LeftHandSettings.TouchSettings.DefaultColliderSize);
+ LeftPinkyFingertipTouchCollider->SetupAttachment(HandLeft, LeftHandSettings.TouchSettings.PinkyColliderSocketName);
+ LeftPinkyFingertipTouchCollider->SetCastShadow(false);
+ LeftPinkyFingertipTouchCollider->bCastDynamicShadow = false;
+ LeftPinkyFingertipTouchCollider->SetReceivesDecals(true);
+ LeftPinkyFingertipTouchCollider->SetCanEverAffectNavigation(false);
+ LeftPinkyFingertipTouchCollider->CanCharacterStepUpOn = ECanBeCharacterBase::ECB_Owner;
+ LeftPinkyFingertipTouchCollider->SetCollisionObjectType(ECC_Pawn);
+ Pimpl->SetFingerColliderCollisionEnabled(LeftPinkyFingertipTouchCollider, true);
+
+ WristTrackerRight = ObjectInitializer.CreateDefaultSubobject(
+ this, TEXT("WristTrackerRight"));
+ WristTrackerRight->SetupAttachment(GetRootComponent());
+ WristTrackerRight->SetRight(true);
+
+ ControllerVisualizerRight = ObjectInitializer.CreateDefaultSubobject(
+ this, TEXT("ControllerVisualizerRight"));
+ ControllerVisualizerRight->SetupAttachment(WristTrackerRight);
+ ControllerVisualizerRight->SetDisplayModelSource(FName("OpenXR"));
+
+ HandRight = ObjectInitializer.CreateDefaultSubobject(
+ this, TEXT("HandRight"));
+ HandRight->SetupAttachment(GetRootComponent());
+ HandRight->SetCanEverAffectNavigation(false);
+ HandRight->SetGenerateOverlapEvents(true);
+ HandRight->CanCharacterStepUpOn = ECanBeCharacterBase::ECB_Owner;
+ HandRight->SetCollisionObjectType(ECC_Pawn);
+ HandRight->SetCollisionEnabled(ECollisionEnabled::QueryAndPhysics);
+ HandRight->SetCollisionResponseToAllChannels(ECR_Block);
+ HandRight->SetCollisionResponseToChannel(ECC_WorldStatic, ECR_Block);
+ HandRight->SetCollisionResponseToChannel(ECC_WorldDynamic, ECR_Block);
+ HandRight->SetCollisionResponseToChannel(ECC_Pawn, ECR_Block);
+ HandRight->SetCollisionResponseToChannel(ECC_Visibility, ECR_Block);
+ HandRight->SetCollisionResponseToChannel(ECC_PhysicsBody, ECR_Block);
+ HandRight->SetCollisionResponseToChannel(ECC_Vehicle, ECR_Block);
+ HandRight->SetCollisionResponseToChannel(ECC_Destructible, ECR_Block);
+ HandRight->SetRight(true);
+
+ const FSGVirtualHandSettings RightHandSettings{HandRight->GetVirtualHandSettings()};
+ HandRight->SetRelativeRotation(RightHandSettings.MeshSettings.RootBoneRotationCorrection,
+ false, nullptr, ETeleportType::None);
+ HandRight->SetRelativeLocation(FVector{30.0f, 20.0f, -10.0f},
+ false, nullptr, ETeleportType::None);
+ HandRight->SetWristTracker(WristTrackerRight);
+
+ RealHandRight = ObjectInitializer.CreateDefaultSubobject(
+ this, TEXT("RealHandRight"));
+ RealHandRight->SetupAttachment(GetRootComponent());
+ RealHandRight->SetCanEverAffectNavigation(false);
+ RealHandRight->SetGenerateOverlapEvents(true);
+ RealHandRight->CanCharacterStepUpOn = ECanBeCharacterBase::ECB_Owner;
+ RealHandRight->SetCollisionObjectType(ECC_Pawn);
+ RealHandRight->SetCollisionEnabled(ECollisionEnabled::QueryOnly);
+ RealHandRight->SetCollisionResponseToAllChannels(ECR_Overlap);
+ RealHandRight->SetCollisionResponseToChannel(ECC_WorldStatic, ECR_Overlap);
+ RealHandRight->SetCollisionResponseToChannel(ECC_WorldDynamic, ECR_Overlap);
+ RealHandRight->SetCollisionResponseToChannel(ECC_Pawn, ECR_Overlap);
+ RealHandRight->SetCollisionResponseToChannel(ECC_Visibility, ECR_Overlap);
+ RealHandRight->SetCollisionResponseToChannel(ECC_PhysicsBody, ECR_Overlap);
+ RealHandRight->SetCollisionResponseToChannel(ECC_Vehicle, ECR_Overlap);
+ RealHandRight->SetCollisionResponseToChannel(ECC_Destructible, ECR_Overlap);
+ RealHandRight->SetRight(true);
+ Pimpl->SetVisibility(RealHandRight, false);
+
+ const FSGVirtualHandMeshSettings RealRightHandMeshSettings{HandRight->GetVirtualHandSettings().MeshSettings};
+ RealHandRight->SetRelativeRotation(RealRightHandMeshSettings.RootBoneRotationCorrection,
+ false, nullptr, ETeleportType::None);
+ RealHandRight->SetRelativeLocation(FVector{30.0f, 20.0f, -10.0f},
+ false, nullptr, ETeleportType::None);
+ RealHandRight->SetWristTracker(WristTrackerRight);
+
+ RightThumbFingertipGrabCollider = ObjectInitializer.CreateDefaultSubobject(
+ this, TEXT("RightThumbFingertipGrabCollider"));
+ RightThumbFingertipGrabCollider->SetRelativeScale3D(RightHandSettings.GrabSettings.DefaultColliderSize);
+ RightThumbFingertipGrabCollider->SetupAttachment(HandRight, RightHandSettings.GrabSettings.ThumbColliderSocketName);
+ RightThumbFingertipGrabCollider->SetCastShadow(false);
+ RightThumbFingertipGrabCollider->bCastDynamicShadow = false;
+ RightThumbFingertipGrabCollider->SetReceivesDecals(true);
+ RightThumbFingertipGrabCollider->SetCanEverAffectNavigation(false);
+ RightThumbFingertipGrabCollider->CanCharacterStepUpOn = ECanBeCharacterBase::ECB_Owner;
+ RightThumbFingertipGrabCollider->SetCollisionObjectType(ECC_Pawn);
+ Pimpl->SetFingerColliderCollisionEnabled(RightThumbFingertipGrabCollider, true);
+
+ RightIndexFingertipGrabCollider = ObjectInitializer.CreateDefaultSubobject(
+ this, TEXT("RightIndexFingertipGrabCollider"));
+ RightIndexFingertipGrabCollider->SetRelativeScale3D(RightHandSettings.GrabSettings.DefaultColliderSize);
+ RightIndexFingertipGrabCollider->SetupAttachment(HandRight, RightHandSettings.GrabSettings.IndexColliderSocketName);
+ RightIndexFingertipGrabCollider->SetCastShadow(false);
+ RightIndexFingertipGrabCollider->bCastDynamicShadow = false;
+ RightIndexFingertipGrabCollider->SetReceivesDecals(true);
+ RightIndexFingertipGrabCollider->SetCanEverAffectNavigation(false);
+ RightIndexFingertipGrabCollider->CanCharacterStepUpOn = ECanBeCharacterBase::ECB_Owner;
+ RightIndexFingertipGrabCollider->SetCollisionObjectType(ECC_Pawn);
+ Pimpl->SetFingerColliderCollisionEnabled(RightIndexFingertipGrabCollider, true);
+
+ RightMiddleFingertipGrabCollider = ObjectInitializer.CreateDefaultSubobject(
+ this, TEXT("RightMiddleFingertipGrabCollider"));
+ RightMiddleFingertipGrabCollider->SetRelativeScale3D(RightHandSettings.GrabSettings.DefaultColliderSize);
+ RightMiddleFingertipGrabCollider->SetupAttachment(
+ HandRight, RightHandSettings.GrabSettings.MiddleColliderSocketName);
+ RightMiddleFingertipGrabCollider->SetCastShadow(false);
+ RightMiddleFingertipGrabCollider->bCastDynamicShadow = false;
+ RightMiddleFingertipGrabCollider->SetReceivesDecals(true);
+ RightMiddleFingertipGrabCollider->SetCanEverAffectNavigation(false);
+ RightMiddleFingertipGrabCollider->CanCharacterStepUpOn = ECanBeCharacterBase::ECB_Owner;
+ RightMiddleFingertipGrabCollider->SetCollisionObjectType(ECC_Pawn);
+ Pimpl->SetFingerColliderCollisionEnabled(RightMiddleFingertipGrabCollider, true);
+
+ RightThumbFingertipTouchCollider = ObjectInitializer.CreateDefaultSubobject(
+ this, TEXT("RightThumbFingertipTouchCollider"));
+ RightThumbFingertipTouchCollider->SetRelativeScale3D(RightHandSettings.TouchSettings.DefaultColliderSize);
+ RightThumbFingertipTouchCollider->SetupAttachment(
+ HandRight, RightHandSettings.TouchSettings.ThumbColliderSocketName);
+ RightThumbFingertipTouchCollider->SetCastShadow(false);
+ RightThumbFingertipTouchCollider->bCastDynamicShadow = false;
+ RightThumbFingertipTouchCollider->SetReceivesDecals(true);
+ RightThumbFingertipTouchCollider->SetCanEverAffectNavigation(false);
+ RightThumbFingertipTouchCollider->CanCharacterStepUpOn = ECanBeCharacterBase::ECB_Owner;
+ RightThumbFingertipTouchCollider->SetCollisionObjectType(ECC_Pawn);
+ Pimpl->SetFingerColliderCollisionEnabled(RightThumbFingertipTouchCollider, true);
+
+ RightIndexFingertipTouchCollider = ObjectInitializer.CreateDefaultSubobject(
+ this, TEXT("RightIndexFingertipTouchCollider"));
+ RightIndexFingertipTouchCollider->SetRelativeScale3D(RightHandSettings.TouchSettings.DefaultColliderSize);
+ RightIndexFingertipTouchCollider->SetupAttachment(
+ HandRight, RightHandSettings.TouchSettings.IndexColliderSocketName);
+ RightIndexFingertipTouchCollider->SetCastShadow(false);
+ RightIndexFingertipTouchCollider->bCastDynamicShadow = false;
+ RightIndexFingertipTouchCollider->SetReceivesDecals(true);
+ RightIndexFingertipTouchCollider->SetCanEverAffectNavigation(false);
+ RightIndexFingertipTouchCollider->CanCharacterStepUpOn = ECanBeCharacterBase::ECB_Owner;
+ RightIndexFingertipTouchCollider->SetCollisionObjectType(ECC_Pawn);
+ Pimpl->SetFingerColliderCollisionEnabled(RightIndexFingertipTouchCollider, true);
+
+ RightMiddleFingertipTouchCollider = ObjectInitializer.CreateDefaultSubobject(
+ this, TEXT("RightMiddleFingertipTouchCollider"));
+ RightMiddleFingertipTouchCollider->SetRelativeScale3D(RightHandSettings.TouchSettings.DefaultColliderSize);
+ RightMiddleFingertipTouchCollider->SetupAttachment(
+ HandRight, RightHandSettings.TouchSettings.MiddleColliderSocketName);
+ RightMiddleFingertipTouchCollider->SetCastShadow(false);
+ RightMiddleFingertipTouchCollider->bCastDynamicShadow = false;
+ RightMiddleFingertipTouchCollider->SetReceivesDecals(true);
+ RightMiddleFingertipTouchCollider->SetCanEverAffectNavigation(false);
+ RightMiddleFingertipTouchCollider->CanCharacterStepUpOn = ECanBeCharacterBase::ECB_Owner;
+ RightMiddleFingertipTouchCollider->SetCollisionObjectType(ECC_Pawn);
+ Pimpl->SetFingerColliderCollisionEnabled(RightMiddleFingertipTouchCollider, true);
+
+ RightRingFingertipTouchCollider = ObjectInitializer.CreateDefaultSubobject(
+ this, TEXT("RightRingFingertipTouchCollider"));
+ RightRingFingertipTouchCollider->SetRelativeScale3D(RightHandSettings.TouchSettings.DefaultColliderSize);
+ RightRingFingertipTouchCollider->SetupAttachment(HandRight, RightHandSettings.TouchSettings.RingColliderSocketName);
+ RightRingFingertipTouchCollider->SetCastShadow(false);
+ RightRingFingertipTouchCollider->bCastDynamicShadow = false;
+ RightRingFingertipTouchCollider->SetReceivesDecals(true);
+ RightRingFingertipTouchCollider->SetCanEverAffectNavigation(false);
+ RightRingFingertipTouchCollider->CanCharacterStepUpOn = ECanBeCharacterBase::ECB_Owner;
+ RightRingFingertipTouchCollider->SetCollisionObjectType(ECC_Pawn);
+ Pimpl->SetFingerColliderCollisionEnabled(RightRingFingertipTouchCollider, true);
+
+ RightPinkyFingertipTouchCollider = ObjectInitializer.CreateDefaultSubobject(
+ this, TEXT("RightPinkyFingertipTouchCollider"));
+ RightPinkyFingertipTouchCollider->SetRelativeScale3D(RightHandSettings.TouchSettings.DefaultColliderSize);
+ RightPinkyFingertipTouchCollider->SetupAttachment(
+ HandRight, RightHandSettings.TouchSettings.PinkyColliderSocketName);
+ RightPinkyFingertipTouchCollider->SetCastShadow(false);
+ RightPinkyFingertipTouchCollider->bCastDynamicShadow = false;
+ RightPinkyFingertipTouchCollider->SetReceivesDecals(true);
+ RightPinkyFingertipTouchCollider->SetCanEverAffectNavigation(false);
+ RightPinkyFingertipTouchCollider->CanCharacterStepUpOn = ECanBeCharacterBase::ECB_Owner;
+ RightPinkyFingertipTouchCollider->SetCollisionObjectType(ECC_Pawn);
+ Pimpl->SetFingerColliderCollisionEnabled(RightPinkyFingertipTouchCollider, true);
+
+ MaxNumberOfHandVelocitySamples = 10;
+
+ LeftHandGrabState = FSGGrabState{
+ HandLeft, FVector::Zero(), {}, nullptr, nullptr, nullptr, nullptr
+ };
+ LeftHandTouchState = FSGTouchState{
+ HandLeft, nullptr, nullptr, nullptr, nullptr, nullptr
+ };
+
+ RightHandGrabState = FSGGrabState{
+ HandRight, FVector::Zero(), {}, nullptr, nullptr, nullptr, nullptr
+ };
+ RightHandTouchState = FSGTouchState{
+ HandRight, nullptr, nullptr, nullptr, nullptr, nullptr
+ };
+}
+
+void ASGPawn::OnGrabStateUpdated_Implementation(const FSGGrabState& GrabState)
+{
+}
+
+void ASGPawn::OnTouchStateUpdated_Implementation(const FSGTouchState& TouchState)
+{
+}
+
+void ASGPawn::OnActorGrabbed_Implementation(const USGVirtualHandComponent* Hand, const AActor* Actor)
+{
+}
+
+void ASGPawn::OnActorReleased_Implementation(const USGVirtualHandComponent* Hand, const AActor* Actor)
+{
+}
+
+void ASGPawn::OnActorBeginTouch_Implementation(const USGVirtualHandComponent* Hand, const AActor* Actor)
+{
+}
+
+void ASGPawn::OnActorEndTouch_Implementation(const USGVirtualHandComponent* Hand, const AActor* Actor)
+{
+}
+
+void ASGPawn::BeginPlay()
+{
+ Super::BeginPlay();
+
+ ensureAlwaysMsgf(
+ WristTrackerLeft->IsLeft() && HandLeft->IsLeft(),
+ TEXT("The left hand and the left wrist do not track the left hand!"));
+ ensureAlwaysMsgf(
+ WristTrackerRight->IsRight() && HandRight->IsRight(),
+ TEXT("The right hand and the right wrist do not track the right hand!"));
+
+ WristTrackerLeft->OnWristLocationChanged().AddWeakLambda(this, [&](const FVector& Location)-> void
+ {
+ Pimpl->UpdateLeftHandLocation(Location, !Pimpl->IsLeftHandOverlappingWithAnyActor());
+ });
+
+ WristTrackerLeft->OnWristRotationChanged().AddWeakLambda(this, [&](const FRotator& Rotation)-> void
+ {
+ Pimpl->UpdateLeftHandRotation(Rotation, !Pimpl->IsLeftHandOverlappingWithAnyActor());
+ });
+
+ WristTrackerRight->OnWristLocationChanged().AddWeakLambda(this, [&](const FVector& Location)-> void
+ {
+ Pimpl->UpdateRightHandLocation(Location, !Pimpl->IsRightHandOverlappingWithAnyActor());
+ });
+
+ WristTrackerRight->OnWristRotationChanged().AddWeakLambda(this, [&](const FRotator& Rotation)-> void
+ {
+ Pimpl->UpdateRightHandRotation(Rotation, !Pimpl->IsRightHandOverlappingWithAnyActor());
+ });
+
+ HandLeft->OnHandVisibilityChanged().AddWeakLambda(this, [&](const bool bNewVisibility)-> void
+ {
+ Pimpl->SetAllFingersColliderCollisionEnabled(false, bNewVisibility);
+ });
+
+ HandRight->OnHandVisibilityChanged().AddWeakLambda(this, [&](const bool bNewVisibility)-> void
+ {
+ Pimpl->SetAllFingersColliderCollisionEnabled(true, bNewVisibility);
+ });
+
+ Pimpl->BindRealHandsOverlapEvents();
+
+ Pimpl->BindFingerGrabOverlapEvents();
+ Pimpl->BindFingerTouchOverlapEvents();
+}
+
+void ASGPawn::EndPlay(const EEndPlayReason::Type EndPlayReason)
+{
+ Super::EndPlay(EndPlayReason);
+
+ Pimpl->UnbindRealHandsOverlapEvents();
+
+ Pimpl->UnbindFingerGrabOverlapEvents();
+ Pimpl->UnbindFingerTouchOverlapEvents();
+}
+
+void ASGPawn::Tick(const float DeltaSeconds)
+{
+ Super::Tick(DeltaSeconds);
+
+ Pimpl->RecordHandVelocity(LeftHandGrabState, DeltaSeconds);
+ Pimpl->RecordHandVelocity(RightHandGrabState, DeltaSeconds);
+}
+
+void ASGPawn::PreInitializeComponents()
+{
+ Super::PreInitializeComponents();
+
+ WristTrackerLeft->SetRight(HandLeft->IsRight());
+ WristTrackerRight->SetRight(HandRight->IsRight());
+}
+
+void ASGPawn::PostInitializeComponents()
+{
+ Super::PostInitializeComponents();
+}
+
+void ASGPawn::OnRealLeftHandOverlapBegins(
+ UPrimitiveComponent* OverlappedComponent,
+ AActor* OtherActor,
+ UPrimitiveComponent* OtherComp,
+ const int32 OtherBodyIndex,
+ const bool bFromSweep,
+ const FHitResult& SweepResult)
+{
+ (void) OverlappedComponent;
+ (void) OtherComp;
+ (void) OtherBodyIndex;
+ (void) bFromSweep;
+ (void) SweepResult;
+
+ if (OtherActor == this)
+ {
+ return;
+ }
+
+ Pimpl->RegisterLeftHandOverlappedActor(OtherActor);
+}
+
+void ASGPawn::OnRealLeftHandOverlapEnds(
+ UPrimitiveComponent* OverlappedComponent,
+ AActor* OtherActor,
+ UPrimitiveComponent* OtherComp,
+ const int32 OtherBodyIndex)
+{
+ (void) OverlappedComponent;
+ (void) OtherComp;
+ (void) OtherBodyIndex;
+
+ if (OtherActor == this)
+ {
+ return;
+ }
+
+ Pimpl->UnregisterLeftHandOverlappedActor(OtherActor);
+}
+
+void ASGPawn::OnRealRightHandOverlapBegins(
+ UPrimitiveComponent* OverlappedComponent,
+ AActor* OtherActor,
+ UPrimitiveComponent* OtherComp,
+ const int32 OtherBodyIndex,
+ const bool bFromSweep,
+ const FHitResult& SweepResult)
+{
+ (void) OverlappedComponent;
+ (void) OtherComp;
+ (void) OtherBodyIndex;
+ (void) bFromSweep;
+ (void) SweepResult;
+
+ if (OtherActor == this)
+ {
+ return;
+ }
+
+ Pimpl->RegisterRightHandOverlappedActor(OtherActor);
+}
+
+void ASGPawn::OnRealRightHandOverlapEnds(
+ UPrimitiveComponent* OverlappedComponent,
+ AActor* OtherActor,
+ UPrimitiveComponent* OtherComp,
+ const int32 OtherBodyIndex)
+{
+ (void) OverlappedComponent;
+ (void) OtherComp;
+ (void) OtherBodyIndex;
+
+ if (OtherActor == this)
+ {
+ return;
+ }
+
+ Pimpl->UnregisterRightHandOverlappedActor(OtherActor);
+}
+
+void ASGPawn::OnLeftThumbFingertipGrabColliderOverlapBegins(
+ UPrimitiveComponent* OverlappedComponent,
+ AActor* OtherActor,
+ UPrimitiveComponent* OtherComp,
+ const int32 OtherBodyIndex,
+ const bool bFromSweep,
+ const FHitResult& SweepResult)
+{
+ (void) OverlappedComponent;
+ (void) OtherComp;
+ (void) OtherBodyIndex;
+ (void) bFromSweep;
+ (void) SweepResult;
+
+ if (!IsValid(LeftHandGrabState.Hand))
+ {
+ return;
+ }
+
+ const bool bHandVisible = LeftHandGrabState.Hand->IsVisible();
+ if (!bHandVisible)
+ {
+ return;
+ }
+
+ if (!USGGrabComponent::IsGrabbable(OtherActor))
+ {
+ return;
+ }
+
+ LeftHandGrabState.ActorThumbCanGrab = OtherActor;
+ Pimpl->TriggerGrabStateUpdatedEvent(LeftHandGrabState);
+}
+
+void ASGPawn::OnLeftThumbFingertipGrabColliderOverlapEnds(
+ UPrimitiveComponent* OverlappedComponent,
+ AActor* OtherActor,
+ UPrimitiveComponent* OtherComp,
+ const int32 OtherBodyIndex)
+{
+ (void) OverlappedComponent;
+ (void) OtherComp;
+ (void) OtherBodyIndex;
+
+ if (!IsValid(LeftHandGrabState.Hand))
+ {
+ return;
+ }
+
+ if (LeftHandGrabState.ActorThumbCanGrab != OtherActor)
+ {
+ return;
+ }
+
+ LeftHandGrabState.ActorThumbCanGrab = nullptr;
+ Pimpl->TriggerGrabStateUpdatedEvent(LeftHandGrabState);
+}
+
+void ASGPawn::OnLeftIndexFingertipGrabColliderOverlapBegins(
+ UPrimitiveComponent* OverlappedComponent,
+ AActor* OtherActor,
+ UPrimitiveComponent* OtherComp,
+ const int32 OtherBodyIndex,
+ const bool bFromSweep,
+ const FHitResult& SweepResult)
+{
+ (void) OverlappedComponent;
+ (void) OtherComp;
+ (void) OtherBodyIndex;
+ (void) bFromSweep;
+ (void) SweepResult;
+
+ if (!IsValid(LeftHandGrabState.Hand))
+ {
+ return;
+ }
+
+ const bool bHandVisible = LeftHandGrabState.Hand->IsVisible();
+ if (!bHandVisible)
+ {
+ return;
+ }
+
+ if (!USGGrabComponent::IsGrabbable(OtherActor))
+ {
+ return;
+ }
+
+ LeftHandGrabState.ActorIndexCanGrab = OtherActor;
+ Pimpl->TriggerGrabStateUpdatedEvent(LeftHandGrabState);
+}
+
+void ASGPawn::OnLeftIndexFingertipGrabColliderOverlapEnds(
+ UPrimitiveComponent* OverlappedComponent,
+ AActor* OtherActor,
+ UPrimitiveComponent* OtherComp,
+ const int32 OtherBodyIndex)
+{
+ (void) OverlappedComponent;
+ (void) OtherComp;
+ (void) OtherBodyIndex;
+
+ if (!IsValid(LeftHandGrabState.Hand))
+ {
+ return;
+ }
+
+ if (LeftHandGrabState.ActorIndexCanGrab != OtherActor)
+ {
+ return;
+ }
+
+ LeftHandGrabState.ActorIndexCanGrab = nullptr;
+ Pimpl->TriggerGrabStateUpdatedEvent(LeftHandGrabState);
+}
+
+void ASGPawn::OnLeftMiddleFingertipGrabColliderOverlapBegins(
+ UPrimitiveComponent* OverlappedComponent,
+ AActor* OtherActor,
+ UPrimitiveComponent* OtherComp,
+ const int32 OtherBodyIndex,
+ const bool bFromSweep,
+ const FHitResult& SweepResult)
+{
+ (void) OverlappedComponent;
+ (void) OtherComp;
+ (void) OtherBodyIndex;
+ (void) bFromSweep;
+ (void) SweepResult;
+
+ if (!IsValid(LeftHandGrabState.Hand))
+ {
+ return;
+ }
+
+ const bool bHandVisible = LeftHandGrabState.Hand->IsVisible();
+ if (!bHandVisible)
+ {
+ return;
+ }
+
+ if (!USGGrabComponent::IsGrabbable(OtherActor))
+ {
+ return;
+ }
+
+ LeftHandGrabState.ActorMiddleCanGrab = OtherActor;
+ Pimpl->TriggerGrabStateUpdatedEvent(LeftHandGrabState);
+}
+
+void ASGPawn::OnLeftMiddleFingertipGrabColliderOverlapEnds(
+ UPrimitiveComponent* OverlappedComponent,
+ AActor* OtherActor,
+ UPrimitiveComponent* OtherComp,
+ const int32 OtherBodyIndex)
+{
+ (void) OverlappedComponent;
+ (void) OtherComp;
+ (void) OtherBodyIndex;
+
+ if (!IsValid(LeftHandGrabState.Hand))
+ {
+ return;
+ }
+
+ if (LeftHandGrabState.ActorMiddleCanGrab != OtherActor)
+ {
+ return;
+ }
+
+ LeftHandGrabState.ActorMiddleCanGrab = nullptr;
+ Pimpl->TriggerGrabStateUpdatedEvent(LeftHandGrabState);
+}
+
+void ASGPawn::OnLeftThumbFingertipTouchColliderOverlapBegins(
+ UPrimitiveComponent* OverlappedComponent,
+ AActor* OtherActor,
+ UPrimitiveComponent* OtherComp,
+ const int32 OtherBodyIndex,
+ const bool bFromSweep,
+ const FHitResult& SweepResult)
+{
+ (void) OverlappedComponent;
+ (void) OtherComp;
+ (void) OtherBodyIndex;
+ (void) bFromSweep;
+ (void) SweepResult;
+
+ if (!IsValid(LeftHandTouchState.Hand))
+ {
+ return;
+ }
+
+ const bool bHandVisible = LeftHandTouchState.Hand->IsVisible();
+ if (!bHandVisible)
+ {
+ return;
+ }
+
+ if (!USGTouchComponent::IsTouchable(OtherActor))
+ {
+ return;
+ }
+
+ LeftHandTouchState.ActorThumbTouching = OtherActor;
+ Pimpl->TriggerTouchStateUpdatedEvent(LeftHandTouchState);
+ Pimpl->TriggerBeginTouch(LeftHandTouchState.Hand, OtherActor);
+}
+
+void ASGPawn::OnLeftThumbFingertipTouchColliderOverlapEnds(
+ UPrimitiveComponent* OverlappedComponent,
+ AActor* OtherActor,
+ UPrimitiveComponent* OtherComp,
+ const int32 OtherBodyIndex)
+{
+ (void) OverlappedComponent;
+ (void) OtherComp;
+ (void) OtherBodyIndex;
+
+ if (!IsValid(LeftHandTouchState.Hand))
+ {
+ return;
+ }
+
+ if (LeftHandTouchState.ActorThumbTouching != OtherActor)
+ {
+ return;
+ }
+
+ LeftHandTouchState.ActorThumbTouching = nullptr;
+ Pimpl->TriggerTouchStateUpdatedEvent(LeftHandTouchState);
+ Pimpl->TriggerEndTouch(LeftHandTouchState.Hand, OtherActor);
+}
+
+void ASGPawn::OnLeftIndexFingertipTouchColliderOverlapBegins(
+ UPrimitiveComponent* OverlappedComponent,
+ AActor* OtherActor,
+ UPrimitiveComponent* OtherComp,
+ const int32 OtherBodyIndex,
+ const bool bFromSweep,
+ const FHitResult& SweepResult)
+{
+ (void) OverlappedComponent;
+ (void) OtherComp;
+ (void) OtherBodyIndex;
+ (void) bFromSweep;
+ (void) SweepResult;
+
+ if (!IsValid(LeftHandTouchState.Hand))
+ {
+ return;
+ }
+
+ const bool bHandVisible = LeftHandTouchState.Hand->IsVisible();
+ if (!bHandVisible)
+ {
+ return;
+ }
+
+ if (!USGTouchComponent::IsTouchable(OtherActor))
+ {
+ return;
+ }
+
+ LeftHandTouchState.ActorIndexTouching = OtherActor;
+ Pimpl->TriggerTouchStateUpdatedEvent(LeftHandTouchState);
+ Pimpl->TriggerBeginTouch(LeftHandTouchState.Hand, OtherActor);
+}
+
+void ASGPawn::OnLeftIndexFingertipTouchColliderOverlapEnds(
+ UPrimitiveComponent* OverlappedComponent,
+ AActor* OtherActor,
+ UPrimitiveComponent* OtherComp,
+ const int32 OtherBodyIndex)
+{
+ (void) OverlappedComponent;
+ (void) OtherComp;
+ (void) OtherBodyIndex;
+
+ if (!IsValid(LeftHandTouchState.Hand))
+ {
+ return;
+ }
+
+ if (LeftHandTouchState.ActorIndexTouching != OtherActor)
+ {
+ return;
+ }
+
+ LeftHandTouchState.ActorIndexTouching = nullptr;
+ Pimpl->TriggerTouchStateUpdatedEvent(LeftHandTouchState);
+ Pimpl->TriggerEndTouch(LeftHandTouchState.Hand, OtherActor);
+}
+
+void ASGPawn::OnLeftMiddleFingertipTouchColliderOverlapBegins(
+ UPrimitiveComponent* OverlappedComponent,
+ AActor* OtherActor,
+ UPrimitiveComponent* OtherComp,
+ const int32 OtherBodyIndex,
+ const bool bFromSweep,
+ const FHitResult& SweepResult)
+{
+ (void) OverlappedComponent;
+ (void) OtherComp;
+ (void) OtherBodyIndex;
+ (void) bFromSweep;
+ (void) SweepResult;
+
+ if (!IsValid(LeftHandTouchState.Hand))
+ {
+ return;
+ }
+
+ const bool bHandVisible = LeftHandTouchState.Hand->IsVisible();
+ if (!bHandVisible)
+ {
+ return;
+ }
+
+ if (!USGTouchComponent::IsTouchable(OtherActor))
+ {
+ return;
+ }
+
+ LeftHandTouchState.ActorMiddleTouching = OtherActor;
+ Pimpl->TriggerTouchStateUpdatedEvent(LeftHandTouchState);
+ Pimpl->TriggerBeginTouch(LeftHandTouchState.Hand, OtherActor);
+}
+
+void ASGPawn::OnLeftMiddleFingertipTouchColliderOverlapEnds(
+ UPrimitiveComponent* OverlappedComponent,
+ AActor* OtherActor,
+ UPrimitiveComponent* OtherComp,
+ const int32 OtherBodyIndex)
+{
+ (void) OverlappedComponent;
+ (void) OtherComp;
+ (void) OtherBodyIndex;
+
+ if (!IsValid(LeftHandTouchState.Hand))
+ {
+ return;
+ }
+
+ if (LeftHandTouchState.ActorMiddleTouching != OtherActor)
+ {
+ return;
+ }
+
+ LeftHandTouchState.ActorMiddleTouching = nullptr;
+ Pimpl->TriggerTouchStateUpdatedEvent(LeftHandTouchState);
+ Pimpl->TriggerEndTouch(LeftHandTouchState.Hand, OtherActor);
+}
+
+void ASGPawn::OnLeftRingFingertipTouchColliderOverlapBegins(
+ UPrimitiveComponent* OverlappedComponent,
+ AActor* OtherActor,
+ UPrimitiveComponent* OtherComp,
+ const int32 OtherBodyIndex,
+ const bool bFromSweep,
+ const FHitResult& SweepResult)
+{
+ (void) OverlappedComponent;
+ (void) OtherComp;
+ (void) OtherBodyIndex;
+ (void) bFromSweep;
+ (void) SweepResult;
+
+ if (!IsValid(LeftHandTouchState.Hand))
+ {
+ return;
+ }
+
+ const bool bHandVisible = LeftHandTouchState.Hand->IsVisible();
+ if (!bHandVisible)
+ {
+ return;
+ }
+
+ if (!USGTouchComponent::IsTouchable(OtherActor))
+ {
+ return;
+ }
+
+ LeftHandTouchState.ActorRingTouching = OtherActor;
+ Pimpl->TriggerTouchStateUpdatedEvent(LeftHandTouchState);
+ Pimpl->TriggerBeginTouch(LeftHandTouchState.Hand, OtherActor);
+}
+
+void ASGPawn::OnLeftRingFingertipTouchColliderOverlapEnds(
+ UPrimitiveComponent* OverlappedComponent,
+ AActor* OtherActor,
+ UPrimitiveComponent* OtherComp,
+ const int32 OtherBodyIndex)
+{
+ (void) OverlappedComponent;
+ (void) OtherComp;
+ (void) OtherBodyIndex;
+
+ if (!IsValid(LeftHandTouchState.Hand))
+ {
+ return;
+ }
+
+ if (LeftHandTouchState.ActorRingTouching != OtherActor)
+ {
+ return;
+ }
+
+ LeftHandTouchState.ActorRingTouching = nullptr;
+ Pimpl->TriggerTouchStateUpdatedEvent(LeftHandTouchState);
+ Pimpl->TriggerEndTouch(LeftHandTouchState.Hand, OtherActor);
+}
+
+void ASGPawn::OnLeftPinkyFingertipTouchColliderOverlapBegins(
+ UPrimitiveComponent* OverlappedComponent,
+ AActor* OtherActor,
+ UPrimitiveComponent* OtherComp,
+ const int32 OtherBodyIndex,
+ const bool bFromSweep,
+ const FHitResult& SweepResult)
+{
+ (void) OverlappedComponent;
+ (void) OtherComp;
+ (void) OtherBodyIndex;
+ (void) bFromSweep;
+ (void) SweepResult;
+
+ if (!IsValid(LeftHandTouchState.Hand))
+ {
+ return;
+ }
+
+ const bool bHandVisible = LeftHandTouchState.Hand->IsVisible();
+ if (!bHandVisible)
+ {
+ return;
+ }
+
+ if (!USGTouchComponent::IsTouchable(OtherActor))
+ {
+ return;
+ }
+
+ LeftHandTouchState.ActorPinkyTouching = OtherActor;
+ Pimpl->TriggerTouchStateUpdatedEvent(LeftHandTouchState);
+ Pimpl->TriggerBeginTouch(LeftHandTouchState.Hand, OtherActor);
+}
+
+void ASGPawn::OnLeftPinkyFingertipTouchColliderOverlapEnds(
+ UPrimitiveComponent* OverlappedComponent,
+ AActor* OtherActor,
+ UPrimitiveComponent* OtherComp,
+ const int32 OtherBodyIndex)
+{
+ (void) OverlappedComponent;
+ (void) OtherComp;
+ (void) OtherBodyIndex;
+
+ if (!IsValid(LeftHandTouchState.Hand))
+ {
+ return;
+ }
+
+ if (LeftHandTouchState.ActorPinkyTouching != OtherActor)
+ {
+ return;
+ }
+
+ LeftHandTouchState.ActorPinkyTouching = nullptr;
+ Pimpl->TriggerTouchStateUpdatedEvent(LeftHandTouchState);
+ Pimpl->TriggerEndTouch(LeftHandTouchState.Hand, OtherActor);
+}
+
+void ASGPawn::OnRightThumbFingertipGrabColliderOverlapBegins(
+ UPrimitiveComponent* OverlappedComponent,
+ AActor* OtherActor,
+ UPrimitiveComponent* OtherComp,
+ const int32 OtherBodyIndex,
+ const bool bFromSweep,
+ const FHitResult& SweepResult)
+{
+ (void) OverlappedComponent;
+ (void) OtherComp;
+ (void) OtherBodyIndex;
+ (void) bFromSweep;
+ (void) SweepResult;
+
+ if (!IsValid(RightHandGrabState.Hand))
+ {
+ return;
+ }
+
+ const bool bHandVisible = RightHandGrabState.Hand->IsVisible();
+ if (!bHandVisible)
+ {
+ return;
+ }
+
+ if (!USGGrabComponent::IsGrabbable(OtherActor))
+ {
+ return;
+ }
+
+ RightHandGrabState.ActorThumbCanGrab = OtherActor;
+ Pimpl->TriggerGrabStateUpdatedEvent(RightHandGrabState);
+}
+
+void ASGPawn::OnRightThumbFingertipGrabColliderOverlapEnds(
+ UPrimitiveComponent* OverlappedComponent,
+ AActor* OtherActor,
+ UPrimitiveComponent* OtherComp,
+ const int32 OtherBodyIndex)
+{
+ (void) OverlappedComponent;
+ (void) OtherComp;
+ (void) OtherBodyIndex;
+
+ if (!IsValid(RightHandGrabState.Hand))
+ {
+ return;
+ }
+
+ if (RightHandGrabState.ActorThumbCanGrab != OtherActor)
+ {
+ return;
+ }
+
+ RightHandGrabState.ActorThumbCanGrab = nullptr;
+ Pimpl->TriggerGrabStateUpdatedEvent(RightHandGrabState);
+}
+
+void ASGPawn::OnRightIndexFingertipGrabColliderOverlapBegins(
+ UPrimitiveComponent* OverlappedComponent,
+ AActor* OtherActor,
+ UPrimitiveComponent* OtherComp,
+ const int32 OtherBodyIndex,
+ const bool bFromSweep,
+ const FHitResult& SweepResult)
+{
+ (void) OverlappedComponent;
+ (void) OtherComp;
+ (void) OtherBodyIndex;
+ (void) bFromSweep;
+ (void) SweepResult;
+
+ if (!IsValid(RightHandGrabState.Hand))
+ {
+ return;
+ }
+
+ const bool bHandVisible = RightHandGrabState.Hand->IsVisible();
+ if (!bHandVisible)
+ {
+ return;
+ }
+
+ if (!USGGrabComponent::IsGrabbable(OtherActor))
+ {
+ return;
+ }
+
+ RightHandGrabState.ActorIndexCanGrab = OtherActor;
+ Pimpl->TriggerGrabStateUpdatedEvent(RightHandGrabState);
+}
+
+void ASGPawn::OnRightIndexFingertipGrabColliderOverlapEnds(
+ UPrimitiveComponent* OverlappedComponent,
+ AActor* OtherActor,
+ UPrimitiveComponent* OtherComp,
+ const int32 OtherBodyIndex)
+{
+ (void) OverlappedComponent;
+ (void) OtherComp;
+ (void) OtherBodyIndex;
+
+ if (!IsValid(RightHandGrabState.Hand))
+ {
+ return;
+ }
+
+ if (RightHandGrabState.ActorThumbCanGrab != OtherActor)
+ {
+ return;
+ }
+
+ RightHandGrabState.ActorIndexCanGrab = nullptr;
+ Pimpl->TriggerGrabStateUpdatedEvent(RightHandGrabState);
+}
+
+void ASGPawn::OnRightMiddleFingertipGrabColliderOverlapBegins(
+ UPrimitiveComponent* OverlappedComponent,
+ AActor* OtherActor,
+ UPrimitiveComponent* OtherComp,
+ const int32 OtherBodyIndex,
+ const bool bFromSweep,
+ const FHitResult& SweepResult)
+{
+ (void) OverlappedComponent;
+ (void) OtherComp;
+ (void) OtherBodyIndex;
+ (void) bFromSweep;
+ (void) SweepResult;
+
+ if (!IsValid(RightHandGrabState.Hand))
+ {
+ return;
+ }
+
+ const bool bHandVisible = RightHandGrabState.Hand->IsVisible();
+ if (!bHandVisible)
+ {
+ return;
+ }
+
+ if (!USGGrabComponent::IsGrabbable(OtherActor))
+ {
+ return;
+ }
+
+ RightHandGrabState.ActorMiddleCanGrab = OtherActor;
+ Pimpl->TriggerGrabStateUpdatedEvent(RightHandGrabState);
+}
+
+void ASGPawn::OnRightMiddleFingertipGrabColliderOverlapEnds(
+ UPrimitiveComponent* OverlappedComponent,
+ AActor* OtherActor,
+ UPrimitiveComponent* OtherComp,
+ const int32 OtherBodyIndex)
+{
+ (void) OverlappedComponent;
+ (void) OtherComp;
+ (void) OtherBodyIndex;
+
+ if (!IsValid(RightHandGrabState.Hand))
+ {
+ return;
+ }
+
+ if (RightHandGrabState.ActorMiddleCanGrab != OtherActor)
+ {
+ return;
+ }
+
+ RightHandGrabState.ActorMiddleCanGrab = nullptr;
+ Pimpl->TriggerGrabStateUpdatedEvent(RightHandGrabState);
+}
+
+void ASGPawn::OnRightThumbFingertipTouchColliderOverlapBegins(
+ UPrimitiveComponent* OverlappedComponent,
+ AActor* OtherActor,
+ UPrimitiveComponent* OtherComp,
+ const int32 OtherBodyIndex,
+ const bool bFromSweep,
+ const FHitResult& SweepResult)
+{
+ (void) OverlappedComponent;
+ (void) OtherComp;
+ (void) OtherBodyIndex;
+ (void) bFromSweep;
+ (void) SweepResult;
+
+ if (!IsValid(RightHandTouchState.Hand))
+ {
+ return;
+ }
+
+ const bool bHandVisible = RightHandTouchState.Hand->IsVisible();
+ if (!bHandVisible)
+ {
+ return;
+ }
+
+ if (!USGTouchComponent::IsTouchable(OtherActor))
+ {
+ return;
+ }
+
+ RightHandTouchState.ActorThumbTouching = OtherActor;
+ Pimpl->TriggerTouchStateUpdatedEvent(RightHandTouchState);
+ Pimpl->TriggerBeginTouch(RightHandTouchState.Hand, OtherActor);
+}
+
+void ASGPawn::OnRightThumbFingertipTouchColliderOverlapEnds(
+ UPrimitiveComponent* OverlappedComponent,
+ AActor* OtherActor,
+ UPrimitiveComponent* OtherComp,
+ const int32 OtherBodyIndex)
+{
+ (void) OverlappedComponent;
+ (void) OtherComp;
+ (void) OtherBodyIndex;
+
+ if (!IsValid(RightHandTouchState.Hand))
+ {
+ return;
+ }
+
+ if (RightHandTouchState.ActorThumbTouching != OtherActor)
+ {
+ return;
+ }
+
+ RightHandTouchState.ActorThumbTouching = nullptr;
+ Pimpl->TriggerTouchStateUpdatedEvent(RightHandTouchState);
+ Pimpl->TriggerEndTouch(RightHandTouchState.Hand, OtherActor);
+}
+
+void ASGPawn::OnRightIndexFingertipTouchColliderOverlapBegins(
+ UPrimitiveComponent* OverlappedComponent,
+ AActor* OtherActor,
+ UPrimitiveComponent* OtherComp,
+ const int32 OtherBodyIndex,
+ const bool bFromSweep,
+ const FHitResult& SweepResult)
+{
+ (void) OverlappedComponent;
+ (void) OtherComp;
+ (void) OtherBodyIndex;
+ (void) bFromSweep;
+ (void) SweepResult;
+
+ if (!IsValid(RightHandTouchState.Hand))
+ {
+ return;
+ }
+
+ const bool bHandVisible = RightHandTouchState.Hand->IsVisible();
+ if (!bHandVisible)
+ {
+ return;
+ }
+
+ if (!USGTouchComponent::IsTouchable(OtherActor))
+ {
+ return;
+ }
+
+ RightHandTouchState.ActorIndexTouching = OtherActor;
+ Pimpl->TriggerTouchStateUpdatedEvent(RightHandTouchState);
+ Pimpl->TriggerBeginTouch(RightHandTouchState.Hand, OtherActor);
+}
+
+void ASGPawn::OnRightIndexFingertipTouchColliderOverlapEnds(
+ UPrimitiveComponent* OverlappedComponent,
+ AActor* OtherActor,
+ UPrimitiveComponent* OtherComp,
+ const int32 OtherBodyIndex)
+{
+ (void) OverlappedComponent;
+ (void) OtherComp;
+ (void) OtherBodyIndex;
+
+ if (!IsValid(RightHandTouchState.Hand))
+ {
+ return;
+ }
+
+ if (RightHandTouchState.ActorIndexTouching != OtherActor)
+ {
+ return;
+ }
+
+ RightHandTouchState.ActorIndexTouching = nullptr;
+ Pimpl->TriggerTouchStateUpdatedEvent(RightHandTouchState);
+ Pimpl->TriggerEndTouch(RightHandTouchState.Hand, OtherActor);
+}
+
+void ASGPawn::OnRightMiddleFingertipTouchColliderOverlapBegins(
+ UPrimitiveComponent* OverlappedComponent,
+ AActor* OtherActor,
+ UPrimitiveComponent* OtherComp,
+ const int32 OtherBodyIndex,
+ const bool bFromSweep,
+ const FHitResult& SweepResult)
+{
+ (void) OverlappedComponent;
+ (void) OtherComp;
+ (void) OtherBodyIndex;
+ (void) bFromSweep;
+ (void) SweepResult;
+
+ if (!IsValid(RightHandTouchState.Hand))
+ {
+ return;
+ }
+
+ const bool bHandVisible = RightHandTouchState.Hand->IsVisible();
+ if (!bHandVisible)
+ {
+ return;
+ }
+
+ if (!USGTouchComponent::IsTouchable(OtherActor))
+ {
+ return;
+ }
+
+ RightHandTouchState.ActorMiddleTouching = OtherActor;
+ Pimpl->TriggerTouchStateUpdatedEvent(RightHandTouchState);
+ Pimpl->TriggerBeginTouch(RightHandTouchState.Hand, OtherActor);
+}
+
+void ASGPawn::OnRightMiddleFingertipTouchColliderOverlapEnds(
+ UPrimitiveComponent* OverlappedComponent,
+ AActor* OtherActor,
+ UPrimitiveComponent* OtherComp,
+ const int32 OtherBodyIndex)
+{
+ (void) OverlappedComponent;
+ (void) OtherComp;
+ (void) OtherBodyIndex;
+
+ if (!IsValid(RightHandTouchState.Hand))
+ {
+ return;
+ }
+
+ if (RightHandTouchState.ActorMiddleTouching != OtherActor)
+ {
+ return;
+ }
+
+ RightHandTouchState.ActorMiddleTouching = nullptr;
+ Pimpl->TriggerTouchStateUpdatedEvent(RightHandTouchState);
+ Pimpl->TriggerEndTouch(RightHandTouchState.Hand, OtherActor);
+}
+
+void ASGPawn::OnRightRingFingertipTouchColliderOverlapBegins(
+ UPrimitiveComponent* OverlappedComponent,
+ AActor* OtherActor,
+ UPrimitiveComponent* OtherComp,
+ const int32 OtherBodyIndex,
+ const bool bFromSweep,
+ const FHitResult& SweepResult)
+{
+ (void) OverlappedComponent;
+ (void) OtherComp;
+ (void) OtherBodyIndex;
+ (void) bFromSweep;
+ (void) SweepResult;
+
+ if (!IsValid(RightHandTouchState.Hand))
+ {
+ return;
+ }
+
+ const bool bHandVisible = RightHandTouchState.Hand->IsVisible();
+ if (!bHandVisible)
+ {
+ return;
+ }
+
+ if (!USGTouchComponent::IsTouchable(OtherActor))
+ {
+ return;
+ }
+
+ RightHandTouchState.ActorRingTouching = OtherActor;
+ Pimpl->TriggerTouchStateUpdatedEvent(RightHandTouchState);
+ Pimpl->TriggerBeginTouch(RightHandTouchState.Hand, OtherActor);
+}
+
+void ASGPawn::OnRightRingFingertipTouchColliderOverlapEnds(
+ UPrimitiveComponent* OverlappedComponent,
+ AActor* OtherActor,
+ UPrimitiveComponent* OtherComp,
+ const int32 OtherBodyIndex)
+{
+ (void) OverlappedComponent;
+ (void) OtherComp;
+ (void) OtherBodyIndex;
+
+ if (!IsValid(RightHandTouchState.Hand))
+ {
+ return;
+ }
+
+ if (RightHandTouchState.ActorRingTouching != OtherActor)
+ {
+ return;
+ }
+
+ RightHandTouchState.ActorRingTouching = nullptr;
+ Pimpl->TriggerTouchStateUpdatedEvent(RightHandTouchState);
+ Pimpl->TriggerEndTouch(RightHandTouchState.Hand, OtherActor);
+}
+
+void ASGPawn::OnRightPinkyFingertipTouchColliderOverlapBegins(
+ UPrimitiveComponent* OverlappedComponent,
+ AActor* OtherActor,
+ UPrimitiveComponent* OtherComp,
+ const int32 OtherBodyIndex,
+ const bool bFromSweep,
+ const FHitResult& SweepResult)
+{
+ (void) OverlappedComponent;
+ (void) OtherComp;
+ (void) OtherBodyIndex;
+ (void) bFromSweep;
+ (void) SweepResult;
+
+ if (!IsValid(RightHandTouchState.Hand))
+ {
+ return;
+ }
+
+ const bool bHandVisible = RightHandTouchState.Hand->IsVisible();
+ if (!bHandVisible)
+ {
+ return;
+ }
+
+ if (!USGTouchComponent::IsTouchable(OtherActor))
+ {
+ return;
+ }
+
+ RightHandTouchState.ActorPinkyTouching = OtherActor;
+ Pimpl->TriggerTouchStateUpdatedEvent(RightHandTouchState);
+ Pimpl->TriggerBeginTouch(RightHandTouchState.Hand, OtherActor);
+}
+
+void ASGPawn::OnRightPinkyFingertipTouchColliderOverlapEnds(
+ UPrimitiveComponent* OverlappedComponent,
+ AActor* OtherActor,
+ UPrimitiveComponent* OtherComp,
+ const int32 OtherBodyIndex)
+{
+ (void) OverlappedComponent;
+ (void) OtherComp;
+ (void) OtherBodyIndex;
+
+ if (!IsValid(RightHandTouchState.Hand))
+ {
+ return;
+ }
+
+ if (RightHandTouchState.ActorPinkyTouching != OtherActor)
+ {
+ return;
+ }
+
+ RightHandTouchState.ActorPinkyTouching = nullptr;
+ Pimpl->TriggerTouchStateUpdatedEvent(RightHandTouchState);
+ Pimpl->TriggerEndTouch(RightHandTouchState.Hand, OtherActor);
+}
+
+bool ASGPawn::IsLeftHandGrabbing(AActor*& OutActor) const
+{
+ if (!IsValid(LeftHandGrabState.Hand))
+ {
+ return false;
+ }
+
+ if (!IsValid(LeftHandGrabState.GrabbedActor))
+ {
+ return false;
+ }
+
+ OutActor = LeftHandGrabState.GrabbedActor;
+ return true;
+}
+
+bool ASGPawn::IsRightHandGrabbing(AActor*& OutActor) const
+{
+ if (!IsValid(RightHandGrabState.Hand))
+ {
+ return false;
+ }
+
+ if (!IsValid(RightHandGrabState.GrabbedActor))
+ {
+ return false;
+ }
+
+ OutActor = RightHandGrabState.GrabbedActor;
+ return true;
+}
+
+bool ASGPawn::IsGrabbing(const USGVirtualHandComponent* Hand, AActor*& OutActor) const
+{
+ return Hand == HandLeft ? IsLeftHandGrabbing(OutActor) : IsRightHandGrabbing(OutActor);
+}
+
+void ASGPawn::Grab(USGVirtualHandComponent* Hand, AActor* Actor)
+{
+ if (!ensureAlwaysMsgf(IsValid(Hand), TEXT("%s"), TEXT("ERROR: invalid hand!")))
+ {
+ return;
+ }
+
+ const bool bHandVisible = Hand->IsVisible();
+ if (!bHandVisible)
+ {
+ return;
+ }
+
+ if (!ensureAlwaysMsgf(IsValid(Actor), TEXT("%s"), TEXT("ERROR: invalid actor!")))
+ {
+ return;
+ }
+
+ const USGGrabComponent* GrabComponent{USGGrabComponent::GetGrabComponent(Actor)};
+ if (!IsValid(GrabComponent))
+ {
+ return;
+ }
+
+ FAttachmentTransformRules AttachmentRules{
+ FAttachmentTransformRules(GrabComponent->GetAttachmentLocationRule(),
+ GrabComponent->GetAttachmentRotationRule(),
+ GrabComponent->GetAttachmentScaleRule(),
+ GrabComponent->GetAttachmentWeldSimulatedBodies())
+ };
+
+ const FName SocketName{GrabComponent->GetAttachmentSocketName()};
+ ensureAlwaysMsgf(SocketName != NAME_None, TEXT("invalid attachment socket name!"));
+
+ if (GrabComponent->GetAffectPhysicsState())
+ {
+ GrabComponent->SimulatePhysics(false);
+ }
+
+ if (Hand->IsRight())
+ {
+ Pimpl->UnregisterRightHandOverlappedActor(Actor);
+ }
+ else
+ {
+ Pimpl->UnregisterLeftHandOverlappedActor(Actor);
+ }
+
+ const bool bAttached = Actor->AttachToComponent(Hand, MoveTemp(AttachmentRules), SocketName);
+ if (bAttached)
+ {
+ if (Hand->IsRight())
+ {
+ Pimpl->SetRightHandGrabbedActor(Actor);
+ }
+ else
+ {
+ Pimpl->SetLeftHandGrabbedActor(Actor);
+ }
+ }
+ else
+ {
+ if (Hand->IsRight())
+ {
+ SGLOG_ERROR("Failed to grab the actor with the right hand!", Actor, SocketName);
+ }
+ else
+ {
+ SGLOG_ERROR("Failed to grab the actor with the left hand!", Actor, SocketName);
+ }
+ }
+
+ Pimpl->TriggerActorGrabbedEvent(Hand, Actor);
+}
+
+void ASGPawn::ReleaseLeft()
+{
+ Pimpl->Release(LeftHandGrabState);
+}
+
+void ASGPawn::ReleaseRight()
+{
+ Pimpl->Release(RightHandGrabState);
+}
+
+ASGPawn::FImpl::FImpl(ASGPawn* InOwner)
+ : Owner(InOwner)
+{
+}
+
+ASGPawn::FImpl::~FImpl() = default;
+
+void ASGPawn::FImpl::BindRealHandsOverlapEvents()
+{
+ Owner->RealHandLeft->OnComponentBeginOverlap.AddDynamic(
+ Owner, &ASGPawn::OnRealLeftHandOverlapBegins);
+ Owner->RealHandLeft->OnComponentEndOverlap.AddDynamic(
+ Owner, &ASGPawn::OnRealLeftHandOverlapEnds);
+
+ Owner->RealHandRight->OnComponentBeginOverlap.AddDynamic(
+ Owner, &ASGPawn::OnRealRightHandOverlapBegins);
+ Owner->RealHandRight->OnComponentEndOverlap.AddDynamic(
+ Owner, &ASGPawn::OnRealRightHandOverlapEnds);
+}
+
+void ASGPawn::FImpl::UnbindRealHandsOverlapEvents()
+{
+ Owner->RealHandLeft->OnComponentBeginOverlap.RemoveDynamic(
+ Owner, &ASGPawn::OnRealLeftHandOverlapBegins);
+ Owner->RealHandLeft->OnComponentEndOverlap.RemoveDynamic(
+ Owner, &ASGPawn::OnRealLeftHandOverlapEnds);
+
+ Owner->RealHandRight->OnComponentBeginOverlap.RemoveDynamic(
+ Owner, &ASGPawn::OnRealRightHandOverlapBegins);
+ Owner->RealHandRight->OnComponentEndOverlap.RemoveDynamic(
+ Owner, &ASGPawn::OnRealRightHandOverlapEnds);
+}
+
+void ASGPawn::FImpl::BindFingerGrabOverlapEvents()
+{
+ Owner->LeftThumbFingertipGrabCollider->OnComponentBeginOverlap.AddDynamic(
+ Owner, &ASGPawn::OnLeftThumbFingertipGrabColliderOverlapBegins);
+ Owner->LeftThumbFingertipGrabCollider->OnComponentEndOverlap.AddDynamic(
+ Owner, &ASGPawn::OnLeftThumbFingertipGrabColliderOverlapEnds);
+
+ Owner->LeftIndexFingertipGrabCollider->OnComponentBeginOverlap.AddDynamic(
+ Owner, &ASGPawn::OnLeftIndexFingertipGrabColliderOverlapBegins);
+ Owner->LeftIndexFingertipGrabCollider->OnComponentEndOverlap.AddDynamic(
+ Owner, &ASGPawn::OnLeftIndexFingertipGrabColliderOverlapEnds);
+
+ Owner->LeftMiddleFingertipGrabCollider->OnComponentBeginOverlap.AddDynamic(
+ Owner, &ASGPawn::OnLeftMiddleFingertipGrabColliderOverlapBegins);
+ Owner->LeftMiddleFingertipGrabCollider->OnComponentEndOverlap.AddDynamic(
+ Owner, &ASGPawn::OnLeftMiddleFingertipGrabColliderOverlapEnds);
+
+ Owner->RightThumbFingertipGrabCollider->OnComponentBeginOverlap.AddDynamic(
+ Owner, &ASGPawn::OnRightThumbFingertipGrabColliderOverlapBegins);
+ Owner->RightThumbFingertipGrabCollider->OnComponentEndOverlap.AddDynamic(
+ Owner, &ASGPawn::OnRightThumbFingertipGrabColliderOverlapEnds);
+
+ Owner->RightIndexFingertipGrabCollider->OnComponentBeginOverlap.AddDynamic(
+ Owner, &ASGPawn::OnRightIndexFingertipGrabColliderOverlapBegins);
+ Owner->RightIndexFingertipGrabCollider->OnComponentEndOverlap.AddDynamic(
+ Owner, &ASGPawn::OnRightIndexFingertipGrabColliderOverlapEnds);
+
+ Owner->RightMiddleFingertipGrabCollider->OnComponentBeginOverlap.AddDynamic(
+ Owner, &ASGPawn::OnRightMiddleFingertipGrabColliderOverlapBegins);
+ Owner->RightMiddleFingertipGrabCollider->OnComponentEndOverlap.AddDynamic(
+ Owner, &ASGPawn::OnRightMiddleFingertipGrabColliderOverlapEnds);
+}
+
+void ASGPawn::FImpl::UnbindFingerGrabOverlapEvents()
+{
+ Owner->LeftThumbFingertipGrabCollider->OnComponentBeginOverlap.RemoveDynamic(
+ Owner, &ASGPawn::OnLeftThumbFingertipGrabColliderOverlapBegins);
+ Owner->LeftThumbFingertipGrabCollider->OnComponentEndOverlap.RemoveDynamic(
+ Owner, &ASGPawn::OnLeftThumbFingertipGrabColliderOverlapEnds);
+
+ Owner->LeftIndexFingertipGrabCollider->OnComponentBeginOverlap.RemoveDynamic(
+ Owner, &ASGPawn::OnLeftIndexFingertipGrabColliderOverlapBegins);
+ Owner->LeftIndexFingertipGrabCollider->OnComponentEndOverlap.RemoveDynamic(
+ Owner, &ASGPawn::OnLeftIndexFingertipGrabColliderOverlapEnds);
+
+ Owner->LeftMiddleFingertipGrabCollider->OnComponentBeginOverlap.RemoveDynamic(
+ Owner, &ASGPawn::OnLeftMiddleFingertipGrabColliderOverlapBegins);
+ Owner->LeftMiddleFingertipGrabCollider->OnComponentEndOverlap.RemoveDynamic(
+ Owner, &ASGPawn::OnLeftMiddleFingertipGrabColliderOverlapEnds);
+
+ Owner->RightThumbFingertipGrabCollider->OnComponentBeginOverlap.RemoveDynamic(
+ Owner, &ASGPawn::OnRightThumbFingertipGrabColliderOverlapBegins);
+ Owner->RightThumbFingertipGrabCollider->OnComponentEndOverlap.RemoveDynamic(
+ Owner, &ASGPawn::OnRightThumbFingertipGrabColliderOverlapEnds);
+
+ Owner->RightIndexFingertipGrabCollider->OnComponentBeginOverlap.RemoveDynamic(
+ Owner, &ASGPawn::OnRightIndexFingertipGrabColliderOverlapBegins);
+ Owner->RightIndexFingertipGrabCollider->OnComponentEndOverlap.RemoveDynamic(
+ Owner, &ASGPawn::OnRightIndexFingertipGrabColliderOverlapEnds);
+
+ Owner->RightMiddleFingertipGrabCollider->OnComponentBeginOverlap.RemoveDynamic(
+ Owner, &ASGPawn::OnRightMiddleFingertipGrabColliderOverlapBegins);
+ Owner->RightMiddleFingertipGrabCollider->OnComponentEndOverlap.RemoveDynamic(
+ Owner, &ASGPawn::OnRightMiddleFingertipGrabColliderOverlapEnds);
+}
+
+void ASGPawn::FImpl::BindFingerTouchOverlapEvents()
+{
+ Owner->LeftThumbFingertipTouchCollider->OnComponentBeginOverlap.AddDynamic(
+ Owner, &ASGPawn::OnLeftThumbFingertipTouchColliderOverlapBegins);
+ Owner->LeftThumbFingertipTouchCollider->OnComponentEndOverlap.AddDynamic(
+ Owner, &ASGPawn::OnLeftThumbFingertipTouchColliderOverlapEnds);
+
+ Owner->LeftIndexFingertipTouchCollider->OnComponentBeginOverlap.AddDynamic(
+ Owner, &ASGPawn::OnLeftIndexFingertipTouchColliderOverlapBegins);
+ Owner->LeftIndexFingertipTouchCollider->OnComponentEndOverlap.AddDynamic(
+ Owner, &ASGPawn::OnLeftIndexFingertipTouchColliderOverlapEnds);
+
+ Owner->LeftMiddleFingertipTouchCollider->OnComponentBeginOverlap.AddDynamic(
+ Owner, &ASGPawn::OnLeftMiddleFingertipTouchColliderOverlapBegins);
+ Owner->LeftMiddleFingertipTouchCollider->OnComponentEndOverlap.AddDynamic(
+ Owner, &ASGPawn::OnLeftMiddleFingertipTouchColliderOverlapEnds);
+
+ Owner->LeftRingFingertipTouchCollider->OnComponentBeginOverlap.AddDynamic(
+ Owner, &ASGPawn::OnLeftRingFingertipTouchColliderOverlapBegins);
+ Owner->LeftRingFingertipTouchCollider->OnComponentEndOverlap.AddDynamic(
+ Owner, &ASGPawn::OnLeftRingFingertipTouchColliderOverlapEnds);
+
+ Owner->LeftPinkyFingertipTouchCollider->OnComponentBeginOverlap.AddDynamic(
+ Owner, &ASGPawn::OnLeftPinkyFingertipTouchColliderOverlapBegins);
+ Owner->LeftPinkyFingertipTouchCollider->OnComponentEndOverlap.AddDynamic(
+ Owner, &ASGPawn::OnLeftPinkyFingertipTouchColliderOverlapEnds);
+
+ Owner->RightThumbFingertipTouchCollider->OnComponentBeginOverlap.AddDynamic(
+ Owner, &ASGPawn::OnRightThumbFingertipTouchColliderOverlapBegins);
+ Owner->RightThumbFingertipTouchCollider->OnComponentEndOverlap.AddDynamic(
+ Owner, &ASGPawn::OnRightThumbFingertipTouchColliderOverlapEnds);
+
+ Owner->RightIndexFingertipTouchCollider->OnComponentBeginOverlap.AddDynamic(
+ Owner, &ASGPawn::OnRightIndexFingertipTouchColliderOverlapBegins);
+ Owner->RightIndexFingertipTouchCollider->OnComponentEndOverlap.AddDynamic(
+ Owner, &ASGPawn::OnRightIndexFingertipTouchColliderOverlapEnds);
+
+ Owner->RightMiddleFingertipTouchCollider->OnComponentBeginOverlap.AddDynamic(
+ Owner, &ASGPawn::OnRightMiddleFingertipTouchColliderOverlapBegins);
+ Owner->RightMiddleFingertipTouchCollider->OnComponentEndOverlap.AddDynamic(
+ Owner, &ASGPawn::OnRightMiddleFingertipTouchColliderOverlapEnds);
+
+ Owner->RightRingFingertipTouchCollider->OnComponentBeginOverlap.AddDynamic(
+ Owner, &ASGPawn::OnRightRingFingertipTouchColliderOverlapBegins);
+ Owner->RightRingFingertipTouchCollider->OnComponentEndOverlap.AddDynamic(
+ Owner, &ASGPawn::OnRightRingFingertipTouchColliderOverlapEnds);
+
+ Owner->RightPinkyFingertipTouchCollider->OnComponentBeginOverlap.AddDynamic(
+ Owner, &ASGPawn::OnRightPinkyFingertipTouchColliderOverlapBegins);
+ Owner->RightPinkyFingertipTouchCollider->OnComponentEndOverlap.AddDynamic(
+ Owner, &ASGPawn::OnRightPinkyFingertipTouchColliderOverlapEnds);
+}
+
+void ASGPawn::FImpl::UnbindFingerTouchOverlapEvents()
+{
+ Owner->LeftThumbFingertipTouchCollider->OnComponentBeginOverlap.RemoveDynamic(
+ Owner, &ASGPawn::OnLeftThumbFingertipTouchColliderOverlapBegins);
+ Owner->LeftThumbFingertipTouchCollider->OnComponentEndOverlap.RemoveDynamic(
+ Owner, &ASGPawn::OnLeftThumbFingertipTouchColliderOverlapEnds);
+
+ Owner->LeftIndexFingertipTouchCollider->OnComponentBeginOverlap.RemoveDynamic(
+ Owner, &ASGPawn::OnLeftIndexFingertipTouchColliderOverlapBegins);
+ Owner->LeftIndexFingertipTouchCollider->OnComponentEndOverlap.RemoveDynamic(
+ Owner, &ASGPawn::OnLeftIndexFingertipTouchColliderOverlapEnds);
+
+ Owner->LeftMiddleFingertipTouchCollider->OnComponentBeginOverlap.RemoveDynamic(
+ Owner, &ASGPawn::OnLeftMiddleFingertipTouchColliderOverlapBegins);
+ Owner->LeftMiddleFingertipTouchCollider->OnComponentEndOverlap.RemoveDynamic(
+ Owner, &ASGPawn::OnLeftMiddleFingertipTouchColliderOverlapEnds);
+
+ Owner->LeftRingFingertipTouchCollider->OnComponentBeginOverlap.RemoveDynamic(
+ Owner, &ASGPawn::OnLeftRingFingertipTouchColliderOverlapBegins);
+ Owner->LeftRingFingertipTouchCollider->OnComponentEndOverlap.RemoveDynamic(
+ Owner, &ASGPawn::OnLeftRingFingertipTouchColliderOverlapEnds);
+
+ Owner->LeftPinkyFingertipTouchCollider->OnComponentBeginOverlap.RemoveDynamic(
+ Owner, &ASGPawn::OnLeftPinkyFingertipTouchColliderOverlapBegins);
+ Owner->LeftPinkyFingertipTouchCollider->OnComponentEndOverlap.RemoveDynamic(
+ Owner, &ASGPawn::OnLeftPinkyFingertipTouchColliderOverlapEnds);
+
+ Owner->RightThumbFingertipGrabCollider->OnComponentBeginOverlap.RemoveDynamic(
+ Owner, &ASGPawn::OnRightThumbFingertipGrabColliderOverlapBegins);
+ Owner->RightThumbFingertipGrabCollider->OnComponentEndOverlap.RemoveDynamic(
+ Owner, &ASGPawn::OnRightThumbFingertipGrabColliderOverlapEnds);
+
+ Owner->RightIndexFingertipGrabCollider->OnComponentBeginOverlap.RemoveDynamic(
+ Owner, &ASGPawn::OnRightIndexFingertipGrabColliderOverlapBegins);
+ Owner->RightIndexFingertipGrabCollider->OnComponentEndOverlap.RemoveDynamic(
+ Owner, &ASGPawn::OnRightIndexFingertipGrabColliderOverlapEnds);
+
+ Owner->RightMiddleFingertipGrabCollider->OnComponentBeginOverlap.RemoveDynamic(
+ Owner, &ASGPawn::OnRightMiddleFingertipGrabColliderOverlapBegins);
+ Owner->RightMiddleFingertipGrabCollider->OnComponentEndOverlap.RemoveDynamic(
+ Owner, &ASGPawn::OnRightMiddleFingertipGrabColliderOverlapEnds);
+
+ Owner->RightThumbFingertipTouchCollider->OnComponentBeginOverlap.RemoveDynamic(
+ Owner, &ASGPawn::OnRightThumbFingertipTouchColliderOverlapBegins);
+ Owner->RightThumbFingertipTouchCollider->OnComponentEndOverlap.RemoveDynamic(
+ Owner, &ASGPawn::OnRightThumbFingertipTouchColliderOverlapEnds);
+
+ Owner->RightIndexFingertipTouchCollider->OnComponentBeginOverlap.RemoveDynamic(
+ Owner, &ASGPawn::OnRightIndexFingertipTouchColliderOverlapBegins);
+ Owner->RightIndexFingertipTouchCollider->OnComponentEndOverlap.RemoveDynamic(
+ Owner, &ASGPawn::OnRightIndexFingertipTouchColliderOverlapEnds);
+
+ Owner->RightMiddleFingertipTouchCollider->OnComponentBeginOverlap.RemoveDynamic(
+ Owner, &ASGPawn::OnRightMiddleFingertipTouchColliderOverlapBegins);
+ Owner->RightMiddleFingertipTouchCollider->OnComponentEndOverlap.RemoveDynamic(
+ Owner, &ASGPawn::OnRightMiddleFingertipTouchColliderOverlapEnds);
+
+ Owner->RightRingFingertipTouchCollider->OnComponentBeginOverlap.RemoveDynamic(
+ Owner, &ASGPawn::OnRightRingFingertipTouchColliderOverlapBegins);
+ Owner->RightRingFingertipTouchCollider->OnComponentEndOverlap.RemoveDynamic(
+ Owner, &ASGPawn::OnRightRingFingertipTouchColliderOverlapEnds);
+
+ Owner->RightPinkyFingertipTouchCollider->OnComponentBeginOverlap.RemoveDynamic(
+ Owner, &ASGPawn::OnRightPinkyFingertipTouchColliderOverlapBegins);
+ Owner->RightPinkyFingertipTouchCollider->OnComponentEndOverlap.RemoveDynamic(
+ Owner, &ASGPawn::OnRightPinkyFingertipTouchColliderOverlapEnds);
+}
+
+void ASGPawn::FImpl::SetVisibility(USceneComponent* SceneComponent, const bool bVisibility) const
+{
+ if (!ensureAlwaysMsgf(IsValid(SceneComponent), TEXT("Invalid SceneComponent!")))
+ {
+ return;
+ }
+
+ SceneComponent->SetHiddenInGame(!bVisibility, false);
+}
+
+void ASGPawn::FImpl::UpdateHandLocation(const bool bRight, const FVector& Location, const bool bUpdateVirtualHand) const
+{
+ if (bRight)
+ {
+ UpdateRightHandLocation(Location, bUpdateVirtualHand);
+ }
+ else
+ {
+ UpdateLeftHandLocation(Location, bUpdateVirtualHand);
+ }
+}
+
+void ASGPawn::FImpl::UpdateLeftHandLocation(const FVector& Location, const bool bUpdateVirtualHand) const
+{
+ if (bUpdateVirtualHand)
+ {
+ Owner->HandLeft->SetWorldLocation(Location);
+ }
+ Owner->RealHandLeft->SetWorldLocation(Location);
+}
+
+void ASGPawn::FImpl::UpdateRightHandLocation(const FVector& Location, const bool bUpdateVirtualHand) const
+{
+ if (bUpdateVirtualHand)
+ {
+ Owner->HandRight->SetWorldLocation(Location);
+ }
+ Owner->RealHandRight->SetWorldLocation(Location);
+}
+
+void ASGPawn::FImpl::UpdateHandRotation(const bool bRight, const FRotator& Rotation, const bool bUpdateVirtualHand)
+{
+ if (bRight)
+ {
+ UpdateRightHandRotation(Rotation, bUpdateVirtualHand);
+ }
+ else
+ {
+ UpdateLeftHandRotation(Rotation, bUpdateVirtualHand);
+ }
+}
+
+void ASGPawn::FImpl::UpdateLeftHandRotation(const FRotator& Rotation, const bool bUpdateVirtualHand)
+{
+ const FSGVirtualHandMeshSettings& MeshSettings{Owner->HandLeft->GetVirtualHandSettings().MeshSettings};
+ const FQuat NewRotation{
+ Rotation.Quaternion() * MeshSettings.RootBoneRotationCorrection.Quaternion()
+ };
+ if (bUpdateVirtualHand)
+ {
+ Owner->HandLeft->SetWorldRotation(NewRotation);
+ }
+ Owner->RealHandLeft->SetWorldRotation(NewRotation);
+}
+
+void ASGPawn::FImpl::UpdateRightHandRotation(const FRotator& Rotation, const bool bUpdateVirtualHand)
+{
+ const FSGVirtualHandMeshSettings& MeshSettings{Owner->HandLeft->GetVirtualHandSettings().MeshSettings};
+ const FQuat NewRotation{
+ Rotation.Quaternion() * MeshSettings.RootBoneRotationCorrection.Quaternion()
+ };
+ if (bUpdateVirtualHand)
+ {
+ Owner->HandRight->SetWorldRotation(NewRotation);
+ }
+ Owner->RealHandRight->SetWorldRotation(NewRotation);
+}
+
+void ASGPawn::FImpl::UpdateHandLocationAndRotation(
+ const bool bRight, const FVector& Location, const FRotator& Rotation, const bool bUpdateVirtualHand)
+{
+ if (bRight)
+ {
+ UpdateRightHandLocationAndRotation(Location, Rotation, bUpdateVirtualHand);
+ }
+ else
+ {
+ UpdateLeftHandLocationAndRotation(Location, Rotation, bUpdateVirtualHand);
+ }
+}
+
+void ASGPawn::FImpl::UpdateLeftHandLocationAndRotation(
+ const FVector& Location, const FRotator& Rotation, const bool bUpdateVirtualHand)
+{
+ UpdateLeftHandLocation(Location, bUpdateVirtualHand);
+ UpdateLeftHandRotation(Rotation, bUpdateVirtualHand);
+}
+
+void ASGPawn::FImpl::UpdateRightHandLocationAndRotation(
+ const FVector& Location, const FRotator& Rotation, const bool bUpdateVirtualHand)
+{
+ UpdateRightHandLocation(Location, bUpdateVirtualHand);
+ UpdateRightHandRotation(Rotation, bUpdateVirtualHand);
+}
+
+void ASGPawn::FImpl::SetFingerColliderCollisionEnabled(USphereComponent* Collider, const bool bEnabled) const
+{
+ if (!ensureAlwaysMsgf(IsValid(Collider), TEXT("Invalid collider object!")))
+ {
+ return;
+ }
+
+ if (bEnabled)
+ {
+ Collider->SetGenerateOverlapEvents(true);
+ Collider->SetCollisionEnabled(ECollisionEnabled::QueryOnly);
+ Collider->SetCollisionResponseToAllChannels(ECR_Overlap);
+ Collider->SetCollisionResponseToChannel(ECC_WorldStatic, ECR_Overlap);
+ Collider->SetCollisionResponseToChannel(ECC_WorldDynamic, ECR_Overlap);
+ Collider->SetCollisionResponseToChannel(ECC_Pawn, ECR_Overlap);
+ Collider->SetCollisionResponseToChannel(ECC_Visibility, ECR_Overlap);
+ Collider->SetCollisionResponseToChannel(ECC_PhysicsBody, ECR_Overlap);
+ Collider->SetCollisionResponseToChannel(ECC_Vehicle, ECR_Overlap);
+ Collider->SetCollisionResponseToChannel(ECC_Destructible, ECR_Overlap);
+ }
+ else
+ {
+ Collider->SetGenerateOverlapEvents(false);
+ Collider->SetCollisionEnabled(ECollisionEnabled::NoCollision);
+ Collider->SetCollisionResponseToAllChannels(ECR_Ignore);
+ }
+}
+
+void ASGPawn::FImpl::SetAllFingersColliderCollisionEnabled(const bool bRight, const bool bEnabled) const
+{
+ if (bRight)
+ {
+ SetFingerColliderCollisionEnabled(Owner->RightThumbFingertipGrabCollider, bEnabled);
+ SetFingerColliderCollisionEnabled(Owner->RightThumbFingertipTouchCollider, bEnabled);
+ SetFingerColliderCollisionEnabled(Owner->RightIndexFingertipGrabCollider, bEnabled);
+ SetFingerColliderCollisionEnabled(Owner->RightIndexFingertipTouchCollider, bEnabled);
+ SetFingerColliderCollisionEnabled(Owner->RightMiddleFingertipGrabCollider, bEnabled);
+ SetFingerColliderCollisionEnabled(Owner->RightMiddleFingertipTouchCollider, bEnabled);
+ SetFingerColliderCollisionEnabled(Owner->RightRingFingertipTouchCollider, bEnabled);
+ SetFingerColliderCollisionEnabled(Owner->RightPinkyFingertipTouchCollider, bEnabled);
+ }
+ else
+ {
+ SetFingerColliderCollisionEnabled(Owner->LeftThumbFingertipGrabCollider, bEnabled);
+ SetFingerColliderCollisionEnabled(Owner->LeftThumbFingertipTouchCollider, bEnabled);
+ SetFingerColliderCollisionEnabled(Owner->LeftIndexFingertipGrabCollider, bEnabled);
+ SetFingerColliderCollisionEnabled(Owner->LeftIndexFingertipTouchCollider, bEnabled);
+ SetFingerColliderCollisionEnabled(Owner->LeftMiddleFingertipGrabCollider, bEnabled);
+ SetFingerColliderCollisionEnabled(Owner->LeftMiddleFingertipTouchCollider, bEnabled);
+ SetFingerColliderCollisionEnabled(Owner->LeftRingFingertipTouchCollider, bEnabled);
+ SetFingerColliderCollisionEnabled(Owner->LeftPinkyFingertipTouchCollider, bEnabled);
+ }
+}
+
+void ASGPawn::FImpl::ApplyHandVelocityToGrabbedActor(const FSGGrabState& GrabState) const
+{
+ if (!IsValid(GrabState.Hand))
+ {
+ return;
+ }
+
+ if (!IsValid(GrabState.GrabbedActor))
+ {
+ return;
+ }
+
+ UPrimitiveComponent* ActorRootComponent{Cast(GrabState.GrabbedActor->GetRootComponent())};
+ if (!ensureAlwaysMsgf(IsValid(ActorRootComponent), TEXT("RootComponent is not a valid UPrimativeComponent!")))
+ {
+ return;
+ }
+
+ FVector HandVelocity{UKismetMathLibrary::GetVectorArrayAverage(GrabState.HandVelocityHistory)};
+
+ ActorRootComponent->SetPhysicsLinearVelocity(HandVelocity);
+ ActorRootComponent->AddImpulse(MoveTemp(HandVelocity), NAME_None, true);
+}
+
+void ASGPawn::FImpl::SetGrabbedActor(FSGGrabState& GrabState, AActor* Actor) const
+{
+ GrabState.GrabbedActor = Actor;
+}
+
+void ASGPawn::FImpl::SetLeftHandGrabbedActor(AActor* Actor) const
+{
+ SetGrabbedActor(Owner->LeftHandGrabState, Actor);
+}
+
+void ASGPawn::FImpl::SetRightHandGrabbedActor(AActor* Actor) const
+{
+ SetGrabbedActor(Owner->RightHandGrabState, Actor);
+}
+
+void ASGPawn::FImpl::RecordHandVelocity(FSGGrabState& GrabState, const float DeltaSeconds) const
+{
+ if (!IsValid(GrabState.Hand))
+ {
+ return;
+ }
+
+ FVector HandVelocity{
+ (GrabState.Hand->GetComponentLocation() - GrabState.PreviousHandLocation) / DeltaSeconds
+ };
+
+ if (GrabState.HandVelocityHistory.Num() >= Owner->MaxNumberOfHandVelocitySamples)
+ {
+ if (GrabState.HandVelocityHistory.IsValidIndex(0))
+ {
+ GrabState.HandVelocityHistory.RemoveAt(0, 1, EAllowShrinking::No);
+ GrabState.HandVelocityHistory.Emplace(MoveTemp(HandVelocity));
+ }
+ }
+ else
+ {
+ GrabState.HandVelocityHistory.Emplace(MoveTemp(HandVelocity));
+ }
+
+ GrabState.PreviousHandLocation = GrabState.Hand->GetComponentLocation();
+}
+
+void ASGPawn::FImpl::Release(FSGGrabState& GrabState) const
+{
+ if (!IsValid(GrabState.GrabbedActor))
+ {
+ return;
+ }
+
+ const USGGrabComponent* GrabComponent{USGGrabComponent::GetGrabComponent(GrabState.GrabbedActor)};
+ if (!IsValid(GrabComponent))
+ {
+ return;
+ }
+
+ GrabState.GrabbedActor->DetachFromActor(
+ FDetachmentTransformRules(GrabComponent->GetDetachmentLocationRule(),
+ GrabComponent->GetDetachmentRotationRule(),
+ GrabComponent->GetDetachmentScaleRule(),
+ GrabComponent->GetDetachmentInCallModify())
+ );
+
+ if (GrabComponent->GetAffectPhysicsState())
+ {
+ GrabComponent->SimulatePhysics(true);
+ }
+
+ ApplyHandVelocityToGrabbedActor(GrabState);
+
+ TriggerActorReleasedEvent(GrabState.Hand, GrabState.GrabbedActor);
+
+ SetGrabbedActor(GrabState, nullptr);
+}
+
+void ASGPawn::FImpl::TriggerGrabStateUpdatedEvent(const FSGGrabState& GrabState) const
+{
+ Owner->GrabStateUpdatedEvent.Broadcast(GrabState);
+ Owner->OnGrabStateUpdated(GrabState);
+}
+
+void ASGPawn::FImpl::TriggerTouchStateUpdatedEvent(const FSGTouchState& TouchState) const
+{
+ Owner->TouchStateUpdatedEvent.Broadcast(TouchState);
+ Owner->OnTouchStateUpdated(TouchState);
+}
+
+void ASGPawn::FImpl::TriggerActorGrabbedEvent(const USGVirtualHandComponent* Hand, const AActor* Actor) const
+{
+ Owner->ActorGrabbedEvent.Broadcast(Hand, Actor);
+ Owner->OnActorGrabbed(Hand, Actor);
+}
+
+void ASGPawn::FImpl::TriggerActorReleasedEvent(const USGVirtualHandComponent* Hand, const AActor* Actor) const
+{
+ Owner->ActorReleasedEvent.Broadcast(Hand, Actor);
+ Owner->OnActorReleased(Hand, Actor);
+}
+
+void ASGPawn::FImpl::TriggerBeginTouch(const USGVirtualHandComponent* Hand, const AActor* Actor) const
+{
+ Owner->ActorBeginTouchEvent.Broadcast(Hand, Actor);
+ Owner->OnActorBeginTouch(Hand, Actor);
+}
+
+void ASGPawn::FImpl::TriggerEndTouch(const USGVirtualHandComponent* Hand, const AActor* Actor) const
+{
+ Owner->ActorEndTouchEvent.Broadcast(Hand, Actor);
+ Owner->OnActorEndTouch(Hand, Actor);
+}
+
+void ASGPawn::FImplDeleter::operator()(const FImpl* P) const
+{
+ delete P;
+}
\ No newline at end of file
diff --git a/Plugins/SenseGlove/Source/SenseGlove/Private/SenseGlove/GameFramework/SGPlayerController.cpp b/Plugins/SenseGlove/Source/SenseGlove/Private/SenseGlove/GameFramework/SGPlayerController.cpp
new file mode 100644
index 0000000..a3bc988
--- /dev/null
+++ b/Plugins/SenseGlove/Source/SenseGlove/Private/SenseGlove/GameFramework/SGPlayerController.cpp
@@ -0,0 +1,320 @@
+/**
+ * @file
+ *
+ * @author Mamadou Babaei
+ *
+ * @section LICENSE
+ *
+ * (The MIT License)
+ *
+ * Copyright (c) 2020 - 2026 SenseGlove
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * @section DESCRIPTION
+ *
+ *
+ */
+
+
+#include "SenseGlove/GameFramework/SGPlayerController.h"
+
+#include "Runtime/Launch/Resources/Version.h"
+
+#include "SenseGlove/Components/SGTouchComponent.h"
+#include "SenseGlove/Components/SGVirtualHandComponent.h"
+#include "SenseGlove/GameFramework/SGPawn.h"
+#include "SenseGlove/Haptics/SGGrabState.h"
+#include "SenseGlove/Haptics/SGTouchState.h"
+#include "SGBuildHacks/SGPlatform.h"
+#include "SGCore/SGCustomWaveform.h"
+#include "SGCore/SGHapticGlove.h"
+
+struct ASGPlayerController::FImpl
+{
+ /************************
+ * Static methods
+ ************************/
+
+ static float GetForceFeedbackLevel(const AActor* Actor);
+
+ static TArray GetForceFeedbackLevels(
+ const AActor* ActorThumbTouching,
+ const AActor* ActorIndexTouching,
+ const AActor* ActorMiddleTouching,
+ const AActor* ActorRingTouching,
+ const AActor* ActorPinkyTouching);
+
+ /************************
+ * Member variables
+ ************************/
+
+ /************************
+ * Owner object
+ ************************/
+
+ ASGPlayerController* Owner;
+
+ /************************
+ * Constructor / Destructor
+ ************************/
+
+ explicit FImpl(ASGPlayerController* InOwner);
+ ~FImpl();
+
+ /************************
+ * Default copy constructor & copy assignment operator
+ ************************/
+
+ FImpl(const FImpl& Rhs) = default;
+ FImpl& operator=(const FImpl& Rhs) = default;
+
+ /************************
+ * Methods
+ ************************/
+
+ USGCustomWaveform* GetCustomWaveform(const AActor* Actor);
+
+ void UpdateHapticsFeedback(const FSGTouchState& TouchState);
+};
+
+float ASGPlayerController::FImpl::GetForceFeedbackLevel(const AActor* Actor)
+{
+ if (IsValid(Actor))
+ {
+ const USGTouchComponent* TouchComponent{USGTouchComponent::GetTouchComponent(Actor)};
+ if (IsValid(TouchComponent))
+ {
+ return TouchComponent->GetForceFeedbackLevel();
+ }
+ }
+
+ return 0.0f;
+}
+
+TArray ASGPlayerController::FImpl::GetForceFeedbackLevels(
+ const AActor* ActorThumbTouching,
+ const AActor* ActorIndexTouching,
+ const AActor* ActorMiddleTouching,
+ const AActor* ActorRingTouching,
+ const AActor* ActorPinkyTouching)
+{
+ const float ThumbForceFeedbackLevel = GetForceFeedbackLevel(ActorThumbTouching);
+ const float IndexForceFeedbackLevel = GetForceFeedbackLevel(ActorIndexTouching);
+ const float MiddleForceFeedbackLevel = GetForceFeedbackLevel(ActorMiddleTouching);
+ const float RingForceFeedbackLevel = GetForceFeedbackLevel(ActorRingTouching);
+ const float PinkyForceFeedbackLevel = GetForceFeedbackLevel(ActorPinkyTouching);
+
+ const TArray ForceFeedbackLevels{
+ ThumbForceFeedbackLevel,
+ IndexForceFeedbackLevel,
+ MiddleForceFeedbackLevel,
+ RingForceFeedbackLevel,
+ PinkyForceFeedbackLevel,
+ };
+
+ return ForceFeedbackLevels;
+}
+
+ASGPlayerController::ASGPlayerController(const FObjectInitializer& ObjectInitializer)
+ : Super(ObjectInitializer),
+ Pimpl(TUniquePtr(new FImpl{this}, PimplDeleter))
+{
+}
+
+void ASGPlayerController::BeginPlay()
+{
+ Super::BeginPlay();
+
+ ASGPawn* SGPawn{Cast(GetPawn())};
+ if (!ensureAlwaysMsgf(IsValid(SGPawn), TEXT("%s"), TEXT("ERROR: invalid SenseGlove pawn!")))
+ {
+ return;
+ }
+
+ SGPawn->OnGrabStateUpdated().AddWeakLambda(
+ this, [= SG_CAPTURE_THIS](const FSGGrabState& GrabState) -> void
+ {
+ if (!IsValid(SGPawn))
+ {
+ return;
+ }
+
+ if (!IsValid(GrabState.Hand))
+ {
+ return;
+ }
+
+ const bool bHandVisible = GrabState.Hand->IsVisible();
+ if (!bHandVisible)
+ {
+ if (SGPawn->IsGrabbing(GrabState.Hand))
+ {
+ SGPawn->Release(GrabState.Hand);
+ }
+ return;
+ }
+
+ if (SGPawn->IsGrabbing(GrabState.Hand))
+ {
+ if (!IsValid(GrabState.ActorThumbCanGrab) ||
+ (GrabState.ActorIndexCanGrab != GrabState.ActorThumbCanGrab
+ && GrabState.ActorMiddleCanGrab != GrabState.ActorThumbCanGrab))
+ {
+ SGPawn->Release(GrabState.Hand);
+ }
+ }
+ else
+ {
+ if (SGPawn->CanGrab(GrabState.Hand, GrabState.ActorThumbCanGrab))
+ {
+ SGPawn->Grab(GrabState.Hand, GrabState.ActorThumbCanGrab);
+ }
+ }
+ });
+
+ SGPawn->OnTouchStateUpdated().AddWeakLambda(
+ this, [= SG_CAPTURE_THIS](const FSGTouchState& TouchState) -> void
+ {
+ if (!IsValid(SGPawn))
+ {
+ return;
+ }
+
+ if (!IsValid(TouchState.Hand))
+ {
+ return;
+ }
+
+ const bool bHandVisible = TouchState.Hand->IsVisible();
+ if (!bHandVisible)
+ {
+ return;
+ }
+
+ const bool bGloveConnected = TouchState.Hand->IsGloveConnected();
+ if (!bGloveConnected)
+ {
+ return;
+ }
+
+ Pimpl->UpdateHapticsFeedback(TouchState);
+ });
+}
+
+ASGPlayerController::FImpl::FImpl(ASGPlayerController* InOwner)
+ : Owner(InOwner)
+{
+}
+
+ASGPlayerController::FImpl::~FImpl() = default;
+
+USGCustomWaveform* ASGPlayerController::FImpl::GetCustomWaveform(const AActor* Actor)
+{
+ float Amplitude = 0.0f;
+ float Duration = 0.0f;
+ float Frequency = 0.0f;
+
+ if (IsValid(Actor))
+ {
+ const USGTouchComponent* TouchComponent{USGTouchComponent::GetTouchComponent(Actor)};
+ if (IsValid(TouchComponent))
+ {
+ Amplitude = TouchComponent->GetVibrotactileAmplitude();
+ Duration = TouchComponent->GetVibrotactileDuration();
+ Frequency = TouchComponent->GetVibrotactileFrequency();
+ }
+ }
+
+ USGCustomWaveform* CustomWaveform{
+ USGCustomWaveform::NewCustomWaveform(Owner, Amplitude, Duration, Frequency)
+ };
+ return CustomWaveform;
+}
+
+void ASGPlayerController::FImpl::UpdateHapticsFeedback(const FSGTouchState& TouchState)
+{
+ if (!IsValid(TouchState.Hand))
+ {
+ return;
+ }
+
+ USGHapticGlove* Glove{TouchState.Hand->GetConnectedGlove()};
+ if (!IsValid(Glove))
+ {
+ return;
+ }
+
+ const bool bGloveConnected = Glove->IsConnected();
+ if (!bGloveConnected)
+ {
+ return;
+ }
+
+ // Queue the Force-Feedback command...
+ TArray ForceFeedbackLevels{
+ GetForceFeedbackLevels(
+ TouchState.ActorThumbTouching, TouchState.ActorIndexTouching, TouchState.ActorMiddleTouching,
+ TouchState.ActorRingTouching, TouchState.ActorPinkyTouching)
+ };
+ Glove->QueueForceFeedbackLevels(MoveTemp(ForceFeedbackLevels));
+
+ // Send the haptics commands!
+ Glove->SendHaptics();
+
+ // Send Vibrotactile to the thumb finger if it's touching an actor...
+ if (IsValid(TouchState.ActorThumbTouching))
+ {
+ USGCustomWaveform* CustomWaveform(GetCustomWaveform(TouchState.ActorThumbTouching));
+ Glove->SendCustomWaveform(CustomWaveform, ESGHapticLocation::ThumbTip);
+ }
+
+ // Send Vibrotactile to the index finger if it's touching an actor...
+ if (IsValid(TouchState.ActorIndexTouching))
+ {
+ USGCustomWaveform* CustomWaveform(GetCustomWaveform(TouchState.ActorIndexTouching));
+ Glove->SendCustomWaveform(CustomWaveform, ESGHapticLocation::IndexTip);
+ }
+
+ // Send Vibrotactile to the middle finger if it's touching an actor...
+ if (IsValid(TouchState.ActorMiddleTouching))
+ {
+ USGCustomWaveform* CustomWaveform(GetCustomWaveform(TouchState.ActorMiddleTouching));
+ Glove->SendCustomWaveform(CustomWaveform, ESGHapticLocation::MiddleTip);
+ }
+
+ // Send Vibrotactile to the ring finger if it's touching an actor...
+ if (IsValid(TouchState.ActorRingTouching))
+ {
+ USGCustomWaveform* CustomWaveform(GetCustomWaveform(TouchState.ActorRingTouching));
+ Glove->SendCustomWaveform(CustomWaveform, ESGHapticLocation::RingTip);
+ }
+
+ // Send Vibrotactile to the pinky finger if it's touching an actor...
+ if (IsValid(TouchState.ActorPinkyTouching))
+ {
+ USGCustomWaveform* CustomWaveform(GetCustomWaveform(TouchState.ActorPinkyTouching));
+ Glove->SendCustomWaveform(CustomWaveform, ESGHapticLocation::PinkyTip);
+ }
+}
+
+void ASGPlayerController::FImplDeleter::operator()(const FImpl* P) const
+{
+ delete P;
+}
\ No newline at end of file
diff --git a/Plugins/SenseGlove/Source/SenseGlove/Private/SenseGlove/Haptics/SGGrabState.cpp b/Plugins/SenseGlove/Source/SenseGlove/Private/SenseGlove/Haptics/SGGrabState.cpp
new file mode 100644
index 0000000..e0ce638
--- /dev/null
+++ b/Plugins/SenseGlove/Source/SenseGlove/Private/SenseGlove/Haptics/SGGrabState.cpp
@@ -0,0 +1,68 @@
+/**
+ * @file
+ *
+ * @author Mamadou Babaei
+ *
+ * @section LICENSE
+ *
+ * (The MIT License)
+ *
+ * Copyright (c) 2020 - 2026 SenseGlove
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * @section DESCRIPTION
+ *
+ *
+ */
+
+
+#include "SenseGlove/Haptics/SGGrabState.h"
+
+#include "GameFramework/Actor.h"
+
+#include "SenseGlove/Components/SGVirtualHandComponent.h"
+
+FSGGrabState::FSGGrabState()
+ : Hand(nullptr),
+ PreviousHandLocation(FVector::Zero()),
+ HandVelocityHistory{},
+ ActorThumbCanGrab(nullptr),
+ ActorIndexCanGrab(nullptr),
+ ActorMiddleCanGrab(nullptr),
+ GrabbedActor(nullptr)
+{
+}
+
+FSGGrabState::FSGGrabState(USGVirtualHandComponent* InHand,
+ const FVector& InPreviousHandLocation,
+ const TArray& InHandVelocityHistory,
+ AActor* InActorThumbCanGrab,
+ AActor* InActorIndexCanGrab,
+ AActor* InActorMiddleCanGrab,
+ AActor* InGrabbedActor)
+ : Hand(InHand),
+ PreviousHandLocation(InPreviousHandLocation),
+ HandVelocityHistory(InHandVelocityHistory),
+ ActorThumbCanGrab(InActorThumbCanGrab),
+ ActorIndexCanGrab(InActorIndexCanGrab),
+ ActorMiddleCanGrab(InActorMiddleCanGrab),
+ GrabbedActor(InGrabbedActor)
+{
+}
\ No newline at end of file
diff --git a/Plugins/SenseGlove/Source/SenseGlove/Private/SenseGlove/Haptics/SGTouchState.cpp b/Plugins/SenseGlove/Source/SenseGlove/Private/SenseGlove/Haptics/SGTouchState.cpp
new file mode 100644
index 0000000..d3ac11b
--- /dev/null
+++ b/Plugins/SenseGlove/Source/SenseGlove/Private/SenseGlove/Haptics/SGTouchState.cpp
@@ -0,0 +1,65 @@
+/**
+ * @file
+ *
+ * @author Mamadou Babaei
+ *
+ * @section LICENSE
+ *
+ * (The MIT License)
+ *
+ * Copyright (c) 2020 - 2026 SenseGlove
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * @section DESCRIPTION
+ *
+ *
+ */
+
+
+#include "SenseGlove/Haptics/SGTouchState.h"
+
+#include "GameFramework/Actor.h"
+
+#include "SenseGlove/Components/SGVirtualHandComponent.h"
+
+FSGTouchState::FSGTouchState()
+ : Hand(nullptr),
+ ActorThumbTouching(nullptr),
+ ActorIndexTouching(nullptr),
+ ActorMiddleTouching(nullptr),
+ ActorRingTouching(nullptr),
+ ActorPinkyTouching(nullptr)
+{
+}
+
+FSGTouchState::FSGTouchState(USGVirtualHandComponent* InHand,
+ AActor* InActorThumbTouching,
+ AActor* InActorIndexTouching,
+ AActor* InActorMiddleTouching,
+ AActor* InActorRingTouching,
+ AActor* InActorPinkyTouching)
+ : Hand(InHand),
+ ActorThumbTouching(InActorThumbTouching),
+ ActorIndexTouching(InActorIndexTouching),
+ ActorMiddleTouching(InActorMiddleTouching),
+ ActorRingTouching(InActorRingTouching),
+ ActorPinkyTouching(InActorPinkyTouching)
+{
+}
\ No newline at end of file
diff --git a/Plugins/SenseGlove/Source/SenseGlove/Private/SenseGloveModule.cpp b/Plugins/SenseGlove/Source/SenseGlove/Private/SenseGloveModule.cpp
new file mode 100644
index 0000000..566542a
--- /dev/null
+++ b/Plugins/SenseGlove/Source/SenseGlove/Private/SenseGloveModule.cpp
@@ -0,0 +1,221 @@
+/**
+ * @file
+ *
+ * @author Mamadou Babaei
+ *
+ * @section LICENSE
+ *
+ * (The MIT License)
+ *
+ * Copyright (c) 2020 - 2026 SenseGlove
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * @section DESCRIPTION
+ *
+ *
+ */
+
+
+#include "SenseGloveModule.h"
+
+#include "Engine/Engine.h"
+#include "GameMapsSettings.h"
+#include "Misc/CoreDelegates.h"
+
+#include "SGLog/SGLog.h"
+#include "SenseGlove/Engine/SGGameInstance.h"
+#include "SenseGlove/GameFramework/SGGameModeBase.h"
+#include "SenseGlove/GameFramework/SGGameUserSettings.h"
+#include "SGSettings/SGSettings.h"
+
+#define LOCTEXT_NAMESPACE "FSenseGloveModule"
+
+void FSenseGloveModule::StartupModule()
+{
+ SGLOG("Starting up the SenseGlove module...");
+
+ OnPostEngineInitHandle = FCoreDelegates::OnPostEngineInit.AddRaw(
+ this, &FSenseGloveModule::OnPostEngineInit);
+}
+
+void FSenseGloveModule::PreUnloadCallback()
+{
+ SGLOG("Unloading the SenseGlove module...");
+}
+
+void FSenseGloveModule::PostLoadCallback()
+{
+ SGLOG("Loading of SenseGlove module has succeeded!");
+}
+
+void FSenseGloveModule::ShutdownModule()
+{
+ SGLOG("Shutting down the SenseGlove module...");
+
+ FCoreDelegates::OnPostEngineInit.Remove(OnPostEngineInitHandle);
+}
+
+void FSenseGloveModule::OnPostEngineInit() const
+{
+ const FSGInitializationSettings& Settings{
+ USGSettings::GetInstance()->GetInitializationSettings()
+ };
+ if (!Settings.bValidateIfDefaultClassesAreSGCompliant)
+ {
+ SGLOG("Will not be checking whether the default classes are SenseGlove-compliant, or not since it's disabled!");
+ return;
+ }
+
+ SGLOG("Checking whether the default classes are SenseGlove-compliant, or not...");
+
+ SGLOG("Checking whether the GameMapsSettings is compliant with SenseGlove, or not...");
+
+ UGameMapsSettings* GameMapsSettings{UGameMapsSettings::GetGameMapsSettings()};
+ if (ensureAlwaysMsgf(GameMapsSettings, TEXT("Invalid GameMapsSettings!")))
+ {
+ /*************************************************************************************************************
+ * GameInstanceClass
+ *************************************************************************************************************/
+
+ {
+ SGLOG("Validating if the GameInstanceClass is a SenseGlove GameInstance...");
+
+ bool bValidGameInstance = false;
+ const UClass* CurrentGameInstanceClass{GameMapsSettings->GameInstanceClass.TryLoadClass()};
+ if (CurrentGameInstanceClass)
+ {
+ if (CurrentGameInstanceClass->IsChildOf(USGGameInstance::StaticClass()))
+ {
+ bValidGameInstance = true;
+ SGLOG("The GameInstanceClass is a SenseGlove GameInstance, or a subclass.");
+ }
+ else
+ {
+ SGLOG("The GameInstanceClass is not a SenseGlove GameInstance, or a subclass.");
+ }
+ }
+ else
+ {
+ SGLOG_ERROR("Failed to construct a valid GameInstance from the current settings!");
+ }
+
+ if (!bValidGameInstance)
+ {
+ SGLOG("Setting the SenseGlove GameInstance as the GameInstanceClass...");
+
+ static const FSoftClassPath GameInstanceClassPath{TEXT("/Script/SenseGlove.SGGameInstance")};
+ GameMapsSettings->GameInstanceClass = GameInstanceClassPath;
+
+ SGLOG("Successfully set the SenseGlove GameInstance as the GameInstanceClass!");
+ }
+ }
+
+ /*************************************************************************************************************
+ * DefaultGameMode
+ *************************************************************************************************************/
+
+ {
+ SGLOG("Validating if the DefaultGameMode is a SenseGlove GameMode...");
+
+ bool bValidDefaultGameMode = false;
+ const FSoftClassPath CurrentDefaultGameModeClassPath{GameMapsSettings->GetGlobalDefaultGameMode()};
+ const UClass* CurrentDefaultGameMode{CurrentDefaultGameModeClassPath.TryLoadClass()};
+ if (CurrentDefaultGameMode)
+ {
+ if (CurrentDefaultGameMode->IsChildOf(ASGGameModeBase::StaticClass()))
+ {
+ bValidDefaultGameMode = true;
+ SGLOG("The DefaultGameMode is a SenseGlove GameMode, or a subclass.");
+ }
+ else
+ {
+ SGLOG("The DefaultGameMode is not a SenseGlove GameMode, or a subclass.");
+ }
+ }
+ else
+ {
+ SGLOG_ERROR("Failed to construct a valid GameMode from the current settings!");
+ }
+
+ if (!bValidDefaultGameMode)
+ {
+ SGLOG("Setting the SenseGlove GameMode as the DefaultGameMode...");
+
+ static const FString DefaultGameMode{TEXT("/Script/SenseGlove.SGGameModeBase")};
+ GameMapsSettings->SetGlobalDefaultGameMode(DefaultGameMode);
+
+ SGLOG("Successfully set the SenseGlove GameMode as the DefaultGameMode!");
+ }
+ }
+
+ /*************************************************************************************************************
+ *
+ *************************************************************************************************************/
+ }
+ else
+ {
+ SGLOG_ERROR("Failed to check whether the GameMapsSettings is compliant with SenseGlove, or not!");
+ }
+
+ SGLOG("Checking whether the GameUserSettingsClass is compliant with SenseGlove, or not...");
+
+ if (ensureAlwaysMsgf(GEngine, TEXT("Invalid GEngine!")))
+ {
+ SGLOG("Validating if the GameUserSettingsClass is a SenseGlove GameUserSettings...");
+
+ bool bValidGameUserSettings = false;
+ const UClass* CurrentGameUserSettingsClass{GEngine->GameUserSettingsClassName.TryLoadClass()};
+ if (CurrentGameUserSettingsClass)
+ {
+ if (CurrentGameUserSettingsClass->IsChildOf(USGGameUserSettings::StaticClass()))
+ {
+ bValidGameUserSettings = true;
+ SGLOG("The GameUserSettingsClass is a SenseGlove GameUserSettings, or a subclass.");
+ }
+ else
+ {
+ SGLOG("The GameUserSettingsClass is not a SenseGlove GameUserSettings, or a subclass.");
+ }
+ }
+ else
+ {
+ SGLOG_ERROR("Failed to construct a valid GameUserSettings from the current settings!");
+ }
+
+ if (!bValidGameUserSettings)
+ {
+ SGLOG("Setting the SenseGlove GameUserSettings as the GameUserSettingsClass...");
+
+ static const FSoftClassPath GameUserSettingsClassPath{TEXT("/Script/SenseGlove.SGGameUserSettings")};
+ GEngine->GameUserSettingsClassName = GameUserSettingsClassPath;
+ GEngine->GameUserSettingsClass = USGGameUserSettings::StaticClass();
+ GEngine->GameUserSettings = NewObject();
+ GEngine->GetGameUserSettings()->LoadSettings(true);
+
+ SGLOG("Successfully set the SenseGlove GameInstance as the GameInstanceClass!");
+ }
+ }
+ else
+ {
+ SGLOG_ERROR("Failed to check whether the GameUserSettings is compliant with SenseGlove, or not!");
+ }
+}
+
+#undef LOCTEXT_NAMESPACE
\ No newline at end of file
diff --git a/Plugins/SenseGlove/Source/SenseGlove/Private/SenseGloveModule.h b/Plugins/SenseGlove/Source/SenseGlove/Private/SenseGloveModule.h
new file mode 100644
index 0000000..63c0e62
--- /dev/null
+++ b/Plugins/SenseGlove/Source/SenseGlove/Private/SenseGloveModule.h
@@ -0,0 +1,54 @@
+/**
+ * @file
+ *
+ * @author Mamadou Babaei
+ *
+ * @section LICENSE
+ *
+ * (The MIT License)
+ *
+ * Copyright (c) 2020 - 2026 SenseGlove
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * @section DESCRIPTION
+ *
+ *
+ */
+
+
+#pragma once
+
+#include "Delegates/IDelegateInstance.h"
+#include "Modules/ModuleInterface.h"
+
+class FSenseGloveModule : public IModuleInterface
+{
+private:
+ FDelegateHandle OnPostEngineInitHandle;
+
+public:
+ virtual void StartupModule() override;
+ virtual void PreUnloadCallback() override;
+ virtual void PostLoadCallback() override;
+ virtual void ShutdownModule() override;
+
+private:
+ void OnPostEngineInit() const;
+};
\ No newline at end of file
diff --git a/Plugins/SenseGlove/Source/SenseGlove/Public/SenseGlove/Animation/SGVirtualHandAnimInstance.h b/Plugins/SenseGlove/Source/SenseGlove/Public/SenseGlove/Animation/SGVirtualHandAnimInstance.h
new file mode 100644
index 0000000..9438aa4
--- /dev/null
+++ b/Plugins/SenseGlove/Source/SenseGlove/Public/SenseGlove/Animation/SGVirtualHandAnimInstance.h
@@ -0,0 +1,51 @@
+/**
+ * @file
+ *
+ * @author Mamadou Babaei
+ *
+ * @section LICENSE
+ *
+ * (The MIT License)
+ *
+ * Copyright (c) 2020 - 2026 SenseGlove
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * @section DESCRIPTION
+ *
+ *
+ */
+
+
+#pragma once
+
+#include "Animation/AnimInstance.h"
+
+#include "SenseGlove/Animation/SGVirtualHandAnimInstanceProxy.h"
+
+#include "SGVirtualHandAnimInstance.generated.h"
+
+UCLASS(Transient, Blueprintable, BlueprintType, meta=(BlueprintThreadSafe), Within=SkeletalMeshComponent)
+class SENSEGLOVE_API USGVirtualHandAnimInstance : public UAnimInstance
+{
+ GENERATED_UCLASS_BODY()
+
+protected:
+ virtual FSGVirtualHandAnimInstanceProxy* CreateAnimInstanceProxy() override;
+};
\ No newline at end of file
diff --git a/Plugins/SenseGlove/Source/SenseGlove/Public/SenseGlove/Animation/SGVirtualHandAnimInstanceProxy.h b/Plugins/SenseGlove/Source/SenseGlove/Public/SenseGlove/Animation/SGVirtualHandAnimInstanceProxy.h
new file mode 100644
index 0000000..39afb44
--- /dev/null
+++ b/Plugins/SenseGlove/Source/SenseGlove/Public/SenseGlove/Animation/SGVirtualHandAnimInstanceProxy.h
@@ -0,0 +1,134 @@
+/**
+ * @file
+ *
+ * @author Mamadou Babaei
+ *
+ * @section LICENSE
+ *
+ * (The MIT License)
+ *
+ * Copyright (c) 2020 - 2026 SenseGlove
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * @section DESCRIPTION
+ *
+ *
+ */
+
+
+#pragma once
+
+#include "Animation/AnimInstanceProxy.h"
+#include "Containers/Array.h"
+#include "HeadMountedDisplayTypes.h"
+#include "Math/Quat.h"
+#include "Math/Transform.h"
+#include "Runtime/Launch/Resources/Version.h"
+#include "Templates/UniquePtr.h"
+#include "UObject/NameTypes.h"
+
+#include "SGVirtualHandAnimInstanceProxy.generated.h"
+
+class USkeletalMesh;
+class USkeleton;
+class USGVirtualHandComponent;
+
+USTRUCT()
+struct SENSEGLOVE_API FSGVirtualHandAnimInstanceProxy : public FAnimInstanceProxy
+{
+ GENERATED_USTRUCT_BODY()
+
+private:
+ UPROPERTY(Transient)
+ USGVirtualHandComponent* VirtualHand;
+
+ UPROPERTY(Transient)
+ USkeletalMesh* HandMeshAsset;
+
+ UPROPERTY(Transient)
+ USkeleton* HandSkeleton;
+
+ bool bVirtualHandVisible;
+ bool bMarkRenderStateDirty;
+
+ FXRHandTrackingState HandTrackingState;
+
+ TMap::SizeType, FName> BoneNamesMap;
+
+ FTransform AnimationBoneCorrectionOffsetTransform;
+ bool bShouldAnimationApplyBoneLocation;
+
+public:
+ FSGVirtualHandAnimInstanceProxy();
+ FSGVirtualHandAnimInstanceProxy(UAnimInstance* Instance);
+
+protected:
+ FORCEINLINE bool IsVirtualHandVisible() const
+ {
+ return bVirtualHandVisible;
+ }
+
+ FORCEINLINE USGVirtualHandComponent* GetVirtualHand() const
+ {
+ return VirtualHand;
+ }
+
+ FORCEINLINE USkeletalMesh* GetHandMeshAsset() const
+ {
+ return HandMeshAsset;
+ }
+
+ FORCEINLINE USkeleton* GetHandSkeleton() const
+ {
+ return HandSkeleton;
+ }
+
+ FORCEINLINE bool IsRenderStateDirty() const
+ {
+ return bMarkRenderStateDirty;
+ }
+
+ FORCEINLINE const FXRHandTrackingState& GetHandTrackingState() const
+ {
+ return HandTrackingState;
+ }
+
+ FORCEINLINE const TMap::SizeType, FName>& GetBoneNamesMap() const
+ {
+ return BoneNamesMap;
+ }
+
+ FORCEINLINE const FTransform& GetAnimationBoneCorrectionOffsetTransform() const
+ {
+ return AnimationBoneCorrectionOffsetTransform;
+ }
+
+ FORCEINLINE bool ShouldAnimationApplyBoneLocation() const
+ {
+ return bShouldAnimationApplyBoneLocation;
+ }
+
+protected:
+ virtual void PreEvaluateAnimation(UAnimInstance* InAnimInstance) override;
+
+ virtual bool Evaluate(FPoseContext& Output) override;
+
+ virtual void PostEvaluate(UAnimInstance* InAnimInstance) override;
+};
\ No newline at end of file
diff --git a/Plugins/SenseGlove/Source/SenseGlove/Public/SenseGlove/Components/SGGrabComponent.h b/Plugins/SenseGlove/Source/SenseGlove/Public/SenseGlove/Components/SGGrabComponent.h
new file mode 100644
index 0000000..56ffeff
--- /dev/null
+++ b/Plugins/SenseGlove/Source/SenseGlove/Public/SenseGlove/Components/SGGrabComponent.h
@@ -0,0 +1,205 @@
+/**
+ * @file
+ *
+ * @author Mamadou Babaei
+ *
+ * @section LICENSE
+ *
+ * (The MIT License)
+ *
+ * Copyright (c) 2020 - 2026 SenseGlove
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * @section DESCRIPTION
+ *
+ *
+ */
+
+
+#pragma once
+
+#include "Components/SceneComponent.h"
+#include "Engine/EngineTypes.h"
+#include "Templates/UniquePtr.h"
+#include "UObject/NameTypes.h"
+
+#include "SGGrabComponent.generated.h"
+
+class AActor;
+
+UCLASS(Blueprintable, BlueprintType, ClassGroup=(SenseGlove), meta=(BlueprintSpawnableComponent))
+class SENSEGLOVE_API USGGrabComponent : public USceneComponent
+{
+ GENERATED_UCLASS_BODY()
+
+public:
+ static USGGrabComponent* GetGrabComponent(const AActor* Actor);
+
+ static bool IsGrabbable(const AActor* Actor);
+
+private:
+ UPROPERTY(EditAnywhere, Category="SenseGlove", meta=(AllowPrivateAccess="false"))
+ EAttachmentRule AttachmentLocationRule;
+
+ UPROPERTY(EditAnywhere, Category="SenseGlove", meta=(AllowPrivateAccess="false"))
+ EAttachmentRule AttachmentRotationRule;
+
+ UPROPERTY(EditAnywhere, Category="SenseGlove", meta=(AllowPrivateAccess="false"))
+ EAttachmentRule AttachmentScaleRule;
+
+ UPROPERTY(EditAnywhere, Category="SenseGlove", meta=(AllowPrivateAccess="false"))
+ bool bAttachmentWeldSimulatedBodies;
+
+ UPROPERTY(EditAnywhere, Category="SenseGlove", meta=(AllowPrivateAccess="false"))
+ FName AttachmentSocketName;
+
+ UPROPERTY(EditAnywhere, Category="SenseGlove", meta=(AllowPrivateAccess="false"))
+ EDetachmentRule DetachmentLocationRule;
+
+ UPROPERTY(EditAnywhere, Category="SenseGlove", meta=(AllowPrivateAccess="false"))
+ EDetachmentRule DetachmentRotationRule;
+
+ UPROPERTY(EditAnywhere, Category="SenseGlove", meta=(AllowPrivateAccess="false"))
+ EDetachmentRule DetachmentScaleRule;
+
+ UPROPERTY(EditAnywhere, Category="SenseGlove", meta=(AllowPrivateAccess="false"))
+ bool bDetachmentCallModify;
+
+ UPROPERTY(EditAnywhere, Category="SenseGlove", meta=(AllowPrivateAccess="false"))
+ bool bAffectPhysicsState;
+
+private:
+ struct FImpl;
+
+ struct FImplDeleter
+ {
+ void operator()(const FImpl* P) const;
+ };
+
+ TUniquePtr Pimpl;
+ FImplDeleter PimplDeleter;
+
+public:
+ FORCEINLINE EAttachmentRule GetAttachmentLocationRule() const
+ {
+ return AttachmentLocationRule;
+ }
+
+ FORCEINLINE void SetAttachmentLocationRule(const EAttachmentRule InAttachmentLocationRule)
+ {
+ AttachmentLocationRule = InAttachmentLocationRule;
+ }
+
+ FORCEINLINE EAttachmentRule GetAttachmentRotationRule() const
+ {
+ return AttachmentRotationRule;
+ }
+
+ FORCEINLINE void SetAttachmentRotationRule(const EAttachmentRule InAttachmentRotationRule)
+ {
+ AttachmentRotationRule = InAttachmentRotationRule;
+ }
+
+ FORCEINLINE EAttachmentRule GetAttachmentScaleRule() const
+ {
+ return AttachmentScaleRule;
+ }
+
+ FORCEINLINE void SetAttachmentScaleRule(const EAttachmentRule InAttachmentScaleRule)
+ {
+ AttachmentScaleRule = InAttachmentScaleRule;
+ }
+
+ FORCEINLINE bool GetAttachmentWeldSimulatedBodies() const
+ {
+ return bAttachmentWeldSimulatedBodies;
+ }
+
+ FORCEINLINE void SetAttachmentWeldSimulatedBodies(const bool bInAttachmentWeldSimulatedBodies)
+ {
+ bAttachmentWeldSimulatedBodies = bInAttachmentWeldSimulatedBodies;
+ }
+
+ FORCEINLINE const FName& GetAttachmentSocketName() const
+ {
+ return AttachmentSocketName;
+ }
+
+ FORCEINLINE void SetAttachmentSocketName(const FName& InAttachmentSocketName)
+ {
+ AttachmentSocketName = InAttachmentSocketName;
+ }
+
+ FORCEINLINE EDetachmentRule GetDetachmentLocationRule() const
+ {
+ return DetachmentLocationRule;
+ }
+
+ FORCEINLINE void SetDetachmentLocationRule(const EDetachmentRule InDetachmentLocationRule)
+ {
+ DetachmentLocationRule = InDetachmentLocationRule;
+ }
+
+ FORCEINLINE EDetachmentRule GetDetachmentRotationRule() const
+ {
+ return DetachmentRotationRule;
+ }
+
+ FORCEINLINE void SetDetachmentRotationRule(const EDetachmentRule InDetachmentRotationRule)
+ {
+ DetachmentRotationRule = InDetachmentRotationRule;
+ }
+
+ FORCEINLINE EDetachmentRule GetDetachmentScaleRule() const
+ {
+ return DetachmentScaleRule;
+ }
+
+ FORCEINLINE void SetDetachmentScaleRule(const EDetachmentRule InDetachmentScaleRule)
+ {
+ DetachmentScaleRule = InDetachmentScaleRule;
+ }
+
+ FORCEINLINE bool GetDetachmentInCallModify() const
+ {
+ return bDetachmentCallModify;
+ }
+
+ FORCEINLINE void SetDetachmentInCallModify(const bool bInDetachmentCallModify)
+ {
+ bDetachmentCallModify = bInDetachmentCallModify;
+ }
+
+ FORCEINLINE bool GetAffectPhysicsState() const
+ {
+ return bAffectPhysicsState;
+ }
+
+ FORCEINLINE void SetAffectPhysicsState(const bool bInAffectPhysicsState)
+ {
+ bAffectPhysicsState = bInAffectPhysicsState;
+ }
+
+public:
+ virtual void SimulatePhysics(bool bSimulatePhysics) const;
+
+public:
+ virtual void BeginPlay() override;
+};
\ No newline at end of file
diff --git a/Plugins/SenseGlove/Source/SenseGlove/Public/SenseGlove/Components/SGHandTrackerComponent.h b/Plugins/SenseGlove/Source/SenseGlove/Public/SenseGlove/Components/SGHandTrackerComponent.h
new file mode 100644
index 0000000..67a5d2b
--- /dev/null
+++ b/Plugins/SenseGlove/Source/SenseGlove/Public/SenseGlove/Components/SGHandTrackerComponent.h
@@ -0,0 +1,119 @@
+/**
+* @file
+ *
+ * @author Mamadou Babaei
+ *
+ * @section LICENSE
+ *
+ * (The MIT License)
+ *
+ * Copyright (c) 2020 - 2026 SenseGlove
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * @section DESCRIPTION
+ *
+ *
+ */
+
+
+#pragma once
+
+#include "Components/SceneComponent.h"
+#include "HeadMountedDisplayTypes.h"
+#include "Templates/UniquePtr.h"
+
+#include "SGSettings/SGVirtualHandSettings.h"
+
+#include "SGHandTrackerComponent.generated.h"
+
+class AActor;
+
+UCLASS(Blueprintable, BlueprintType, ClassGroup=(SenseGlove), meta=(BlueprintSpawnableComponent))
+class SENSEGLOVE_API USGHandTrackerComponent : public USceneComponent
+{
+ GENERATED_UCLASS_BODY()
+
+private:
+ /**
+ * Determines whether this component tracks the right or the left hand.
+ */
+ UPROPERTY(EditAnywhere, Category="SenseGlove", meta=(AllowPrivateAccess="false"))
+ bool bRight;
+
+ /**
+ * If enabled, visualizes the XR hand-tracking data using debug gizmos.
+ */
+ UPROPERTY(EditAnywhere, Category="SenseGlove", meta=(AllowPrivateAccess="false"))
+ bool bVisualize;
+
+ /**
+ * XR hand-tracking data will be visualized only if bVisualize is enabled.
+ */
+ UPROPERTY(EditAnywhere, Category="SenseGlove",
+ meta=(AllowPrivateAccess="false",
+ EditCondition="bVisualize", EditConditionHides))
+ FSGDebugGizmoSettings VisualizationSettings;
+
+private:
+ struct FImpl;
+
+ struct FImplDeleter
+ {
+ void operator()(const FImpl* P) const;
+ };
+
+ TUniquePtr Pimpl;
+ FImplDeleter PimplDeleter;
+
+public:
+ FORCEINLINE bool IsLeft() const
+ {
+ return !IsRight();
+ }
+
+ FORCEINLINE bool IsRight() const
+ {
+ return bRight;
+ }
+
+ void SetRight(const bool bInRight);
+
+ FORCEINLINE bool IsVisualized() const
+ {
+ return bVisualize;
+ }
+
+ FORCEINLINE void SetVisualize(const bool bInVisualize)
+ {
+ bVisualize = bInVisualize;
+ }
+
+public:
+ virtual void InitializeComponent() override;
+
+ virtual void BeginPlay() override;
+
+ virtual void TickComponent(float DeltaTime,
+ enum ELevelTick TickType,
+ FActorComponentTickFunction* ThisTickFunction) override;
+
+public:
+ const FXRHandTrackingState& GetHandTrackingState() const;
+};
\ No newline at end of file
diff --git a/Plugins/SenseGlove/Source/SenseGlove/Public/SenseGlove/Components/SGHapticsComponent.h b/Plugins/SenseGlove/Source/SenseGlove/Public/SenseGlove/Components/SGHapticsComponent.h
new file mode 100644
index 0000000..9f5855c
--- /dev/null
+++ b/Plugins/SenseGlove/Source/SenseGlove/Public/SenseGlove/Components/SGHapticsComponent.h
@@ -0,0 +1,215 @@
+/**
+ * @file
+ *
+ * @author Mamadou Babaei
+ *
+ * @section LICENSE
+ *
+ * (The MIT License)
+ *
+ * Copyright (c) 2020 - 2026 SenseGlove
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * @section DESCRIPTION
+ *
+ *
+ */
+
+
+#pragma once
+
+#include "Components/SceneComponent.h"
+#include "Templates/UniquePtr.h"
+
+#include "SGTypes/SGCoreTypes.h"
+
+#include "SGHapticsComponent.generated.h"
+
+class AActor;
+
+class USGCustomWaveform;
+
+UCLASS(Blueprintable, BlueprintType, ClassGroup=(SenseGlove), meta=(BlueprintSpawnableComponent))
+class SENSEGLOVE_API USGHapticsComponent : public USceneComponent
+{
+ GENERATED_UCLASS_BODY()
+
+private:
+ /**
+ * Determines whether this component operates on the right or the left hand.
+ */
+ UPROPERTY(EditAnywhere, Category="SenseGlove", meta=(AllowPrivateAccess="false"))
+ bool bRight;
+
+ /**
+ * If enabled, forces all haptics to stop automatically on the UninitializeComponent or EndPlay events, or when the
+ * handedness changes. This is useful for situations where the simulation has ended, but ongoing haptic feedback
+ * might remain active on the glove indefinitely. By default, this setting is enabled.
+ */
+ UPROPERTY(EditAnywhere, Category="SenseGlove", meta=(AllowPrivateAccess="false"))
+ bool bAutoStopAllHaptics;
+
+private:
+ struct FImpl;
+
+ struct FImplDeleter
+ {
+ void operator()(const FImpl* P) const;
+ };
+
+ TUniquePtr Pimpl;
+ FImplDeleter PimplDeleter;
+
+public:
+ FORCEINLINE bool IsLeft() const
+ {
+ return !IsRight();
+ }
+
+ FORCEINLINE bool IsRight() const
+ {
+ return bRight;
+ }
+
+ void SetRight(const bool bInRight);
+
+ FORCEINLINE bool AutoStopsAllHaptics() const
+ {
+ return bAutoStopAllHaptics;
+ }
+
+ void SetAutoStopAllHaptics(const bool bInAutoStopAllHaptics)
+ {
+ bAutoStopAllHaptics = bInAutoStopAllHaptics;
+ }
+
+public:
+ virtual void UninitializeComponent() override;
+
+ virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override;
+
+public:
+ /**
+ * Stops all Haptic effects if any are currently playing. Useful at the end of simulations or when restarting the
+ * level.
+ */
+ void StopHaptics();
+
+ /**
+ * Stops only vibrations.
+ */
+ void StopVibrations();
+
+ /**
+ * Take all active commands in the device queue, compile them into one and send them to the device.
+ *
+ * @return Returns true if the message was successfully sent to SenseCom.
+ */
+ bool SendHaptics();
+
+ /**
+ * Returns true if the haptic glove supports vibration feedback at the specified location.
+ *
+ * @param AtLocation
+ */
+ bool SupportsCustomWaveform(ESGHapticLocation AtLocation) const;
+
+ /**
+ * Sends a custom waveform to the location specified, provided that the glove has a motor there, and can support
+ * custom waveforms.
+ *
+ * @param OutWaveform
+ * @param Location
+ */
+ bool SendCustomWaveform(USGCustomWaveform* OutWaveform, ESGHapticLocation Location);
+
+ /**
+ * Sends a custom waveform to the location specified, provided that the glove has a motor there, and can support
+ * custom waveforms.
+ *
+ * @param Amplitude
+ * @param Duration
+ * @param Location
+ */
+ bool SendCustomWaveform(float Amplitude, float Duration, ESGHapticLocation Location);
+
+ /**
+ * Sends a custom waveform to the location specified, provided that the glove has a motor there, and can support
+ * custom waveforms.
+ *
+ * @param Amplitude
+ * @param Duration
+ * @param Frequency
+ * @param Location
+ */
+ bool SendCustomWaveform(float Amplitude, float Duration, float Frequency, ESGHapticLocation Location);
+
+ /**
+ * Queue a list of force-feedback levels, between 0.0f and 1.0f. Your list should be sorted from thumb to pinky.
+ *
+ * @param Levels01 Array containing the Force-Feedback levels, from 0.0f (no FFB) to 1.0f. A value < 0.0f will be
+ * ignored.
+ *
+ * @remarks Devices that 'only' have on/off FFB will treat any value > 0.0 as 1.0.
+ */
+ bool QueueForceFeedbackLevels(const TArray& Levels01);
+
+ /**
+ * Set the Force-Feedback value of a particular finger to a specific level
+ *
+ * @param Level01 Value will be clamped between [0...1], where 0.0f means no Force-Feedback, and 1.0 means full
+ * force-feedback.
+ * @param Finger The finger to which to send the command.
+ */
+ bool QueueForceFeedbackLevel(int32 Finger, float Level01);
+
+ /**
+ * Queue a list of vibration levels, between 0.0 and 1.0. Your list should be sorted from thumb to pinky.
+ *
+ * @param Levels01 Array containing the vibration levels, from 0.0 (no vibration) to 1.0. A value < 0.0f will be
+ * ignored.
+ *
+ * @remarks Devices that 'only' have on/off FFB will treat any value > 0.0 as 1.0.
+ */
+ bool QueueVibroLevels(const TArray& Levels01);
+
+ /**
+ * Queue a command to set the (continuous) vibration level at a specific location to a set amplitude.
+ *
+ * @param Location
+ * @param Level01 Value will be clamped between [0...1], where 0.0f means no vibration, and 1.0 means full
+ * vibration.
+ */
+ bool QueueVibroLevel(ESGHapticLocation Location, float Level01);
+
+ /**
+ * Returns true if the chosen glove supports active contact feedback on the Wrist.
+ */
+ bool SupportsWristSqueeze() const;
+
+ /**
+ * Queue a command to set the amount of squeeze level (a.k.a. squeeze-feedback) to the desired level
+ * (0 = no squeeze, 1 = full squeeze) on the wrist, and optionally send it right away.
+ *
+ * @param SqueezeLevel01
+ * @param bSendImmediate
+ */
+ bool QueueWristSqueeze(float SqueezeLevel01, bool bSendImmediate);
+};
\ No newline at end of file
diff --git a/Plugins/SenseGlove/Source/SenseGlove/Public/SenseGlove/Components/SGTouchComponent.h b/Plugins/SenseGlove/Source/SenseGlove/Public/SenseGlove/Components/SGTouchComponent.h
new file mode 100644
index 0000000..ffdb887
--- /dev/null
+++ b/Plugins/SenseGlove/Source/SenseGlove/Public/SenseGlove/Components/SGTouchComponent.h
@@ -0,0 +1,123 @@
+/**
+ * @file
+ *
+ * @author Mamadou Babaei
+ *
+ * @section LICENSE
+ *
+ * (The MIT License)
+ *
+ * Copyright (c) 2020 - 2026 SenseGlove
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * @section DESCRIPTION
+ *
+ *
+ */
+
+
+#pragma once
+
+#include "Components/SceneComponent.h"
+#include "Templates/UniquePtr.h"
+
+#include "SGTouchComponent.generated.h"
+
+class AActor;
+
+UCLASS(Blueprintable, BlueprintType, ClassGroup=(SenseGlove), meta=(BlueprintSpawnableComponent))
+class SENSEGLOVE_API USGTouchComponent : public USceneComponent
+{
+ GENERATED_UCLASS_BODY()
+
+public:
+ static USGTouchComponent* GetTouchComponent(const AActor* Actor);
+
+ static bool IsTouchable(const AActor* Actor);
+
+private:
+ UPROPERTY(EditAnywhere, Category="SenseGlove",
+ meta=(AllowPrivateAccess="false", ClampMin = "0.0", ClampMax = "1.0", UIMin = "0.0", UIMax = "1.0"))
+ float ForceFeedbackLevel;
+
+ UPROPERTY(EditAnywhere, Category="SenseGlove",
+ meta=(AllowPrivateAccess="false", ClampMin = "0.0", ClampMax = "1.0", UIMin = "0.0", UIMax = "1.0"))
+ float VibrotactileAmplitude;
+
+ UPROPERTY(EditAnywhere, Category="SenseGlove",
+ meta=(AllowPrivateAccess="false", ClampMin = "0.0", ClampMax = "1.0", UIMin = "0.0", UIMax = "1.0"))
+ float VibrotactileDuration;
+
+ UPROPERTY(EditAnywhere, Category="SenseGlove",
+ meta=(AllowPrivateAccess="false", ClampMin = "10.0", ClampMax = "500.0", UIMin = "10.0", UIMax = "500.0"))
+ float VibrotactileFrequency;
+
+private:
+ struct FImpl;
+
+ struct FImplDeleter
+ {
+ void operator()(const FImpl* P) const;
+ };
+
+ TUniquePtr Pimpl;
+ FImplDeleter PimplDeleter;
+
+public:
+ FORCEINLINE float GetForceFeedbackLevel() const
+ {
+ return ForceFeedbackLevel;
+ }
+
+ FORCEINLINE void SetForceFeedbackLevel(const float Level)
+ {
+ ForceFeedbackLevel = Level;
+ }
+
+ FORCEINLINE float GetVibrotactileAmplitude() const
+ {
+ return VibrotactileAmplitude;
+ }
+
+ FORCEINLINE void SetVibrotactileAmplitude(const float Amplitude)
+ {
+ VibrotactileAmplitude = Amplitude;
+ }
+
+ FORCEINLINE float GetVibrotactileDuration() const
+ {
+ return VibrotactileDuration;
+ }
+
+ FORCEINLINE void SetVibrotactileDuration(const float Duration)
+ {
+ VibrotactileDuration = Duration;
+ }
+
+ FORCEINLINE float GetVibrotactileFrequency() const
+ {
+ return VibrotactileFrequency;
+ }
+
+ FORCEINLINE void SetVibrotactileFrequency(const float Frequency)
+ {
+ VibrotactileFrequency = Frequency;
+ }
+};
\ No newline at end of file
diff --git a/Plugins/SenseGlove/Source/SenseGlove/Public/SenseGlove/Components/SGVirtualHandComponent.h b/Plugins/SenseGlove/Source/SenseGlove/Public/SenseGlove/Components/SGVirtualHandComponent.h
new file mode 100644
index 0000000..93e5465
--- /dev/null
+++ b/Plugins/SenseGlove/Source/SenseGlove/Public/SenseGlove/Components/SGVirtualHandComponent.h
@@ -0,0 +1,193 @@
+/**
+ * @file
+ *
+ * @author Mamadou Babaei
+ *
+ * @section LICENSE
+ *
+ * (The MIT License)
+ *
+ * Copyright (c) 2020 - 2026 SenseGlove
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * @section DESCRIPTION
+ *
+ *
+ */
+
+
+#pragma once
+
+#include "Containers/Array.h"
+#include "Components/SkeletalMeshComponent.h"
+#include "Containers/Map.h"
+#include "GameFramework/Actor.h"
+#include "HeadMountedDisplayTypes.h"
+#include "Math/Rotator.h"
+#include "Math/Transform.h"
+#include "Misc/AssertionMacros.h"
+#include "ReferenceSkeleton.h"
+#include "Runtime/Launch/Resources/Version.h"
+#include "Templates/UniquePtr.h"
+#include "UObject/NameTypes.h"
+
+#include "SGSettings/SGVirtualHandSettings.h"
+#include "SGSettings/SGVirtualHandSettingsOverrides.h"
+
+#include "SGVirtualHandComponent.generated.h"
+
+class USphereComponent;
+class USkeletalMesh;
+
+class USGHapticGlove;
+class USGWristTrackerComponent;
+
+UCLASS(Config=Engine, Blueprintable, ClassGroup=(SenseGlove), hidecategories=(Object, "Mesh|SkeletalAsset"),
+ editinlinenew, meta=(BlueprintSpawnableComponent))
+class SENSEGLOVE_API USGVirtualHandComponent : public USkeletalMeshComponent
+{
+ GENERATED_UCLASS_BODY()
+
+public:
+ DECLARE_EVENT_OneParam(USGVirtualHandComponent, FHandVisibilityChangedEvent, bool bNewVisibiliy);
+
+ FHandVisibilityChangedEvent& OnHandVisibilityChanged()
+ {
+ return HandVisibilityChangedEvent;
+ };
+
+private:
+ FHandVisibilityChangedEvent HandVisibilityChangedEvent;
+
+public:
+ static const TArray& GetLeftHandBoneNames();
+ static const TArray& GetRightHandBoneNames();
+
+ static const FName& GetLeftHandBoneName(int32 Joint);
+ static const FName& GetRightHandBoneName(int32 Joint);
+
+private:
+ struct FImpl;
+
+ struct FImplDeleter
+ {
+ void operator()(const FImpl* P) const;
+ };
+
+ TUniquePtr Pimpl;
+ FImplDeleter PimplDeleter;
+
+private:
+ UPROPERTY(EditAnywhere, Category="SenseGlove", meta=(AllowPrivateAccess="false"))
+ bool bRight;
+
+ /**
+ * Overrides the plugin's virtual hand settings.
+ */
+ UPROPERTY(EditAnywhere, Category = "SenseGlove", meta=(AllowPrivateAccess="false"))
+ FSGVirtualHandSettingsOverrides VirtualHandSettingsOverrides;
+
+private:
+ UPROPERTY(Transient)
+ USGWristTrackerComponent* WristTracker;
+
+protected:
+ UFUNCTION(BlueprintNativeEvent, Category="SenseGlove | Components | Virtual Hand Component")
+ void OnHandVisibilityChanged(bool bNewVisibility);
+
+public:
+ FORCEINLINE bool IsLeft() const
+ {
+ return !IsRight();
+ }
+
+ FORCEINLINE bool IsRight() const
+ {
+ return bRight;
+ }
+
+ void SetRight(const bool bInRight);
+
+ FORCEINLINE bool OverridesPluginVirtualHandSettings() const
+ {
+ return VirtualHandSettingsOverrides.bOverridePluginSettings;
+ }
+
+ FORCEINLINE const FSGVirtualHandSettingsOverrides& GetVirtualHandSettingsOverrides() const
+ {
+ return VirtualHandSettingsOverrides;
+ }
+
+ void SetVirtualHandSettingsOverrides(const FSGVirtualHandSettingsOverrides& InVirtualHandSettingsOverrides);
+
+ FSGVirtualHandSettings GetVirtualHandSettings() const;
+
+public:
+ FORCEINLINE void SetWristTracker(USGWristTrackerComponent* InWristTracker)
+ {
+ WristTracker = InWristTracker;
+ }
+
+public:
+ virtual void SetAnimInstanceClass(UClass* NewClass) override;
+
+ virtual void PostInitProperties() override;
+
+#if WITH_EDITOR
+ virtual void PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) override;
+#endif /* WITH_EDITOR */
+
+ virtual void InitializeComponent() override;
+
+ virtual void UninitializeComponent() override;
+
+ virtual void BeginPlay() override;
+
+ virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override;
+
+ virtual void TickComponent(float DeltaTime,
+ enum ELevelTick TickType,
+ FActorComponentTickFunction* ThisTickFunction) override;
+
+ virtual void SetSkeletalMesh(USkeletalMesh* NewMesh, bool bReinitPose = true) override;
+
+protected:
+ virtual void EditorTick(float DeltaTime,
+ enum ELevelTick TickType,
+ FActorComponentTickFunction* ThisTickFunction);
+
+public:
+ bool IsGloveConnected() const;
+ USGHapticGlove* GetConnectedGlove() const;
+
+ const FXRHandTrackingState& GetHandTrackingState() const;
+
+public:
+ const TArray& GetHandBoneNames() const;
+ const FName& GetHandBoneName(int32 Joint) const;
+
+ const FTransform& GetHandBoneRefTransform(const FName& BoneName) const;
+ const FTransform& GetHandBoneRefTransform(int32 Joint) const;
+
+ const FTransform& GetHandRootBoneRefTransform() const;
+
+ FRotator GetHandBoneRefRotation(const FName& BoneName) const;
+ FRotator GetHandBoneRefRotation(int32 Joint) const;
+};
diff --git a/Plugins/SenseGlove/Source/SenseGlove/Public/SenseGlove/Components/SGWristTrackerComponent.h b/Plugins/SenseGlove/Source/SenseGlove/Public/SenseGlove/Components/SGWristTrackerComponent.h
new file mode 100644
index 0000000..713d9b9
--- /dev/null
+++ b/Plugins/SenseGlove/Source/SenseGlove/Public/SenseGlove/Components/SGWristTrackerComponent.h
@@ -0,0 +1,169 @@
+/**
+ * @file
+ *
+ * @author Mamadou Babaei
+ *
+ * @section LICENSE
+ *
+ * (The MIT License)
+ *
+ * Copyright (c) 2020 - 2026 SenseGlove
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * @section DESCRIPTION
+ *
+ *
+ */
+
+
+#pragma once
+
+#include "GameFramework/Actor.h"
+#include "HeadMountedDisplayTypes.h"
+#include "Math/Rotator.h"
+#include "Math/Vector.h"
+#include "MotionControllerComponent.h"
+#include "Runtime/Launch/Resources/Version.h"
+#include "Templates/UniquePtr.h"
+
+#include "SGSettings/SGWristTrackingSettings.h"
+#include "SGSettings/SGWristTrackingSettingsOverrides.h"
+
+#include "SGWristTrackerComponent.generated.h"
+
+UCLASS(Blueprintable, ClassGroup=(SenseGlove), meta = (BlueprintSpawnableComponent))
+class SENSEGLOVE_API USGWristTrackerComponent : public UMotionControllerComponent
+{
+ GENERATED_UCLASS_BODY()
+
+public:
+ DECLARE_EVENT_OneParam(USGWristTrackerComponent, FWristLocationChangedEvent, const FVector&)
+
+ FWristLocationChangedEvent& OnWristLocationChanged()
+ {
+ return WristLocationChangedEvent;
+ };
+
+ DECLARE_EVENT_OneParam(USGWristTrackerComponent, FWristRotationChangedEvent, const FRotator&)
+
+ FWristRotationChangedEvent& OnWristRotationChanged()
+ {
+ return WristRotationChangedEvent;
+ };
+
+ DECLARE_EVENT_TwoParams(USGWristTrackerComponent, FWristLocationAndRotationChangedEvent,
+ const FVector&, const FRotator&)
+
+ FWristLocationAndRotationChangedEvent& OnWristLocationAndRotationChanged()
+ {
+ return WristLocationAndRotationChangedEvent;
+ };
+
+private:
+ FWristLocationChangedEvent WristLocationChangedEvent;
+ FWristRotationChangedEvent WristRotationChangedEvent;
+ FWristLocationAndRotationChangedEvent WristLocationAndRotationChangedEvent;
+
+private:
+ struct FImpl;
+
+ struct FImplDeleter
+ {
+ void operator()(const FImpl* P) const;
+ };
+
+ TUniquePtr Pimpl;
+ FImplDeleter PimplDeleter;
+
+private:
+ UPROPERTY(EditAnywhere, Category="SenseGlove", meta=(AllowPrivateAccess="false"))
+ bool bRight;
+
+ /**
+ * Overrides the plugin's wrist tracking settings.
+ */
+ UPROPERTY(EditAnywhere, Category = "SenseGlove", meta=(AllowPrivateAccess="false"))
+ FSGWristTrackingSettingsOverrides WristTrackingSettingsOverrides;
+
+private:
+ UPROPERTY(Transient)
+ FVector WristLocation;
+
+ UPROPERTY(Transient)
+ FRotator WristRotation;
+
+public:
+ FORCEINLINE bool IsLeft() const
+ {
+ return !IsRight();
+ }
+
+ FORCEINLINE bool IsRight() const
+ {
+ return bRight;
+ }
+
+ void SetRight(const bool bInRight);
+
+ FORCEINLINE bool OverridesPluginWristTrackingSettings() const
+ {
+ return WristTrackingSettingsOverrides.bOverridePluginSettings;
+ }
+
+ FORCEINLINE const FSGWristTrackingSettingsOverrides& GetWristTrackingSettingsOverrides() const
+ {
+ return WristTrackingSettingsOverrides;
+ }
+
+ void SetWristTrackingSettingsOverrides(const FSGWristTrackingSettingsOverrides& InWristTrackingSettingsOverrides);
+
+ FSGWristTrackingSettings GetWristTrackingSettings() const;
+
+ FORCEINLINE const FVector& GetWristLocation() const
+ {
+ return WristLocation;
+ }
+
+ FORCEINLINE const FRotator& GetWristRotation() const
+ {
+ return WristRotation;
+ }
+
+public:
+#if WITH_EDITOR
+ virtual void PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) override;
+#endif /* WITH_EDITOR */
+
+ virtual void InitializeComponent() override;
+
+ virtual void BeginPlay() override;
+
+ virtual void TickComponent(float DeltaTime,
+ enum ELevelTick TickType,
+ FActorComponentTickFunction* ThisTickFunction) override;
+
+protected:
+ virtual void EditorTick(float DeltaTime,
+ enum ELevelTick TickType,
+ FActorComponentTickFunction* ThisTickFunction);
+
+public:
+ const FXRHandTrackingState& GetHandTrackingState() const;
+};
\ No newline at end of file
diff --git a/Plugins/SenseGlove/Source/SenseGlove/Public/SenseGlove/Engine/SGGameInstance.h b/Plugins/SenseGlove/Source/SenseGlove/Public/SenseGlove/Engine/SGGameInstance.h
new file mode 100644
index 0000000..fde4966
--- /dev/null
+++ b/Plugins/SenseGlove/Source/SenseGlove/Public/SenseGlove/Engine/SGGameInstance.h
@@ -0,0 +1,59 @@
+/**
+ * @file
+ *
+ * @author Mamadou Babaei
+ *
+ * @section LICENSE
+ *
+ * (The MIT License)
+ *
+ * Copyright (c) 2020 - 2026 SenseGlove
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * @section DESCRIPTION
+ *
+ *
+ */
+
+
+#pragma once
+
+#include "Engine/GameInstance.h"
+
+#include "SGTypes/SGTypes.h"
+
+#include "SGGameInstance.generated.h"
+
+UCLASS(config=Game, Transient, BlueprintType, Blueprintable)
+class SENSEGLOVE_API USGGameInstance : public UGameInstance
+{
+ GENERATED_UCLASS_BODY()
+
+public:
+ UFUNCTION(Exec, Category = "Console Command")
+ static void SG_GetEngineScalabilitySettings();
+
+ UFUNCTION(Exec, Category = "Console Command")
+ static void SG_SetEngineScalabilitySettings(ESGEngineScalabilitySettings Scalability);
+
+public:
+ virtual void Init() override;
+ virtual void Shutdown() override;
+};
\ No newline at end of file
diff --git a/Plugins/SenseGlove/Source/SenseGlove/Public/SenseGlove/GameFramework/SGGameModeBase.h b/Plugins/SenseGlove/Source/SenseGlove/Public/SenseGlove/GameFramework/SGGameModeBase.h
new file mode 100644
index 0000000..c8d61b4
--- /dev/null
+++ b/Plugins/SenseGlove/Source/SenseGlove/Public/SenseGlove/GameFramework/SGGameModeBase.h
@@ -0,0 +1,46 @@
+/**
+ * @file
+ *
+ * @author Mamadou Babaei
+ *
+ * @section LICENSE
+ *
+ * (The MIT License)
+ *
+ * Copyright (c) 2020 - 2026 SenseGlove
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * @section DESCRIPTION
+ *
+ *
+ */
+
+
+#pragma once
+
+#include "GameFramework/GameModeBase.h"
+
+#include "SGGameModeBase.generated.h"
+
+UCLASS(Config=Game, NotPlaceable, BlueprintType, Blueprintable, Transient)
+class SENSEGLOVE_API ASGGameModeBase : public AGameModeBase
+{
+ GENERATED_UCLASS_BODY()
+};
\ No newline at end of file
diff --git a/Plugins/SenseGlove/Source/SenseGlove/Public/SenseGlove/GameFramework/SGGameUserSettings.h b/Plugins/SenseGlove/Source/SenseGlove/Public/SenseGlove/GameFramework/SGGameUserSettings.h
new file mode 100644
index 0000000..6a035a1
--- /dev/null
+++ b/Plugins/SenseGlove/Source/SenseGlove/Public/SenseGlove/GameFramework/SGGameUserSettings.h
@@ -0,0 +1,67 @@
+/**
+ * @file
+ *
+ * @author Mamadou Babaei
+ *
+ * @section LICENSE
+ *
+ * (The MIT License)
+ *
+ * Copyright (c) 2020 - 2026 SenseGlove
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * @section DESCRIPTION
+ *
+ *
+ */
+
+
+#pragma once
+
+#include "GameFramework/GameUserSettings.h"
+#include "Templates/UniquePtr.h"
+
+#include "SGTypes/SGTypes.h"
+
+#include "SGGameUserSettings.generated.h"
+
+UCLASS(config=GameUserSettings, configdonotcheckdefaults, BlueprintType, Blueprintable)
+class SENSEGLOVE_API USGGameUserSettings : public UGameUserSettings
+{
+ GENERATED_UCLASS_BODY()
+
+public:
+ static USGGameUserSettings* GetInstance();
+
+private:
+ struct FImpl;
+
+ struct FImplDeleter
+ {
+ void operator()(const FImpl* P) const;
+ };
+
+ TUniquePtr Pimpl;
+ FImplDeleter PimplDeleter;
+
+public:
+ ESGEngineScalabilitySettings GetEngineScalabilitySettings() const;
+ void SetEngineScalabilitySettings(ESGEngineScalabilitySettings Scalability);
+};
\ No newline at end of file
diff --git a/Plugins/SenseGlove/Source/SenseGlove/Public/SenseGlove/GameFramework/SGPawn.h b/Plugins/SenseGlove/Source/SenseGlove/Public/SenseGlove/GameFramework/SGPawn.h
new file mode 100644
index 0000000..6fe93db
--- /dev/null
+++ b/Plugins/SenseGlove/Source/SenseGlove/Public/SenseGlove/GameFramework/SGPawn.h
@@ -0,0 +1,750 @@
+/**
+ * @file
+ *
+ * @author Mamadou Babaei
+ *
+ * @section LICENSE
+ *
+ * (The MIT License)
+ *
+ * Copyright (c) 2020 - 2026 SenseGlove
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * @section DESCRIPTION
+ *
+ *
+ */
+
+
+#pragma once
+
+#include "Containers/Array.h"
+#include "GameFramework/Pawn.h"
+#include "Runtime/Launch/Resources/Version.h"
+#include "Templates/UniquePtr.h"
+
+#include "SenseGlove/Haptics/SGGrabState.h"
+#include "SenseGlove/Haptics/SGTouchState.h"
+
+#include "SGPawn.generated.h"
+
+class UCameraComponent;
+class UPrimitiveComponent;
+class USphereComponent;
+class UXRDeviceVisualizationComponent;
+
+class USGVirtualHandComponent;
+class USGWristTrackerComponent;
+
+UCLASS(Config=Game, BlueprintType, Blueprintable)
+class SENSEGLOVE_API ASGPawn : public APawn
+{
+ GENERATED_UCLASS_BODY()
+
+public:
+ DECLARE_EVENT_OneParam(ASGPawn, FGrabStateUpdatedEvent, const FSGGrabState& GrabState);
+
+ FGrabStateUpdatedEvent& OnGrabStateUpdated()
+ {
+ return GrabStateUpdatedEvent;
+ };
+
+ DECLARE_EVENT_OneParam(ASGPawn, FTouchStateUpdatedEvent, const FSGTouchState& TouchState)
+
+ FTouchStateUpdatedEvent& OnTouchStateUpdated()
+ {
+ return TouchStateUpdatedEvent;
+ };
+
+ DECLARE_EVENT_TwoParams(ASGPawn, FActorGrabbedEvent, const USGVirtualHandComponent* Hand, const AActor* Actor);
+
+ FActorGrabbedEvent& OnGrabbed()
+ {
+ return ActorGrabbedEvent;
+ };
+
+ DECLARE_EVENT_TwoParams(ASGPawn, FActorReleasedEvent, const USGVirtualHandComponent* Hand, const AActor* Actor);
+
+ FActorReleasedEvent& OnReleased()
+ {
+ return ActorReleasedEvent;
+ };
+
+ DECLARE_EVENT_TwoParams(ASGPawn, FActorBeginTouchEvent, const USGVirtualHandComponent* Hand, const AActor* Actor);
+
+ FActorBeginTouchEvent& OnActorBeginTouch()
+ {
+ return ActorBeginTouchEvent;
+ };
+
+ DECLARE_EVENT_TwoParams(ASGPawn, FActorEndTouchEvent, const USGVirtualHandComponent* Hand, const AActor* Actor);
+
+ FActorEndTouchEvent& OnActorEndTouch()
+ {
+ return ActorEndTouchEvent;
+ };
+
+private:
+ FGrabStateUpdatedEvent GrabStateUpdatedEvent;
+ FTouchStateUpdatedEvent TouchStateUpdatedEvent;
+ FActorGrabbedEvent ActorGrabbedEvent;
+ FActorReleasedEvent ActorReleasedEvent;
+ FActorBeginTouchEvent ActorBeginTouchEvent;
+ FActorEndTouchEvent ActorEndTouchEvent;
+
+private:
+ struct FImpl;
+
+ struct FImplDeleter
+ {
+ void operator()(const FImpl* P) const;
+ };
+
+ TUniquePtr Pimpl;
+ FImplDeleter PimplDeleter;
+
+private:
+ UPROPERTY(VisibleAnywhere, Category="SenseGlove", meta=(AllowPrivateAccess="false"))
+ USceneComponent* SceneRoot;
+
+ UPROPERTY(VisibleAnywhere, Category="SenseGlove", meta=(AllowPrivateAccess="false"))
+ UCameraComponent* Camera;
+
+ UPROPERTY(VisibleAnywhere, Category="SenseGlove", meta=(AllowPrivateAccess="false"))
+ USGWristTrackerComponent* WristTrackerLeft;
+
+ UPROPERTY(VisibleAnywhere, Category="SenseGlove", meta=(AllowPrivateAccess="false"))
+ UXRDeviceVisualizationComponent* ControllerVisualizerLeft;
+
+ UPROPERTY(VisibleAnywhere, Category="SenseGlove", meta=(AllowPrivateAccess="false"))
+ USGVirtualHandComponent* HandLeft;
+
+ UPROPERTY(VisibleAnywhere, Category="SenseGlove", meta=(AllowPrivateAccess="false"))
+ USGVirtualHandComponent* RealHandLeft;
+
+ UPROPERTY(EditAnywhere, Category="SenseGlove", meta=(AllowPrivateAccess="false"))
+ USphereComponent* LeftThumbFingertipGrabCollider;
+
+ UPROPERTY(EditAnywhere, Category="SenseGlove", meta=(AllowPrivateAccess="false"))
+ USphereComponent* LeftIndexFingertipGrabCollider;
+
+ UPROPERTY(EditAnywhere, Category="SenseGlove", meta=(AllowPrivateAccess="false"))
+ USphereComponent* LeftMiddleFingertipGrabCollider;
+
+ UPROPERTY(EditAnywhere, Category="SenseGlove", meta=(AllowPrivateAccess="false"))
+ USphereComponent* LeftThumbFingertipTouchCollider;
+
+ UPROPERTY(EditAnywhere, Category="SenseGlove", meta=(AllowPrivateAccess="false"))
+ USphereComponent* LeftIndexFingertipTouchCollider;
+
+ UPROPERTY(EditAnywhere, Category="SenseGlove", meta=(AllowPrivateAccess="false"))
+ USphereComponent* LeftMiddleFingertipTouchCollider;
+
+ UPROPERTY(EditAnywhere, Category="SenseGlove", meta=(AllowPrivateAccess="false"))
+ USphereComponent* LeftRingFingertipTouchCollider;
+
+ UPROPERTY(EditAnywhere, Category="SenseGlove", meta=(AllowPrivateAccess="false"))
+ USphereComponent* LeftPinkyFingertipTouchCollider;
+
+ UPROPERTY(VisibleAnywhere, Category="SenseGlove", meta=(AllowPrivateAccess="false"))
+ USGWristTrackerComponent* WristTrackerRight;
+
+ UPROPERTY(VisibleAnywhere, Category="SenseGlove", meta=(AllowPrivateAccess="false"))
+ UXRDeviceVisualizationComponent* ControllerVisualizerRight;
+
+ UPROPERTY(VisibleAnywhere, Category="SenseGlove", meta=(AllowPrivateAccess="false"))
+ USGVirtualHandComponent* HandRight;
+
+ UPROPERTY(VisibleAnywhere, Category="SenseGlove", meta=(AllowPrivateAccess="false"))
+ USGVirtualHandComponent* RealHandRight;
+
+ UPROPERTY(EditAnywhere, Category="SenseGlove", meta=(AllowPrivateAccess="false"))
+ USphereComponent* RightThumbFingertipGrabCollider;
+
+ UPROPERTY(EditAnywhere, Category="SenseGlove", meta=(AllowPrivateAccess="false"))
+ USphereComponent* RightIndexFingertipGrabCollider;
+
+ UPROPERTY(EditAnywhere, Category="SenseGlove", meta=(AllowPrivateAccess="false"))
+ USphereComponent* RightMiddleFingertipGrabCollider;
+
+ UPROPERTY(EditAnywhere, Category="SenseGlove", meta=(AllowPrivateAccess="false"))
+ USphereComponent* RightThumbFingertipTouchCollider;
+
+ UPROPERTY(EditAnywhere, Category="SenseGlove", meta=(AllowPrivateAccess="false"))
+ USphereComponent* RightIndexFingertipTouchCollider;
+
+ UPROPERTY(EditAnywhere, Category="SenseGlove", meta=(AllowPrivateAccess="false"))
+ USphereComponent* RightMiddleFingertipTouchCollider;
+
+ UPROPERTY(EditAnywhere, Category="SenseGlove", meta=(AllowPrivateAccess="false"))
+ USphereComponent* RightRingFingertipTouchCollider;
+
+ UPROPERTY(EditAnywhere, Category="SenseGlove", meta=(AllowPrivateAccess="false"))
+ USphereComponent* RightPinkyFingertipTouchCollider;
+
+ UPROPERTY(EditAnywhere, Category="SenseGlove", meta=(AllowPrivateAccess="false"))
+ int32 MaxNumberOfHandVelocitySamples;
+
+private:
+ UPROPERTY(Transient)
+ FSGGrabState LeftHandGrabState;
+
+ UPROPERTY(Transient)
+ FSGTouchState LeftHandTouchState;
+
+ UPROPERTY(Transient)
+ FSGGrabState RightHandGrabState;
+
+ UPROPERTY(Transient)
+ FSGTouchState RightHandTouchState;
+
+ UPROPERTY(Transient)
+ TArray LeftHandOverlappedActors;
+
+ UPROPERTY(Transient)
+ TArray RightHandOverlappedActors;
+
+protected:
+ UFUNCTION(BlueprintNativeEvent, Category="SenseGlove | Game Framework | Pawn")
+ void OnGrabStateUpdated(const FSGGrabState& GrabState);
+
+ UFUNCTION(BlueprintNativeEvent, Category="SenseGlove | Game Framework | Pawn")
+ void OnTouchStateUpdated(const FSGTouchState& TouchState);
+
+ UFUNCTION(BlueprintNativeEvent, Category="SenseGlove | Game Framework | Pawn")
+ void OnActorGrabbed(const USGVirtualHandComponent* Hand, const AActor* Actor);
+
+ UFUNCTION(BlueprintNativeEvent, Category="SenseGlove | Game Framework | Pawn")
+ void OnActorReleased(const USGVirtualHandComponent* Hand, const AActor* Actor);
+
+ UFUNCTION(BlueprintNativeEvent, Category="SenseGlove | Game Framework | Pawn")
+ void OnActorBeginTouch(const USGVirtualHandComponent* Hand, const AActor* Actor);
+
+ UFUNCTION(BlueprintNativeEvent, Category="SenseGlove | Game Framework | Pawn")
+ void OnActorEndTouch(const USGVirtualHandComponent* Hand, const AActor* Actor);
+
+public:
+ FORCEINLINE UCameraComponent* GetCamera() const
+ {
+ return Camera;
+ }
+
+ FORCEINLINE USGWristTrackerComponent* GetWristTrackerLeft() const
+ {
+ return WristTrackerLeft;
+ }
+
+ FORCEINLINE UXRDeviceVisualizationComponent* GetControllerVisualizerLeft() const
+ {
+ return ControllerVisualizerLeft;
+ }
+
+ FORCEINLINE USGVirtualHandComponent* GetHandLeft() const
+ {
+ return HandLeft;
+ }
+
+ FORCEINLINE USGVirtualHandComponent* GetRealHandLeft() const
+ {
+ return RealHandLeft;
+ }
+
+ FORCEINLINE USphereComponent* GetLeftThumbFingertipGrabCollider() const
+ {
+ return LeftThumbFingertipGrabCollider;
+ }
+
+ FORCEINLINE USphereComponent* GetLeftIndexFingertipGrabCollider() const
+ {
+ return LeftIndexFingertipGrabCollider;
+ }
+
+ FORCEINLINE USphereComponent* GetLeftMiddleFingertipGrabCollider() const
+ {
+ return LeftMiddleFingertipGrabCollider;
+ }
+
+ FORCEINLINE USphereComponent* GetLeftThumbFingertipTouchCollider() const
+ {
+ return LeftThumbFingertipTouchCollider;
+ }
+
+ FORCEINLINE USphereComponent* GetLeftIndexFingertipTouchCollider() const
+ {
+ return LeftIndexFingertipTouchCollider;
+ }
+
+ FORCEINLINE USphereComponent* GetLeftMiddleFingertipTouchCollider() const
+ {
+ return LeftMiddleFingertipTouchCollider;
+ }
+
+ FORCEINLINE USphereComponent* GetLeftRingFingertipTouchCollider() const
+ {
+ return LeftRingFingertipTouchCollider;
+ }
+
+ FORCEINLINE USphereComponent* GetLeftPinkyFingertipTouchCollider() const
+ {
+ return LeftPinkyFingertipTouchCollider;
+ }
+
+ FORCEINLINE USGWristTrackerComponent* GetWristTrackerRight() const
+ {
+ return WristTrackerRight;
+ }
+
+ FORCEINLINE UXRDeviceVisualizationComponent* GetControllerVisualizerRight() const
+ {
+ return ControllerVisualizerLeft;
+ }
+
+ FORCEINLINE USGVirtualHandComponent* GetHandRight() const
+ {
+ return HandRight;
+ }
+
+ FORCEINLINE USGVirtualHandComponent* GetRealHandRight() const
+ {
+ return RealHandRight;
+ }
+
+ FORCEINLINE USphereComponent* GetRightThumbFingertipGrabCollider() const
+ {
+ return RightThumbFingertipGrabCollider;
+ }
+
+ FORCEINLINE USphereComponent* GetRightIndexFingertipGrabCollider() const
+ {
+ return RightIndexFingertipGrabCollider;
+ }
+
+ FORCEINLINE USphereComponent* GetRightMiddleFingertipGrabCollider() const
+ {
+ return RightMiddleFingertipGrabCollider;
+ }
+
+ FORCEINLINE USphereComponent* GetRightThumbFingertipTouchCollider() const
+ {
+ return RightThumbFingertipTouchCollider;
+ }
+
+ FORCEINLINE USphereComponent* GetRightIndexFingertipTouchCollider() const
+ {
+ return RightIndexFingertipTouchCollider;
+ }
+
+ FORCEINLINE USphereComponent* GetRightMiddleFingertipTouchCollider() const
+ {
+ return RightMiddleFingertipTouchCollider;
+ }
+
+ FORCEINLINE USphereComponent* GetRightRingFingertipTouchCollider() const
+ {
+ return RightRingFingertipTouchCollider;
+ }
+
+ FORCEINLINE USphereComponent* GetRightPinkyFingertipTouchCollider() const
+ {
+ return RightPinkyFingertipTouchCollider;
+ }
+
+ FORCEINLINE int32 GetMaxNumberOfHandVelocitySamples() const
+ {
+ return MaxNumberOfHandVelocitySamples;
+ }
+
+ FORCEINLINE void SetMaxNumberOfHandVelocitySamples(const int32 InMaxNumberOfHandVelocitySamples)
+ {
+ MaxNumberOfHandVelocitySamples = InMaxNumberOfHandVelocitySamples;
+ }
+
+public:
+ virtual void BeginPlay() override;
+ virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override;
+ virtual void Tick(float DeltaSeconds) override;
+ virtual void PreInitializeComponents() override;
+ virtual void PostInitializeComponents() override;
+
+protected:
+ UFUNCTION()
+ virtual void OnRealLeftHandOverlapBegins(
+ UPrimitiveComponent* OverlappedComponent,
+ AActor* OtherActor,
+ UPrimitiveComponent* OtherComp,
+ int32 OtherBodyIndex,
+ bool bFromSweep,
+ const FHitResult& SweepResult);
+
+ UFUNCTION()
+ virtual void OnRealLeftHandOverlapEnds(
+ UPrimitiveComponent* OverlappedComponent,
+ AActor* OtherActor,
+ UPrimitiveComponent* OtherComp,
+ int32 OtherBodyIndex);
+
+ UFUNCTION()
+ virtual void OnRealRightHandOverlapBegins(
+ UPrimitiveComponent* OverlappedComponent,
+ AActor* OtherActor,
+ UPrimitiveComponent* OtherComp,
+ int32 OtherBodyIndex,
+ bool bFromSweep,
+ const FHitResult& SweepResult);
+
+ UFUNCTION()
+ virtual void OnRealRightHandOverlapEnds(
+ UPrimitiveComponent* OverlappedComponent,
+ AActor* OtherActor,
+ UPrimitiveComponent* OtherComp,
+ int32 OtherBodyIndex);
+
+ UFUNCTION()
+ virtual void OnLeftThumbFingertipGrabColliderOverlapBegins(
+ UPrimitiveComponent* OverlappedComponent,
+ AActor* OtherActor,
+ UPrimitiveComponent* OtherComp,
+ int32 OtherBodyIndex,
+ bool bFromSweep,
+ const FHitResult& SweepResult);
+
+ UFUNCTION()
+ virtual void OnLeftThumbFingertipGrabColliderOverlapEnds(
+ UPrimitiveComponent* OverlappedComponent,
+ AActor* OtherActor,
+ UPrimitiveComponent* OtherComp,
+ int32 OtherBodyIndex);
+
+ UFUNCTION()
+ virtual void OnLeftIndexFingertipGrabColliderOverlapBegins(
+ UPrimitiveComponent* OverlappedComponent,
+ AActor* OtherActor,
+ UPrimitiveComponent* OtherComp,
+ int32 OtherBodyIndex,
+ bool bFromSweep,
+ const FHitResult& SweepResult);
+
+ UFUNCTION()
+ virtual void OnLeftIndexFingertipGrabColliderOverlapEnds(
+ UPrimitiveComponent* OverlappedComponent,
+ AActor* OtherActor,
+ UPrimitiveComponent* OtherComp,
+ int32 OtherBodyIndex);
+
+ UFUNCTION()
+ virtual void OnLeftMiddleFingertipGrabColliderOverlapBegins(
+ UPrimitiveComponent* OverlappedComponent,
+ AActor* OtherActor,
+ UPrimitiveComponent* OtherComp,
+ int32 OtherBodyIndex,
+ bool bFromSweep,
+ const FHitResult& SweepResult);
+
+ UFUNCTION()
+ virtual void OnLeftMiddleFingertipGrabColliderOverlapEnds(
+ UPrimitiveComponent* OverlappedComponent,
+ AActor* OtherActor,
+ UPrimitiveComponent* OtherComp,
+ int32 OtherBodyIndex);
+
+ UFUNCTION()
+ virtual void OnLeftThumbFingertipTouchColliderOverlapBegins(
+ UPrimitiveComponent* OverlappedComponent,
+ AActor* OtherActor,
+ UPrimitiveComponent* OtherComp,
+ int32 OtherBodyIndex,
+ bool bFromSweep,
+ const FHitResult& SweepResult);
+
+ UFUNCTION()
+ virtual void OnLeftThumbFingertipTouchColliderOverlapEnds(
+ UPrimitiveComponent* OverlappedComponent,
+ AActor* OtherActor,
+ UPrimitiveComponent* OtherComp,
+ int32 OtherBodyIndex);
+
+ UFUNCTION()
+ virtual void OnLeftIndexFingertipTouchColliderOverlapBegins(
+ UPrimitiveComponent* OverlappedComponent,
+ AActor* OtherActor,
+ UPrimitiveComponent* OtherComp,
+ int32 OtherBodyIndex,
+ bool bFromSweep,
+ const FHitResult& SweepResult);
+
+ UFUNCTION()
+ virtual void OnLeftIndexFingertipTouchColliderOverlapEnds(
+ UPrimitiveComponent* OverlappedComponent,
+ AActor* OtherActor,
+ UPrimitiveComponent* OtherComp,
+ int32 OtherBodyIndex);
+
+ UFUNCTION()
+ virtual void OnLeftMiddleFingertipTouchColliderOverlapBegins(
+ UPrimitiveComponent* OverlappedComponent,
+ AActor* OtherActor,
+ UPrimitiveComponent* OtherComp,
+ int32 OtherBodyIndex,
+ bool bFromSweep,
+ const FHitResult& SweepResult);
+
+ UFUNCTION()
+ virtual void OnLeftMiddleFingertipTouchColliderOverlapEnds(
+ UPrimitiveComponent* OverlappedComponent,
+ AActor* OtherActor,
+ UPrimitiveComponent* OtherComp,
+ int32 OtherBodyIndex);
+
+ UFUNCTION()
+ virtual void OnLeftRingFingertipTouchColliderOverlapBegins(
+ UPrimitiveComponent* OverlappedComponent,
+ AActor* OtherActor,
+ UPrimitiveComponent* OtherComp,
+ int32 OtherBodyIndex,
+ bool bFromSweep,
+ const FHitResult& SweepResult);
+
+ UFUNCTION()
+ virtual void OnLeftRingFingertipTouchColliderOverlapEnds(
+ UPrimitiveComponent* OverlappedComponent,
+ AActor* OtherActor,
+ UPrimitiveComponent* OtherComp,
+ int32 OtherBodyIndex);
+
+ UFUNCTION()
+ virtual void OnLeftPinkyFingertipTouchColliderOverlapBegins(
+ UPrimitiveComponent* OverlappedComponent,
+ AActor* OtherActor,
+ UPrimitiveComponent* OtherComp,
+ int32 OtherBodyIndex,
+ bool bFromSweep,
+ const FHitResult& SweepResult);
+
+ UFUNCTION()
+ virtual void OnLeftPinkyFingertipTouchColliderOverlapEnds(
+ UPrimitiveComponent* OverlappedComponent,
+ AActor* OtherActor,
+ UPrimitiveComponent* OtherComp,
+ int32 OtherBodyIndex);
+
+ UFUNCTION()
+ virtual void OnRightThumbFingertipGrabColliderOverlapBegins(
+ UPrimitiveComponent* OverlappedComponent,
+ AActor* OtherActor,
+ UPrimitiveComponent* OtherComp,
+ int32 OtherBodyIndex,
+ bool bFromSweep,
+ const FHitResult& SweepResult);
+
+ UFUNCTION()
+ virtual void OnRightThumbFingertipGrabColliderOverlapEnds(
+ UPrimitiveComponent* OverlappedComponent,
+ AActor* OtherActor,
+ UPrimitiveComponent* OtherComp,
+ int32 OtherBodyIndex);
+
+ UFUNCTION()
+ virtual void OnRightIndexFingertipGrabColliderOverlapBegins(
+ UPrimitiveComponent* OverlappedComponent,
+ AActor* OtherActor,
+ UPrimitiveComponent* OtherComp,
+ int32 OtherBodyIndex,
+ bool bFromSweep,
+ const FHitResult& SweepResult);
+
+ UFUNCTION()
+ virtual void OnRightIndexFingertipGrabColliderOverlapEnds(
+ UPrimitiveComponent* OverlappedComponent,
+ AActor* OtherActor,
+ UPrimitiveComponent* OtherComp,
+ int32 OtherBodyIndex);
+
+ UFUNCTION()
+ virtual void OnRightMiddleFingertipGrabColliderOverlapBegins(
+ UPrimitiveComponent* OverlappedComponent,
+ AActor* OtherActor,
+ UPrimitiveComponent* OtherComp,
+ int32 OtherBodyIndex,
+ bool bFromSweep,
+ const FHitResult& SweepResult);
+
+ UFUNCTION()
+ virtual void OnRightMiddleFingertipGrabColliderOverlapEnds(
+ UPrimitiveComponent* OverlappedComponent,
+ AActor* OtherActor,
+ UPrimitiveComponent* OtherComp,
+ int32 OtherBodyIndex);
+
+ UFUNCTION()
+ virtual void OnRightThumbFingertipTouchColliderOverlapBegins(
+ UPrimitiveComponent* OverlappedComponent,
+ AActor* OtherActor,
+ UPrimitiveComponent* OtherComp,
+ int32 OtherBodyIndex,
+ bool bFromSweep,
+ const FHitResult& SweepResult);
+
+ UFUNCTION()
+ virtual void OnRightThumbFingertipTouchColliderOverlapEnds(
+ UPrimitiveComponent* OverlappedComponent,
+ AActor* OtherActor,
+ UPrimitiveComponent* OtherComp,
+ int32 OtherBodyIndex);
+
+ UFUNCTION()
+ virtual void OnRightIndexFingertipTouchColliderOverlapBegins(
+ UPrimitiveComponent* OverlappedComponent,
+ AActor* OtherActor,
+ UPrimitiveComponent* OtherComp,
+ int32 OtherBodyIndex,
+ bool bFromSweep,
+ const FHitResult& SweepResult);
+
+ UFUNCTION()
+ virtual void OnRightIndexFingertipTouchColliderOverlapEnds(
+ UPrimitiveComponent* OverlappedComponent,
+ AActor* OtherActor,
+ UPrimitiveComponent* OtherComp,
+ int32 OtherBodyIndex);
+
+ UFUNCTION()
+ virtual void OnRightMiddleFingertipTouchColliderOverlapBegins(
+ UPrimitiveComponent* OverlappedComponent,
+ AActor* OtherActor,
+ UPrimitiveComponent* OtherComp,
+ int32 OtherBodyIndex,
+ bool bFromSweep,
+ const FHitResult& SweepResult);
+
+ UFUNCTION()
+ virtual void OnRightMiddleFingertipTouchColliderOverlapEnds(
+ UPrimitiveComponent* OverlappedComponent,
+ AActor* OtherActor,
+ UPrimitiveComponent* OtherComp,
+ int32 OtherBodyIndex);
+
+ UFUNCTION()
+ virtual void OnRightRingFingertipTouchColliderOverlapBegins(
+ UPrimitiveComponent* OverlappedComponent,
+ AActor* OtherActor,
+ UPrimitiveComponent* OtherComp,
+ int32 OtherBodyIndex,
+ bool bFromSweep,
+ const FHitResult& SweepResult);
+
+ UFUNCTION()
+ virtual void OnRightRingFingertipTouchColliderOverlapEnds(
+ UPrimitiveComponent* OverlappedComponent,
+ AActor* OtherActor,
+ UPrimitiveComponent* OtherComp,
+ int32 OtherBodyIndex);
+
+ UFUNCTION()
+ virtual void OnRightPinkyFingertipTouchColliderOverlapBegins(
+ UPrimitiveComponent* OverlappedComponent,
+ AActor* OtherActor,
+ UPrimitiveComponent* OtherComp,
+ int32 OtherBodyIndex,
+ bool bFromSweep,
+ const FHitResult& SweepResult);
+
+ UFUNCTION()
+ virtual void OnRightPinkyFingertipTouchColliderOverlapEnds(
+ UPrimitiveComponent* OverlappedComponent,
+ AActor* OtherActor,
+ UPrimitiveComponent* OtherComp,
+ int32 OtherBodyIndex);
+
+public:
+ FORCEINLINE bool CanLeftHandGrab(const AActor* Actor) const
+ {
+ return !IsLeftHandGrabbing() && ((IsValid(Actor) && Actor == LeftHandGrabState.ActorThumbCanGrab)
+ && (Actor == LeftHandGrabState.ActorIndexCanGrab || Actor == LeftHandGrabState.ActorMiddleCanGrab));
+ }
+
+ FORCEINLINE bool IsLeftHandGrabbing(const AActor* Actor) const
+ {
+ return IsValid(Actor) && LeftHandGrabState.GrabbedActor == Actor;
+ }
+
+ bool IsLeftHandGrabbing(AActor*& OutActor) const;
+
+ FORCEINLINE bool IsLeftHandGrabbing() const
+ {
+ return IsValid(LeftHandGrabState.GrabbedActor);
+ }
+
+ FORCEINLINE bool CanRightHandGrab(const AActor* Actor) const
+ {
+ return !IsRightHandGrabbing() && ((IsValid(Actor) && Actor == RightHandGrabState.ActorThumbCanGrab)
+ && (Actor == RightHandGrabState.ActorIndexCanGrab || Actor == RightHandGrabState.ActorMiddleCanGrab));
+ }
+
+ FORCEINLINE bool IsRightHandGrabbing(const AActor* Actor) const
+ {
+ return IsValid(Actor) && RightHandGrabState.GrabbedActor == Actor;
+ }
+
+ FORCEINLINE bool IsRightHandGrabbing() const
+ {
+ return IsValid(RightHandGrabState.GrabbedActor);
+ }
+
+ bool IsRightHandGrabbing(AActor*& OutActor) const;
+
+ FORCEINLINE bool CanGrab(const USGVirtualHandComponent* Hand, const AActor* Actor) const
+ {
+ return Hand == HandRight ? CanRightHandGrab(Actor) : CanLeftHandGrab(Actor);
+ }
+
+ FORCEINLINE bool IsGrabbing(const USGVirtualHandComponent* Hand, const AActor* Actor) const
+ {
+ return Hand == HandRight ? IsRightHandGrabbing(Actor) : IsLeftHandGrabbing(Actor);
+ }
+
+ bool IsGrabbing(const USGVirtualHandComponent* Hand, AActor*& OutActor) const;
+
+ FORCEINLINE bool IsGrabbing(const USGVirtualHandComponent* Hand) const
+ {
+ return Hand == HandRight ? IsRightHandGrabbing() : IsLeftHandGrabbing();
+ }
+
+public:
+ FORCEINLINE void GrabLeft(AActor* Actor)
+ {
+ Grab(HandLeft, Actor);
+ }
+
+ FORCEINLINE void GrabRight(AActor* Actor)
+ {
+ Grab(HandRight, Actor);
+ }
+
+ void Grab(USGVirtualHandComponent* Hand, AActor* Actor);
+
+ void ReleaseLeft();
+
+ void ReleaseRight();
+
+ FORCEINLINE void Release(const USGVirtualHandComponent* Hand)
+ {
+ return Hand == HandRight ? ReleaseRight() : ReleaseLeft();
+ }
+};
\ No newline at end of file
diff --git a/Plugins/SenseGlove/Source/SenseGlove/Public/SenseGlove/GameFramework/SGPlayerController.h b/Plugins/SenseGlove/Source/SenseGlove/Public/SenseGlove/GameFramework/SGPlayerController.h
new file mode 100644
index 0000000..e5d585f
--- /dev/null
+++ b/Plugins/SenseGlove/Source/SenseGlove/Public/SenseGlove/GameFramework/SGPlayerController.h
@@ -0,0 +1,61 @@
+/**
+ * @file
+ *
+ * @author Mamadou Babaei
+ *
+ * @section LICENSE
+ *
+ * (The MIT License)
+ *
+ * Copyright (c) 2020 - 2026 SenseGlove
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * @section DESCRIPTION
+ *
+ *
+ */
+
+
+#pragma once
+
+#include "GameFramework/PlayerController.h"
+#include "Templates/UniquePtr.h"
+
+#include "SGPlayerController.generated.h"
+
+UCLASS(Config=Game, BlueprintType, Blueprintable)
+class SENSEGLOVE_API ASGPlayerController : public APlayerController
+{
+ GENERATED_UCLASS_BODY()
+
+private:
+ struct FImpl;
+
+ struct FImplDeleter
+ {
+ void operator()(const FImpl* P) const;
+ };
+
+ TUniquePtr Pimpl;
+ FImplDeleter PimplDeleter;
+
+protected:
+ virtual void BeginPlay() override;
+};
\ No newline at end of file
diff --git a/Plugins/SenseGlove/Source/SenseGlove/Public/SenseGlove/Haptics/SGGrabState.h b/Plugins/SenseGlove/Source/SenseGlove/Public/SenseGlove/Haptics/SGGrabState.h
new file mode 100644
index 0000000..26b647c
--- /dev/null
+++ b/Plugins/SenseGlove/Source/SenseGlove/Public/SenseGlove/Haptics/SGGrabState.h
@@ -0,0 +1,86 @@
+/**
+ * @file
+ *
+ * @author Mamadou Babaei
+ *
+ * @section LICENSE
+ *
+ * (The MIT License)
+ *
+ * Copyright (c) 2020 - 2026 SenseGlove
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * @section DESCRIPTION
+ *
+ *
+ */
+
+
+#pragma once
+
+#include "Containers/Array.h"
+#include "Math/Vector.h"
+#include "UObject/ObjectMacros.h"
+
+#include "SGGrabState.generated.h"
+
+class AActor;
+class UPrimitiveComponent;
+
+class USGVirtualHandComponent;
+
+USTRUCT(BlueprintType)
+struct SENSEGLOVE_API FSGGrabState
+{
+ GENERATED_USTRUCT_BODY()
+
+public:
+ UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "SenseGlove")
+ USGVirtualHandComponent* Hand;
+
+ UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "SenseGlove")
+ FVector PreviousHandLocation;
+
+ UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "SenseGlove")
+ TArray HandVelocityHistory;
+
+ UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "SenseGlove")
+ AActor* ActorThumbCanGrab;
+
+ UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "SenseGlove")
+ AActor* ActorIndexCanGrab;
+
+ UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "SenseGlove")
+ AActor* ActorMiddleCanGrab;
+
+ UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "SenseGlove")
+ AActor* GrabbedActor;
+
+public:
+ FSGGrabState();
+
+ explicit FSGGrabState(USGVirtualHandComponent* InHand,
+ const FVector& InPreviousHandLocation,
+ const TArray& InHandVelocityHistory,
+ AActor* InActorThumbCanGrab,
+ AActor* InActorIndexCanGrab,
+ AActor* InActorMiddleCanGrab,
+ AActor* InGrabbedActor);
+};
\ No newline at end of file
diff --git a/Plugins/SenseGlove/Source/SenseGlove/Public/SenseGlove/Haptics/SGTouchState.h b/Plugins/SenseGlove/Source/SenseGlove/Public/SenseGlove/Haptics/SGTouchState.h
new file mode 100644
index 0000000..737cc09
--- /dev/null
+++ b/Plugins/SenseGlove/Source/SenseGlove/Public/SenseGlove/Haptics/SGTouchState.h
@@ -0,0 +1,79 @@
+/**
+ * @file
+ *
+ * @author Mamadou Babaei
+ *
+ * @section LICENSE
+ *
+ * (The MIT License)
+ *
+ * Copyright (c) 2020 - 2026 SenseGlove
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * @section DESCRIPTION
+ *
+ *
+ */
+
+
+#pragma once
+
+#include "UObject/ObjectMacros.h"
+
+#include "SGTouchState.generated.h"
+
+class AActor;
+
+class USGVirtualHandComponent;
+
+USTRUCT(BlueprintType)
+struct SENSEGLOVE_API FSGTouchState
+{
+ GENERATED_USTRUCT_BODY()
+
+public:
+ UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "SenseGlove")
+ USGVirtualHandComponent* Hand;
+
+ UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "SenseGlove")
+ AActor* ActorThumbTouching;
+
+ UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "SenseGlove")
+ AActor* ActorIndexTouching;
+
+ UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "SenseGlove")
+ AActor* ActorMiddleTouching;
+
+ UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "SenseGlove")
+ AActor* ActorRingTouching;
+
+ UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "SenseGlove")
+ AActor* ActorPinkyTouching;
+
+public:
+ FSGTouchState();
+
+ explicit FSGTouchState(USGVirtualHandComponent* InHand,
+ AActor* InActorThumbTouching,
+ AActor* InActorIndexTouching,
+ AActor* InActorMiddleTouching,
+ AActor* InActorRingTouching,
+ AActor* InActorPinkyTouching);
+};
\ No newline at end of file
diff --git a/Plugins/SenseGlove/Source/SenseGlove/SenseGlove.Build.cs b/Plugins/SenseGlove/Source/SenseGlove/SenseGlove.Build.cs
new file mode 100644
index 0000000..f28fb12
--- /dev/null
+++ b/Plugins/SenseGlove/Source/SenseGlove/SenseGlove.Build.cs
@@ -0,0 +1,80 @@
+/**
+ * @file
+ *
+ * @author Mamadou Babaei
+ *
+ * @section LICENSE
+ *
+ * (The MIT License)
+ *
+ * Copyright (c) 2020 - 2026 SenseGlove
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * @section DESCRIPTION
+ *
+ *
+ */
+
+
+using System;
+using UnrealBuildTool;
+
+public class SenseGlove : ModuleRules
+{
+ public SenseGlove(ReadOnlyTargetRules Target) : base(Target)
+ {
+ PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs;
+
+ PrivateDependencyModuleNames.AddRange(
+ new string[]
+ {
+ "AnimGraphRuntime",
+ "Core",
+ "CoreUObject",
+ "Engine",
+ "EngineSettings",
+ "HeadMountedDisplay",
+ "InputCore",
+ "InputDevice",
+ "OpenXRHMD",
+ "XRBase",
+ }
+ );
+
+ PrivateDependencyModuleNames.AddRange(
+ new string[]
+ {
+ "SenseGloveCore",
+ "SenseGloveDebug",
+ "SenseGloveLog",
+ "SenseGloveSettings",
+ "SenseGloveTracking",
+ "SenseGloveTypes",
+ }
+ );
+
+ PrivateIncludePathModuleNames.AddRange(
+ new string[]
+ {
+ "SenseGloveBuildHacks",
+ }
+ );
+ }
+}
\ No newline at end of file
diff --git a/Plugins/SenseGlove/Source/SenseGloveAndroid/Private/SGAndroid/SGAndroidPermissions.cpp b/Plugins/SenseGlove/Source/SenseGloveAndroid/Private/SGAndroid/SGAndroidPermissions.cpp
new file mode 100644
index 0000000..7ef857f
--- /dev/null
+++ b/Plugins/SenseGlove/Source/SenseGloveAndroid/Private/SGAndroid/SGAndroidPermissions.cpp
@@ -0,0 +1,190 @@
+/**
+ * @file
+ *
+ * @author Mamadou Babaei
+ *
+ * @section LICENSE
+ *
+ * (The MIT License)
+ *
+ * Copyright (c) 2020 - 2026 SenseGlove
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * @section DESCRIPTION
+ *
+ *
+ */
+
+
+#include "SGAndroid/SGAndroidPermissions.h"
+
+#include "AndroidPermissionCallbackProxy.h"
+#include "AndroidPermissionFunctionLibrary.h"
+#include "Engine/Engine.h"
+
+#include "SGLog/SGLog.h"
+
+struct USGAndroidPermissions::FImpl
+{
+ /************************
+ * Member variables
+ ************************/
+
+ TFunction&, const TArray&)> PermissionsGrantedCallback;
+
+ /************************
+ * Owner object
+ ************************/
+
+ USGAndroidPermissions* Owner;
+
+ /************************
+ * Constructor / Destructor
+ ************************/
+
+ explicit FImpl(USGAndroidPermissions* InOwner);
+ ~FImpl();
+
+ /************************
+ * Default copy constructor & copy assignment operator
+ ************************/
+
+ FImpl(const FImpl& Rhs) = default;
+ FImpl& operator=(const FImpl& Rhs) = default;
+};
+
+USGAndroidPermissions* USGAndroidPermissions::GetInstance()
+{
+ if (!ensureAlwaysMsgf(GEngine, TEXT("The engine has not been initialized!")))
+ {
+ return nullptr;
+ }
+
+ USGAndroidPermissions* Instance{GEngine->GetEngineSubsystem()};
+ return Instance;
+}
+
+USGAndroidPermissions::USGAndroidPermissions()
+ : Pimpl(TUniquePtr(new FImpl{this}, PimplDeleter))
+{
+}
+
+void USGAndroidPermissions::Request(
+ const TFunction&, const TArray&)>& PermissionsGrantedCallback)
+{
+ TArray Permissions{
+ "android.permission.ACCESS_BACKGROUND_LOCATION",
+ "android.permission.ACCESS_FINE_LOCATION",
+ "android.permission.BLUETOOTH",
+ "android.permission.BLUETOOTH_ADMIN",
+ "android.permission.BLUETOOTH_ADVERTISE",
+ "android.permission.BLUETOOTH_CONNECT",
+ "android.permission.BLUETOOTH_SCAN",
+ };
+
+ bool bHasAllRequiredPermissions = false;
+ for (const FString& Permission: Permissions)
+ {
+ const bool bHasPermission = UAndroidPermissionFunctionLibrary::CheckPermission(Permission);
+ bHasAllRequiredPermissions = bHasPermission;
+ if (!bHasAllRequiredPermissions)
+ {
+ break;
+ }
+ }
+
+ if (!bHasAllRequiredPermissions)
+ {
+ Pimpl->PermissionsGrantedCallback = PermissionsGrantedCallback;
+
+ UAndroidPermissionFunctionLibrary::AcquirePermissions(
+ MoveTemp(Permissions))->OnPermissionsGrantedDelegate.AddUObject(
+ this, &USGAndroidPermissions::OnPermissionsGranted);
+ }
+ else
+ {
+ TArray GrantedResults;
+ for (int32 i = 0; i < Permissions.Num(); ++i)
+ {
+ GrantedResults.Add(true);
+ }
+ PermissionsGrantedCallback(MoveTemp(Permissions), MoveTemp(GrantedResults));
+ }
+}
+
+void USGAndroidPermissions::OnPermissionsGranted(const TArray& Permissions, const TArray& GrantResults)
+{
+ if (!ensureAlwaysMsgf(Permissions.Num() == GrantResults.Num(),
+ TEXT("Mismatched number of Permissions and GrantedResults!")))
+ {
+ return;
+ }
+
+ for (int32 i = 0; i < Permissions.Num(); ++i)
+ {
+ if (GrantResults[i])
+ {
+ SGLOG("Permission Granted", Permissions[i]);
+ }
+ else
+ {
+ SGLOG("Permission Denied", Permissions[i]);
+ }
+ }
+
+ Pimpl->PermissionsGrantedCallback(Permissions, GrantResults);
+}
+
+bool USGAndroidPermissions::ShouldCreateSubsystem(UObject* Outer) const
+{
+ return true;
+}
+
+void USGAndroidPermissions::Initialize(FSubsystemCollectionBase& Collection)
+{
+ SGLOG("Initializing the SenseGlove Android permissions singleton with the engine lifetime...");
+
+ Super::Initialize(Collection);
+
+ SGLOG("The SenseGlove Android permissions singleton has been successfully initialized with the engine lifetime!");
+}
+
+void USGAndroidPermissions::Deinitialize()
+{
+ SGLOG("De-initializing the SenseGlove Android permissions singleton due to the engine lifetime expiration...");
+
+ Super::Deinitialize();
+
+ (void) Pimpl.Release();
+
+ SGLOG("The SenseGlove Android permissions singleton has been successfully de-initialized!");
+}
+
+USGAndroidPermissions::FImpl::FImpl(USGAndroidPermissions* InOwner)
+ : Owner(InOwner)
+{
+}
+
+USGAndroidPermissions::FImpl::~FImpl() = default;
+
+void USGAndroidPermissions::FImplDeleter::operator()(const FImpl* P) const
+{
+ delete P;
+}
\ No newline at end of file
diff --git a/Plugins/SenseGlove/Source/SenseGloveAndroid/Private/SGAndroid/SGConnectJNI.cpp b/Plugins/SenseGlove/Source/SenseGloveAndroid/Private/SGAndroid/SGConnectJNI.cpp
new file mode 100644
index 0000000..3e3c129
--- /dev/null
+++ b/Plugins/SenseGlove/Source/SenseGloveAndroid/Private/SGAndroid/SGConnectJNI.cpp
@@ -0,0 +1,128 @@
+/**
+ * @file
+ *
+ * @author Mamadou Babaei
+ *
+ * @section LICENSE
+ *
+ * (The MIT License)
+ *
+ * Copyright (c) 2020 - 2026 SenseGlove
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * @section DESCRIPTION
+ *
+ *
+ */
+
+
+#include "SGAndroid/SGConnectJNI.h"
+
+#if PLATFORM_ANDROID
+#include "Android/AndroidApplication.h"
+#include "Android/AndroidJava.h"
+#endif /* PLATFORM_ANDROID */
+
+#include "Misc/Paths.h"
+
+#if PLATFORM_ANDROID
+#include "SGConnectImpl/SGExportedFunctions.h"
+#include "SGInterop/SGIC_FString.h"
+#include "SGInterop/SGIC_jclass.h"
+#include "SGInterop/SGIC_jmethodID.h"
+#include "SGInterop/SGIC_JNIEnv_Ptr.h"
+#endif /* PLATFORM_ANDROID */
+
+#include "SGLog/SGLog.h"
+
+int32 FSGConnectJNI::Initialize()
+{
+ int32 Result = -1000;
+
+#if PLATFORM_ANDROID
+ static constexpr char SGCONNECT_JAVA_CLASS_NAME[] = "com.senseglove.sgconnect.SGConnect";
+
+ SGLOG("FSGCoreJNI: attempting to get the Android JNI environment...");
+
+ JNIEnv* Env = FAndroidApplication::GetJavaEnv();
+ FAndroidApplication::CheckJavaException();
+ if (!Env)
+ {
+ SGLOG_ERROR("FSGCoreJNI: failed to get the Android JNI environmnet!");
+ return -1001;
+ }
+ FSGIC_JNIEnv_Ptr InEnvContainer{Env};
+
+ SGLOG("FSGCoreJNI: attempting to find the Android Java class...");
+
+ jclass Class = FAndroidApplication::FindJavaClass(SGCONNECT_JAVA_CLASS_NAME);
+ FAndroidApplication::CheckJavaException();
+ if (Env->IsSameObject(Class, nullptr))
+ {
+ SGLOG_ERROR("FSGCoreJNI: failed to get the Android Java class!");
+ return -1002;
+ }
+ Class = (jclass)Env->NewGlobalRef(Class);
+ FSGIC_jclass InClassContainer{Class};
+
+ SGLOG("FSGCoreJNI: attempting to find the Android JNI method 'Init'...");
+
+ jmethodID InitMethodID = Env->GetStaticMethodID(Class, "Init", "()I");
+ FAndroidApplication::CheckJavaException();
+ if (!InitMethodID)
+ {
+ SGLOG_ERROR("FSGCoreJNI: failed to get the Android JNI method 'Init'!");
+ return -1003;
+ }
+ FSGIC_jmethodID InInitMethodIDContainer{InitMethodID};
+
+ SGLOG("FSGCoreJNI: attempting to find the Android JNI method 'Dispose'...");
+
+ jmethodID DisposeMethodID = Env->GetStaticMethodID(Class, "Dispose", "()I");
+ FAndroidApplication::CheckJavaException();
+ if (!DisposeMethodID)
+ {
+ SGLOG_ERROR("FSGCoreJNI: failed to get the Android JNI method 'Dispose'!");
+ return -1004;
+ }
+ FSGIC_jmethodID InDisposeMethodIDContainer{DisposeMethodID};
+
+ SGLOG("FSGCoreJNI: attempting to find the Android JNI method 'GetLibraryVersion'...");
+
+ jmethodID GetLibraryVersionMethodID = Env->GetStaticMethodID(Class, "GetLibraryVersion", "()Ljava/lang/String;");
+ FAndroidApplication::CheckJavaException();
+ if (!GetLibraryVersionMethodID)
+ {
+ SGLOG_ERROR("FSGCoreJNI: failed to get the Android JNI method 'GetLibraryVersion'!");
+ return -1005;
+ }
+ FSGIC_jmethodID InGetLibraryVersionMethodIDContainer{GetLibraryVersionMethodID};
+
+ FSGIC_FString ProjectSavedDirContainer{FPaths::ProjectSavedDir()};
+
+ Result = SGConnectImpl_Android_Initialize(&InEnvContainer, &InClassContainer,
+ &InInitMethodIDContainer,
+ &InDisposeMethodIDContainer,
+ &InGetLibraryVersionMethodIDContainer,
+ &ProjectSavedDirContainer);
+#endif /* PLATFORM_ANDROID */
+
+ return Result;
+}
\ No newline at end of file
diff --git a/Plugins/SenseGlove/Source/SenseGloveAndroid/Private/SGAndroid/SGCoreJNI.cpp b/Plugins/SenseGlove/Source/SenseGloveAndroid/Private/SGAndroid/SGCoreJNI.cpp
new file mode 100644
index 0000000..997658f
--- /dev/null
+++ b/Plugins/SenseGlove/Source/SenseGloveAndroid/Private/SGAndroid/SGCoreJNI.cpp
@@ -0,0 +1,140 @@
+/**
+ * @file
+ *
+ * @author Mamadou Babaei
+ *
+ * @section LICENSE
+ *
+ * (The MIT License)
+ *
+ * Copyright (c) 2020 - 2026 SenseGlove
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * @section DESCRIPTION
+ *
+ *
+ */
+
+
+#include "SGAndroid/SGCoreJNI.h"
+
+#if PLATFORM_ANDROID
+#include "Android/AndroidApplication.h"
+#include "Android/AndroidJava.h"
+#endif /* PLATFORM_ANDROID */
+
+#include "Misc/Paths.h"
+
+#if PLATFORM_ANDROID
+#include "SGCoreImpl/SGExportedFunctions.h"
+#include "SGInterop/SGIC_FString.h"
+#include "SGInterop/SGIC_jclass.h"
+#include "SGInterop/SGIC_jmethodID.h"
+#include "SGInterop/SGIC_JNIEnv_Ptr.h"
+#endif /* PLATFORM_ANDROID */
+
+#include "SGLog/SGLog.h"
+
+int32 FSGCoreJNI::Initialize()
+{
+ int32 Result = -1000;
+
+#if PLATFORM_ANDROID
+ static constexpr char SGCONNECT_JAVA_CLASS_NAME[] = "com.senseglove.sgconnect.SGConnect";
+
+ SGLOG("FSGCoreJNI: attempting to get the Android JNI environment...");
+
+ JNIEnv* Env = FAndroidApplication::GetJavaEnv();
+ FAndroidApplication::CheckJavaException();
+ if (!Env)
+ {
+ SGLOG_ERROR("FSGCoreJNI: failed to get the Android JNI environmnet!");
+ return -1001;
+ }
+ FSGIC_JNIEnv_Ptr InEnvContainer{Env};
+
+ SGLOG("FSGCoreJNI: attempting to find the Android Java class...");
+
+ jclass Class = FAndroidApplication::FindJavaClass(SGCONNECT_JAVA_CLASS_NAME);
+ FAndroidApplication::CheckJavaException();
+ if (Env->IsSameObject(Class, nullptr))
+ {
+ SGLOG_ERROR("FSGCoreJNI: failed to get the Android Java class!");
+ return -1002;
+ }
+ Class = (jclass)Env->NewGlobalRef(Class);
+ FSGIC_jclass InClassContainer{Class};
+
+ SGLOG("FSGCoreJNI: attempting to find the Android JNI method 'ActiveDevices'...");
+
+ jmethodID ActiveDevicesMethodID = Env->GetStaticMethodID(Class, "ActiveDevices", "()I");
+ FAndroidApplication::CheckJavaException();
+ if (!ActiveDevicesMethodID)
+ {
+ SGLOG_ERROR("FSGCoreJNI: failed to get the Android JNI method 'ActiveDevices'!");
+ return -1003;
+ }
+ FSGIC_jmethodID InActiveDevicesMethodIDContainer{ActiveDevicesMethodID};
+
+ SGLOG("FSGCoreJNI: attempting to find the Android JNI method 'GetDeviceString'...");
+
+ jmethodID GetDeviceStringMethodID = Env->GetStaticMethodID(Class, "GetDeviceString", "(I)Ljava/lang/String;");
+ FAndroidApplication::CheckJavaException();
+ if (!GetDeviceStringMethodID)
+ {
+ SGLOG_ERROR("FSGCoreJNI: failed to get the Android JNI method 'GetDeviceString'!");
+ return -1004;
+ }
+ FSGIC_jmethodID InGetDeviceStringMethodIDContainer{GetDeviceStringMethodID};
+
+ SGLOG("FSGCoreJNI: attempting to find the Android JNI method 'GetSensorString'...");
+
+ jmethodID GetSensorStringMethodID = Env->GetStaticMethodID(Class, "GetSensorString", "(I)Ljava/lang/String;");
+ FAndroidApplication::CheckJavaException();
+ if (!GetSensorStringMethodID)
+ {
+ SGLOG_ERROR("FSGCoreJNI: failed to get the Android JNI method 'GetSensorString'!");
+ return -1005;
+ }
+ FSGIC_jmethodID InGetSensorStringMethodIDContainer{GetSensorStringMethodID};
+
+ SGLOG("FSGCoreJNI: attempting to find the Android JNI method 'WriteHaptics'...");
+
+ jmethodID WriteHapticsMethodID = Env->GetStaticMethodID(Class, "WriteHaptics", "(IILjava/lang/String;)I");
+ FAndroidApplication::CheckJavaException();
+ if (!WriteHapticsMethodID)
+ {
+ SGLOG_ERROR("FSGCoreJNI: failed to get the Android JNI method 'WriteHaptics'!");
+ return -1006;
+ }
+ FSGIC_jmethodID InWriteHapticsMethodIDContainer{WriteHapticsMethodID};
+
+ FSGIC_FString ProjectSavedDirContainer{FPaths::ProjectSavedDir()};
+
+ Result = SGCoreImpl_Android_Initialize(&InEnvContainer, &InClassContainer,
+ &InActiveDevicesMethodIDContainer,
+ &InGetDeviceStringMethodIDContainer,
+ &InGetSensorStringMethodIDContainer,
+ &InWriteHapticsMethodIDContainer,
+ &ProjectSavedDirContainer);
+#endif /* PLATFORM_ANDROID */
+
+ return Result;
+}
\ No newline at end of file
diff --git a/Plugins/SenseGlove/Source/SenseGloveAndroid/Private/SenseGloveAndroid.cpp b/Plugins/SenseGlove/Source/SenseGloveAndroid/Private/SenseGloveAndroid.cpp
new file mode 100644
index 0000000..5c44fdf
--- /dev/null
+++ b/Plugins/SenseGlove/Source/SenseGloveAndroid/Private/SenseGloveAndroid.cpp
@@ -0,0 +1,41 @@
+/**
+ * @file
+ *
+ * @author Mamadou Babaei
+ *
+ * @section LICENSE
+ *
+ * (The MIT License)
+ *
+ * Copyright (c) 2020 - 2026 SenseGlove
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * @section DESCRIPTION
+ *
+ *
+ */
+
+
+#include "SenseGloveAndroid.h"
+
+#include "Modules/ModuleManager.h"
+#include "SenseGloveAndroidModule.h"
+
+IMPLEMENT_MODULE(FSenseGloveAndroidModule, SenseGloveAndroid)
\ No newline at end of file
diff --git a/Plugins/SenseGlove/Source/SenseGloveAndroid/Private/SenseGloveAndroid.h b/Plugins/SenseGlove/Source/SenseGloveAndroid/Private/SenseGloveAndroid.h
new file mode 100644
index 0000000..65941e7
--- /dev/null
+++ b/Plugins/SenseGlove/Source/SenseGloveAndroid/Private/SenseGloveAndroid.h
@@ -0,0 +1,38 @@
+/**
+ * @file
+ *
+ * @author Mamadou Babaei
+ *
+ * @section LICENSE
+ *
+ * (The MIT License)
+ *
+ * Copyright (c) 2020 - 2026 SenseGlove
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * @section DESCRIPTION
+ *
+ *
+ */
+
+
+#pragma once
+
+#include "CoreMinimal.h"
\ No newline at end of file
diff --git a/Plugins/SenseGlove/Source/SenseGloveAndroid/Private/SenseGloveAndroidModule.cpp b/Plugins/SenseGlove/Source/SenseGloveAndroid/Private/SenseGloveAndroidModule.cpp
new file mode 100644
index 0000000..df60612
--- /dev/null
+++ b/Plugins/SenseGlove/Source/SenseGloveAndroid/Private/SenseGloveAndroidModule.cpp
@@ -0,0 +1,95 @@
+/**
+ * @file
+ *
+ * @author Mamadou Babaei
+ *
+ * @section LICENSE
+ *
+ * (The MIT License)
+ *
+ * Copyright (c) 2020 - 2026 SenseGlove
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * @section DESCRIPTION
+ *
+ *
+ */
+
+
+#include "SenseGloveAndroidModule.h"
+
+#include "Misc/CoreDelegates.h"
+
+#include "SGAndroid/SGAndroidPermissions.h"
+#include "SGAndroid/SGConnectJNI.h"
+#include "SGAndroid/SGCoreJNI.h"
+#include "SGLog/SGLog.h"
+
+#define LOCTEXT_NAMESPACE "FSenseGloveAndroidModule"
+
+void FSenseGloveAndroidModule::StartupModule()
+{
+ SGLOG("Starting up the SenseGloveAndroid module...");
+
+ OnPostEngineInitHandle = FCoreDelegates::OnPostEngineInit.AddRaw(
+ this, &FSenseGloveAndroidModule::OnPostEngineInit);
+}
+
+void FSenseGloveAndroidModule::PreUnloadCallback()
+{
+ SGLOG("Unloading the SenseGloveAndroid module...");
+}
+
+void FSenseGloveAndroidModule::PostLoadCallback()
+{
+ SGLOG("Loading the SenseGloveAndroid module has succeeded!");
+}
+
+void FSenseGloveAndroidModule::ShutdownModule()
+{
+ SGLOG("Shutting down the SenseGloveAndroid module...");
+}
+
+void FSenseGloveAndroidModule::OnPostEngineInit() const
+{
+#if PLATFORM_ANDROID
+ USGAndroidPermissions::GetInstance()->Request(
+ [](const TArray& Permissions, const TArray& GrantResults)
+ {
+ const int32 R1 = FSGConnectJNI::Initialize();
+ const int32 R2 = FSGCoreJNI::Initialize();
+
+ SGLOG("FSGConnectJNI::Initialize() return code", R1);
+ SGLOG("FSGCoreJNI::Initialize() return code", R2);
+
+ ensureAlwaysMsgf(R1 == 0,
+ TEXT(
+ "Failed to initialize the SGConnect JNI bridge to SGConnect for Android! Status Code: %d"
+ ), R1);
+
+ ensureAlwaysMsgf(R2 == 0,
+ TEXT("Failed to initialize the SGCore JNI bridge to SGConnect for Android! Status Code: %d"
+ ), R2);
+
+ });
+#endif /* PLATFORM_ANDROID */
+}
+
+#undef LOCTEXT_NAMESPACE
\ No newline at end of file
diff --git a/Plugins/SenseGlove/Source/SenseGloveAndroid/Private/SenseGloveAndroidModule.h b/Plugins/SenseGlove/Source/SenseGloveAndroid/Private/SenseGloveAndroidModule.h
new file mode 100644
index 0000000..0e3d38d
--- /dev/null
+++ b/Plugins/SenseGlove/Source/SenseGloveAndroid/Private/SenseGloveAndroidModule.h
@@ -0,0 +1,58 @@
+/**
+ * @file
+ *
+ * @author Mamadou Babaei
+ *
+ * @section LICENSE
+ *
+ * (The MIT License)
+ *
+ * Copyright (c) 2020 - 2026 SenseGlove
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * @section DESCRIPTION
+ *
+ *
+ */
+
+
+#pragma once
+
+#include "Delegates/IDelegateInstance.h"
+#include "Modules/ModuleInterface.h"
+
+#define LOCTEXT_NAMESPACE "SenseGloveAndroid"
+
+class FSenseGloveAndroidModule : public IModuleInterface
+{
+private:
+ FDelegateHandle OnPostEngineInitHandle;
+
+public:
+ virtual void StartupModule() override;
+ virtual void PreUnloadCallback() override;
+ virtual void PostLoadCallback() override;
+ virtual void ShutdownModule() override;
+
+private:
+ void OnPostEngineInit() const;
+};
+
+#undef LOCTEXT_NAMESPACE
\ No newline at end of file
diff --git a/Plugins/SenseGlove/Source/SenseGloveAndroid/Public/SGAndroid/SGAndroidPermissions.h b/Plugins/SenseGlove/Source/SenseGloveAndroid/Public/SGAndroid/SGAndroidPermissions.h
new file mode 100644
index 0000000..59d4506
--- /dev/null
+++ b/Plugins/SenseGlove/Source/SenseGloveAndroid/Public/SGAndroid/SGAndroidPermissions.h
@@ -0,0 +1,82 @@
+/**
+ * @file
+ *
+ * @author Mamadou Babaei
+ *
+ * @section LICENSE
+ *
+ * (The MIT License)
+ *
+ * Copyright (c) 2020 - 2026 SenseGlove
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * @section DESCRIPTION
+ *
+ *
+ */
+
+
+#pragma once
+
+#include "Containers/Array.h"
+#include "Containers/UnrealString.h"
+#include "Subsystems/EngineSubsystem.h"
+#include "Templates/Function.h"
+#include "Templates/SharedPointer.h"
+#include "UObject/ObjectMacros.h"
+
+#include "SGAndroidPermissions.generated.h"
+
+UCLASS()
+class SENSEGLOVEANDROID_API USGAndroidPermissions : public UEngineSubsystem
+{
+ GENERATED_BODY()
+
+public:
+ static USGAndroidPermissions* GetInstance();
+
+private:
+ struct FImpl;
+
+ struct FImplDeleter
+ {
+ void operator()(const FImpl* P) const;
+ };
+
+ TUniquePtr Pimpl;
+ FImplDeleter PimplDeleter;
+
+private:
+ USGAndroidPermissions();
+
+public:
+ void Request(
+ const TFunction&, const TArray&)>& PermissionsGrantedCallback);
+
+private:
+ void OnPermissionsGranted(
+ const TArray& Permissions, const TArray& GrantResults);
+
+public:
+ virtual bool ShouldCreateSubsystem(UObject* Outer) const override;
+
+ virtual void Initialize(FSubsystemCollectionBase& Collection) override;
+ virtual void Deinitialize() override;
+};
\ No newline at end of file
diff --git a/Plugins/SenseGlove/Source/SenseGloveAndroid/Public/SGAndroid/SGConnectJNI.h b/Plugins/SenseGlove/Source/SenseGloveAndroid/Public/SGAndroid/SGConnectJNI.h
new file mode 100644
index 0000000..11b6143
--- /dev/null
+++ b/Plugins/SenseGlove/Source/SenseGloveAndroid/Public/SGAndroid/SGConnectJNI.h
@@ -0,0 +1,49 @@
+/**
+ * @file
+ *
+ * @author Mamadou Babaei
+ *
+ * @section LICENSE
+ *
+ * (The MIT License)
+ *
+ * Copyright (c) 2020 - 2026 SenseGlove
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * @section DESCRIPTION
+ *
+ *
+ */
+
+
+#pragma once
+
+#include "UObject/ObjectMacros.h"
+
+#include "SGConnectJNI.generated.h"
+
+USTRUCT()
+struct SENSEGLOVEANDROID_API FSGConnectJNI
+{
+ GENERATED_BODY()
+
+public:
+ static int32 Initialize();
+};
\ No newline at end of file
diff --git a/Plugins/SenseGlove/Source/SenseGloveAndroid/Public/SGAndroid/SGCoreJNI.h b/Plugins/SenseGlove/Source/SenseGloveAndroid/Public/SGAndroid/SGCoreJNI.h
new file mode 100644
index 0000000..5a1c7b7
--- /dev/null
+++ b/Plugins/SenseGlove/Source/SenseGloveAndroid/Public/SGAndroid/SGCoreJNI.h
@@ -0,0 +1,49 @@
+/**
+ * @file
+ *
+ * @author Mamadou Babaei
+ *
+ * @section LICENSE
+ *
+ * (The MIT License)
+ *
+ * Copyright (c) 2020 - 2026 SenseGlove
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * @section DESCRIPTION
+ *
+ *
+ */
+
+
+#pragma once
+
+#include "UObject/ObjectMacros.h"
+
+#include "SGCoreJNI.generated.h"
+
+USTRUCT()
+struct SENSEGLOVEANDROID_API FSGCoreJNI
+{
+ GENERATED_BODY()
+
+public:
+ static int32 Initialize();
+};
\ No newline at end of file
diff --git a/Plugins/SenseGlove/Source/SenseGloveAndroid/SenseGloveAndroid.Build.cs b/Plugins/SenseGlove/Source/SenseGloveAndroid/SenseGloveAndroid.Build.cs
new file mode 100644
index 0000000..2d9d59a
--- /dev/null
+++ b/Plugins/SenseGlove/Source/SenseGloveAndroid/SenseGloveAndroid.Build.cs
@@ -0,0 +1,78 @@
+/**
+ * @file
+ *
+ * @author Mamadou Babaei
+ *
+ * @section LICENSE
+ *
+ * (The MIT License)
+ *
+ * Copyright (c) 2020 - 2026 SenseGlove
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * @section DESCRIPTION
+ *
+ *
+ */
+
+
+using System;
+using System.IO;
+using UnrealBuildTool;
+
+public class SenseGloveAndroid : ModuleRules
+{
+ public SenseGloveAndroid(ReadOnlyTargetRules Target) : base(Target)
+ {
+ PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs;
+
+ PrivateDependencyModuleNames.AddRange(
+ new string[]
+ {
+ "AndroidPermission",
+ "Core",
+ "CoreUObject",
+ "Engine",
+ }
+ );
+
+ PrivateDependencyModuleNames.AddRange(
+ new string[]
+ {
+ "SenseGloveConnectImpl",
+ "SenseGloveCoreImpl",
+ "SenseGloveLog",
+ }
+ );
+
+ PrivateIncludePathModuleNames.AddRange(
+ new string[]
+ {
+ "SenseGloveInterop",
+ }
+ );
+
+ if (Target.Platform == UnrealTargetPlatform.Android)
+ {
+ AdditionalPropertiesForReceipt.Add("AndroidPlugin",
+ Path.Combine(ModuleDirectory, "SenseGloveAndroid_UPL.xml"));
+ }
+ }
+}
\ No newline at end of file
diff --git a/Plugins/SenseGlove/Source/SenseGloveAndroid/SenseGloveAndroid_UPL.xml b/Plugins/SenseGlove/Source/SenseGloveAndroid/SenseGloveAndroid_UPL.xml
new file mode 100644
index 0000000..88621c6
--- /dev/null
+++ b/Plugins/SenseGlove/Source/SenseGloveAndroid/SenseGloveAndroid_UPL.xml
@@ -0,0 +1,168 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ kapt.incremental.apt=true
+
+
+
+
+
+
+
+
+
+ -dontwarn com.senseglove.**
+ -keep class com.senseglove.** { *; }
+ -keep interface com.senseglove.** { *; }
+ -keep public class com.senseglove.sgconnect.** { public protected *; }
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ext {
+ android {
+ buildTypes {
+ release {
+ minifyEnabled false
+ }
+ }
+ }
+ }
+
+ repositories {
+ mavenCentral()
+ }
+
+ dependencies {
+ implementation files('libs/sgconnect.jar')
+ }
+
+
+
+
+
+
+
+
+ allprojects {
+ def mappings = [
+ ]
+ beforeEvaluate {
+ }
+ }
+
+
+
+
\ No newline at end of file
diff --git a/Plugins/SenseGlove/Source/SenseGloveBackend/Private/SGBackend/SGBackend.cpp b/Plugins/SenseGlove/Source/SenseGloveBackend/Private/SGBackend/SGBackend.cpp
new file mode 100644
index 0000000..37170cc
--- /dev/null
+++ b/Plugins/SenseGlove/Source/SenseGloveBackend/Private/SGBackend/SGBackend.cpp
@@ -0,0 +1,259 @@
+/**
+ * @file
+ *
+ * @author Mamadou Babaei
+ *
+ * @section LICENSE
+ *
+ * (The MIT License)
+ *
+ * Copyright (c) 2020 - 2026 SenseGlove
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * @section DESCRIPTION
+ *
+ *
+ */
+
+
+#include "SGBackend/SGBackend.h"
+
+#include "Engine/Engine.h"
+#include "Templates/Atomic.h"
+
+#if PLATFORM_ANDROID
+#include "SGConnect/SGConnect.h"
+#endif /* PLATFORM_ANDROID */
+
+#include "SGCore/SGDeviceList.h"
+#include "SGCore/SGHandLayer.h"
+#include "SGLog/SGLog.h"
+
+struct USGBackend::FImpl
+{
+ /************************
+ * Member variables
+ ************************/
+
+ TAtomic bInitialized;
+
+ /************************
+ * Owner object
+ ************************/
+
+ USGBackend* Owner;
+
+ /************************
+ * Constructor / Destructor
+ ************************/
+
+ explicit FImpl(USGBackend* InOwner);
+ ~FImpl();
+
+ /************************
+ * Default copy constructor & copy assignment operator
+ ************************/
+
+ FImpl(const FImpl& Rhs) = delete;
+ FImpl& operator=(const FImpl& Rhs) = delete;
+};
+
+USGBackend* USGBackend::GetInstance()
+{
+ if (!ensureAlwaysMsgf(GEngine, TEXT("The engine has not been initialized!")))
+ {
+ return nullptr;
+ }
+
+ USGBackend* Instance{GEngine->GetEngineSubsystem()};
+ return Instance;
+}
+
+USGBackend::USGBackend()
+ : Pimpl(TUniquePtr(new FImpl{this}, PimplDeleter))
+{
+}
+
+bool USGBackend::IsBackendInitialized() const
+{
+ return Pimpl->bInitialized;
+}
+
+bool USGBackend::InitializeBackend()
+{
+ if (!IsBackendInitialized())
+ {
+#if PLATFORM_ANDROID
+ const int32 Result = FSGConnect::Init();
+ switch (Result)
+ {
+ case -103:
+ SGLOG_ERROR("Android JNI-specific Error: Invalid Java method ID!");
+ return false;
+ case -102:
+ SGLOG_ERROR("Android JNI-specific Error: Invalid Java class!");
+ return false;
+ case -101:
+ SGLOG_ERROR("Android JNI-specific Error: Invalid Java environment!");
+ return false;
+ case -100:
+ SGLOG_ERROR("Android JNI-specific Error: Generic Error! Note: you should never see this!");
+ return false;
+ case -3:
+ SGLOG_ERROR("An Unexpected error occurred. Please try again.");
+ return false;
+ case -2:
+ SGLOG_ERROR("Device Scanning is already running within a different program.");
+ return false;
+ case -1:
+ SGLOG_ERROR("Device Scanning already being initialized (function called twice in short succession).");
+ return false;
+ case 0:
+ SGLOG_ERROR("Device Scanning is already running within this program.");
+ return false;
+ case 1:
+ SGLOG("Successfully started up DeviceScanning from the current program.");
+ break;
+ default:
+ ensureAlwaysMsgf(false, TEXT("Unhandled Init return status code!"));
+ return false;
+ }
+#endif /* PLATFORM_ANDROID */
+
+ // Ensure that the device list is empty before doing anything.
+ FSGDeviceList::Dispose();
+ FSGDeviceList::Reinitialize();
+
+ Pimpl->bInitialized = true;
+
+ return true;
+ }
+
+ return false;
+}
+
+bool USGBackend::UninitializeBackend()
+{
+ if (IsBackendInitialized())
+ {
+ FSGDeviceList::Dispose();
+
+#if PLATFORM_ANDROID
+ const int32 Result = FSGConnect::Dispose();
+ switch (Result)
+ {
+ case -103:
+ SGLOG_ERROR("Android JNI-specific Error: Invalid Java method ID!");
+ return false;
+ case -102:
+ SGLOG_ERROR("Android JNI-specific Error: Invalid Java class!");
+ return false;
+ case -101:
+ SGLOG_ERROR("Android JNI-specific Error: Invalid Java environment!");
+ return false;
+ case -100:
+ SGLOG_ERROR("Android JNI-specific Error: Generic Error! Note: you should never see this!");
+ return false;
+ case -3:
+ SGLOG_ERROR("An Unexpected error occurred. Please try again.");
+ return false;
+ case -2:
+ SGLOG_ERROR("Not allowed to dispose of Device Scanning because this is not the program which started it.");
+ return false;
+ case -1:
+ SGLOG_ERROR("Device Scanning is currently being disposed off. This takes a second or two. (function called twice in short succession).");
+ return false;
+ case 0:
+ SGLOG_ERROR("There is no deviceScanner running from any program, so disposing is skipped.");
+ return false;
+ case 1:
+ SGLOG("Successfully disposed of DeviceScanner resources.");
+ break;
+ default:
+ ensureAlwaysMsgf(false, TEXT("Unhandled Dispose return status code!"));
+ return false;
+ }
+#endif /* PLATFORM_ANDROID */
+
+ Pimpl->bInitialized = false;
+
+ return true;
+ }
+
+ return false;
+}
+
+bool USGBackend::ShouldCreateSubsystem(UObject* Outer) const
+{
+ return true;
+}
+
+void USGBackend::Initialize(FSubsystemCollectionBase& Collection)
+{
+ SGLOG("Initializing the SenseGlove Backend singleton with the engine lifetime...");
+
+ Super::Initialize(Collection);
+
+ SGLOG("Initializing the SenseGlove Backend singleton with the engine lifetime...");
+
+ if (InitializeBackend())
+ {
+ SGLOG("The bridge to the SenseGlove Backend has been initialized successfully!")
+ }
+ else
+ {
+ SGLOG_ERROR("Failed to initialize the bridge to the SenseGlove Backend!")
+ }
+
+ SGLOG("The SenseGlove Backend singleton has been successfully initialized with the engine lifetime!");
+}
+
+void USGBackend::Deinitialize()
+{
+ SGLOG("De-initializing the SenseGlove Backend singleton due to the engine lifetime expiration...");
+
+ Super::Deinitialize();
+
+ if (UninitializeBackend())
+ {
+ SGLOG("The bridge to the SenseGlove Backend has been uninitialized successfully!")
+ }
+ else
+ {
+ SGLOG_ERROR("Failed to uninitialize the bridge to the SenseGlove Backend!")
+ }
+
+ (void) Pimpl.Release();
+
+ SGLOG("The SenseGlove Backend singleton has been successfully de-initialized!");
+}
+
+USGBackend::FImpl::FImpl(USGBackend* InOwner)
+ : bInitialized(false),
+ Owner(InOwner)
+{
+}
+
+USGBackend::FImpl::~FImpl() = default;
+
+void USGBackend::FImplDeleter::operator()(const FImpl* P) const
+{
+ delete P;
+}
\ No newline at end of file
diff --git a/Plugins/SenseGlove/Source/SenseGloveBackend/Private/SenseGloveBackend.cpp b/Plugins/SenseGlove/Source/SenseGloveBackend/Private/SenseGloveBackend.cpp
new file mode 100644
index 0000000..65d88da
--- /dev/null
+++ b/Plugins/SenseGlove/Source/SenseGloveBackend/Private/SenseGloveBackend.cpp
@@ -0,0 +1,40 @@
+/**
+ * @file
+ *
+ * @author Mamadou Babaei
+ *
+ * @section LICENSE
+ *
+ * (The MIT License)
+ *
+ * Copyright (c) 2020 - 2026 SenseGlove
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * @section DESCRIPTION
+ *
+ *
+ */
+
+
+#include "SenseGloveBackend.h"
+#include "Modules/ModuleManager.h"
+#include "SenseGloveBackendModule.h"
+
+IMPLEMENT_MODULE(FSenseGloveBackendModule, SenseGloveBackend)
\ No newline at end of file
diff --git a/Plugins/SenseGlove/Source/SenseGloveBackend/Private/SenseGloveBackend.h b/Plugins/SenseGlove/Source/SenseGloveBackend/Private/SenseGloveBackend.h
new file mode 100644
index 0000000..65941e7
--- /dev/null
+++ b/Plugins/SenseGlove/Source/SenseGloveBackend/Private/SenseGloveBackend.h
@@ -0,0 +1,38 @@
+/**
+ * @file
+ *
+ * @author Mamadou Babaei
+ *
+ * @section LICENSE
+ *
+ * (The MIT License)
+ *
+ * Copyright (c) 2020 - 2026 SenseGlove
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * @section DESCRIPTION
+ *
+ *
+ */
+
+
+#pragma once
+
+#include "CoreMinimal.h"
\ No newline at end of file
diff --git a/Plugins/SenseGlove/Source/SenseGloveBackend/Private/SenseGloveBackendModule.cpp b/Plugins/SenseGlove/Source/SenseGloveBackend/Private/SenseGloveBackendModule.cpp
new file mode 100644
index 0000000..da04478
--- /dev/null
+++ b/Plugins/SenseGlove/Source/SenseGloveBackend/Private/SenseGloveBackendModule.cpp
@@ -0,0 +1,63 @@
+/**
+ * @file
+ *
+ * @author Mamadou Babaei
+ *
+ * @section LICENSE
+ *
+ * (The MIT License)
+ *
+ * Copyright (c) 2020 - 2026 SenseGlove
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * @section DESCRIPTION
+ *
+ *
+ */
+
+
+#include "SenseGloveBackendModule.h"
+
+#include "SGLog/SGLog.h"
+#include "SGBackend/SGBackend.h"
+
+#define LOCTEXT_NAMESPACE "FSenseGloveBackendModule"
+
+void FSenseGloveBackendModule::StartupModule()
+{
+ SGLOG("Starting up SenseGloveBackend module...");
+}
+
+void FSenseGloveBackendModule::PreUnloadCallback()
+{
+ SGLOG("Unloading SenseGloveBackend module...");
+}
+
+void FSenseGloveBackendModule::PostLoadCallback()
+{
+ SGLOG("Loading of SenseGloveBackend module has succeeded!");
+}
+
+void FSenseGloveBackendModule::ShutdownModule()
+{
+ SGLOG("Shutting down SenseGloveBackend module...");
+}
+
+#undef LOCTEXT_NAMESPACE
\ No newline at end of file
diff --git a/Plugins/SenseGlove/Source/SenseGloveBackend/Private/SenseGloveBackendModule.h b/Plugins/SenseGlove/Source/SenseGloveBackend/Private/SenseGloveBackendModule.h
new file mode 100644
index 0000000..c32d0f8
--- /dev/null
+++ b/Plugins/SenseGlove/Source/SenseGloveBackend/Private/SenseGloveBackendModule.h
@@ -0,0 +1,51 @@
+/**
+ * @file
+ *
+ * @author Mamadou Babaei
+ *
+ * @section LICENSE
+ *
+ * (The MIT License)
+ *
+ * Copyright (c) 2020 - 2026 SenseGlove
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * @section DESCRIPTION
+ *
+ *
+ */
+
+
+#pragma once
+
+#include "Modules/ModuleInterface.h"
+
+#define LOCTEXT_NAMESPACE "SenseGloveBackend"
+
+class FSenseGloveBackendModule : public IModuleInterface
+{
+public:
+ virtual void StartupModule() override;
+ virtual void PreUnloadCallback() override;
+ virtual void PostLoadCallback() override;
+ virtual void ShutdownModule() override;
+};
+
+#undef LOCTEXT_NAMESPACE
\ No newline at end of file
diff --git a/Plugins/SenseGlove/Source/SenseGloveBackend/Public/SGBackend/SGBackend.h b/Plugins/SenseGlove/Source/SenseGloveBackend/Public/SGBackend/SGBackend.h
new file mode 100644
index 0000000..63fcefa
--- /dev/null
+++ b/Plugins/SenseGlove/Source/SenseGloveBackend/Public/SGBackend/SGBackend.h
@@ -0,0 +1,80 @@
+/**
+ * @file
+ *
+ * @author Mamadou Babaei
+ *
+ * @section LICENSE
+ *
+ * (The MIT License)
+ *
+ * Copyright (c) 2020 - 2026 SenseGlove
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * @section DESCRIPTION
+ *
+ *
+ */
+
+
+#pragma once
+
+#include "Math/Color.h"
+#include "Math/Rotator.h"
+#include "Math/Vector.h"
+#include "Subsystems/EngineSubsystem.h"
+#include "Templates/UniquePtr.h"
+#include "UObject/ObjectMacros.h"
+
+#include "SGBackend.generated.h"
+
+UCLASS()
+class SENSEGLOVEBACKEND_API USGBackend : public UEngineSubsystem
+{
+ GENERATED_BODY()
+
+public:
+ static USGBackend* GetInstance();
+
+private:
+ struct FImpl;
+
+ struct FImplDeleter
+ {
+ void operator()(const FImpl* P) const;
+ };
+
+ TUniquePtr Pimpl;
+ FImplDeleter PimplDeleter;
+
+private:
+ USGBackend();
+
+public:
+ bool IsBackendInitialized() const;
+
+ bool InitializeBackend();
+ bool UninitializeBackend();
+
+public:
+ virtual bool ShouldCreateSubsystem(UObject* Outer) const override;
+
+ virtual void Initialize(FSubsystemCollectionBase& Collection) override;
+ virtual void Deinitialize() override;
+};
\ No newline at end of file
diff --git a/Plugins/SenseGlove/Source/SenseGloveBackend/SenseGloveBackend.Build.cs b/Plugins/SenseGlove/Source/SenseGloveBackend/SenseGloveBackend.Build.cs
new file mode 100644
index 0000000..d6a4970
--- /dev/null
+++ b/Plugins/SenseGlove/Source/SenseGloveBackend/SenseGloveBackend.Build.cs
@@ -0,0 +1,65 @@
+/**
+ * @file
+ *
+ * @author Mamadou Babaei
+ *
+ * @section LICENSE
+ *
+ * (The MIT License)
+ *
+ * Copyright (c) 2020 - 2026 SenseGlove
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * @section DESCRIPTION
+ *
+ *
+ */
+
+
+using System;
+using UnrealBuildTool;
+
+public class SenseGloveBackend : ModuleRules
+{
+ public SenseGloveBackend(ReadOnlyTargetRules Target) : base(Target)
+ {
+ PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs;
+
+ PrivateDependencyModuleNames.AddRange(
+ new string[]
+ {
+ "Core",
+ "CoreUObject",
+ "Engine",
+ "InputCore",
+ }
+ );
+
+ PrivateDependencyModuleNames.AddRange(
+ new string[]
+ {
+ "SenseGloveConnect",
+ "SenseGloveCore",
+ "SenseGloveLog",
+ "SenseGloveTypes",
+ }
+ );
+ }
+}
\ No newline at end of file
diff --git a/Plugins/SenseGlove/Source/SenseGloveBackendKismet/Private/SGBackendKismet/SGBackendKismetLibrary.cpp b/Plugins/SenseGlove/Source/SenseGloveBackendKismet/Private/SGBackendKismet/SGBackendKismetLibrary.cpp
new file mode 100644
index 0000000..3704ada
--- /dev/null
+++ b/Plugins/SenseGlove/Source/SenseGloveBackendKismet/Private/SGBackendKismet/SGBackendKismetLibrary.cpp
@@ -0,0 +1,58 @@
+/**
+ * @file
+ *
+ * @author Mamadou Babaei
+ *
+ * @section LICENSE
+ *
+ * (The MIT License)
+ *
+ * Copyright (c) 2020 - 2026 SenseGlove
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * @section DESCRIPTION
+ *
+ *
+ */
+
+
+#include "SGBackendKismet/SGBackendKismetLibrary.h"
+
+#include "SGBackend/SGBackend.h"
+
+USGBackend* USGBackendKismetLibrary::GetInstance()
+{
+ return USGBackend::GetInstance();
+}
+
+bool USGBackendKismetLibrary::IsBackendInitialized(const USGBackend* Backend)
+{
+ return Backend->IsBackendInitialized();
+}
+
+bool USGBackendKismetLibrary::InitializeBackend(USGBackend* Backend)
+{
+ return Backend->InitializeBackend();
+}
+
+bool USGBackendKismetLibrary::UninitializeBackend(USGBackend* Backend)
+{
+ return Backend->UninitializeBackend();
+}
\ No newline at end of file
diff --git a/Plugins/SenseGlove/Source/SenseGloveBackendKismet/Private/SenseGloveBackendKismet.cpp b/Plugins/SenseGlove/Source/SenseGloveBackendKismet/Private/SenseGloveBackendKismet.cpp
new file mode 100644
index 0000000..711bf39
--- /dev/null
+++ b/Plugins/SenseGlove/Source/SenseGloveBackendKismet/Private/SenseGloveBackendKismet.cpp
@@ -0,0 +1,40 @@
+/**
+ * @file
+ *
+ * @author Mamadou Babaei
+ *
+ * @section LICENSE
+ *
+ * (The MIT License)
+ *
+ * Copyright (c) 2020 - 2026 SenseGlove
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * @section DESCRIPTION
+ *
+ *
+ */
+
+
+#include "SenseGloveBackendKismet.h"
+#include "Modules/ModuleManager.h"
+#include "SenseGloveBackendKismetModule.h"
+
+IMPLEMENT_MODULE(FSenseGloveBackendKismetModule, SenseGloveBackendKismet)
diff --git a/Plugins/SenseGlove/Source/SenseGloveBackendKismet/Private/SenseGloveBackendKismet.h b/Plugins/SenseGlove/Source/SenseGloveBackendKismet/Private/SenseGloveBackendKismet.h
new file mode 100644
index 0000000..65941e7
--- /dev/null
+++ b/Plugins/SenseGlove/Source/SenseGloveBackendKismet/Private/SenseGloveBackendKismet.h
@@ -0,0 +1,38 @@
+/**
+ * @file
+ *
+ * @author Mamadou Babaei
+ *
+ * @section LICENSE
+ *
+ * (The MIT License)
+ *
+ * Copyright (c) 2020 - 2026 SenseGlove
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * @section DESCRIPTION
+ *
+ *
+ */
+
+
+#pragma once
+
+#include "CoreMinimal.h"
\ No newline at end of file
diff --git a/Plugins/SenseGlove/Source/SenseGloveBackendKismet/Private/SenseGloveBackendKismetModule.cpp b/Plugins/SenseGlove/Source/SenseGloveBackendKismet/Private/SenseGloveBackendKismetModule.cpp
new file mode 100644
index 0000000..b35c113
--- /dev/null
+++ b/Plugins/SenseGlove/Source/SenseGloveBackendKismet/Private/SenseGloveBackendKismetModule.cpp
@@ -0,0 +1,62 @@
+/**
+ * @file
+ *
+ * @author Mamadou Babaei
+ *
+ * @section LICENSE
+ *
+ * (The MIT License)
+ *
+ * Copyright (c) 2020 - 2026 SenseGlove
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * @section DESCRIPTION
+ *
+ *
+ */
+
+
+#include "SenseGloveBackendKismetModule.h"
+
+#include "SGLog/SGLog.h"
+
+#define LOCTEXT_NAMESPACE "FSenseGloveBackendKismetModule"
+
+void FSenseGloveBackendKismetModule::StartupModule()
+{
+ SGLOG("Starting up the SenseGloveBackendKismet module...");
+}
+
+void FSenseGloveBackendKismetModule::PreUnloadCallback()
+{
+ SGLOG("Unloading the SenseGloveBackendKismet module...");
+}
+
+void FSenseGloveBackendKismetModule::PostLoadCallback()
+{
+ SGLOG("Loading of SenseGloveBackendKismet module has succeeded!");
+}
+
+void FSenseGloveBackendKismetModule::ShutdownModule()
+{
+ SGLOG("Shutting down the SenseGloveBackendKismet module...");
+}
+
+#undef LOCTEXT_NAMESPACE
diff --git a/Plugins/SenseGlove/Source/SenseGloveBackendKismet/Private/SenseGloveBackendKismetModule.h b/Plugins/SenseGlove/Source/SenseGloveBackendKismet/Private/SenseGloveBackendKismetModule.h
new file mode 100644
index 0000000..d1dc974
--- /dev/null
+++ b/Plugins/SenseGlove/Source/SenseGloveBackendKismet/Private/SenseGloveBackendKismetModule.h
@@ -0,0 +1,51 @@
+/**
+ * @file
+ *
+ * @author Mamadou Babaei
+ *
+ * @section LICENSE
+ *
+ * (The MIT License)
+ *
+ * Copyright (c) 2020 - 2026 SenseGlove
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * @section DESCRIPTION
+ *
+ *
+ */
+
+
+#pragma once
+
+#include "Modules/ModuleInterface.h"
+
+#define LOCTEXT_NAMESPACE "SenseGloveBackendKismet"
+
+class FSenseGloveBackendKismetModule : public IModuleInterface
+{
+public:
+ virtual void StartupModule() override;
+ virtual void PreUnloadCallback() override;
+ virtual void PostLoadCallback() override;
+ virtual void ShutdownModule() override;
+};
+
+#undef LOCTEXT_NAMESPACE
diff --git a/Plugins/SenseGlove/Source/SenseGloveBackendKismet/Public/SGBackendKismet/SGBackendKismetLibrary.h b/Plugins/SenseGlove/Source/SenseGloveBackendKismet/Public/SGBackendKismet/SGBackendKismetLibrary.h
new file mode 100644
index 0000000..cf807a8
--- /dev/null
+++ b/Plugins/SenseGlove/Source/SenseGloveBackendKismet/Public/SGBackendKismet/SGBackendKismetLibrary.h
@@ -0,0 +1,61 @@
+/**
+ * @file
+ *
+ * @author Mamadou Babaei
+ *
+ * @section LICENSE
+ *
+ * (The MIT License)
+ *
+ * Copyright (c) 2020 - 2026 SenseGlove
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * @section DESCRIPTION
+ *
+ *
+ */
+
+
+#pragma once
+
+#include "SGKismet/SGBlueprintFunctionLibrary.h"
+
+#include "SGBackendKismetLibrary.generated.h"
+
+class USGBackend;
+
+UCLASS(meta=(ScriptName = "SenseGloveBackendKismetLibrary"))
+class SENSEGLOVEBACKENDKISMET_API USGBackendKismetLibrary final : public USGBlueprintFunctionLibrary
+{
+ GENERATED_BODY()
+
+public:
+ UFUNCTION(BlueprintCallable, Category="SenseGlove | Backend")
+ static USGBackend* GetInstance();
+
+ UFUNCTION(BlueprintPure, Category="SenseGlove | Backend")
+ static bool IsBackendInitialized(const USGBackend* Backend);
+
+ UFUNCTION(BlueprintCallable, Category="SenseGlove | Backend")
+ bool InitializeBackend(UPARAM(ref) USGBackend* Backend);
+
+ UFUNCTION(BlueprintCallable, Category="SenseGlove | Backend")
+ bool UninitializeBackend(UPARAM(ref) USGBackend* Backend);
+};
\ No newline at end of file
diff --git a/Plugins/SenseGlove/Source/SenseGloveBackendKismet/SenseGloveBackendKismet.Build.cs b/Plugins/SenseGlove/Source/SenseGloveBackendKismet/SenseGloveBackendKismet.Build.cs
new file mode 100644
index 0000000..09393c7
--- /dev/null
+++ b/Plugins/SenseGlove/Source/SenseGloveBackendKismet/SenseGloveBackendKismet.Build.cs
@@ -0,0 +1,64 @@
+/**
+ * @file
+ *
+ * @author Mamadou Babaei
+ *
+ * @section LICENSE
+ *
+ * (The MIT License)
+ *
+ * Copyright (c) 2020 - 2026 SenseGlove
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * @section DESCRIPTION
+ *
+ *
+ */
+
+
+using System;
+using UnrealBuildTool;
+
+public class SenseGloveBackendKismet : ModuleRules
+{
+ public SenseGloveBackendKismet(ReadOnlyTargetRules Target) : base(Target)
+ {
+ PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs;
+
+ PrivateDependencyModuleNames.AddRange(
+ new string[]
+ {
+ "Core",
+ "CoreUObject",
+ "Engine",
+ }
+ );
+
+ PrivateDependencyModuleNames.AddRange(
+ new string[]
+ {
+ "SenseGloveBackend",
+ "SenseGloveLog",
+ "SenseGloveKismet",
+ "SenseGloveTypes",
+ }
+ );
+ }
+}
\ No newline at end of file
diff --git a/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Private/SGBuildHacks/SGDisable_C4263.cpp b/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Private/SGBuildHacks/SGDisable_C4263.cpp
new file mode 100644
index 0000000..8a88f91
--- /dev/null
+++ b/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Private/SGBuildHacks/SGDisable_C4263.cpp
@@ -0,0 +1,36 @@
+/**
+ * @file
+ *
+ * @author Mamadou Babaei
+ *
+ * @section LICENSE
+ *
+ * (The MIT License)
+ *
+ * Copyright (c) 2020 - 2026 SenseGlove
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * @section DESCRIPTION
+ *
+ *
+ */
+
+
+#include "SGBuildHacks/SGDisable_C4263.h"
\ No newline at end of file
diff --git a/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Private/SGBuildHacks/SGDisable_C4264.cpp b/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Private/SGBuildHacks/SGDisable_C4264.cpp
new file mode 100644
index 0000000..330adc9
--- /dev/null
+++ b/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Private/SGBuildHacks/SGDisable_C4264.cpp
@@ -0,0 +1,36 @@
+/**
+ * @file
+ *
+ * @author Mamadou Babaei
+ *
+ * @section LICENSE
+ *
+ * (The MIT License)
+ *
+ * Copyright (c) 2020 - 2026 SenseGlove
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * @section DESCRIPTION
+ *
+ *
+ */
+
+
+#include "SGBuildHacks/SGDisable_C4264.h"
\ No newline at end of file
diff --git a/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Private/SGBuildHacks/SGDisable_C4706.cpp b/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Private/SGBuildHacks/SGDisable_C4706.cpp
new file mode 100644
index 0000000..d09f0c3
--- /dev/null
+++ b/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Private/SGBuildHacks/SGDisable_C4706.cpp
@@ -0,0 +1,36 @@
+/**
+ * @file
+ *
+ * @author Mamadou Babaei
+ *
+ * @section LICENSE
+ *
+ * (The MIT License)
+ *
+ * Copyright (c) 2020 - 2026 SenseGlove
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * @section DESCRIPTION
+ *
+ *
+ */
+
+
+#include "SGBuildHacks/SGDisable_C4706.h"
\ No newline at end of file
diff --git a/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Private/SGBuildHacks/SGDisable_C4800.cpp b/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Private/SGBuildHacks/SGDisable_C4800.cpp
new file mode 100644
index 0000000..f3da0ee
--- /dev/null
+++ b/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Private/SGBuildHacks/SGDisable_C4800.cpp
@@ -0,0 +1,36 @@
+/**
+ * @file
+ *
+ * @author Mamadou Babaei
+ *
+ * @section LICENSE
+ *
+ * (The MIT License)
+ *
+ * Copyright (c) 2020 - 2026 SenseGlove
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * @section DESCRIPTION
+ *
+ *
+ */
+
+
+#include "SGBuildHacks/SGDisable_C4800.h"
\ No newline at end of file
diff --git a/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Private/SGBuildHacks/SGInclude_Connect_Android.cpp b/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Private/SGBuildHacks/SGInclude_Connect_Android.cpp
new file mode 100644
index 0000000..8023405
--- /dev/null
+++ b/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Private/SGBuildHacks/SGInclude_Connect_Android.cpp
@@ -0,0 +1,36 @@
+/**
+ * @file
+ *
+ * @author Mamadou Babaei
+ *
+ * @section LICENSE
+ *
+ * (The MIT License)
+ *
+ * Copyright (c) 2020 - 2026 SenseGlove
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * @section DESCRIPTION
+ *
+ *
+ */
+
+
+#include "SGBuildHacks/SGInclude_Connect_Android.h"
\ No newline at end of file
diff --git a/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Private/SGBuildHacks/SGInclude_Connect_SGConnect.cpp b/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Private/SGBuildHacks/SGInclude_Connect_SGConnect.cpp
new file mode 100644
index 0000000..6a8b739
--- /dev/null
+++ b/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Private/SGBuildHacks/SGInclude_Connect_SGConnect.cpp
@@ -0,0 +1,36 @@
+/**
+ * @file
+ *
+ * @author Mamadou Babaei
+ *
+ * @section LICENSE
+ *
+ * (The MIT License)
+ *
+ * Copyright (c) 2020 - 2026 SenseGlove
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * @section DESCRIPTION
+ *
+ *
+ */
+
+
+#include "SGBuildHacks/SGInclude_Connect_SGConnect.h"
\ No newline at end of file
diff --git a/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Private/SGBuildHacks/SGInclude_Core_Anatomy.cpp b/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Private/SGBuildHacks/SGInclude_Core_Anatomy.cpp
new file mode 100644
index 0000000..2c5221f
--- /dev/null
+++ b/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Private/SGBuildHacks/SGInclude_Core_Anatomy.cpp
@@ -0,0 +1,36 @@
+/**
+ * @file
+ *
+ * @author Mamadou Babaei
+ *
+ * @section LICENSE
+ *
+ * (The MIT License)
+ *
+ * Copyright (c) 2020 - 2026 SenseGlove
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * @section DESCRIPTION
+ *
+ *
+ */
+
+
+#include "SGBuildHacks/SGInclude_Core_Anatomy.h"
\ No newline at end of file
diff --git a/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Private/SGBuildHacks/SGInclude_Core_Android.cpp b/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Private/SGBuildHacks/SGInclude_Core_Android.cpp
new file mode 100644
index 0000000..fe2de45
--- /dev/null
+++ b/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Private/SGBuildHacks/SGInclude_Core_Android.cpp
@@ -0,0 +1,36 @@
+/**
+ * @file
+ *
+ * @author Mamadou Babaei
+ *
+ * @section LICENSE
+ *
+ * (The MIT License)
+ *
+ * Copyright (c) 2020 - 2026 SenseGlove
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * @section DESCRIPTION
+ *
+ *
+ */
+
+
+#include "SGBuildHacks/SGInclude_Core_Android.h"
\ No newline at end of file
diff --git a/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Private/SGBuildHacks/SGInclude_Core_BasicHandModel.cpp b/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Private/SGBuildHacks/SGInclude_Core_BasicHandModel.cpp
new file mode 100644
index 0000000..34cb7f0
--- /dev/null
+++ b/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Private/SGBuildHacks/SGInclude_Core_BasicHandModel.cpp
@@ -0,0 +1,36 @@
+/**
+ * @file
+ *
+ * @author Mamadou Babaei
+ *
+ * @section LICENSE
+ *
+ * (The MIT License)
+ *
+ * Copyright (c) 2020 - 2026 SenseGlove
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * @section DESCRIPTION
+ *
+ *
+ */
+
+
+#include "SGBuildHacks/SGInclude_Core_BasicHandModel.h"
\ No newline at end of file
diff --git a/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Private/SGBuildHacks/SGInclude_Core_BetaDevice.cpp b/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Private/SGBuildHacks/SGInclude_Core_BetaDevice.cpp
new file mode 100644
index 0000000..b282f84
--- /dev/null
+++ b/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Private/SGBuildHacks/SGInclude_Core_BetaDevice.cpp
@@ -0,0 +1,36 @@
+/**
+ * @file
+ *
+ * @author Mamadou Babaei
+ *
+ * @section LICENSE
+ *
+ * (The MIT License)
+ *
+ * Copyright (c) 2020 - 2026 SenseGlove
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * @section DESCRIPTION
+ *
+ *
+ */
+
+
+#include "SGBuildHacks/SGInclude_Core_BetaDevice.h"
\ No newline at end of file
diff --git a/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Private/SGBuildHacks/SGInclude_Core_CustomWaveform.cpp b/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Private/SGBuildHacks/SGInclude_Core_CustomWaveform.cpp
new file mode 100644
index 0000000..3fe2d73
--- /dev/null
+++ b/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Private/SGBuildHacks/SGInclude_Core_CustomWaveform.cpp
@@ -0,0 +1,36 @@
+/**
+ * @file
+ *
+ * @author Mamadou Babaei
+ *
+ * @section LICENSE
+ *
+ * (The MIT License)
+ *
+ * Copyright (c) 2020 - 2026 SenseGlove
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * @section DESCRIPTION
+ *
+ *
+ */
+
+
+#include "SGBuildHacks/SGInclude_Core_CustomWaveform.h"
\ No newline at end of file
diff --git a/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Private/SGBuildHacks/SGInclude_Core_DeviceList.cpp b/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Private/SGBuildHacks/SGInclude_Core_DeviceList.cpp
new file mode 100644
index 0000000..ae36f3c
--- /dev/null
+++ b/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Private/SGBuildHacks/SGInclude_Core_DeviceList.cpp
@@ -0,0 +1,36 @@
+/**
+ * @file
+ *
+ * @author Mamadou Babaei
+ *
+ * @section LICENSE
+ *
+ * (The MIT License)
+ *
+ * Copyright (c) 2020 - 2026 SenseGlove
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * @section DESCRIPTION
+ *
+ *
+ */
+
+
+#include "SGBuildHacks/SGInclude_Core_DeviceList.h"
\ No newline at end of file
diff --git a/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Private/SGBuildHacks/SGInclude_Core_DeviceModel.cpp b/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Private/SGBuildHacks/SGInclude_Core_DeviceModel.cpp
new file mode 100644
index 0000000..f2bc54b
--- /dev/null
+++ b/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Private/SGBuildHacks/SGInclude_Core_DeviceModel.cpp
@@ -0,0 +1,36 @@
+/**
+ * @file
+ *
+ * @author Mamadou Babaei
+ *
+ * @section LICENSE
+ *
+ * (The MIT License)
+ *
+ * Copyright (c) 2020 - 2026 SenseGlove
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * @section DESCRIPTION
+ *
+ *
+ */
+
+
+#include "SGBuildHacks/SGInclude_Core_DeviceModel.h"
\ No newline at end of file
diff --git a/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Private/SGBuildHacks/SGInclude_Core_Fingers.cpp b/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Private/SGBuildHacks/SGInclude_Core_Fingers.cpp
new file mode 100644
index 0000000..9c4a84e
--- /dev/null
+++ b/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Private/SGBuildHacks/SGInclude_Core_Fingers.cpp
@@ -0,0 +1,36 @@
+/**
+ * @file
+ *
+ * @author Mamadou Babaei
+ *
+ * @section LICENSE
+ *
+ * (The MIT License)
+ *
+ * Copyright (c) 2020 - 2026 SenseGlove
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * @section DESCRIPTION
+ *
+ *
+ */
+
+
+#include "SGBuildHacks/SGInclude_Core_Fingers.h"
\ No newline at end of file
diff --git a/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Private/SGBuildHacks/SGInclude_Core_HandInterpolator.cpp b/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Private/SGBuildHacks/SGInclude_Core_HandInterpolator.cpp
new file mode 100644
index 0000000..31c94ee
--- /dev/null
+++ b/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Private/SGBuildHacks/SGInclude_Core_HandInterpolator.cpp
@@ -0,0 +1,36 @@
+/**
+ * @file
+ *
+ * @author Mamadou Babaei
+ *
+ * @section LICENSE
+ *
+ * (The MIT License)
+ *
+ * Copyright (c) 2020 - 2026 SenseGlove
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * @section DESCRIPTION
+ *
+ *
+ */
+
+
+#include "SGBuildHacks/SGInclude_Core_HandInterpolator.h"
\ No newline at end of file
diff --git a/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Private/SGBuildHacks/SGInclude_Core_HandLayer.cpp b/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Private/SGBuildHacks/SGInclude_Core_HandLayer.cpp
new file mode 100644
index 0000000..fba9c91
--- /dev/null
+++ b/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Private/SGBuildHacks/SGInclude_Core_HandLayer.cpp
@@ -0,0 +1,36 @@
+/**
+ * @file
+ *
+ * @author Mamadou Babaei
+ *
+ * @section LICENSE
+ *
+ * (The MIT License)
+ *
+ * Copyright (c) 2020 - 2026 SenseGlove
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * @section DESCRIPTION
+ *
+ *
+ */
+
+
+#include "SGBuildHacks/SGInclude_Core_HandLayer.h"
\ No newline at end of file
diff --git a/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Private/SGBuildHacks/SGInclude_Core_HandPose.cpp b/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Private/SGBuildHacks/SGInclude_Core_HandPose.cpp
new file mode 100644
index 0000000..0c7182e
--- /dev/null
+++ b/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Private/SGBuildHacks/SGInclude_Core_HandPose.cpp
@@ -0,0 +1,36 @@
+/**
+ * @file
+ *
+ * @author Mamadou Babaei
+ *
+ * @section LICENSE
+ *
+ * (The MIT License)
+ *
+ * Copyright (c) 2020 - 2026 SenseGlove
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * @section DESCRIPTION
+ *
+ *
+ */
+
+
+#include "SGBuildHacks/SGInclude_Core_HandPose.h"
\ No newline at end of file
diff --git a/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Private/SGBuildHacks/SGInclude_Core_HapticChannelInfo.cpp b/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Private/SGBuildHacks/SGInclude_Core_HapticChannelInfo.cpp
new file mode 100644
index 0000000..2eb35ab
--- /dev/null
+++ b/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Private/SGBuildHacks/SGInclude_Core_HapticChannelInfo.cpp
@@ -0,0 +1,36 @@
+/**
+ * @file
+ *
+ * @author Mamadou Babaei
+ *
+ * @section LICENSE
+ *
+ * (The MIT License)
+ *
+ * Copyright (c) 2020 - 2026 SenseGlove
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * @section DESCRIPTION
+ *
+ *
+ */
+
+
+#include "SGBuildHacks/SGInclude_Core_HapticChannelInfo.h"
\ No newline at end of file
diff --git a/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Private/SGBuildHacks/SGInclude_Core_HapticGlove.cpp b/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Private/SGBuildHacks/SGInclude_Core_HapticGlove.cpp
new file mode 100644
index 0000000..f2f0bb0
--- /dev/null
+++ b/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Private/SGBuildHacks/SGInclude_Core_HapticGlove.cpp
@@ -0,0 +1,36 @@
+/**
+ * @file
+ *
+ * @author Mamadou Babaei
+ *
+ * @section LICENSE
+ *
+ * (The MIT License)
+ *
+ * Copyright (c) 2020 - 2026 SenseGlove
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * @section DESCRIPTION
+ *
+ *
+ */
+
+
+#include "SGBuildHacks/SGInclude_Core_HapticGlove.h"
diff --git a/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Private/SGBuildHacks/SGInclude_Core_InterpolationSet.cpp b/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Private/SGBuildHacks/SGInclude_Core_InterpolationSet.cpp
new file mode 100644
index 0000000..2eb09b5
--- /dev/null
+++ b/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Private/SGBuildHacks/SGInclude_Core_InterpolationSet.cpp
@@ -0,0 +1,36 @@
+/**
+ * @file
+ *
+ * @author Mamadou Babaei
+ *
+ * @section LICENSE
+ *
+ * (The MIT License)
+ *
+ * Copyright (c) 2020 - 2026 SenseGlove
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * @section DESCRIPTION
+ *
+ *
+ */
+
+
+#include "SGBuildHacks/SGInclude_Core_InterpolationSet.h"
\ No newline at end of file
diff --git a/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Private/SGBuildHacks/SGInclude_Core_JointKinematics.cpp b/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Private/SGBuildHacks/SGInclude_Core_JointKinematics.cpp
new file mode 100644
index 0000000..0463bba
--- /dev/null
+++ b/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Private/SGBuildHacks/SGInclude_Core_JointKinematics.cpp
@@ -0,0 +1,36 @@
+/**
+ * @file
+ *
+ * @author Mamadou Babaei
+ *
+ * @section LICENSE
+ *
+ * (The MIT License)
+ *
+ * Copyright (c) 2020 - 2026 SenseGlove
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * @section DESCRIPTION
+ *
+ *
+ */
+
+
+#include "SGBuildHacks/SGInclude_Core_JointKinematics.h"
\ No newline at end of file
diff --git a/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Private/SGBuildHacks/SGInclude_Core_Library.cpp b/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Private/SGBuildHacks/SGInclude_Core_Library.cpp
new file mode 100644
index 0000000..0eafdcc
--- /dev/null
+++ b/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Private/SGBuildHacks/SGInclude_Core_Library.cpp
@@ -0,0 +1,36 @@
+/**
+ * @file
+ *
+ * @author Mamadou Babaei
+ *
+ * @section LICENSE
+ *
+ * (The MIT License)
+ *
+ * Copyright (c) 2020 - 2026 SenseGlove
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * @section DESCRIPTION
+ *
+ *
+ */
+
+
+#include "SGBuildHacks/SGInclude_Core_Library.h"
\ No newline at end of file
diff --git a/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Private/SGBuildHacks/SGInclude_Core_NormalizationState.cpp b/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Private/SGBuildHacks/SGInclude_Core_NormalizationState.cpp
new file mode 100644
index 0000000..7352146
--- /dev/null
+++ b/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Private/SGBuildHacks/SGInclude_Core_NormalizationState.cpp
@@ -0,0 +1,36 @@
+/**
+ * @file
+ *
+ * @author Mamadou Babaei
+ *
+ * @section LICENSE
+ *
+ * (The MIT License)
+ *
+ * Copyright (c) 2020 - 2026 SenseGlove
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * @section DESCRIPTION
+ *
+ *
+ */
+
+
+#include "SGBuildHacks/SGInclude_Core_NormalizationState.h"
\ No newline at end of file
diff --git a/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Private/SGBuildHacks/SGInclude_Core_Nova2Glove.cpp b/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Private/SGBuildHacks/SGInclude_Core_Nova2Glove.cpp
new file mode 100644
index 0000000..984ec57
--- /dev/null
+++ b/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Private/SGBuildHacks/SGInclude_Core_Nova2Glove.cpp
@@ -0,0 +1,36 @@
+/**
+ * @file
+ *
+ * @author Mamadou Babaei
+ *
+ * @section LICENSE
+ *
+ * (The MIT License)
+ *
+ * Copyright (c) 2020 - 2026 SenseGlove
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * @section DESCRIPTION
+ *
+ *
+ */
+
+
+#include "SGBuildHacks/SGInclude_Core_Nova2Glove.h"
\ No newline at end of file
diff --git a/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Private/SGBuildHacks/SGInclude_Core_Nova2GloveSensorData.cpp b/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Private/SGBuildHacks/SGInclude_Core_Nova2GloveSensorData.cpp
new file mode 100644
index 0000000..1f1742c
--- /dev/null
+++ b/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Private/SGBuildHacks/SGInclude_Core_Nova2GloveSensorData.cpp
@@ -0,0 +1,36 @@
+/**
+ * @file
+ *
+ * @author Mamadou Babaei
+ *
+ * @section LICENSE
+ *
+ * (The MIT License)
+ *
+ * Copyright (c) 2020 - 2026 SenseGlove
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * @section DESCRIPTION
+ *
+ *
+ */
+
+
+#include "SGBuildHacks/SGInclude_Core_Nova2GloveSensorData.h"
\ No newline at end of file
diff --git a/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Private/SGBuildHacks/SGInclude_Core_NovaGlove.cpp b/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Private/SGBuildHacks/SGInclude_Core_NovaGlove.cpp
new file mode 100644
index 0000000..ff9ef06
--- /dev/null
+++ b/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Private/SGBuildHacks/SGInclude_Core_NovaGlove.cpp
@@ -0,0 +1,36 @@
+/**
+ * @file
+ *
+ * @author Mamadou Babaei
+ *
+ * @section LICENSE
+ *
+ * (The MIT License)
+ *
+ * Copyright (c) 2020 - 2026 SenseGlove
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * @section DESCRIPTION
+ *
+ *
+ */
+
+
+#include "SGBuildHacks/SGInclude_Core_NovaGlove.h"
\ No newline at end of file
diff --git a/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Private/SGBuildHacks/SGInclude_Core_NovaGloveInfo.cpp b/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Private/SGBuildHacks/SGInclude_Core_NovaGloveInfo.cpp
new file mode 100644
index 0000000..63e3630
--- /dev/null
+++ b/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Private/SGBuildHacks/SGInclude_Core_NovaGloveInfo.cpp
@@ -0,0 +1,36 @@
+/**
+ * @file
+ *
+ * @author Mamadou Babaei
+ *
+ * @section LICENSE
+ *
+ * (The MIT License)
+ *
+ * Copyright (c) 2020 - 2026 SenseGlove
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * @section DESCRIPTION
+ *
+ *
+ */
+
+
+#include "SGBuildHacks/SGInclude_Core_NovaGloveInfo.h"
\ No newline at end of file
diff --git a/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Private/SGBuildHacks/SGInclude_Core_NovaGloveSensorData.cpp b/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Private/SGBuildHacks/SGInclude_Core_NovaGloveSensorData.cpp
new file mode 100644
index 0000000..203ad79
--- /dev/null
+++ b/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Private/SGBuildHacks/SGInclude_Core_NovaGloveSensorData.cpp
@@ -0,0 +1,36 @@
+/**
+ * @file
+ *
+ * @author Mamadou Babaei
+ *
+ * @section LICENSE
+ *
+ * (The MIT License)
+ *
+ * Copyright (c) 2020 - 2026 SenseGlove
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * @section DESCRIPTION
+ *
+ *
+ */
+
+
+#include "SGBuildHacks/SGInclude_Core_NovaGloveSensorData.h"
\ No newline at end of file
diff --git a/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Private/SGBuildHacks/SGInclude_Core_NovaGloveVars.cpp b/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Private/SGBuildHacks/SGInclude_Core_NovaGloveVars.cpp
new file mode 100644
index 0000000..d09177a
--- /dev/null
+++ b/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Private/SGBuildHacks/SGInclude_Core_NovaGloveVars.cpp
@@ -0,0 +1,36 @@
+/**
+ * @file
+ *
+ * @author Mamadou Babaei
+ *
+ * @section LICENSE
+ *
+ * (The MIT License)
+ *
+ * Copyright (c) 2020 - 2026 SenseGlove
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * @section DESCRIPTION
+ *
+ *
+ */
+
+
+#include "SGBuildHacks/SGInclude_Core_NovaGloveVars.h"
\ No newline at end of file
diff --git a/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Private/SGBuildHacks/SGInclude_Core_Quat.cpp b/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Private/SGBuildHacks/SGInclude_Core_Quat.cpp
new file mode 100644
index 0000000..563f5cd
--- /dev/null
+++ b/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Private/SGBuildHacks/SGInclude_Core_Quat.cpp
@@ -0,0 +1,36 @@
+/**
+ * @file
+ *
+ * @author Mamadou Babaei
+ *
+ * @section LICENSE
+ *
+ * (The MIT License)
+ *
+ * Copyright (c) 2020 - 2026 SenseGlove
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * @section DESCRIPTION
+ *
+ *
+ */
+
+
+#include "SGBuildHacks/SGInclude_Core_Quat.h"
\ No newline at end of file
diff --git a/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Private/SGBuildHacks/SGInclude_Core_SGDevice.cpp b/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Private/SGBuildHacks/SGInclude_Core_SGDevice.cpp
new file mode 100644
index 0000000..6896df6
--- /dev/null
+++ b/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Private/SGBuildHacks/SGInclude_Core_SGDevice.cpp
@@ -0,0 +1,36 @@
+/**
+ * @file
+ *
+ * @author Mamadou Babaei
+ *
+ * @section LICENSE
+ *
+ * (The MIT License)
+ *
+ * Copyright (c) 2020 - 2026 SenseGlove
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * @section DESCRIPTION
+ *
+ *
+ */
+
+
+#include "SGBuildHacks/SGInclude_Core_SGDevice.h"
\ No newline at end of file
diff --git a/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Private/SGBuildHacks/SGInclude_Core_SenseCom.cpp b/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Private/SGBuildHacks/SGInclude_Core_SenseCom.cpp
new file mode 100644
index 0000000..101fdb2
--- /dev/null
+++ b/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Private/SGBuildHacks/SGInclude_Core_SenseCom.cpp
@@ -0,0 +1,36 @@
+/**
+ * @file
+ *
+ * @author Mamadou Babaei
+ *
+ * @section LICENSE
+ *
+ * (The MIT License)
+ *
+ * Copyright (c) 2020 - 2026 SenseGlove
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * @section DESCRIPTION
+ *
+ *
+ */
+
+
+#include "SGBuildHacks/SGInclude_Core_SenseCom.h"
\ No newline at end of file
diff --git a/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Private/SGBuildHacks/SGInclude_Core_SenseGlove.cpp b/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Private/SGBuildHacks/SGInclude_Core_SenseGlove.cpp
new file mode 100644
index 0000000..4d5f13c
--- /dev/null
+++ b/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Private/SGBuildHacks/SGInclude_Core_SenseGlove.cpp
@@ -0,0 +1,36 @@
+/**
+ * @file
+ *
+ * @author Mamadou Babaei
+ *
+ * @section LICENSE
+ *
+ * (The MIT License)
+ *
+ * Copyright (c) 2020 - 2026 SenseGlove
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * @section DESCRIPTION
+ *
+ *
+ */
+
+
+#include "SGBuildHacks/SGInclude_Core_SenseGlove.h"
\ No newline at end of file
diff --git a/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Private/SGBuildHacks/SGInclude_Core_SenseGloveInfo.cpp b/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Private/SGBuildHacks/SGInclude_Core_SenseGloveInfo.cpp
new file mode 100644
index 0000000..e3886a4
--- /dev/null
+++ b/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Private/SGBuildHacks/SGInclude_Core_SenseGloveInfo.cpp
@@ -0,0 +1,36 @@
+/**
+ * @file
+ *
+ * @author Mamadou Babaei
+ *
+ * @section LICENSE
+ *
+ * (The MIT License)
+ *
+ * Copyright (c) 2020 - 2026 SenseGlove
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * @section DESCRIPTION
+ *
+ *
+ */
+
+
+#include "SGBuildHacks/SGInclude_Core_SenseGloveInfo.h"
\ No newline at end of file
diff --git a/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Private/SGBuildHacks/SGInclude_Core_SenseGlovePose.cpp b/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Private/SGBuildHacks/SGInclude_Core_SenseGlovePose.cpp
new file mode 100644
index 0000000..e5fa570
--- /dev/null
+++ b/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Private/SGBuildHacks/SGInclude_Core_SenseGlovePose.cpp
@@ -0,0 +1,36 @@
+/**
+ * @file
+ *
+ * @author Mamadou Babaei
+ *
+ * @section LICENSE
+ *
+ * (The MIT License)
+ *
+ * Copyright (c) 2020 - 2026 SenseGlove
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * @section DESCRIPTION
+ *
+ *
+ */
+
+
+#include "SGBuildHacks/SGInclude_Core_SenseGlovePose.h"
\ No newline at end of file
diff --git a/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Private/SGBuildHacks/SGInclude_Core_SenseGloveSensorData.cpp b/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Private/SGBuildHacks/SGInclude_Core_SenseGloveSensorData.cpp
new file mode 100644
index 0000000..f2cbcae
--- /dev/null
+++ b/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Private/SGBuildHacks/SGInclude_Core_SenseGloveSensorData.cpp
@@ -0,0 +1,36 @@
+/**
+ * @file
+ *
+ * @author Mamadou Babaei
+ *
+ * @section LICENSE
+ *
+ * (The MIT License)
+ *
+ * Copyright (c) 2020 - 2026 SenseGlove
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * @section DESCRIPTION
+ *
+ *
+ */
+
+
+#include "SGBuildHacks/SGInclude_Core_SenseGloveSensorData.h"
\ No newline at end of file
diff --git a/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Private/SGBuildHacks/SGInclude_Core_SenseGloveSensorNormalizer.cpp b/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Private/SGBuildHacks/SGInclude_Core_SenseGloveSensorNormalizer.cpp
new file mode 100644
index 0000000..184dbe8
--- /dev/null
+++ b/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Private/SGBuildHacks/SGInclude_Core_SenseGloveSensorNormalizer.cpp
@@ -0,0 +1,36 @@
+/**
+ * @file
+ *
+ * @author Mamadou Babaei
+ *
+ * @section LICENSE
+ *
+ * (The MIT License)
+ *
+ * Copyright (c) 2020 - 2026 SenseGlove
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * @section DESCRIPTION
+ *
+ *
+ */
+
+
+#include "SGBuildHacks/SGInclude_Core_SenseGloveSensorNormalizer.h"
\ No newline at end of file
diff --git a/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Private/SGBuildHacks/SGInclude_Core_SenseGloveThumperCommand.cpp b/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Private/SGBuildHacks/SGInclude_Core_SenseGloveThumperCommand.cpp
new file mode 100644
index 0000000..bcee462
--- /dev/null
+++ b/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Private/SGBuildHacks/SGInclude_Core_SenseGloveThumperCommand.cpp
@@ -0,0 +1,36 @@
+/**
+ * @file
+ *
+ * @author Mamadou Babaei
+ *
+ * @section LICENSE
+ *
+ * (The MIT License)
+ *
+ * Copyright (c) 2020 - 2026 SenseGlove
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * @section DESCRIPTION
+ *
+ *
+ */
+
+
+#include "SGBuildHacks/SGInclude_Core_SenseGloveThumperCommand.h"
\ No newline at end of file
diff --git a/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Private/SGBuildHacks/SGInclude_Core_SenseGloveVars.cpp b/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Private/SGBuildHacks/SGInclude_Core_SenseGloveVars.cpp
new file mode 100644
index 0000000..ed47f75
--- /dev/null
+++ b/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Private/SGBuildHacks/SGInclude_Core_SenseGloveVars.cpp
@@ -0,0 +1,36 @@
+/**
+ * @file
+ *
+ * @author Mamadou Babaei
+ *
+ * @section LICENSE
+ *
+ * (The MIT License)
+ *
+ * Copyright (c) 2020 - 2026 SenseGlove
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * @section DESCRIPTION
+ *
+ *
+ */
+
+
+#include "SGBuildHacks/SGInclude_Core_SenseGloveVars.h"
\ No newline at end of file
diff --git a/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Private/SGBuildHacks/SGInclude_Core_SensorNormalization.cpp b/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Private/SGBuildHacks/SGInclude_Core_SensorNormalization.cpp
new file mode 100644
index 0000000..bf9f2b0
--- /dev/null
+++ b/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Private/SGBuildHacks/SGInclude_Core_SensorNormalization.cpp
@@ -0,0 +1,36 @@
+/**
+ * @file
+ *
+ * @author Mamadou Babaei
+ *
+ * @section LICENSE
+ *
+ * (The MIT License)
+ *
+ * Copyright (c) 2020 - 2026 SenseGlove
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * @section DESCRIPTION
+ *
+ *
+ */
+
+
+#include "SGBuildHacks/SGInclude_Core_SensorNormalization.h"
\ No newline at end of file
diff --git a/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Private/SGBuildHacks/SGInclude_Core_ThresholdCommand.cpp b/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Private/SGBuildHacks/SGInclude_Core_ThresholdCommand.cpp
new file mode 100644
index 0000000..9d91033
--- /dev/null
+++ b/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Private/SGBuildHacks/SGInclude_Core_ThresholdCommand.cpp
@@ -0,0 +1,36 @@
+/**
+ * @file
+ *
+ * @author Mamadou Babaei
+ *
+ * @section LICENSE
+ *
+ * (The MIT License)
+ *
+ * Copyright (c) 2020 - 2026 SenseGlove
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * @section DESCRIPTION
+ *
+ *
+ */
+
+
+#include "SGBuildHacks/SGInclude_Core_ThresholdCommand.h"
\ No newline at end of file
diff --git a/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Private/SGBuildHacks/SGInclude_Core_Tracking.cpp b/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Private/SGBuildHacks/SGInclude_Core_Tracking.cpp
new file mode 100644
index 0000000..3292eac
--- /dev/null
+++ b/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Private/SGBuildHacks/SGInclude_Core_Tracking.cpp
@@ -0,0 +1,36 @@
+/**
+ * @file
+ *
+ * @author Mamadou Babaei
+ *
+ * @section LICENSE
+ *
+ * (The MIT License)
+ *
+ * Copyright (c) 2020 - 2026 SenseGlove
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * @section DESCRIPTION
+ *
+ *
+ */
+
+
+#include "SGBuildHacks/SGInclude_Core_Tracking.h"
\ No newline at end of file
diff --git a/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Private/SGBuildHacks/SGInclude_Core_Vect3D.cpp b/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Private/SGBuildHacks/SGInclude_Core_Vect3D.cpp
new file mode 100644
index 0000000..0ebc601
--- /dev/null
+++ b/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Private/SGBuildHacks/SGInclude_Core_Vect3D.cpp
@@ -0,0 +1,36 @@
+/**
+ * @file
+ *
+ * @author Mamadou Babaei
+ *
+ * @section LICENSE
+ *
+ * (The MIT License)
+ *
+ * Copyright (c) 2020 - 2026 SenseGlove
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * @section DESCRIPTION
+ *
+ *
+ */
+
+
+#include "SGBuildHacks/SGInclude_Core_Vect3D.h"
\ No newline at end of file
diff --git a/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Private/SGBuildHacks/SGInclude_Windows.cpp b/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Private/SGBuildHacks/SGInclude_Windows.cpp
new file mode 100644
index 0000000..3cb3c29
--- /dev/null
+++ b/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Private/SGBuildHacks/SGInclude_Windows.cpp
@@ -0,0 +1,36 @@
+/**
+ * @file
+ *
+ * @author Mamadou Babaei
+ *
+ * @section LICENSE
+ *
+ * (The MIT License)
+ *
+ * Copyright (c) 2020 - 2026 SenseGlove
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * @section DESCRIPTION
+ *
+ *
+ */
+
+
+#include "SGBuildHacks/SGInclude_Windows.h"
\ No newline at end of file
diff --git a/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Private/SGBuildHacks/SGPlatform.cpp b/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Private/SGBuildHacks/SGPlatform.cpp
new file mode 100644
index 0000000..54a3546
--- /dev/null
+++ b/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Private/SGBuildHacks/SGPlatform.cpp
@@ -0,0 +1,36 @@
+/**
+ * @file
+ *
+ * @author Mamadou Babaei
+ *
+ * @section LICENSE
+ *
+ * (The MIT License)
+ *
+ * Copyright (c) 2020 - 2026 SenseGlove
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * @section DESCRIPTION
+ *
+ *
+ */
+
+
+#include "SGBuildHacks/SGPlatform.h"
\ No newline at end of file
diff --git a/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Private/SGBuildHacks/SGRestore_C4263.cpp b/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Private/SGBuildHacks/SGRestore_C4263.cpp
new file mode 100644
index 0000000..733b282
--- /dev/null
+++ b/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Private/SGBuildHacks/SGRestore_C4263.cpp
@@ -0,0 +1,36 @@
+/**
+ * @file
+ *
+ * @author Mamadou Babaei
+ *
+ * @section LICENSE
+ *
+ * (The MIT License)
+ *
+ * Copyright (c) 2020 - 2026 SenseGlove
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * @section DESCRIPTION
+ *
+ *
+ */
+
+
+#include "SGBuildHacks/SGRestore_C4263.h"
\ No newline at end of file
diff --git a/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Private/SGBuildHacks/SGRestore_C4264.cpp b/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Private/SGBuildHacks/SGRestore_C4264.cpp
new file mode 100644
index 0000000..e58a9c1
--- /dev/null
+++ b/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Private/SGBuildHacks/SGRestore_C4264.cpp
@@ -0,0 +1,36 @@
+/**
+ * @file
+ *
+ * @author Mamadou Babaei
+ *
+ * @section LICENSE
+ *
+ * (The MIT License)
+ *
+ * Copyright (c) 2020 - 2026 SenseGlove
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * @section DESCRIPTION
+ *
+ *
+ */
+
+
+#include "SGBuildHacks/SGRestore_C4264.h"
\ No newline at end of file
diff --git a/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Private/SGBuildHacks/SGRestore_C4706.cpp b/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Private/SGBuildHacks/SGRestore_C4706.cpp
new file mode 100644
index 0000000..7c46b70
--- /dev/null
+++ b/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Private/SGBuildHacks/SGRestore_C4706.cpp
@@ -0,0 +1,36 @@
+/**
+ * @file
+ *
+ * @author Mamadou Babaei
+ *
+ * @section LICENSE
+ *
+ * (The MIT License)
+ *
+ * Copyright (c) 2020 - 2026 SenseGlove
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * @section DESCRIPTION
+ *
+ *
+ */
+
+
+#include "SGBuildHacks/SGRestore_C4706.h"
\ No newline at end of file
diff --git a/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Private/SGBuildHacks/SGRestore_C4800.cpp b/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Private/SGBuildHacks/SGRestore_C4800.cpp
new file mode 100644
index 0000000..2348547
--- /dev/null
+++ b/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Private/SGBuildHacks/SGRestore_C4800.cpp
@@ -0,0 +1,36 @@
+/**
+ * @file
+ *
+ * @author Mamadou Babaei
+ *
+ * @section LICENSE
+ *
+ * (The MIT License)
+ *
+ * Copyright (c) 2020 - 2026 SenseGlove
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * @section DESCRIPTION
+ *
+ *
+ */
+
+
+#include "SGBuildHacks/SGRestore_C4800.h"
\ No newline at end of file
diff --git a/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Private/SGBuildHacks/SGRestore_TEXT.cpp b/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Private/SGBuildHacks/SGRestore_TEXT.cpp
new file mode 100644
index 0000000..d83316e
--- /dev/null
+++ b/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Private/SGBuildHacks/SGRestore_TEXT.cpp
@@ -0,0 +1,36 @@
+/**
+ * @file
+ *
+ * @author Mamadou Babaei
+ *
+ * @section LICENSE
+ *
+ * (The MIT License)
+ *
+ * Copyright (c) 2020 - 2026 SenseGlove
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * @section DESCRIPTION
+ *
+ *
+ */
+
+
+#include "SGBuildHacks/SGRestore_TEXT.h"
\ No newline at end of file
diff --git a/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Private/SGBuildHacks/SGRestore_check.cpp b/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Private/SGBuildHacks/SGRestore_check.cpp
new file mode 100644
index 0000000..b831046
--- /dev/null
+++ b/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Private/SGBuildHacks/SGRestore_check.cpp
@@ -0,0 +1,36 @@
+/**
+ * @file
+ *
+ * @author Mamadou Babaei
+ *
+ * @section LICENSE
+ *
+ * (The MIT License)
+ *
+ * Copyright (c) 2020 - 2026 SenseGlove
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * @section DESCRIPTION
+ *
+ *
+ */
+
+
+#include "SGBuildHacks/SGRestore_check.h"
\ No newline at end of file
diff --git a/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Private/SGBuildHacks/SGUndef_TEXT.cpp b/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Private/SGBuildHacks/SGUndef_TEXT.cpp
new file mode 100644
index 0000000..6c06aa2
--- /dev/null
+++ b/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Private/SGBuildHacks/SGUndef_TEXT.cpp
@@ -0,0 +1,36 @@
+/**
+ * @file
+ *
+ * @author Mamadou Babaei
+ *
+ * @section LICENSE
+ *
+ * (The MIT License)
+ *
+ * Copyright (c) 2020 - 2026 SenseGlove
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * @section DESCRIPTION
+ *
+ *
+ */
+
+
+#include "SGBuildHacks/SGUndef_TEXT.h"
\ No newline at end of file
diff --git a/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Private/SGBuildHacks/SGUndef_check.cpp b/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Private/SGBuildHacks/SGUndef_check.cpp
new file mode 100644
index 0000000..b348b91
--- /dev/null
+++ b/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Private/SGBuildHacks/SGUndef_check.cpp
@@ -0,0 +1,36 @@
+/**
+ * @file
+ *
+ * @author Mamadou Babaei
+ *
+ * @section LICENSE
+ *
+ * (The MIT License)
+ *
+ * Copyright (c) 2020 - 2026 SenseGlove
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * @section DESCRIPTION
+ *
+ *
+ */
+
+
+#include "SGBuildHacks/SGUndef_check.h"
\ No newline at end of file
diff --git a/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Private/SenseGloveBuildHacks.cpp b/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Private/SenseGloveBuildHacks.cpp
new file mode 100644
index 0000000..7445886
--- /dev/null
+++ b/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Private/SenseGloveBuildHacks.cpp
@@ -0,0 +1,40 @@
+/**
+ * @file
+ *
+ * @author Mamadou Babaei
+ *
+ * @section LICENSE
+ *
+ * (The MIT License)
+ *
+ * Copyright (c) 2020 - 2026 SenseGlove
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * @section DESCRIPTION
+ *
+ *
+ */
+
+
+#include "SenseGloveBuildHacks.h"
+#include "Modules/ModuleManager.h"
+#include "SenseGloveBuildHacksModule.h"
+
+IMPLEMENT_MODULE(FSenseGloveBuildHacksModule, SenseGloveBuildHacks)
\ No newline at end of file
diff --git a/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Private/SenseGloveBuildHacks.h b/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Private/SenseGloveBuildHacks.h
new file mode 100644
index 0000000..65941e7
--- /dev/null
+++ b/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Private/SenseGloveBuildHacks.h
@@ -0,0 +1,38 @@
+/**
+ * @file
+ *
+ * @author Mamadou Babaei
+ *
+ * @section LICENSE
+ *
+ * (The MIT License)
+ *
+ * Copyright (c) 2020 - 2026 SenseGlove
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * @section DESCRIPTION
+ *
+ *
+ */
+
+
+#pragma once
+
+#include "CoreMinimal.h"
\ No newline at end of file
diff --git a/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Private/SenseGloveBuildHacksModule.cpp b/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Private/SenseGloveBuildHacksModule.cpp
new file mode 100644
index 0000000..6503576
--- /dev/null
+++ b/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Private/SenseGloveBuildHacksModule.cpp
@@ -0,0 +1,61 @@
+/**
+ * @file
+ *
+ * @author Mamadou Babaei
+ *
+ * @section LICENSE
+ *
+ * (The MIT License)
+ *
+ * Copyright (c) 2020 - 2026 SenseGlove
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * @section DESCRIPTION
+ *
+ *
+ */
+
+
+#include "SenseGloveBuildHacksModule.h"
+#include "SGLog/SGLog.h"
+
+#define LOCTEXT_NAMESPACE "FSenseGloveBuildHacksModule"
+
+void FSenseGloveBuildHacksModule::StartupModule()
+{
+ SGLOG("Starting up the SenseGloveBuildHacks module...");
+}
+
+void FSenseGloveBuildHacksModule::PreUnloadCallback()
+{
+ SGLOG("Unloading the SenseGloveBuildHacks module...");
+}
+
+void FSenseGloveBuildHacksModule::PostLoadCallback()
+{
+ SGLOG("Loading of SenseGloveBuildHacks module has succeeded!");
+}
+
+void FSenseGloveBuildHacksModule::ShutdownModule()
+{
+ SGLOG("Shutting down the SenseGloveBuildHacks module...");
+}
+
+#undef LOCTEXT_NAMESPACE
\ No newline at end of file
diff --git a/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Private/SenseGloveBuildHacksModule.h b/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Private/SenseGloveBuildHacksModule.h
new file mode 100644
index 0000000..d6cd433
--- /dev/null
+++ b/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Private/SenseGloveBuildHacksModule.h
@@ -0,0 +1,51 @@
+/**
+ * @file
+ *
+ * @author Mamadou Babaei
+ *
+ * @section LICENSE
+ *
+ * (The MIT License)
+ *
+ * Copyright (c) 2020 - 2026 SenseGlove
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * @section DESCRIPTION
+ *
+ *
+ */
+
+
+#pragma once
+
+#include "Modules/ModuleInterface.h"
+
+#define LOCTEXT_NAMESPACE "SenseGloveBuildHacks"
+
+class FSenseGloveBuildHacksModule : public IModuleInterface
+{
+public:
+ virtual void StartupModule() override;
+ virtual void PreUnloadCallback() override;
+ virtual void PostLoadCallback() override;
+ virtual void ShutdownModule() override;
+};
+
+#undef LOCTEXT_NAMESPACE
\ No newline at end of file
diff --git a/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Public/SGBuildHacks/SGDisable_C4263.h b/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Public/SGBuildHacks/SGDisable_C4263.h
new file mode 100644
index 0000000..83637bc
--- /dev/null
+++ b/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Public/SGBuildHacks/SGDisable_C4263.h
@@ -0,0 +1,36 @@
+/**
+ * @file
+ *
+ * @author Mamadou Babaei
+ *
+ * @section LICENSE
+ *
+ * (The MIT License)
+ *
+ * Copyright (c) 2020 - 2026 SenseGlove
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * @section DESCRIPTION
+ *
+ *
+ */
+
+
+#pragma warning(disable: 4263)
\ No newline at end of file
diff --git a/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Public/SGBuildHacks/SGDisable_C4264.h b/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Public/SGBuildHacks/SGDisable_C4264.h
new file mode 100644
index 0000000..4d9b661
--- /dev/null
+++ b/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Public/SGBuildHacks/SGDisable_C4264.h
@@ -0,0 +1,36 @@
+/**
+ * @file
+ *
+ * @author Mamadou Babaei
+ *
+ * @section LICENSE
+ *
+ * (The MIT License)
+ *
+ * Copyright (c) 2020 - 2026 SenseGlove
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * @section DESCRIPTION
+ *
+ *
+ */
+
+
+#pragma warning(disable: 4264)
\ No newline at end of file
diff --git a/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Public/SGBuildHacks/SGDisable_C4706.h b/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Public/SGBuildHacks/SGDisable_C4706.h
new file mode 100644
index 0000000..d57ea0b
--- /dev/null
+++ b/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Public/SGBuildHacks/SGDisable_C4706.h
@@ -0,0 +1,36 @@
+/**
+ * @file
+ *
+ * @author Mamadou Babaei
+ *
+ * @section LICENSE
+ *
+ * (The MIT License)
+ *
+ * Copyright (c) 2020 - 2026 SenseGlove
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * @section DESCRIPTION
+ *
+ *
+ */
+
+
+#pragma warning(disable: 4706)
\ No newline at end of file
diff --git a/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Public/SGBuildHacks/SGDisable_C4800.h b/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Public/SGBuildHacks/SGDisable_C4800.h
new file mode 100644
index 0000000..c07ad4e
--- /dev/null
+++ b/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Public/SGBuildHacks/SGDisable_C4800.h
@@ -0,0 +1,36 @@
+/**
+ * @file
+ *
+ * @author Mamadou Babaei
+ *
+ * @section LICENSE
+ *
+ * (The MIT License)
+ *
+ * Copyright (c) 2020 - 2026 SenseGlove
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * @section DESCRIPTION
+ *
+ *
+ */
+
+
+#pragma warning(disable: 4800)
\ No newline at end of file
diff --git a/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Public/SGBuildHacks/SGDisable_Woverloaded_virtual.h b/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Public/SGBuildHacks/SGDisable_Woverloaded_virtual.h
new file mode 100644
index 0000000..6eff6d3
--- /dev/null
+++ b/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Public/SGBuildHacks/SGDisable_Woverloaded_virtual.h
@@ -0,0 +1,43 @@
+/**
+ * @file
+ *
+ * @author Mamadou Babaei
+ *
+ * @section LICENSE
+ *
+ * (The MIT License)
+ *
+ * Copyright (c) 2020 - 2026 SenseGlove
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * @section DESCRIPTION
+ *
+ *
+ */
+
+
+#include "HAL/Platform.h"
+
+#if ! PLATFORM_WINDOWS
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Woverloaded-virtual"
+
+#endif /* ! PLATFORM_WINDOWS */
\ No newline at end of file
diff --git a/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Public/SGBuildHacks/SGInclude_Connect_Android.h b/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Public/SGBuildHacks/SGInclude_Connect_Android.h
new file mode 100644
index 0000000..f176b31
--- /dev/null
+++ b/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Public/SGBuildHacks/SGInclude_Connect_Android.h
@@ -0,0 +1,44 @@
+/**
+ * @file
+ *
+ * @author Mamadou Babaei
+ *
+ * @section LICENSE
+ *
+ * (The MIT License)
+ *
+ * Copyright (c) 2020 - 2026 SenseGlove
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * @section DESCRIPTION
+ *
+ *
+ */
+
+
+#include "HAL/Platform.h"
+
+#include "SGBuildHacks/SGUndef_TEXT.h"
+
+THIRD_PARTY_INCLUDES_START
+#include "SenseGlove/Connect/Android.hpp"
+THIRD_PARTY_INCLUDES_END
+
+#include "SGBuildHacks/SGRestore_TEXT.h"
\ No newline at end of file
diff --git a/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Public/SGBuildHacks/SGInclude_Connect_SGConnect.h b/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Public/SGBuildHacks/SGInclude_Connect_SGConnect.h
new file mode 100644
index 0000000..33d3cf7
--- /dev/null
+++ b/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Public/SGBuildHacks/SGInclude_Connect_SGConnect.h
@@ -0,0 +1,44 @@
+/**
+ * @file
+ *
+ * @author Mamadou Babaei
+ *
+ * @section LICENSE
+ *
+ * (The MIT License)
+ *
+ * Copyright (c) 2020 - 2026 SenseGlove
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * @section DESCRIPTION
+ *
+ *
+ */
+
+
+#include "HAL/Platform.h"
+
+#include "SGBuildHacks/SGUndef_TEXT.h"
+
+THIRD_PARTY_INCLUDES_START
+#include "SenseGlove/Connect/SGConnect.hpp"
+THIRD_PARTY_INCLUDES_END
+
+#include "SGBuildHacks/SGRestore_TEXT.h"
\ No newline at end of file
diff --git a/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Public/SGBuildHacks/SGInclude_Core_Anatomy.h b/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Public/SGBuildHacks/SGInclude_Core_Anatomy.h
new file mode 100644
index 0000000..be18f61
--- /dev/null
+++ b/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Public/SGBuildHacks/SGInclude_Core_Anatomy.h
@@ -0,0 +1,44 @@
+/**
+ * @file
+ *
+ * @author Mamadou Babaei
+ *
+ * @section LICENSE
+ *
+ * (The MIT License)
+ *
+ * Copyright (c) 2020 - 2026 SenseGlove
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * @section DESCRIPTION
+ *
+ *
+ */
+
+
+#include "HAL/Platform.h"
+
+#include "SGBuildHacks/SGUndef_TEXT.h"
+
+THIRD_PARTY_INCLUDES_START
+#include "SenseGlove/Core/Anatomy.hpp"
+THIRD_PARTY_INCLUDES_END
+
+#include "SGBuildHacks/SGRestore_TEXT.h"
\ No newline at end of file
diff --git a/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Public/SGBuildHacks/SGInclude_Core_Android.h b/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Public/SGBuildHacks/SGInclude_Core_Android.h
new file mode 100644
index 0000000..9f7ef53
--- /dev/null
+++ b/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Public/SGBuildHacks/SGInclude_Core_Android.h
@@ -0,0 +1,44 @@
+/**
+ * @file
+ *
+ * @author Mamadou Babaei
+ *
+ * @section LICENSE
+ *
+ * (The MIT License)
+ *
+ * Copyright (c) 2020 - 2026 SenseGlove
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * @section DESCRIPTION
+ *
+ *
+ */
+
+
+#include "HAL/Platform.h"
+
+#include "SGBuildHacks/SGUndef_TEXT.h"
+
+THIRD_PARTY_INCLUDES_START
+#include "SenseGlove/Core/Android.hpp"
+THIRD_PARTY_INCLUDES_END
+
+#include "SGBuildHacks/SGRestore_TEXT.h"
\ No newline at end of file
diff --git a/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Public/SGBuildHacks/SGInclude_Core_BasicHandModel.h b/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Public/SGBuildHacks/SGInclude_Core_BasicHandModel.h
new file mode 100644
index 0000000..fa706bf
--- /dev/null
+++ b/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Public/SGBuildHacks/SGInclude_Core_BasicHandModel.h
@@ -0,0 +1,44 @@
+/**
+ * @file
+ *
+ * @author Mamadou Babaei
+ *
+ * @section LICENSE
+ *
+ * (The MIT License)
+ *
+ * Copyright (c) 2020 - 2026 SenseGlove
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * @section DESCRIPTION
+ *
+ *
+ */
+
+
+#include "HAL/Platform.h"
+
+#include "SGBuildHacks/SGUndef_TEXT.h"
+
+THIRD_PARTY_INCLUDES_START
+#include "SenseGlove/Core/BasicHandModel.hpp"
+THIRD_PARTY_INCLUDES_END
+
+#include "SGBuildHacks/SGRestore_TEXT.h"
\ No newline at end of file
diff --git a/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Public/SGBuildHacks/SGInclude_Core_BetaDevice.h b/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Public/SGBuildHacks/SGInclude_Core_BetaDevice.h
new file mode 100644
index 0000000..20aa4df
--- /dev/null
+++ b/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Public/SGBuildHacks/SGInclude_Core_BetaDevice.h
@@ -0,0 +1,44 @@
+/**
+ * @file
+ *
+ * @author Mamadou Babaei
+ *
+ * @section LICENSE
+ *
+ * (The MIT License)
+ *
+ * Copyright (c) 2020 - 2026 SenseGlove
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * @section DESCRIPTION
+ *
+ *
+ */
+
+
+#include "HAL/Platform.h"
+
+#include "SGBuildHacks/SGUndef_TEXT.h"
+
+THIRD_PARTY_INCLUDES_START
+#include "SenseGlove/Core/BetaDevice.hpp"
+THIRD_PARTY_INCLUDES_END
+
+#include "SGBuildHacks/SGRestore_TEXT.h"
\ No newline at end of file
diff --git a/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Public/SGBuildHacks/SGInclude_Core_CustomWaveform.h b/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Public/SGBuildHacks/SGInclude_Core_CustomWaveform.h
new file mode 100644
index 0000000..68acf7a
--- /dev/null
+++ b/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Public/SGBuildHacks/SGInclude_Core_CustomWaveform.h
@@ -0,0 +1,44 @@
+/**
+ * @file
+ *
+ * @author Mamadou Babaei
+ *
+ * @section LICENSE
+ *
+ * (The MIT License)
+ *
+ * Copyright (c) 2020 - 2026 SenseGlove
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * @section DESCRIPTION
+ *
+ *
+ */
+
+
+#include "HAL/Platform.h"
+
+#include "SGBuildHacks/SGUndef_TEXT.h"
+
+THIRD_PARTY_INCLUDES_START
+#include "SenseGlove/Core/CustomWaveform.hpp"
+THIRD_PARTY_INCLUDES_END
+
+#include "SGBuildHacks/SGRestore_TEXT.h"
\ No newline at end of file
diff --git a/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Public/SGBuildHacks/SGInclude_Core_DeviceList.h b/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Public/SGBuildHacks/SGInclude_Core_DeviceList.h
new file mode 100644
index 0000000..83ffa0f
--- /dev/null
+++ b/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Public/SGBuildHacks/SGInclude_Core_DeviceList.h
@@ -0,0 +1,44 @@
+/**
+ * @file
+ *
+ * @author Mamadou Babaei
+ *
+ * @section LICENSE
+ *
+ * (The MIT License)
+ *
+ * Copyright (c) 2020 - 2026 SenseGlove
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * @section DESCRIPTION
+ *
+ *
+ */
+
+
+#include "HAL/Platform.h"
+
+#include "SGBuildHacks/SGUndef_TEXT.h"
+
+THIRD_PARTY_INCLUDES_START
+#include "SenseGlove/Core/DeviceList.hpp"
+THIRD_PARTY_INCLUDES_END
+
+#include "SGBuildHacks/SGRestore_TEXT.h"
diff --git a/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Public/SGBuildHacks/SGInclude_Core_DeviceModel.h b/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Public/SGBuildHacks/SGInclude_Core_DeviceModel.h
new file mode 100644
index 0000000..0ef2004
--- /dev/null
+++ b/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Public/SGBuildHacks/SGInclude_Core_DeviceModel.h
@@ -0,0 +1,44 @@
+/**
+ * @file
+ *
+ * @author Mamadou Babaei
+ *
+ * @section LICENSE
+ *
+ * (The MIT License)
+ *
+ * Copyright (c) 2020 - 2026 SenseGlove
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * @section DESCRIPTION
+ *
+ *
+ */
+
+
+#include "HAL/Platform.h"
+
+#include "SGBuildHacks/SGUndef_TEXT.h"
+
+THIRD_PARTY_INCLUDES_START
+#include "SenseGlove/Core/DeviceModel.hpp"
+THIRD_PARTY_INCLUDES_END
+
+#include "SGBuildHacks/SGRestore_TEXT.h"
\ No newline at end of file
diff --git a/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Public/SGBuildHacks/SGInclude_Core_Fingers.h b/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Public/SGBuildHacks/SGInclude_Core_Fingers.h
new file mode 100644
index 0000000..8ee4c31
--- /dev/null
+++ b/Plugins/SenseGlove/Source/SenseGloveBuildHacks/Public/SGBuildHacks/SGInclude_Core_Fingers.h
@@ -0,0 +1,44 @@
+/**
+ * @file
+ *
+ * @author Mamadou Babaei