using System.Collections.Generic; using System.Linq; using UnityEngine; using UnityEngine.XR; namespace VRC.SDK3.ClientSim { public class ClientSimStackedVRCameraSystem : MonoBehaviour { [SerializeField] private ClientSimStackedCamera[] cameraStack; private bool _isCameraStackingEnabled; private Camera _mainSceneCamera; private bool _isInitialized; private bool _isReady; private List _cameras; private ClientSimMenu _clientSimMenu; private IClientSimEventDispatcher _eventDispatcher; public IReadOnlyList Cameras => _cameras; // all layers handled by this system - note that this does not change whether the system is active or not, // meaning it is valid to call this at any time, which `OnCameraSettingsChanged` may need to do public LayerMask RequestedStackLayers => cameraStack.Aggregate(0, (mask, cam) => mask | cam.RenderLayer); public void Initialize(Camera playerCamera, ClientSimMenu menu, IClientSimEventDispatcher eventDispatcher) { _mainSceneCamera = playerCamera; _clientSimMenu = menu; _eventDispatcher = eventDispatcher; } public void Ready() { _isReady = true; } void Update() { if(!_isReady) return; if (!_isInitialized) { InitializeStackedSystem(); } } void OnDisable() { if (_mainSceneCamera != null) { if (_isCameraStackingEnabled) DestroyCameraStack(); } } private void InitializeStackedSystem() { _cameras = new List(); if (_mainSceneCamera != null) { CreateCameraStack(); _isInitialized = true; _eventDispatcher.SendEvent(new ClientSimStackedCameraReadyEvent()); } } private void CreateCameraStack() { for (int i = 0; i < cameraStack.Length; i++) { AddCamera(i); } _isCameraStackingEnabled = true; } private void DestroyCameraStack() { for (int i = 0; i < _cameras.Count; i++) { DestroyCamera(i); } _isCameraStackingEnabled = false; } private void AddCamera(int index) { var cameraObj = new GameObject(); cameraObj.transform.parent = _mainSceneCamera.transform; Camera cam = cameraObj.AddComponent(); XRDevice.DisableAutoXRCameraTracking(cam, true); cam.CopyFrom(_mainSceneCamera); // Start by copying all the settings from the main camera #if VRC_VR_STEAM // We only want this on SteamVR. cameraObj.AddComponent(); #endif _cameras.Add(cam); cameraObj.tag = "Untagged"; cameraObj.name = $"StackedCamera : {cameraStack[index].CameraName}"; cam.clearFlags = CameraClearFlags.Depth; cam.depth = 100 - index; cam.cullingMask = cameraStack[index].RenderLayer; cam.useOcclusionCulling = cameraStack[index].UseOcclusionCulling; //Remove this cameras layers from the base camera _mainSceneCamera.cullingMask = _mainSceneCamera.cullingMask ^ cameraStack[index].RenderLayer; // Set the ClientSim UI canvas to use this camera _clientSimMenu.SetCanvasCamera(cam); } private void DestroyCamera(int index) { Camera cam = _cameras[index]; _cameras.RemoveAt(index); //Restore Layers from this camera to the main camera _mainSceneCamera.cullingMask = _mainSceneCamera.cullingMask | cameraStack[index].RenderLayer; Destroy(cam.gameObject); } } }