/// <summary>
        /// Gets or creates the package settings, stored as an asset in the project folder.
        /// </summary>
        /// <returns></returns> The package settings.
        private static COLIBRIVRSettings GetOrCreateSettings()
        {
            if (!Directory.Exists(settingsFolderAbsolutePath))
            {
                GeneralToolkit.CreateOrClear(PathType.Directory, settingsFolderAbsolutePath);
            }
            if (!Directory.Exists(settingsResourcesAbsolutePath))
            {
                GeneralToolkit.CreateOrClear(PathType.Directory, settingsResourcesAbsolutePath);
            }
            string            settingsAssetPath = Path.Combine(GeneralToolkit.ToRelativePath(COLIBRIVRSettings.settingsFolderAbsolutePath), "COLIBRIVRSettings.asset");
            COLIBRIVRSettings settings          = AssetDatabase.LoadAssetAtPath <COLIBRIVRSettings>(settingsAssetPath);

            if (settings == null)
            {
                settings = ScriptableObject.CreateInstance <COLIBRIVRSettings>();
                settings.COLMAPSettings        = (COLMAPSettings)ScriptableObject.CreateInstance <COLMAPSettings>().Initialize();
                settings.BlenderSettings       = (BlenderSettings)ScriptableObject.CreateInstance <BlenderSettings>().Initialize();
                settings.InstantMeshesSettings = (InstantMeshesSettings)ScriptableObject.CreateInstance <InstantMeshesSettings>().Initialize();
                settings.previewMaxResolution  = 256;
                AssetDatabase.CreateAsset(settings, settingsAssetPath);
                AssetDatabase.AddObjectToAsset(settings.COLMAPSettings, settingsAssetPath);
                AssetDatabase.AddObjectToAsset(settings.BlenderSettings, settingsAssetPath);
                AssetDatabase.AddObjectToAsset(settings.InstantMeshesSettings, settingsAssetPath);
                AssetDatabase.SaveAssets();
            }
            return(settings);
        }
        /// <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>
 /// Creates a directory structure that can directly be read by COLMAP. Erases the existing workspace if it exists.
 /// </summary>
 /// <param name="workspace"></param> The path to the COLMAP workspace.
 public static void CreateDirectoryStructureForAcquisition(string workspace)
 {
     GeneralToolkit.CreateOrClear(PathType.Directory, workspace);
     GeneralToolkit.CreateOrClear(PathType.Directory, GetImagesDir(workspace));
     GeneralToolkit.CreateOrClear(PathType.Directory, GetSparseDir(workspace));
     GeneralToolkit.CreateOrClear(PathType.File, GetCamerasFile(workspace));
     GeneralToolkit.CreateOrClear(PathType.File, GetImagesFile(workspace));
     GeneralToolkit.CreateOrClear(PathType.File, GetPointsFile(workspace));
     GeneralToolkit.CreateOrClear(PathType.Directory, GetStereoDir(workspace));
     GeneralToolkit.CreateOrClear(PathType.Directory, GetDepthMapsDir(workspace));
 }
        /// <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>
        /// Coroutine that runs the sparse reconstruction process.
        /// </summary>
        /// <param name="caller"></param> The processing object calling this method.
        /// <param name="workspace"></param> The workspace from which to perform this method.
        /// <param name="COLMAPCameraIndex"></param> The index of the type of source camera (in the list of COLMAP cameras).
        /// <param name="isSingleCamera"></param> True if the source images were acquired by the same camera, false otherwise.
        /// <param name="maxImageSize"></param> The maximum image size for the undistortion step.
        /// <returns></returns>
        public static IEnumerator RunSparseReconstructionCoroutine(Processing.Processing caller, string workspace, int COLMAPCameraIndex, bool isSingleCamera, int maxImageSize)
        {
            // Indicate to the user that the process has started.
            GeneralToolkit.ResetCancelableProgressBar(true, true);
            // Create or clear the folders needed for the reconstruction.
            GeneralToolkit.Delete(GetDatabaseFile(workspace));
            GeneralToolkit.CreateOrClear(PathType.Directory, GetSparseDir(workspace));
            GeneralToolkit.CreateOrClear(PathType.Directory, GetSparse0Dir(workspace));
            GeneralToolkit.CreateOrClear(PathType.Directory, GetDenseDir(workspace));
            GeneralToolkit.CreateOrClear(PathType.Directory, GetDense0Dir(workspace));
            // Initialize the command parameters.
            bool displayProgressBar = true;
            bool stopOnError        = true;

            string[] progressBarParams = new string[3];
            int      maxStep           = 6;

            progressBarParams[0] = GeneralToolkit.ToString(maxStep);
            progressBarParams[2] = "Processing canceled by user.";
            // Launch the different steps of the sparse reconstruction process.
            float focalLengthFactor = 0;

            for (int step = 1; step <= maxStep; step++)
            {
                // Step one: launch feature extraction.
                if (step == 1)
                {
                    progressBarParams[1] = GetProgressBarParamsOne("Feature extraction", true, step, maxStep);
                    CameraModel[] cameraModels = caller.cameraSetup.cameraModels;
                    if (cameraModels != null && cameraModels.Length > 0)
                    {
                        CameraModel cameraParams = cameraModels[0];
                        float       focalLength  = Camera.FieldOfViewToFocalLength(cameraParams.fieldOfView.x, cameraParams.pixelResolution.x);
                        focalLengthFactor = focalLength / Mathf.Max(cameraParams.pixelResolution.x, cameraParams.pixelResolution.y);
                    }
                    yield return(caller.StartCoroutine(RunFeatureExtractionCommand(caller, workspace, displayProgressBar, stopOnError, progressBarParams, COLMAPCameraIndex, isSingleCamera, focalLengthFactor)));
                }
                // Step two: launch feature matching.
                else if (step == 2)
                {
                    progressBarParams[1] = GetProgressBarParamsOne("Feature matching", true, step, maxStep);
                    yield return(caller.StartCoroutine(RunFeatureMatchingCommand(caller, workspace, displayProgressBar, stopOnError, progressBarParams)));
                }
                // Step three: launch mapping.
                else if (step == 3)
                {
                    progressBarParams[1] = GetProgressBarParamsOne("Mapping", true, step, maxStep);
                    yield return(caller.StartCoroutine(RunMappingCommand(caller, workspace, displayProgressBar, stopOnError, progressBarParams, (focalLengthFactor > 0))));
                }
                // Step four: launch exporting original camera setup as text.
                else if (step == 4)
                {
                    progressBarParams[1] = GetProgressBarParamsOne("Exporting camera setup (original) as text", true, step, maxStep);
                    yield return(caller.StartCoroutine(RunExportModelAsTextCommand(caller, workspace, displayProgressBar, stopOnError, progressBarParams)));
                }
                // Step five: launch image undistortion.
                else if (step == 5)
                {
                    // Launch undistortion.
                    progressBarParams[1] = GetProgressBarParamsOne("Undistortion", true, step, maxStep);
                    yield return(caller.StartCoroutine(RunUndistortionCommand(caller, workspace, displayProgressBar, stopOnError, progressBarParams, maxImageSize)));
                    // Change the workspace and the data directory to the one created in the dense folder.
                    // workspace = GetDense0Dir(workspace);
                    // caller.dataHandler.ChangeDataDirectory(caller, workspace);
                    // Debug.Log(GeneralToolkit.FormatScriptMessage(typeof(COLMAPConnector), "Changed data directory to: " + workspace));
                }
                // Step six: launch exporting undistorted camera setup as text.
                else if (step == 6)
                {
                    // Launch export process.
                    progressBarParams[1] = GetProgressBarParamsOne("Exporting camera setup (undistorted) as text", true, step, maxStep);
                    yield return(caller.StartCoroutine(RunExportModelAsTextCommand(caller, GetDense0Dir(workspace), displayProgressBar, stopOnError, progressBarParams)));

                    // Display the parsed camera setup in the Scene view.
                    caller.Deselected();
                    caller.Selected();
                    yield return(null);
                }
                // For each step, continue only if the user does not cancel the process.
                if (GeneralToolkit.progressBarCanceled)
                {
                    break;
                }
            }
            // Change the data directory to the one created in the dense folder.
            if (!GeneralToolkit.progressBarCanceled)
            {
                Debug.Log(GeneralToolkit.FormatScriptMessage(typeof(COLMAPConnector), "Sparse reconstruction was a success."));
            }
            // Indicate to the user that the process has ended.
            GeneralToolkit.ResetCancelableProgressBar(false, false);
        }