public override void OnInspectorGUI() { PhysicalDisplayManager manager = target as PhysicalDisplayManager; manager.machineName = EditorGUILayout.TextField("Machine Name", manager.machineName); manager.displayNumber = EditorGUILayout.IntField("Display Number", manager.displayNumber); manager.displayResolution = EditorGUILayout.Vector2IntField("Resolution", manager.displayResolution); if (GUILayout.Button("Assign children to this manager")) { foreach (Transform child in manager.transform) { PhysicalDisplay disp = child.GetComponent <PhysicalDisplay>(); if (disp != null) { if (disp.manager != null) { disp.manager.displays.Remove(disp); } disp.manager = manager; EditorUtility.SetDirty(disp); if (!manager.displays.Contains(disp)) { manager.displays.Add(disp); } } } } GUILayout.Label("Associated displays:"); for (int i = 0; i < manager.displays.Count; i++) { if (EditorGUILayout.ObjectField(manager.displays[i], typeof(PhysicalDisplay)) == null) { manager.displays.RemoveAt(i); i--; } } PhysicalDisplay addedDisp = (PhysicalDisplay)EditorGUILayout.ObjectField("Add Display", null, typeof(PhysicalDisplay)); if (addedDisp != null) { if (addedDisp.manager != null) { addedDisp.manager.displays.Remove(addedDisp); } addedDisp.manager = manager; if (!manager.displays.Contains(addedDisp)) { manager.displays.Add(addedDisp); } } if (GUI.changed) { EditorUtility.SetDirty(manager); EditorSceneManager.MarkSceneDirty(EditorSceneManager.GetActiveScene()); } }
void Start() { PhysicalDisplay disp = this.display = gameObject.GetComponent <PhysicalDisplay>(); disp.transform.localPosition = disp.transform.localPosition + disp.transform.right.normalized * (rightBlend - leftBlend) * disp.halfWidth() * 0.5f + disp.transform.up.normalized * (topBlend - bottomBlend) * disp.halfHeight() * 0.5f; Vector2 shift = new Vector2((leftBlend + rightBlend) * disp.halfWidth(), (bottomBlend + topBlend) * disp.halfHeight()); disp.width += shift.x; disp.height += shift.y; }
void Update() { if (!initialized) { if (GetComponent <PhysicalDisplay>().Initialized()) { SetupPostProcessing(); initialized = true; } } else { PhysicalDisplay display = GetComponent <PhysicalDisplay>(); } }
void OnDrawGizmosSelected() { #if UNITY_EDITOR if (EditorApplication.isPlaying) { return; } #endif PhysicalDisplay disp = GetComponent <PhysicalDisplay>(); if (leftBlend != 0) { Gizmos.color = Color.red; Gizmos.DrawLine(disp.ScreenspaceToWorld(new Vector2(-1.0f - leftBlend, 1.0f)), disp.ScreenspaceToWorld(new Vector2(-1.0f - leftBlend, -1.0f))); Gizmos.color = Color.blue; Gizmos.DrawLine(disp.ScreenspaceToWorld(new Vector2(-1.0f + leftBlend, 1.0f)), disp.ScreenspaceToWorld(new Vector2(-1.0f + leftBlend, -1.0f))); } if (topBlend != 0) { Gizmos.color = Color.red; Gizmos.DrawLine(disp.ScreenspaceToWorld(new Vector2(-1.0f, 1.0f + topBlend)), disp.ScreenspaceToWorld(new Vector2(1.0f, 1.0f + topBlend))); Gizmos.color = Color.blue; Gizmos.DrawLine(disp.ScreenspaceToWorld(new Vector2(-1.0f, 1.0f - topBlend)), disp.ScreenspaceToWorld(new Vector2(1.0f, 1.0f - topBlend))); } if (rightBlend != 0) { Gizmos.color = Color.red; Gizmos.DrawLine(disp.ScreenspaceToWorld(new Vector2(1.0f + rightBlend, 1.0f)), disp.ScreenspaceToWorld(new Vector2(1.0f + rightBlend, -1.0f))); Gizmos.color = Color.blue; Gizmos.DrawLine(disp.ScreenspaceToWorld(new Vector2(1.0f - rightBlend, 1.0f)), disp.ScreenspaceToWorld(new Vector2(1.0f - rightBlend, -1.0f))); } if (bottomBlend != 0) { Gizmos.color = Color.red; Gizmos.DrawLine(disp.ScreenspaceToWorld(new Vector2(-1.0f, -1.0f - bottomBlend)), disp.ScreenspaceToWorld(new Vector2(1.0f, -1.0f - bottomBlend))); Gizmos.color = Color.blue; Gizmos.DrawLine(disp.ScreenspaceToWorld(new Vector2(-1.0f, -1.0f + bottomBlend)), disp.ScreenspaceToWorld(new Vector2(1.0f, -1.0f + bottomBlend))); } }
/// <summary> /// helper function to assign this manager to all its children /// </summary> /// <param name="it">initial object to recursively iterate through</param> void AssignManagerChildren_h(GameObject it = null) { if (it == null) { it = gameObject; } for (int i = 0; i < it.transform.childCount; i++) { GameObject child = it.transform.GetChild(i).gameObject; PhysicalDisplay disp = child.GetComponent <PhysicalDisplay>(); if (disp != null) { if (disp.manager != null) { disp.manager.displays.Remove(disp); } displays.Remove(disp); disp.manager = this; displays.Add(disp); } AssignManagerChildren_h(child); } }
public override void OnInspectorGUI() { PhysicalDisplay display = target as PhysicalDisplay; PhysicalDisplayManager newMan = (PhysicalDisplayManager)EditorGUILayout.ObjectField("Manager", display.manager, typeof(PhysicalDisplayManager), true); if (newMan != display.manager) { if (newMan != null) { newMan.displays.Add(display); } if (display.manager != null) { display.manager.displays.Remove(display); } } display.manager = newMan; if (newMan == null) { display.machineName = EditorGUILayout.TextField("Machine Name", display.machineName); } display.width = EditorGUILayout.FloatField("Physical Width", display.width); display.height = EditorGUILayout.FloatField("Physical Height", display.height); display.head = (HeadConfiguration)EditorGUILayout.ObjectField("Head", display.head, typeof(HeadConfiguration), true); display.useXRCameras = EditorGUILayout.Toggle(new GUIContent( "Use XR Cameras (Quad Buffer)", @"Whether the cameras associated with this display should output to an XR device (such as headset or quad-buffered stereo 3D display) If you do post processing on the cameras (such as a PhysicalDisplayCalibration) set this to false This is probably also unnecessary if using a Dual-Pipe 3D display" ), display.useXRCameras ); display.useRenderTextures = EditorGUILayout.Toggle(new GUIContent("Use Render Textures", "Render to render textures instead of screen, for post processing"), display.useRenderTextures); if (display.useRenderTextures) { display.renderTextureSize = EditorGUILayout.Vector2IntField("Render Texture Size", display.renderTextureSize); } if (!display.useRenderTextures && display.manager == null) { if (display.exclusiveFullscreen = EditorGUILayout.Toggle("Use Specific Display", display.exclusiveFullscreen)) { display.display = EditorGUILayout.IntField("Display", display.display); } } if (display.manager != null) { display.exclusiveFullscreen = false; } if (display.is3D = EditorGUILayout.Toggle("Is 3D", display.is3D)) { if (display.dualPipe = EditorGUILayout.Toggle(new GUIContent("Dual Pipe", "Does the display use a dual pipe setup?"), display.dualPipe)) { if (!display.exclusiveFullscreen && !(display.dualInstance = EditorGUILayout.Toggle(new GUIContent("Dual Instance", "Use one instance of Unity for each eye?"), display.dualInstance))) { //3d, dual pipe, single instance display.windowBounds = EditorGUILayout.RectIntField(new GUIContent("Viewport Rect", "Where the window will be positioned on the screen"), display.windowBounds); } display.leftViewport = EditorGUILayout.RectIntField("Left Viewport", display.leftViewport); display.rightViewport = EditorGUILayout.RectIntField("Right Viewport", display.rightViewport); } else { display.windowBounds = EditorGUILayout.RectIntField(new GUIContent("Viewport Rect", "Where the window will be positioned on the screen"), display.windowBounds); display.dualInstance = false; } } else { display.windowBounds = EditorGUILayout.RectIntField(new GUIContent("Viewport Rect", "Where the window will be positioned on the screen"), display.windowBounds); display.dualPipe = false; display.dualInstance = false; } List <string> errors = display.GetSettingsErrors(); if (errors.Count != 0) { GUIStyle style = new GUIStyle(); style.richText = true; EditorGUILayout.LabelField("<color=red>This PhysicalDisplay has some incompatible or invalid settings, behavior may be undefined!</color>", style); } foreach (string error in errors) { GUIStyle style = new GUIStyle(); style.richText = true; EditorGUILayout.LabelField("<color=red>" + error + "</color>", style); } if (display.loadSettingsAtRuntime = EditorGUILayout.Toggle("Load Settings At Runtime", display.loadSettingsAtRuntime)) { display.serializedLocation = EditorGUILayout.TextField(display.serializedLocation); } if (GUILayout.Button("Export Display Settings to JSON")) { string path = EditorUtility.SaveFilePanel("Export Settings", "./", "settings.json", "json"); if (path != null) { display.TryToSerialize(path); } } if (GUILayout.Button("Import Display Settings from JSON")) { string path = EditorUtility.SaveFilePanel("Import Settings", "./", "settings.json", "json"); if (path != null) { display.TryToDeSerialize(path); } } if (GUI.changed) { EditorUtility.SetDirty(display); EditorSceneManager.MarkSceneDirty(EditorSceneManager.GetActiveScene()); } }
void SetupPostProcessing() { PhysicalDisplay display = GetComponent <PhysicalDisplay>(); GameObject staticParent = new GameObject("Post Holder For: " + gameObject.name); // Apply multiplier factor on all vertices int numVertices = this.dewarpMeshPositions.verts.Length; Vector3[] verts = this.dewarpMeshPositions.verts; for (int i = 0; i < numVertices; i++) { verts[i] *= GetMultiplierFactor(); } bool stereo = true; if (this.GetDisplay().is3D) { if (this.GetDisplay().leftCam != null) { this.displayCalibrations.Add(HeadCamera.LEFT, new Dewarp(gameObject.name, this.postProcessMaterial, dewarpMeshPositions, this.GetDisplay().leftTex)); this.RemovePostProcessingFromHeadCamera(this.GetDisplay().leftCam); } else { stereo = false; } if (this.GetDisplay().rightCam != null) { this.displayCalibrations.Add(HeadCamera.RIGHT, new Dewarp(gameObject.name, this.postProcessMaterial, dewarpMeshPositions, this.GetDisplay().rightTex)); this.RemovePostProcessingFromHeadCamera(this.GetDisplay().rightCam); } else { stereo = false; } } else { stereo = false; this.displayCalibrations.Add(HeadCamera.CENTER, new Dewarp(gameObject.name, this.postProcessMaterial, dewarpMeshPositions, this.GetDisplay().centerTex)); this.RemovePostProcessingFromHeadCamera(this.GetDisplay().centerCam); } this.SetDewarpPositions(stereo); foreach (var displayCalibration in this.displayCalibrations) { GameObject obj = displayCalibration.Value.GetDewarpGameObject(); obj.layer = this.postProcessLayer; obj.transform.parent = staticParent.transform; } { camChild = new GameObject("Calibration Cam (Left)"); camChild.transform.parent = staticParent.transform; Camera postCam = camChild.AddComponent <Camera>(); postCam.transform.localPosition = globalPostOffset + new Vector3(stereo ? -displayRatio * 2.0f : 0.0f, 0.0f, -1.0f); postCam.stereoTargetEye = StereoTargetEyeMask.Left; this.SetPostCameraProperties(postCam); postCams.Add(postCam); } { GameObject calibrationCamera = new GameObject("Calibration Cam (Right)"); calibrationCamera.transform.parent = staticParent.transform; Camera postCam = calibrationCamera.AddComponent <Camera>(); postCam.transform.localPosition = globalPostOffset + new Vector3(stereo ? displayRatio * 2.0f : 0.0f, 0.0f, -1.0f); postCam.stereoTargetEye = StereoTargetEyeMask.Right; this.SetPostCameraProperties(postCam); postCams.Add(postCam); } globalPostOffset = globalPostOffset + new Vector3(10, 10, 10); }
/// <summary> /// Waits for all displays to be initialized, then repositions camera viewports and/or sets up post processing /// </summary> void Update() { if (!_initialized) { bool displaysInitialized = true; for (int i = 0; i < displays.Count; i++) { if (!displays[i].gameObject.activeSelf) { continue; } if (displays[i].enabled && !displays[i].Initialized()) { displaysInitialized = false; break; } PhysicalDisplayCalibration cali = displays[i].gameObject.GetComponent <PhysicalDisplayCalibration>(); if (cali != null && !cali.Initialized()) { displaysInitialized = false; break; } } //wait until every other display is initialized if (displaysInitialized) { for (int i = 0; i < displays.Count; i++) { PhysicalDisplay display = displays[i]; PhysicalDisplayCalibration cali = display.gameObject.GetComponent <PhysicalDisplayCalibration>(); if (cali == null) { if (!display.useRenderTextures) { if (display.dualPipe) { Vector2Int windowSpaceOffset = display.dualInstance ? new Vector2Int(0, 0) : new Vector2Int(display.windowBounds.x, display.windowBounds.y); display.leftCam.pixelRect = new Rect( windowSpaceOffset.x + display.leftViewport.x, windowSpaceOffset.y + display.leftViewport.y, display.leftViewport.width, display.leftViewport.height); display.rightCam.pixelRect = new Rect( windowSpaceOffset.x + display.rightViewport.x, windowSpaceOffset.y + display.rightViewport.y, display.rightViewport.width, display.rightViewport.height); } else { //if stereo blit is enabled, only update the viewport of the center cam (in the future perhaps consolidate this logic with useRenderTextures) if (display.centerCam != null && display.centerCam.GetComponent <StereoBlit>() != null) { display.centerCam.pixelRect = new Rect(display.windowBounds.x, display.windowBounds.y, display.windowBounds.width, display.windowBounds.height); } else { foreach (Camera cam in display.GetAllCameras()) { Debug.Log("Manager [" + name + "] set Camera [" + cam.name + "] viewport to <" + display.windowBounds.x + ", " + display.windowBounds.y + ", " + display.windowBounds.width + ", " + display.windowBounds.height + ">"); cam.pixelRect = new Rect(display.windowBounds.x, display.windowBounds.y, display.windowBounds.width, display.windowBounds.height); } } } } } else { //special case for PhysicalDisplayCalibration //Debug.Log("Display:"); foreach (Camera cam in cali.postCams) { Rect r = new Rect(display.windowBounds.x, display.windowBounds.y, display.windowBounds.width, display.windowBounds.height); //Debug.Log("Set cam " + cam.name + " to " + r); cam.pixelRect = r; } } } _initialized = true; } } }
void Update() { if (!_initialized) { bool displaysInitialized = true; for (int i = 0; i < displays.Count; i++) { if (displays[i].gameObject.activeSelf && displays[i].enabled && !displays[i].Initialized()) { displaysInitialized = false; break; } PhysicalDisplayCalibration cali = displays[i].gameObject.GetComponent <PhysicalDisplayCalibration>(); if (cali != null && !cali.Initialized()) { displaysInitialized = false; break; } } //wait until every other display is initialized if (displaysInitialized) { for (int i = 0; i < displays.Count; i++) { PhysicalDisplay display = displays[i]; PhysicalDisplayCalibration cali = display.gameObject.GetComponent <PhysicalDisplayCalibration>(); if (cali == null) { if (!display.useRenderTextures) { if (display.dualPipe) { Vector2Int windowSpaceOffset = display.dualInstance ? new Vector2Int(0, 0) : new Vector2Int(display.windowBounds.x, display.windowBounds.y); display.leftCam.pixelRect = new Rect( windowSpaceOffset.x + display.leftViewport.x, windowSpaceOffset.y + display.leftViewport.y, display.leftViewport.width, display.leftViewport.height); display.rightCam.pixelRect = new Rect( windowSpaceOffset.x + display.rightViewport.x, windowSpaceOffset.y + display.rightViewport.y, display.rightViewport.width, display.rightViewport.height); } else { foreach (Camera cam in display.GetAllCameras()) { Debug.Log("Manager [" + name + "] set Camera [" + cam.name + "] viewport to <" + display.windowBounds.x + ", " + display.windowBounds.y + ", " + display.windowBounds.width + ", " + display.windowBounds.height + ">"); cam.pixelRect = new Rect(display.windowBounds.x, display.windowBounds.y, display.windowBounds.width, display.windowBounds.height); } } } } else { //special case for PhysicalDisplayCalibration Debug.Log("Display:"); foreach (Camera cam in cali.postCams) { cam.pixelRect = new Rect(display.windowBounds.x, display.windowBounds.y, display.windowBounds.width, display.windowBounds.height); } } } _initialized = true; } } }
void SetupPostProcessing() { PhysicalDisplay display = GetComponent <PhysicalDisplay>(); GameObject staticParent = new GameObject("Post Holder For: " + gameObject.name); bool stereo = true; if (display.leftCam != null) { //create left child object that will contain the dewarping mesh leftChild = new GameObject("Dewarp Mesh (left) For: " + gameObject.name); leftChild.layer = 8; leftChild.transform.parent = staticParent.transform; //add the dewarping mesh to the left child MeshFilter meshComponent = leftChild.AddComponent <MeshFilter>(); Mesh mesh = new Mesh(); Vector2 multiplier = new Vector2(displayRatio, 1.0f); Vector3[] verts = { upperRightPosition *multiplier, upperLeftPosition *multiplier, lowerLeftPosition *multiplier, lowerRightPosition *multiplier }; Vector2[] uvs = { new Vector2(1.0f, 1.0f), new Vector2(0.0f, 1.0f), new Vector2(0.0f, 0.0f), new Vector2(1.0f, 0.0f) }; Vector3[] normals = { -Vector3.forward, -Vector3.forward, -Vector3.forward, -Vector3.forward }; int[] triangles = { 0, 2, 1, 0, 3, 2 }; mesh.vertices = verts; mesh.triangles = triangles; mesh.uv = uvs; mesh.normals = normals; meshComponent.mesh = mesh; leftChild.layer = 8; //post processing layer is 8 //create material for left mesh leftRenderMat = new Material(postProcessMaterial); leftRenderMat.name = "Left Material"; //create render texture for left camera display.leftCam.cullingMask &= ~(1 << 8); //remove post processing layer of the worldspace camera Vector3 oldPos = display.leftCam.transform.localPosition; display.leftCam.stereoTargetEye = StereoTargetEyeMask.None; display.leftCam.transform.localPosition = oldPos; //assign the render texture to the material and the material to the mesh leftRenderMat.mainTexture = display.leftTex; leftChild.AddComponent <MeshRenderer>().material = leftRenderMat; } else { stereo = false; } if (display.rightCam != null) { //create right child object that will contain the dewarping mesh rightChild = new GameObject("Dewarp Mesh (right) For: " + gameObject.name); rightChild.layer = 8; rightChild.transform.parent = staticParent.transform; //add the dewarping mesh to the right child MeshFilter meshComponent = rightChild.AddComponent <MeshFilter>(); Mesh mesh = new Mesh(); Vector2 multiplier = new Vector2(displayRatio, 1.0f); Vector3[] verts = { upperRightPosition *multiplier, upperLeftPosition *multiplier, lowerLeftPosition *multiplier, lowerRightPosition *multiplier }; Vector2[] uvs = { new Vector2(1.0f, 1.0f), new Vector2(0.0f, 1.0f), new Vector2(0.0f, 0.0f), new Vector2(1.0f, 0.0f) }; Vector3[] normals = { -Vector3.forward, -Vector3.forward, -Vector3.forward, -Vector3.forward }; int[] triangles = { 0, 2, 1, 0, 3, 2 }; mesh.vertices = verts; mesh.triangles = triangles; mesh.uv = uvs; mesh.normals = normals; meshComponent.mesh = mesh; rightChild.layer = 8; //post processing layer is 8 //create material for right mesh rightRenderMat = new Material(postProcessMaterial); rightRenderMat.name = "Right Material"; //create render texture for right camera display.rightCam.cullingMask &= ~(1 << 8); //remove post processing layer of the worldspace camera Vector3 oldPos = display.rightCam.transform.localPosition; display.rightCam.stereoTargetEye = StereoTargetEyeMask.None; display.rightCam.transform.localPosition = oldPos; //assign the render texture to the material and the material to the mesh rightRenderMat.mainTexture = display.rightTex; rightChild.AddComponent <MeshRenderer>().material = rightRenderMat; } else { stereo = false; } //set the positions of the dewarping mesh children //we have to do this later because if its not stereo only one exists and it should be at the center of the screen if (leftChild != null) { leftChild.transform.localPosition = globalPostOffset + new Vector3(stereo ? -displayRatio * 2.0f : 0.0f, 0.0f, 0.0f); } if (rightChild != null) { rightChild.transform.localPosition = globalPostOffset + new Vector3(stereo ? displayRatio * 2.0f : 0.0f, 0.0f, 0.0f); } { camChild = new GameObject("Calibration Cam (Left)"); camChild.transform.parent = staticParent.transform; Camera postCam = camChild.AddComponent <Camera>(); postCam.transform.localPosition = globalPostOffset + new Vector3(stereo ? -displayRatio * 2.0f : 0.0f, 0.0f, -1.0f); postCam.nearClipPlane = 0.1f; postCam.farClipPlane = 10.0f; postCam.fieldOfView = 90.0f; postCam.stereoSeparation = 0.0f; postCam.stereoConvergence = 1000.0f; //probably doesn't matter but far away makes the most sense postCam.cullingMask = 1 << 8; //post processing layer postCam.backgroundColor = Color.black; postCam.clearFlags = CameraClearFlags.SolidColor; postCam.depth = 1; postCam.stereoTargetEye = StereoTargetEyeMask.Left; postCam.allowHDR = false; postCam.allowMSAA = false; postCam.renderingPath = RenderingPath.Forward; postCams.Add(postCam); } { GameObject obj2 = new GameObject("Calibration Cam (Right)"); obj2.transform.parent = staticParent.transform; Camera postCam = obj2.AddComponent <Camera>(); postCam.transform.localPosition = globalPostOffset + new Vector3(stereo ? displayRatio * 2.0f : 0.0f, 0.0f, -1.0f); postCam.nearClipPlane = 0.1f; postCam.farClipPlane = 10.0f; postCam.fieldOfView = 90.0f; postCam.stereoSeparation = 0.0f; postCam.stereoConvergence = 1000.0f; //probably doesn't matter but far away makes the most sense postCam.cullingMask = 1 << 8; //post processing layer postCam.backgroundColor = Color.black; postCam.clearFlags = CameraClearFlags.SolidColor; postCam.depth = 1; postCam.stereoTargetEye = StereoTargetEyeMask.Right; postCam.allowHDR = false; postCam.allowMSAA = false; postCam.renderingPath = RenderingPath.Forward; postCams.Add(postCam); } globalPostOffset = globalPostOffset + new Vector3(10, 10, 10); }