Index: Content/EnhancedInput/MotionControllerAction.uasset
===================================================================
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream

Property changes on: Content/EnhancedInput/MotionControllerAction.uasset
___________________________________________________________________
Added: svn:mime-type
## -0,0 +1 ##
+application/octet-stream
\ No newline at end of property
Index: Source/NoesisEditor/Private/NoesisBlueprintCompilerContext.cpp
===================================================================
--- Source/NoesisEditor/Private/NoesisBlueprintCompilerContext.cpp	(revision 13421)
+++ Source/NoesisEditor/Private/NoesisBlueprintCompilerContext.cpp	(working copy)
@@ -62,6 +62,8 @@
 		DefaultInstance->EmulateTouch = NoesisBlueprint->EmulateTouch;
 		DefaultInstance->EnableTouch = NoesisBlueprint->EnableTouch;
 		DefaultInstance->EnableActions = NoesisBlueprint->EnableActions;
+		DefaultInstance->EnableMotionController = NoesisBlueprint->EnableMotionController;
+		DefaultInstance->MotionSource = NoesisBlueprint->MotionSource;
 		DefaultInstance->PixelDepthBias = NoesisBlueprint->PixelDepthBias;
 	}
 }
Index: Source/NoesisRuntime/Classes/NoesisBlueprint.h
===================================================================
--- Source/NoesisRuntime/Classes/NoesisBlueprint.h	(revision 13421)
+++ Source/NoesisRuntime/Classes/NoesisBlueprint.h	(working copy)
@@ -62,6 +62,12 @@
 	UPROPERTY(EditAnywhere, Category = "Noesis View")
 	bool EnableActions;
 
+	UPROPERTY(BlueprintReadWrite, Category = "Noesis View")
+	bool EnableMotionController;
+
+	UPROPERTY(BlueprintReadWrite, Category = "Noesis View")
+	FName MotionSource;
+
 	/** Set to a positive value, this property is added to the values returned by the PixelDepth node in a Material. */
 	UPROPERTY(EditAnywhere, Category = "Noesis View", meta = (DisplayName = "Material PixelDepth Bias"))
 	float PixelDepthBias;
Index: Source/NoesisRuntime/Classes/NoesisInstance.h
===================================================================
--- Source/NoesisRuntime/Classes/NoesisInstance.h	(revision 13421)
+++ Source/NoesisRuntime/Classes/NoesisInstance.h	(working copy)
@@ -137,6 +137,8 @@
 	bool IsGamepadSimulatedClick = false;
 	bool Is3DWidget = false;
 	mutable bool SupportsKeyboardFocus = true;
+	bool ValidCachedMotionControllerScreenPosition = false;
+	Noesis::Point CachedMotionControllerScreenPosition;
 
 	typedef TSharedPtr<class FNoesisSlateElement, ESPMode::ThreadSafe> FNoesisSlateElementPtr;
 	FNoesisSlateElementPtr NoesisSlateElement;
@@ -172,6 +174,12 @@
 	bool EnableActions;
 
 	UPROPERTY(BlueprintReadWrite, Category = "NoesisGUI")
+	bool EnableMotionController;
+
+	UPROPERTY(BlueprintReadWrite, Category = "NoesisGUI")
+	FName MotionSource;
+
+	UPROPERTY(BlueprintReadWrite, Category = "NoesisGUI")
 	float PixelDepthBias;
 
 	UFUNCTION(BlueprintCallable, Category = "NoesisGUI")
@@ -247,6 +255,8 @@
 	void OnEnhancedInputActionCompleted(Noesis::Key);
 	void OnEnhancedInputActionHorizontalScroll(const struct FInputActionValue&);
 	void OnEnhancedInputActionVerticalScroll(const struct FInputActionValue&);
+	void OnEnhancedInputActionMotionControllerActionTriggered();
+	void OnEnhancedInputActionMotionControllerActionCompleted();
 #endif
 
 	// UUserWidget interface
Index: Source/NoesisRuntime/NoesisRuntime.Build.cs
===================================================================
--- Source/NoesisRuntime/NoesisRuntime.Build.cs	(revision 13421)
+++ Source/NoesisRuntime/NoesisRuntime.Build.cs	(working copy)
@@ -67,7 +67,8 @@
 				"UElibPNG",
 				"Renderer",
 				"NetCore",
-				"Noesis"
+				"Noesis",
+				"HeadMountedDisplay"
 			}
 			);
 
Index: Source/NoesisRuntime/Private/NoesisBlueprint.cpp
===================================================================
--- Source/NoesisRuntime/Private/NoesisBlueprint.cpp	(revision 13421)
+++ Source/NoesisRuntime/Private/NoesisBlueprint.cpp	(working copy)
@@ -20,6 +20,8 @@
 	EmulateTouch = false;
 	EnableTouch = true;
 	EnableActions = false;
+	EnableMotionController = false;
+	MotionSource = "RightAim";
 	PixelDepthBias = -1.0f;
 }
 
Index: Source/NoesisRuntime/Private/NoesisInstance.cpp
===================================================================
--- Source/NoesisRuntime/Private/NoesisInstance.cpp	(revision 13421)
+++ Source/NoesisRuntime/Private/NoesisInstance.cpp	(working copy)
@@ -7,6 +7,7 @@
 
 // Core includes
 #include "MathUtil.h"
+#include "Features/IModularFeatures.h"
 #include "Stats/Stats.h"
 #include "Stats/Stats2.h"
 
@@ -26,7 +27,12 @@
 #else
 #include "Materials/MaterialRenderProxy.h"
 #endif
 
+// HeadMountedDisplay includes
+#include "IMotionController.h"
+#include "IXRTrackingSystem.h"
+
 // RHI includes
 #if UE_VERSION_OLDER_THAN(5, 2, 0)
 #include "RHIDefinitions.h"
@@ -265,8 +271,11 @@
 	ENQUEUE_RENDER_COMMAND(FNoesisInstance_DrawOffscreen)
 	(
 		[Renderer = Renderer, RenderDevice = RenderDevice,
-		WorldTime = WorldTime, Scene = Scene](FRHICommandListImmediate& RHICmdList)
+		WorldTime = WorldTime, Scene = Scene, ViewProjectionMatrix = ViewProjectionMatrix](FRHICommandListImmediate& RHICmdList)
 		{
+			/*if (fabs(ViewProjectionMatrix.Determinant()) < FLT_EPSILON)
+				return;*/
+
 			RenderOffscreen(Renderer, RenderDevice, WorldTime, Scene, RHICmdList);
 		}
 	);
@@ -312,6 +321,9 @@
 	if ((View->bIsMobileMultiViewEnabled || View->bIsInstancedStereoEnabled) && (View == View->GetInstancedSceneView()))
 		return;
 
+	if (fabs(ViewProjectionMatrix.Determinant()) < FLT_EPSILON)
+		return;
+
 	const auto ViewFamily = View->Family;
 
 	ViewRect = View->ViewRect;
@@ -504,6 +516,8 @@
 	EmulateTouch = false;
 	EnableTouch = true;
 	EnableActions = false;
+	EnableMotionController = false;
+	MotionSource = "RightAim";
 	PixelDepthBias = -1.0f;
 }
 
@@ -608,6 +622,17 @@
 					EnhancedInputComponent->BindAction(Action, ETriggerEvent::Triggered, this, Event.Value);
 				}
 			}
+
+			{
+				UInputAction* Action = LoadObject<UInputAction>(nullptr, TEXT("/NoesisGUI/EnhancedInput/MotionControllerAction"), nullptr, LOAD_NoWarn);
+
+				if (Action != nullptr)
+				{
+					EnhancedInputComponent->BindAction(Action, ETriggerEvent::Triggered, this, &UNoesisInstance::OnEnhancedInputActionMotionControllerActionTriggered);
+					EnhancedInputComponent->BindAction(Action, ETriggerEvent::Completed, this, &UNoesisInstance::OnEnhancedInputActionMotionControllerActionCompleted);
+					EnhancedInputComponent->BindAction(Action, ETriggerEvent::Canceled, this, &UNoesisInstance::OnEnhancedInputActionMotionControllerActionCompleted);
+				}
+			}
 		}
 #endif
 	}
@@ -737,6 +762,28 @@
 		XamlView->Scroll(-Value.Get<float>());
 	}
 }
+
+void UNoesisInstance::OnEnhancedInputActionMotionControllerActionTriggered()
+{
+	if (EnableActions && EnableMotionController && XamlView != nullptr)
+	{
+		if (ValidCachedMotionControllerScreenPosition)
+		{
+			XamlView->MouseButtonDown(CachedMotionControllerScreenPosition.x, CachedMotionControllerScreenPosition.y, Noesis::MouseButton_Left);
+		}
+	}
+}
+
+void UNoesisInstance::OnEnhancedInputActionMotionControllerActionCompleted()
+{
+	if (EnableActions && EnableMotionController && XamlView != nullptr)
+	{
+		if (ValidCachedMotionControllerScreenPosition)
+		{
+			XamlView->MouseButtonUp(CachedMotionControllerScreenPosition.x, CachedMotionControllerScreenPosition.y, Noesis::MouseButton_Left);
+		}
+	}
+}
 #endif
 
 void UNoesisInstance::ExecuteConsoleCommand(FString Command, class APlayerController* SpecificPlayer)
@@ -834,6 +881,8 @@
 	AddToRoot();
 	Is3DWidget = true;
 	EnablePPAA = true;
+	EnableActions = true;
+	EnableMotionController = true;
 	TessellationQuality = ENoesisTessellationQuality::High;
 	SetPlayerContext(FLocalPlayerContext(World->GetFirstLocalPlayerFromController(), World));
 	InitInstance();
@@ -924,6 +973,8 @@
 						const FMatrix ViewProjForCulling = ViewMatrixForCulling * StereoRenderingDevice->GetStereoProjectionMatrix(eSSE_MONOSCOPIC);
 
 						Noesis::Matrix4 ViewProj = UnrealToNoesisViewProj(ViewProjForCulling, Width, Height);
+						if (fabs(Noesis::Determinant(ViewProj)) < FLT_EPSILON)
+							return;
 
 						ENQUEUE_RENDER_COMMAND(CopyViewProj)
 						(
@@ -933,7 +984,72 @@
 							}
 						);
 						XamlView->SetProjectionMatrix(ViewProj);
+
+						if (EnableMotionController)
+						{
+							ValidCachedMotionControllerScreenPosition = false;
+							TArray<IMotionController*> MotionControllers = IModularFeatures::Get().GetModularFeatureImplementations<IMotionController>(IMotionController::GetModularFeatureName());
+							{
+								FTransform TrackingToWorld;
+								IXRTrackingSystem* TrackingSys = GEngine->XRSystem.Get();
+								if (TrackingSys)
+								{
+									TrackingToWorld = TrackingSys->GetTrackingToWorldTransform();
 								}
+
+								for (auto MotionController : MotionControllers)
+								{
+									if (MotionController == nullptr)
+									{
+										continue;
+									}
+
+									auto PlayerIndex = LocalPlayer->GetLocalPlayerIndex();
+									FRotator Orientation;
+									FVector Position;
+									if (MotionController->GetControllerOrientationAndPosition(PlayerIndex, MotionSource, Orientation, Position, WorldToMetersScale))
+									{
+										auto Direction = Orientation.Vector();
+										Direction = TrackingToWorld.TransformVector(Direction);
+										Position = TrackingToWorld.TransformPosition(Position);
+
+										Noesis::FrameworkElement* Root = XamlView->GetContent();
+										Noesis::Point3D NoesisPosition(-Position.Y, -Position.Z, -Position.X);
+										Noesis::Point3D NoesisDirection(-Direction.Y, -Direction.Z, -Direction.X);
+										Noesis::Point3D WorldPos;
+
+										auto Mouse = Root->GetMouse();
+										auto Captured = Mouse->GetCaptured();
+										if (Captured != nullptr)
+										{
+											//if (VisualTreeHelper::IntersectPlane(Captured, ...)
+										}
+										else
+										{
+											Noesis::HitTest3DResult HitTestResult = Noesis::VisualTreeHelper::HitTest3D(Root, NoesisPosition, NoesisDirection);
+											if (Noesis::Visual* Visual = HitTestResult.visualHit; Visual != nullptr)
+											{
+												ValidCachedMotionControllerScreenPosition = true;
+												WorldPos = HitTestResult.worldPos;
+											}
+										}
+										
+										if (ValidCachedMotionControllerScreenPosition)
+										{
+											auto ScreenPoint = Noesis::Vector4(WorldPos.x, WorldPos.y, WorldPos.z, 1.0f) * ViewProj;
+											CachedMotionControllerScreenPosition = Noesis::Point(ScreenPoint.x / ScreenPoint.w, ScreenPoint.y / ScreenPoint.w);
+											XamlView->MouseMove(CachedMotionControllerScreenPosition.x, CachedMotionControllerScreenPosition.y);
+										}
+									}
+								}
+							}
+						}
+					}
 					else
 					{
 						Noesis::Matrix4 ViewProj = UnrealToNoesisViewProj(ViewProjectionData.ComputeViewProjectionMatrix(), Width, Height);
@@ -1187,6 +1303,9 @@
 		(
 			[NoesisSlateElement = NoesisSlateElement](FRHICommandListImmediate& RHICmdList)
 			{
+				/*if (fabs(NoesisSlateElement->ViewProjectionMatrix.Determinant()) < FLT_EPSILON)
+					return;*/
+
 				auto Renderer = NoesisSlateElement->Renderer.GetPtr();
 				if (Renderer != nullptr)
 				{
@@ -1831,7 +1950,10 @@
 
 	InitInstance();
 
-	PreTickDelegateHandle = FSlateApplication::Get().OnPreTick().AddUObject(this, &UNoesisInstance::PreTick);
+	if (!Is3DWidget)
+	{
+		PreTickDelegateHandle = FSlateApplication::Get().OnPreTick().AddUObject(this, &UNoesisInstance::PreTick);
+	}
 }
 
 void UNoesisInstance::NativeDestruct()
@@ -1920,13 +2042,54 @@
 	return !RendererInfo.IsMobileShadingPath || RendererInfo.IsMobileDeferredShadingPath;
 }
 
+static void AddWorldUIRenderPass(FRDGBuilder& GraphBuilder, FRDGTexture* ColorTexture, FRDGTexture* DepthStencilTexture, const FViewInfo& View)
+{
+	FRenderTargetParameters* PassParameters = GraphBuilder.AllocParameters<FRenderTargetParameters>();
+	{
+		PassParameters->RenderTargets[0] = FRenderTargetBinding(ColorTexture, ERenderTargetLoadAction::ELoad);
+		PassParameters->RenderTargets.DepthStencil = FDepthStencilBinding(DepthStencilTexture, ERenderTargetLoadAction::ELoad, ERenderTargetLoadAction::EClear, FExclusiveDepthStencil::DepthRead_StencilWrite);
+	}
+	GraphBuilder.AddPass(RDG_EVENT_NAME("NoesisTranslucentPass"), PassParameters, ERDGPassFlags::Raster | ERDGPassFlags::NeverCull,
+		[&View](FRHICommandListImmediate& RHICmdList)
+		{
+			for (auto NoesisSlateElement : GNoesis3DSlateElements)
+			{
+				FNoesisRenderDevice* RenderDevice = NoesisSlateElement->RenderDevice;
+				RenderDevice->IsWorldUI = true;
+				NoesisSlateElement->RenderView(RHICmdList, (FViewInfo*)&View);
+				RenderDevice->IsWorldUI = false;
+			}
+		}
+	);
+}
+
 void NoesisDrawRenderThreadOverlay(FPostOpaqueRenderParameters& Params)
 {
+	const auto& View = (const FViewInfo&)*Params.View;
+	const auto ViewFamily = View.Family;
+
+	if (ViewFamily->Scene->IsEditorScene())
+		return;
+
+	if (GNoesis3DSlateElements.Num() == 0)
+		return;
+
+	if (ViewFamily->AllowTranslucencyAfterDOF())
+		return;
+
+	auto ColorTexture = Params.ColorTexture;
+	auto DepthStencilTexture = Params.DepthTexture;
+
+	if (ColorTexture == nullptr || DepthStencilTexture == nullptr)
+		return;
+
+	auto& GraphBuilder = *Params.GraphBuilder;
+	AddWorldUIRenderPass(GraphBuilder, ColorTexture, DepthStencilTexture, View);
 }
 
 FDelegateHandle NoesisRegisterOverlayRender()
 {
-	return GetRendererModule().RegisterPostOpaqueRenderDelegate(FPostOpaqueRenderDelegate::CreateStatic(&NoesisDrawRenderThreadOverlay));
+	return GetRendererModule().RegisterOverlayRenderDelegate(FPostOpaqueRenderDelegate::CreateStatic(&NoesisDrawRenderThreadOverlay));
 }
 
 void NoesisUnregisterOverlayRender(FDelegateHandle InOverlayRenderDelegateHandle)
@@ -1987,6 +2150,7 @@
 	virtual void PrePostProcessPass_RenderThread(FRDGBuilder& GraphBuilder, const FSceneView& InView, const FPostProcessingInputs& Inputs) override
 	{
 		const auto& View = (const FViewInfo&)InView;
+		const auto ViewFamily = View.Family;
 
 #if UE_VERSION_OLDER_THAN(5, 1, 0)
 		if (!View.bIsSceneCapture)
@@ -1998,7 +2162,7 @@
 			GViewProjectionMatrix = View.ViewMatrices.GetProjectionMatrix();
 		}
 
-		if (View.Family->Scene->IsEditorScene())
+		if (ViewFamily->Scene->IsEditorScene())
 			return;
 
 		if (GNoesis3DSlateElements.Num() == 0)
@@ -2012,23 +2176,7 @@
 		if (ColorTexture == nullptr || DepthStencilTexture == nullptr)
 			return;
 
-		FRenderTargetParameters* PassParameters = GraphBuilder.AllocParameters<FRenderTargetParameters>();
-		{
-			PassParameters->RenderTargets[0] = FRenderTargetBinding(ColorTexture, ERenderTargetLoadAction::ELoad);
-			PassParameters->RenderTargets.DepthStencil = FDepthStencilBinding(DepthStencilTexture, ERenderTargetLoadAction::ELoad, ERenderTargetLoadAction::EClear, FExclusiveDepthStencil::DepthRead_StencilWrite);
-		}
-		GraphBuilder.AddPass(RDG_EVENT_NAME("NoesisTranslucentPass"), PassParameters, ERDGPassFlags::Raster | ERDGPassFlags::NeverCull,
-			[&View](FRHICommandListImmediate& RHICmdList)
-			{
-				for (auto NoesisSlateElement : GNoesis3DSlateElements)
-				{
-					FNoesisRenderDevice* RenderDevice = NoesisSlateElement->RenderDevice;
-					RenderDevice->IsWorldUI = true;
-					NoesisSlateElement->RenderView(RHICmdList, (FViewInfo*)&View);
-					RenderDevice->IsWorldUI = false;
-				}
-			}
-		);
+		AddWorldUIRenderPass(GraphBuilder, ColorTexture, DepthStencilTexture, View);
 	}
 
 	virtual void SubscribeToPostProcessingPass(EPostProcessingPass Pass, FAfterPassCallbackDelegateArray& InOutPassCallbacks, bool bIsPassEnabled) override
@@ -2080,28 +2228,18 @@
 	virtual void PostRenderBasePassMobile_RenderThread(FRHICommandList& RHICmdList, FSceneView& InView) override
 #endif
 	{
-		check(InView.bIsViewInfo);
-		const auto& View = (const FViewInfo&)InView;
-
-#if UE_VERSION_OLDER_THAN(5, 1, 0)
-		if (!View.bIsSceneCapture)
-#else
-		if (!View.bIsSceneCapture && !View.bIsSceneCaptureCube)
-#endif
-		{
-			GViewRect = View.ViewRect;
-			GViewProjectionMatrix = View.ViewMatrices.GetProjectionMatrix();
-		}
-
 		if (GNoesis3DSlateElements.Num() == 0)
 			return;
 
-		FRendererInfo RendererInfo = GetRendererInfo(&View);
+		check(InView.bIsViewInfo);
+		const FViewInfo* View = (FViewInfo*)&InView;
+
+		FRendererInfo RendererInfo = GetRendererInfo(View);
 		if (Render3DPostOpaque(RendererInfo))
 			return;
 
 		// We need to clear the stencil buffer, since we're in the SceneColor pass
-		const auto& ViewRect = View.ViewRect;
+		const auto& ViewRect = View->ViewRect;
 		auto Left = ViewRect.Min.X;
 		auto Top = ViewRect.Min.Y;
 		auto Right = ViewRect.Max.X;
@@ -2110,7 +2248,7 @@
 		DrawClearQuad(RHICmdList, false, FLinearColor(), false, 0.f, true, 0);
 		for (auto NoesisSlateElement : GNoesis3DSlateElements)
 		{
-			NoesisSlateElement->RenderView(RHICmdList, (FViewInfo*)&View);
+			NoesisSlateElement->RenderView(RHICmdList, View);
 		}
 	}
 
