#if UNITY_2020_2_OR_NEWER using System; using System.Collections.Generic; using System.IO; using UnityEngine; using UnityEngine.UIElements; using UnityEngine.XR; using UnityEditor; namespace MockHMD.Editor.MultiCamera { public class AddCameraWindow : EditorWindow { private List camerasToRemove = new List(); private AdditionalMockCameras additionalMockCameras = new AdditionalMockCameras(); private Vector2 camerasScrollPos; private static NativeApi.MockCameraProperties cameraProperties = new NativeApi.MockCameraProperties{position = new NativeApi.float3(), rotation = new NativeApi.float4()}; private static List s_displaySubsystems = new List(); private int m_CameraIds = 0; private bool isPlaying = false; XRDisplaySubsystem GetXRDisplaySubsystem() { XRDisplaySubsystem ret = null; SubsystemManager.GetInstances(s_displaySubsystems); if (s_displaySubsystems.Count > 0) { ret = s_displaySubsystems[0]; } return ret; } [MenuItem ("Window/XR/Mock HMD/Additional Cameras")] public static void ShowWindow () { EditorWindow w = EditorWindow.GetWindow(typeof(AddCameraWindow)); w.titleContent = new GUIContent("Mock HMD Add Camera"); } void OnEnable() { if (EditorPrefs.HasKey("MOCK Cameras")) { string data = EditorPrefs.GetString("MOCK Cameras"); JsonUtility.FromJsonOverwrite(data, additionalMockCameras); foreach (var mockCam in additionalMockCameras.extraCameras) { mockCam.id = -1; } } EditorApplication.playModeStateChanged += SetCamerasOnPlayStateChanged; } void OnDisable() { EditorApplication.playModeStateChanged -= SetCamerasOnPlayStateChanged; string data = JsonUtility.ToJson(additionalMockCameras); EditorPrefs.SetString("MOCK Cameras", data); } void SetCamerasOnPlayStateChanged(PlayModeStateChange state) { switch(state) { case PlayModeStateChange.EnteredEditMode: break; case PlayModeStateChange.ExitingEditMode: break; case PlayModeStateChange.EnteredPlayMode: foreach (var mockCam in additionalMockCameras.extraCameras) { if (mockCam.isActive) { if (mockCam.id < 0) { mockCam.id = m_CameraIds; m_CameraIds++; } if (!NativeApi.HasCameraWithId(mockCam.id)) AddCamera(mockCam.id); } } isPlaying = true; break; case PlayModeStateChange.ExitingPlayMode: isPlaying = false; foreach (var mockCam in additionalMockCameras.extraCameras) { if (mockCam.isActive) { if (NativeApi.HasCameraWithId(mockCam.id)) RemoveCamera(mockCam.id); mockCam.id = -1; } } break; } } void UpdateAllCameras() { if (isPlaying) { foreach (var mockCam in additionalMockCameras.extraCameras) { if (mockCam.isActive) { if (mockCam.id < 0) { mockCam.id = m_CameraIds; m_CameraIds++; } if (!NativeApi.HasCameraWithId(mockCam.id)) AddCamera(mockCam.id); UpdateCamera(mockCam.id, mockCam); } else { if (NativeApi.HasCameraWithId(mockCam.id)) RemoveCamera(mockCam.id); } } } } void OnGUI() { var displaySubsystem = GetXRDisplaySubsystem(); EditorGUILayout.BeginVertical(); EditorGUILayout.Space(); if (GUILayout.Button("Add Camera")) { var newCam = new MockCamera(); newCam.id = -1; additionalMockCameras.extraCameras.Add(newCam); } EditorGUILayout.LabelField("", GUI.skin.horizontalSlider); EditorGUILayout.Space(); int camIndex = 0; camerasScrollPos = EditorGUILayout.BeginScrollView(camerasScrollPos, GUILayout.Width(EditorGUIUtility.currentViewWidth)); foreach (var mockCam in additionalMockCameras.extraCameras) { EditorGUILayout.LabelField($"Mock Camera {camIndex}", EditorStyles.largeLabel); EditorGUILayout.Space(); camIndex++; mockCam.position = EditorGUILayout.Vector3Field("Position", mockCam.position); mockCam.rotation.eulerAngles = EditorGUILayout.Vector3Field("Rotation", mockCam.rotation.eulerAngles); mockCam.fov = EditorGUILayout.FloatField("Field of View", mockCam.fov); mockCam.near = EditorGUILayout.FloatField("Near", mockCam.near); mockCam.far = EditorGUILayout.FloatField("Far", mockCam.far); mockCam.textureWidth = EditorGUILayout.IntField("Texture Width", mockCam.textureWidth); mockCam.textureHeight = EditorGUILayout.IntField("Texture Height", mockCam.textureHeight); mockCam.stereoRenderingMode = (MockRenderingMode)EditorGUILayout.EnumPopup("Rendering Mode", mockCam.stereoRenderingMode); if (mockCam.stereoRenderingMode != MockRenderingMode.None) { mockCam.eyeSeparation = EditorGUILayout.FloatField("Eye Separation", mockCam.eyeSeparation); mockCam.eyeSeparation = Math.Max(Math.Min(5.0f, mockCam.eyeSeparation), 0.01f); } mockCam.enableLeftOcclusion = EditorGUILayout.Toggle("Left Occlusion: ", mockCam.enableLeftOcclusion); mockCam.leftAbberation = EditorGUILayout.Vector2Field("Left Abberation: ", mockCam.leftAbberation); EditorGUILayout.Space(); mockCam.enableRightOcclusion = EditorGUILayout.Toggle("Right Occlusion: ", mockCam.enableRightOcclusion); mockCam.rightAbberation = EditorGUILayout.Vector2Field("Right Abberation: ", mockCam.rightAbberation); bool preIsActive = mockCam.isActive; mockCam.isActive = EditorApplication.isPlayingOrWillChangePlaymode ? EditorGUILayout.Toggle("Active", mockCam.isActive) : false; if (GUILayout.Button("Remove Camera")) { camerasToRemove.Add(mockCam); mockCam.isActive = false; } EditorGUILayout.LabelField("", GUI.skin.horizontalSlider); } EditorGUILayout.EndScrollView(); EditorGUILayout.EndVertical(); UpdateAllCameras(); foreach (var mockCam in camerasToRemove) { additionalMockCameras.extraCameras.Remove(mockCam); } } private void AddCamera(int id) { if (NativeApi.HasCameraWithId(id)) { Debug.LogWarning($"Attmept to add a camera with id {id} when one already exists!"); return; } if (!NativeApi.AddCameraWithId(id)) { Debug.LogError($"Attempt to add camera with id {id} failed!"); } } private void UpdateCamera(int id, MockCamera mockCam) { if (NativeApi.HasCameraWithId(id)) { cameraProperties.position.x = mockCam.position.x; cameraProperties.position.y = mockCam.position.y; cameraProperties.position.z = mockCam.position.z; cameraProperties.rotation.x = mockCam.rotation.x; cameraProperties.rotation.y = mockCam.rotation.y; cameraProperties.rotation.z = mockCam.rotation.z; cameraProperties.rotation.w = mockCam.rotation.w; cameraProperties.fov = mockCam.fov; cameraProperties.near = mockCam.near; cameraProperties.far = mockCam.far; cameraProperties.textureWidth = mockCam.textureWidth; cameraProperties.textureHeight = mockCam.textureHeight; cameraProperties.renderingMode = (int)mockCam.stereoRenderingMode; cameraProperties.eyeSeparation = mockCam.eyeSeparation; cameraProperties.leftAbberation.x = mockCam.leftAbberation.x; cameraProperties.leftAbberation.y = mockCam.leftAbberation.y; cameraProperties.leftAbberation.z = 0; cameraProperties.rightAbberation.x = mockCam.rightAbberation.x; cameraProperties.rightAbberation.y = mockCam.rightAbberation.y; cameraProperties.rightAbberation.z = 0; cameraProperties.enableLeftOcclusion = mockCam.enableLeftOcclusion ? 1 : 0; cameraProperties.enableRightOcclusion = mockCam.enableRightOcclusion ? 1 : 0; NativeApi.UpdateCameraWithId(id, cameraProperties); } } private void RemoveCamera(int id) { if (!NativeApi.RemoveCameraWithId(id)) { Debug.LogWarning($"Could not remove camera with id {id}"); } } } } #endif