/// <summary> /// Computes camera poses on a grid. /// </summary> /// <param name="parentTransform"></param> The parent transform. /// <param name="cameraCount"></param> The number of cameras on each of the axes of the grid. public void ComputeGridPoses(Transform parentTransform, Vector2Int cameraCount) { // Compute several preliminary values. Vector2 numberOfIntervals = cameraCount - Vector2.one; Vector2 intervalSize = new Vector2(parentTransform.lossyScale.x / Mathf.Max(1, numberOfIntervals.x), parentTransform.lossyScale.y / Mathf.Max(1, numberOfIntervals.y)); // Update the camera model of all source cameras. for (int j = 0; j < cameraCount.y; j++) { for (int i = 0; i < cameraCount.x; i++) { int index = j * cameraCount.x + i; CameraModel cameraModel = cameraModels[index]; cameraModel.SetCameraReferenceIndexAndImageName(index, index.ToString("0000") + ".png"); Vector2 planePos = (new Vector2(i, j) - 0.5f * numberOfIntervals) * intervalSize; cameraModel.transform.position = parentTransform.position + planePos.x * parentTransform.right + planePos.y * parentTransform.up; cameraModel.transform.rotation = parentTransform.rotation; } } // Set the initial viewing position so that the viewer will initially look at the center of the grid. Vector2 absScale = new Vector2(Mathf.Abs(parentTransform.lossyScale.x), Mathf.Abs(parentTransform.lossyScale.y)); float largestDim = Mathf.Max(absScale.x, absScale.y); initialViewingPosition = parentTransform.position - 0.5f * largestDim * parentTransform.forward; // Compute inter-camera distance factor (here: minimum inter-camera distance). float interCamDistanceFactor = Mathf.Min(intervalSize.x, intervalSize.y); #if UNITY_EDITOR // Compute the gizmo size. gizmoSize = ComputeGizmoSize(cameraModels, interCamDistanceFactor); UpdateGizmosSize(); #endif //UNITY_EDITOR }
/// <summary> /// Computes camera poses on a sphere. /// </summary> /// <param name="cameraCount"></param> The number of cameras on each of the arcs of the sphere. /// <param name="setupDirection"></param> The direction of the sphere setup, inward or outward. public void ComputeSpherePoses(Vector2Int cameraCount, SetupDirection setupDirection) { // Compute several preliminary values. Transform parentTransform = cameraModels[0].transform.parent; Vector3 lossyScale = parentTransform.lossyScale; Vector2 intervalArcDistance = Vector2.one / cameraCount; Vector2 degreesPerIteration = new Vector2(360f, 180f) * intervalArcDistance; int facingDirection = (setupDirection == SetupDirection.Outwards) ? 1 : -1; // Update the camera model of all source cameras. for (int j = 0; j < cameraCount.y; j++) { for (int i = 0; i < cameraCount.x; i++) { int index = j * cameraCount.x + i; CameraModel cameraModel = cameraModels[index]; cameraModel.SetCameraReferenceIndexAndImageName(index + 1, index.ToString("0000") + ".png"); cameraModel.transform.localRotation = Quaternion.AngleAxis(i * degreesPerIteration.x, -Vector3.up) * Quaternion.AngleAxis(-90f + (j + 0.5f) * degreesPerIteration.y, -Vector3.right); cameraModel.transform.localPosition = facingDirection * (cameraModel.transform.localRotation * Vector3.forward); CheckCamDistanceWithOthersInSetup(index, Mathf.Max(0, index - cameraCount.x), index); } } // Set the initial viewing position so that the viewer will look outwards from within, or inwards from without, based on the specified setup direction. initialViewingPosition = (facingDirection == 1) ? parentTransform.position : parentTransform.position - 1.5f * parentTransform.localScale.magnitude * parentTransform.forward; }
/// <summary> /// Updates the camera setup to have the given number of cameras. /// </summary> /// <param name="cameraCount"></param> The number of cameras for the setup. public void ChangeCameraCount(int cameraCount) { CameraModel cameraParams = CameraModel.CreateCameraModel(); // If there are already cameras in the setup, use one of them as the model. if (cameraModels != null && cameraModels.Length > 0) { cameraParams.ParametersFromCameraModel(cameraModels[0]); } else { cameraParams.SetCameraReferenceIndexAndImageName(cameraParams.cameraReferenceIndex, "Image"); } // Update the camera setup, using this camera as a model. ResetCameraModels(); cameraModels = new CameraModel[cameraCount]; for (int iter = 0; iter < cameraModels.Length; iter++) { CameraModel cameraModel = AddCameraModel(iter); cameraModel.ParametersFromCameraModel(cameraParams); cameraModel.SetCameraReferenceIndexAndImageName(cameraModel.cameraReferenceIndex, cameraModel.imageName + GeneralToolkit.ToString(iter)); } // Destroy the temporary camera model. DestroyImmediate(cameraParams.gameObject); }
/// <summary> /// Computes camera poses on a cylinder. /// </summary> /// <param name="cameraCount"></param> The number of cameras on the circular section and on the vertical side of the cylinder. /// <param name="setupDirection"></param> The direction of the cylinder setup, inward or outward. public void ComputeCylinderPoses(Vector2Int cameraCount, SetupDirection setupDirection) { // Compute several preliminary values. Transform parentTransform = cameraModels[0].transform.parent; float horizontalDegreesPerIteration = 360f / cameraCount.x; int facingDirection = (setupDirection == SetupDirection.Outwards) ? 1 : -1; int numberOfVerticalIntervals = cameraCount.y - 1; float verticalIntervalSize = 1f / Mathf.Max(1, numberOfVerticalIntervals); float distanceToClosestVerticalCam = Mathf.Abs(parentTransform.lossyScale.y) * verticalIntervalSize; // Update the camera model of all source cameras. for (int j = 0; j < cameraCount.y; j++) { for (int i = 0; i < cameraCount.x; i++) { int index = j * cameraCount.x + i; CameraModel cameraModel = cameraModels[index]; cameraModel.SetCameraReferenceIndexAndImageName(index + 1, index.ToString("0000") + ".png"); cameraModel.transform.localRotation = Quaternion.AngleAxis(i * horizontalDegreesPerIteration, -Vector3.up); cameraModel.transform.localPosition = facingDirection * (cameraModel.transform.localRotation * Vector3.forward) + ((j - 0.5f * numberOfVerticalIntervals) * verticalIntervalSize) * Vector3.up; cameraModel.UpdateDistanceToClosestCam(distanceToClosestVerticalCam); CheckCamDistanceWithOthersInSetup(index, Mathf.Max(0, index - 1), index); } } // Set the initial viewing position so that the viewer will look outwards from within, or inwards from without, based on the specified setup direction. initialViewingPosition = (facingDirection == 1) ? parentTransform.position : parentTransform.position - 1.5f * parentTransform.localScale.magnitude * parentTransform.forward; }
/// <summary> /// Computes camera poses on a grid. /// </summary> /// <param name="cameraCount"></param> The number of cameras on each of the axes of the grid. public void ComputeGridPoses(Vector2Int cameraCount) { // Compute several preliminary values. Transform parentTransform = cameraModels[0].transform.parent; Vector3 lossyScale = parentTransform.lossyScale; Vector2 absScale = new Vector2(Mathf.Abs(lossyScale.x), Mathf.Abs(lossyScale.y)); Vector2 numberOfIntervals = cameraCount - Vector2.one; Vector2 intervalSize = new Vector2(1f / Mathf.Max(1, numberOfIntervals.x), 1f / Mathf.Max(1, numberOfIntervals.y)); // Compute the minimum inter-camera distance. float distanceToClosestCam = Mathf.Min(absScale.x * intervalSize.x, absScale.y * intervalSize.y); // Update the camera model of all source cameras. for (int j = 0; j < cameraCount.y; j++) { for (int i = 0; i < cameraCount.x; i++) { int index = j * cameraCount.x + i; CameraModel cameraModel = cameraModels[index]; cameraModel.SetCameraReferenceIndexAndImageName(index + 1, index.ToString("0000") + ".png"); Vector2 planePos = (new Vector2(i, j) - 0.5f * numberOfIntervals) * intervalSize; cameraModel.transform.localPosition = planePos.x * Vector3.right + planePos.y * Vector3.up; cameraModel.transform.localRotation = Quaternion.identity; cameraModel.UpdateDistanceToClosestCam(distanceToClosestCam); } } // Set the initial viewing position so that the viewer will initially look at the center of the grid. float largestDim = Mathf.Max(absScale.x, absScale.y); initialViewingPosition = parentTransform.position - 0.5f * largestDim * parentTransform.forward; }
/// <summary> /// Saves additional information related to acquisition, with the given values. /// </summary> /// <param name="cameraSetup"></param> The camera setup containing the acquisition information. public void SaveCOLIBRIVRAdditionalInformation(CameraSetup cameraSetup) { // Determine the camera model, or initialize a new one if there is none. CameraModel cameraParams; if (cameraSetup.cameraModels != null) { cameraParams = cameraSetup.cameraModels[0]; } else { cameraParams = CameraModel.CreateCameraModel(); } // Get the initial viewing position. Vector3 initialViewingPos = cameraSetup.initialViewingPosition; // Store this information in the additional information file. GeneralToolkit.CreateOrClear(PathType.File, additionalInfoFile); StringBuilder stringBuilder = new StringBuilder(); stringBuilder.AppendLine("# COLIBRI VR additional information:"); stringBuilder.AppendLine("# INITIAL_VIEWING_POSITION"); string line = initialViewingPos.x + " " + initialViewingPos.y + " " + initialViewingPos.z; stringBuilder.AppendLine(line); File.WriteAllText(additionalInfoFile, stringBuilder.ToString()); // Delete any temporary camera model. if (cameraSetup.cameraModels == null) { DestroyImmediate(cameraParams.gameObject); } }
/// <summary> /// Adds a camera model to the setup. /// </summary> /// <param name="setupIndex"></param> Index of the newly-created camera in the setup. /// <returns></returns> The created camera model. public CameraModel AddCameraModel(int setupIndex) { CameraModel cameraModel = CameraModel.CreateCameraModel(transform); cameraModels[setupIndex] = cameraModel; #if UNITY_EDITOR UpdateGizmoColor(setupIndex); #endif //UNITY_EDITOR return(cameraModel); }
/// <summary> /// Updates the preview camera with a given set of camera models. /// </summary> /// <param name="sourceCameraModel"></param> The camera model with which to update the preview camera. public void UpdateCameraModel(CameraModel sourceCameraModel) { if (previewCamera != null) { // Store the camera model for further use. _cameraModel = sourceCameraModel; // Set up the camera's parameters and target texture from the given camera model. previewCamera.targetTexture = null; _cameraModel.TransferParametersToCamera(ref previewCamera); GeneralToolkit.CreateRenderTexture(ref targetTexture, _cameraModel.pixelResolution, targetTexture.depth, targetTexture.format, targetTexture.sRGB == false, targetTexture.filterMode, targetTexture.wrapMode); previewCamera.targetTexture = targetTexture; } }
/// <summary> /// Computes camera poses on a sphere. /// </summary> /// <param name="parentTransform"></param> The parent transform. /// <param name="cameraCount"></param> The number of cameras on each of the arcs of the sphere. /// <param name="setupDirection"></param> The direction of the sphere setup, inward or outward. public void ComputeSpherePoses(Transform parentTransform, Vector2Int cameraCount, SetupDirection setupDirection) { // Compute several preliminary values. Vector2 intervalArcDistance = Vector2.one / cameraCount; Vector2 degreesPerIteration = new Vector2(360f, 180f) * intervalArcDistance; int facingDirection = (setupDirection == SetupDirection.Outwards) ? 1 : -1; Vector3 absScale = new Vector3(Mathf.Abs(parentTransform.lossyScale.x), Mathf.Abs(parentTransform.lossyScale.y), Mathf.Abs(parentTransform.lossyScale.z)); // Update the camera model of all source cameras. for (int j = 0; j < cameraCount.y; j++) { for (int i = 0; i < cameraCount.x; i++) { int index = j * cameraCount.x + i; CameraModel cameraModel = cameraModels[index]; cameraModel.SetCameraReferenceIndexAndImageName(index, index.ToString("0000") + ".png"); cameraModel.transform.rotation = Quaternion.AngleAxis(i * degreesPerIteration.x, -parentTransform.up) * Quaternion.AngleAxis(-90f + (j + 0.5f) * degreesPerIteration.y, -parentTransform.right) * parentTransform.rotation; cameraModel.transform.position = parentTransform.position + Vector3.Scale(cameraModels[index].transform.rotation * Vector3.forward, facingDirection * absScale); } } // Set the initial viewing position so that the viewer will look outwards from within, or inwards from without, based on the specified setup direction. initialViewingPosition = (facingDirection == 1) ? parentTransform.position : parentTransform.position - 1.5f * parentTransform.localScale.magnitude * parentTransform.forward; // Compute the inter-camera distance factor (here: minimum inter-camera distance). float interCamDistanceFactor = 1f; int totalCameraCount = cameraCount.x * cameraCount.y; if (totalCameraCount > 1) { float xDistance = (cameraModels[1].transform.position - cameraModels[0].transform.position).magnitude; if (xDistance > 0) { interCamDistanceFactor = xDistance; } int halfHeightIndexOne = (Mathf.FloorToInt(cameraCount.y / 2) - 1) * cameraCount.x; int halfHeightIndexTwo = (halfHeightIndexOne + cameraCount.x); float yDistance = (cameraModels[halfHeightIndexOne % totalCameraCount].transform.position - cameraModels[halfHeightIndexTwo % totalCameraCount].transform.position).magnitude; if (yDistance > 0) { interCamDistanceFactor = Mathf.Min(interCamDistanceFactor, yDistance); } } #if UNITY_EDITOR // Compute the gizmo size. gizmoSize = ComputeGizmoSize(cameraModels, interCamDistanceFactor); UpdateGizmosSize(); #endif //UNITY_EDITOR }
/// <summary> /// Updates the given camera model's gizmo color. /// </summary> /// <param name="setupIndex"></param> private void UpdateGizmoColor(int setupIndex) { CameraModel cameraModel = cameraModels[setupIndex]; cameraModel.gizmoColor = CameraModel.baseColor; if (_isColorSourceCamIndices) { cameraModel.gizmoColor = GeneralToolkit.GetColorForIndex(setupIndex, cameraModels.Length); } else { Transform activeTransform = Selection.activeTransform; if (activeTransform != null && transform.IsChildOf(activeTransform) && setupIndex == previewIndex) { cameraModel.gizmoColor = CameraModel.selectedColor; } } }
/// <summary> /// Updates the "distance to closest camera" attribute for each camera model in the setup, by comparison with the camera at the given index. /// </summary> /// <param name="indexToCompare"></param> The index of the source camera with which to check relative distance. /// <param name="startSourceCamIndex"></param> The source camera index at which to start checking. /// <param name="endSourceCamIndex"></param> The source camera index before which to end checking. public void CheckCamDistanceWithOthersInSetup(int indexOfCameraToCompare, int startSourceCamIndex, int endSourceCamIndex) { CameraModel cameraModel = cameraModels[indexOfCameraToCompare]; for (int sourceCamIndex = startSourceCamIndex; sourceCamIndex < endSourceCamIndex; sourceCamIndex++) { if (sourceCamIndex != indexOfCameraToCompare) { CameraModel otherCameraModel = cameraModels[sourceCamIndex]; float distanceBetweenCams = (cameraModel.transform.position - otherCameraModel.transform.position).magnitude; if (cameraModel.distanceToClosestCam == 0f || distanceBetweenCams < cameraModel.distanceToClosestCam) { cameraModel.UpdateDistanceToClosestCam(distanceBetweenCams); } if (otherCameraModel.distanceToClosestCam == 0f || distanceBetweenCams < otherCameraModel.distanceToClosestCam) { otherCameraModel.UpdateDistanceToClosestCam(distanceBetweenCams); } } } }
/// <summary> /// Saves additional information related to acquisition, with the given values. /// </summary> /// <param name="cameraSetup"></param> The camera setup containing the acquisition information. public void SaveAdditionalSetupInformation(CameraSetup cameraSetup) { // Determine the camera model, or initialize a new one if there is none. CameraModel cameraParams; if (cameraSetup.cameraModels != null && cameraSetup.cameraModels.Length > 0) { cameraParams = cameraSetup.cameraModels[0]; } else { cameraParams = CameraModel.CreateCameraModel(); } // Store the initial viewing position. Vector3 initialViewingPos = cameraSetup.initialViewingPosition; GeneralToolkit.CreateOrClear(PathType.File, additionalInfoFile); StringBuilder stringBuilder = new StringBuilder(); stringBuilder.AppendLine("# Additional setup information:"); stringBuilder.AppendLine("# INITIAL_VIEWING_POSITION"); string line = GeneralToolkit.ToString(initialViewingPos.x) + " " + GeneralToolkit.ToString(initialViewingPos.y) + " " + GeneralToolkit.ToString(initialViewingPos.z); stringBuilder.AppendLine(line); // Store the minimum/maximum distance range stringBuilder.AppendLine("# Distance ranges with one line of data per camera:"); stringBuilder.AppendLine("# CAMERA_ID DISTANCE_RANGE_MIN DISTANCE_RANGE_MAX"); foreach (CameraModel camera in cameraSetup.cameraModels) { Vector2 distanceRange = camera.distanceRange; stringBuilder.AppendLine(GeneralToolkit.ToString(camera.cameraReferenceIndex) + " " + GeneralToolkit.ToString(distanceRange.x) + " " + GeneralToolkit.ToString(distanceRange.y)); } // Save the file. File.WriteAllText(additionalInfoFile, stringBuilder.ToString()); // Delete any temporary camera model. if (cameraSetup.cameraModels == null) { DestroyImmediate(cameraParams.gameObject); } }
/// <summary> /// Copies another camera model's parameters. /// </summary> /// <param name="cameraModel"></param> The camera model to copy parameters from. public void ParametersFromCameraModel(CameraModel cameraModel) { isOmnidirectional = cameraModel.isOmnidirectional; focalDistance = cameraModel.focalDistance; if (isOmnidirectional) { _omnidirectionalPixelResolution = cameraModel.pixelResolution; cameraModel.isOmnidirectional = false; _perspectivePixelResolution = cameraModel.pixelResolution; _perspectiveFOV = cameraModel.fieldOfView; } else { _perspectivePixelResolution = cameraModel.pixelResolution; _perspectiveFOV = cameraModel.fieldOfView; cameraModel.isOmnidirectional = true; _omnidirectionalPixelResolution = cameraModel.pixelResolution; } cameraModel.isOmnidirectional = isOmnidirectional; distanceRange = cameraModel.distanceRange; transform.position = cameraModel.transform.position; transform.rotation = cameraModel.transform.rotation; SetCameraReferenceIndexAndImageName(cameraModel.cameraReferenceIndex, cameraModel.imageName); }
/// <summary> /// Creates the preview camera given a camera model. /// </summary> /// <param name="callerGO"></param> The caller gameobject. /// <param name="previewCameraTransform"></param> The transform on which to create a preview camera. /// <param name="sourceCameraModel"></param> The camera model with which to update the preview camera. public void CreatePreviewCamera(GameObject callerGO, Transform previewCameraTransform, CameraModel sourceCameraModel) { // The preview camera is only created if it is not already so. if (previewCamera == null) { // Set the camera's hide flags. previewCameraTransform.gameObject.hideFlags = HideFlags.HideAndDontSave; // Indicate that the camera object's parent is the caller object. previewCameraTransform.parent = callerGO.transform; // Set up the camera component on the given transform object. previewCamera = GeneralToolkit.GetOrAddComponent <Camera>(previewCameraTransform.gameObject); previewCamera.hideFlags = previewCameraTransform.gameObject.hideFlags; // Indicate that this camera is monoscopic, even in VR mode. previewCamera.stereoTargetEye = StereoTargetEyeMask.None; // Disable the camera component so that it only renders when necessary. previewCamera.enabled = false; // Update the camera object from the parameters of the camera model. UpdateCameraModel(sourceCameraModel, false); } }
/// <summary> /// On selection, sets up the target properties. /// </summary> void OnEnable() { // Get the target object. _targetObject = (CameraModel)serializedObject.targetObject; }