From 2242b4d56e9549310df21cff76a524528b92cac5 Mon Sep 17 00:00:00 2001
From: Sebastian Pape <Sebastian.Pape@rwth-aachen.de>
Date: Thu, 17 Oct 2019 13:11:32 +0200
Subject: [PATCH] Added support for two screen mode

---
 LaunchConfig/twoscreen.cfg                    | 215 ++++++++++++++++++
 .../Private/NDisplayLaunchButton.cpp          |  24 +-
 .../Public/NDisplayLaunchButtonSettings.h     |  15 ++
 3 files changed, 252 insertions(+), 2 deletions(-)
 create mode 100644 LaunchConfig/twoscreen.cfg

diff --git a/LaunchConfig/twoscreen.cfg b/LaunchConfig/twoscreen.cfg
new file mode 100644
index 0000000..47d81cc
--- /dev/null
+++ b/LaunchConfig/twoscreen.cfg
@@ -0,0 +1,215 @@
+#####################################################################
+# nDisplay config file for aixCAVE
+#####################################################################
+
+#####################################################################
+# Config info
+#******************************************************************** 
+# This is a config file header.
+#
+# Properties:
+# version - specifies the version of the configuration file (UE4.xx)
+#******************************************************************** 
+[info] version=22
+
+#####################################################################
+# Cluster nodes
+#******************************************************************** 
+# Cluster node is an application instance. It's allowed to use
+# multiple instances on the same PC. Sometimes its necessary.
+#
+# Properties:
+# id       - Unique node name
+# window   - Window ID
+# addr     - Network address (IPv4 only)
+# master   - Specifies if current node is master; default is 'false'
+# port_cs  - Cluster Synchronization port (required on master node only)
+# port_ss  - Swap Synchronization port (required on master node only)
+# port_ce  - Cluster Events port (required on master node only)
+#
+# Optional properties:
+# eye_swap - Swap eyes for this node; default is 'false'
+# sound    - turns on/off sound for this application instance; default is 'false'
+#********************************************************************      
+[cluster_node] id=node_left  addr=127.0.0.1 window=wnd_left port_cs=41001 port_ss=41002 port_ce=41003 master=true sound=true
+[cluster_node] id=node_right addr=127.0.0.1 window=wnd_right
+ 
+#####################################################################
+# Application windows
+#******************************************************************** 
+# The window entitty defines properties of application's game window.
+#
+# Properties:
+# id         - Unique window name
+# fullscreen - Fullscreen or windowed mode
+# winx       - X location
+# winy       - Y location
+# resx       - Width
+# resy       - Height
+# viewports  - Array of viewports
+#******************************************************************** 
+
+[window] id=wnd_left  viewports=vp_left
+[window] id=wnd_right viewports=vp_right
+
+#####################################################################
+# Projection screens
+#******************************************************************** 
+# Projection screen is a rectangle which determines the camera frustum.
+# Usually the projection screen has the same dimensions as an output
+# display but in some cases it may differ.
+#
+# Properties:
+# id     - unique projection screen name
+# loc    - relative location to the parent component. Location is relative
+#          to the VR root if no parent specified. The pivot is a screen's
+#          center and the values are in meters.
+# rot    - relative rotation to the parent component. Rotation is relative
+#          to the VR root if no parent specified. The pivot is a screen's
+#          center and the values are in degrees.
+# size   - width (X) and height (Y) of the screen. Values are in meters.
+#
+# Optional properties:
+# parent     - ID of parent component in VR hierarchy; default is VR root.
+# tracker_id - ID of tracking device; no tracking by default.
+# tracker_ch - ID of tracking device's channel; no tracking by default.
+#******************************************************************** 
+
+[screen] id=screen_left  loc="X=0,Y=0,Z=0" rot="P=0,Y=0,R=0" size="X=2.0,Y=2.0"  parent=loc_left 
+[screen] id=screen_right loc="X=0,Y=0,Z=0" rot="P=0,Y=0,R=0" size="X=2.0,Y=2.0"  parent=loc_right
+
+#####################################################################
+# Viewports
+#******************************************************************** 
+# Viewport is a rectangle area of game window where rendered frame is
+# mapped. Usually the viewport starts at 0:0 and has the same size as
+# its parent window but in some cases these settings may differ.
+#
+# Properties:
+# id     - unique viewport name
+# x      - X coordinate of viewport's top left corner
+# y      - Y coordinate of viewport's top left corner
+# width  - width of viewport in pixels
+# height - height of viewport in pixels
+#******************************************************************** 
+
+[viewport] id=vp_left  screen=screen_left  x=0 y=0 width=480 height=480
+[viewport] id=vp_right screen=screen_right x=0 y=0 width=480 height=480
+
+#####################################################################
+# Cameras
+#******************************************************************** 
+# Camera is a predefined point frome where the stereoscopic view built.
+# It's possible to define multiple cameras and swith the active one
+# during runtime. You're free to attach any camera to a tracking device
+# for head tracking. Consider a camera as a viewer's head.
+#
+# Properties:
+# id  - unique camera name
+# loc - relative location to the parent component. Location is relative
+#       to the VR root if no parent specified.
+# rot - relative rotation to the parent component. Rotation is relative
+#       to the VR root if no parent specified.
+#
+# Optional properties:
+# parent     - ID of parent component in VR hierarchy; default is VR root.
+# tracker_id - ID of tracking device; no tracking by default.
+# tracker_ch - ID of tracking device's channel; no tracking by default.
+#******************************************************************** 
+
+[camera] id=camera_dynamic loc="X=0,Y=0,Z=0" parent=shutter_glasses
+
+#####################################################################
+# Scene nodes (hierarchy transforms) 
+#******************************************************************** 
+# Scene node is an actor component which is basically a transformation
+# matrix. Scene nodes can be helpful to build a component hierarchy, to
+# define some special places (like a socket) within VR space.
+#
+# It might be difficult to understand what VR space origin is. Consider
+# it as a point in space where VR space starts. Any componenent listed
+# in this config file is relative to its parent or this origin.
+#
+# Properties:
+# id  - unique scene node name
+# loc - relative location to the parent component. Location is relative
+#       to the VR root if no parent specified.
+# rot - relative rotation to the parent component. Rotation is relative
+#       to the VR root if no parent specified.
+#
+# Optional properties:
+# parent     - ID of parent component in VR hierarchy; default is VR root.
+# tracker_id - ID of tracking device; no tracking by default.
+# tracker_ch - ID of tracking device's channel; no tracking by default.
+#******************************************************************** 
+
+[scene_node] id=cave_origin     loc="X=0,Y=0,Z=0"    rot="P=0,Y=0,R=0"
+[scene_node] id=cave_center     loc="X=0,Y=0,Z=1.65" rot="P=0,Y=0,R=0" parent=cave_origin
+[scene_node] id=flystick        loc="X=0,Y=0,Z=0"    rot="P=0,Y=0,R=0" parent=cave_origin
+[scene_node] id=shutter_glasses loc="X=0,Y=0,Z=1.65" rot="P=0,Y=0,R=0" parent=cave_origin
+
+
+[scene_node] id=loc_left  loc="X=2.0,Y=-1.0,Z=0.01" rot="P=0,Y=0,R=0" parent=cave_center
+[scene_node] id=loc_right loc="X=2.0,Y=1.0,Z=0.01" rot="P=0,Y=0,R=0" parent=cave_center
+
+#####################################################################
+# Input devices
+#******************************************************************** 
+# Input devices are VRPN devices. The nDisplay supports the following
+# types: analog, button and tracker. Many of physical input devices
+# can be connected via VRPN.
+#
+# Properties:
+# id    - nique device name
+# type  - VRPN type (analog, button or tracker).
+# addr  - address of a VRPN server which handles this particular device.
+#         The value must match the following format: DEVICENAME@SERVER_ADDRESS
+#         where DEVICENAME is a VRPN name of this device and SERVER_ADDRESS
+#         is IPv4 address of VRPN server.
+# loc   - relative location to the parent component. Location is relative
+#         to the VR root if no parent specified.
+# rot   - relative rotation to the parent component. Rotation is relative
+#         to the VR root if no parent specified.
+#
+# front (tracker only) - mapping of a tracking system axis to X axis of VR origin
+# right (tracker only) - mapping of a tracking system axis to Y axis of VR origin
+# up    (tracker only) - mapping of a tracking system axis to Z axis of VR origin
+# * The following values are allowed for axes mapping: X, -X, Y, -Y, Z, -Z
+#
+# Optional properties:
+# remap - VRPN device channel remapping. Value format is: "from0:to0,from1:to1,...,fromN:toN".
+#         For example: remap="0:3,1:4,5:2"
+#******************************************************************** 
+
+#####################################################################
+# Stereoscopic settings
+#******************************************************************** 
+# Properties:
+# eye_dist - interoccular distance in meters
+[stereo] eye_dist=0.064
+#####################################################################
+# General settings
+#******************************************************************** 
+# Properties:
+# swap_sync_policy - swap synchronization policy
+#                  - 0 - no synchronization
+#                  - 1 - software swap synchronization
+#                  - 2 - NV swap lock (Nvidia cards only, OpenGL only)
+[general] swap_sync_policy=1
+
+#####################################################################
+# Network settings
+#******************************************************************** 
+# Optional properties:
+# cln_conn_tries_amount - how many times a client tries to connect to a server
+# cln_conn_retry_delay  - delay before next client connection try (milliseconds)
+# game_start_timeout    - timeout before all data is loaded and game started (milliseconds)
+# barrier_wait_timeout  - barrier timeout for both game and render threads (milliseconds)
+[network] cln_conn_tries_amount=300 cln_conn_retry_delay=1000 game_start_timeout=60000 barrier_wait_timeout=60000
+
+#####################################################################
+# Custom arguments
+#******************************************************************** 
+# Any custom arguments available in runtime can be specified here.
+# Format:  ARG_NAME=ARG_VAL
+[custom] Hardware_Platform=TwoScreen
diff --git a/Source/NDisplayLaunchButton/Private/NDisplayLaunchButton.cpp b/Source/NDisplayLaunchButton/Private/NDisplayLaunchButton.cpp
index 5b89b28..f6da583 100644
--- a/Source/NDisplayLaunchButton/Private/NDisplayLaunchButton.cpp
+++ b/Source/NDisplayLaunchButton/Private/NDisplayLaunchButton.cpp
@@ -86,7 +86,7 @@ void FNDisplayLaunchButtonModule::PluginButtonClicked()
 
 		const int Num_Nodes = 5;
 		FString Windows_Node_Specific_Commands[Num_Nodes] = {
-			"dc_node=node_floor WinX=720  WinY=300 ResX=480 ResY=480" + ((Settings->MiniCAVELogToProjectDir) ? (" -log ABSLOG=" + FPaths::ProjectDir() + "\\MiniCaveMulti.log") : "") + " " + Settings->MiniCAVEAdditionalLaunchParametersMaster,
+			"dc_node=node_floor WinX=720  WinY=300 ResX=480 ResY=480" + ((Settings->MiniCAVELogToProjectDir) ? (" -log ABSLOG=" + FPaths::ProjectDir() + "\\MiniCave.log") : "") + " " + Settings->MiniCAVEAdditionalLaunchParametersMaster,
 			"dc_node=node_front WinX=720  WinY=0   ResX=480 ResY=300",
 			"dc_node=node_left  WinX=420  WinY=300 ResX=300 ResY=480",
 			"dc_node=node_right WinX=1200 WinY=300 ResX=300 ResY=480",
@@ -98,7 +98,27 @@ void FNDisplayLaunchButtonModule::PluginButtonClicked()
 		{
 			Processes[i] = FPlatformProcess::CreateProc(*EditorExecutable, *(Parameters + " " + Windows_Node_Specific_Commands[i]), true, false, false, nullptr, 0, nullptr, nullptr);
 		}
-		FPlatformProcess::WaitForProc(Processes[Num_Nodes - 1]); //wait for only one of them
+		FPlatformProcess::WaitForProc(Processes[0]); //wait for only one of them
+	}
+
+	if (Settings->LaunchType == ButtonLaunchType_TWO_SCREEN)
+	{
+		FString Config = IPluginManager::Get().FindPlugin("NDisplayLaunchButton")->GetBaseDir() + "/LaunchConfig/twoscreen.cfg";
+		FString EditorExecutable = "UE4Editor.exe";
+		FString Parameters = "\"" + FPaths::GetProjectFilePath() + "\" -game dc_cfg=\"" + Config + "\" " + Settings->TwoScreenLaunchParameters;
+
+		const int Num_Nodes = 2;
+		FString Windows_Node_Specific_Commands[Num_Nodes] = {
+			"dc_node=node_left  WinX=200 WinY=200 ResX=480 ResY=480" + ((Settings->TwoScreenLogToProjectDir) ? (" -log ABSLOG=" + FPaths::ProjectDir() + "\\TwoScreenL.log") : "") + " " + Settings->MiniCAVEAdditionalLaunchParametersMaster,
+			"dc_node=node_right WinX=682 WinY=200 ResX=480 ResY=480 -log ABSLOG=" + FPaths::ProjectDir() + "\\TwoScreenR.log"
+		};
+
+		FProcHandle Processes[Num_Nodes];
+		for (int i = 0; i < Num_Nodes; i++)
+		{
+			Processes[i] = FPlatformProcess::CreateProc(*EditorExecutable, *(Parameters + " " + Windows_Node_Specific_Commands[i]), true, false, false, nullptr, 0, nullptr, nullptr);
+		}
+		FPlatformProcess::WaitForProc(Processes[0]); //wait for only one of them
 	}
 
 	if (Settings->LaunchType == ButtonLaunchType_CAVE)
diff --git a/Source/NDisplayLaunchButton/Public/NDisplayLaunchButtonSettings.h b/Source/NDisplayLaunchButton/Public/NDisplayLaunchButtonSettings.h
index fbded7a..90e10af 100644
--- a/Source/NDisplayLaunchButton/Public/NDisplayLaunchButtonSettings.h
+++ b/Source/NDisplayLaunchButton/Public/NDisplayLaunchButtonSettings.h
@@ -19,6 +19,7 @@ enum ButtonLaunchType
 {
 	ButtonLaunchType_NONE UMETA(DisplayName = "Nothing"),
 	ButtonLaunchType_MiniCAVE UMETA(DisplayName = "MiniCAVE"),
+	ButtonLaunchType_TWO_SCREEN UMETA(DisplayName = "Two Screen"),
 	ButtonLaunchType_CAVE UMETA(DisplayName = "CAVE"),
 	ButtonLaunchType_ROLV UMETA(DisplayName = "ROLV")
 };
@@ -32,6 +33,16 @@ public:
 	UPROPERTY(EditAnywhere, config, Category = "General", meta = (DisplayName = "Start "))
 	TEnumAsByte<ButtonLaunchType> LaunchType = ButtonLaunchType_MiniCAVE;
 
+	/*
+	 * TwoScreen Options
+	 */
+	UPROPERTY(EditAnywhere, config, Category = "General|TwoScreen", meta = (DisplayName = "Launch Parameters"))
+	FString TwoScreenLaunchParameters = "-dc_cluster -dc_dev_mono -windowed -fixedseed -notexturestreaming -opengl4";
+	UPROPERTY(EditAnywhere, config, Category = "General|TwoScreen", meta = (DisplayName = "Additioanl Launch Parameters for Master"))
+	FString TwoScreenAdditionalLaunchParametersMaster = "";
+	UPROPERTY(EditAnywhere, config, Category = "General|TwoScreen", meta = (DisplayName = "Write Log to Project Directory"))
+	bool TwoScreenLogToProjectDir = true;
+	
 	/*
 	 * Mini CAVE Options
 	 */
@@ -89,6 +100,10 @@ public:
 		const bool ParentVal = Super::CanEditChange(InProperty);
 
 		//TODO: This Code is not executed for all FFilePath Properties. Check if this works in 4.23 with the EditCondition Parser and remove this method
+
+		PROPERTY_CONDITION_CHECK(TwoScreenLaunchParameters, LaunchType == ButtonLaunchType_TWO_SCREEN)
+		PROPERTY_CONDITION_CHECK(TwoScreenAdditionalLaunchParametersMaster, LaunchType == ButtonLaunchType_TWO_SCREEN)
+		PROPERTY_CONDITION_CHECK(TwoScreenLogToProjectDir, LaunchType == ButtonLaunchType_TWO_SCREEN)
 		
 		PROPERTY_CONDITION_CHECK(MiniCAVELaunchParameters, LaunchType == ButtonLaunchType_MiniCAVE)
 		PROPERTY_CONDITION_CHECK(MiniCAVEAdditionalLaunchParametersMaster, LaunchType == ButtonLaunchType_MiniCAVE)
-- 
GitLab