Esempio n. 1
0
    //
    // User-callable AR methods.
    //
    public bool StartAR()
    {
        // Catch attempts to inadvertently call StartAR() twice.
        if (_running) {
            Log(LogTag + "WARNING: StartAR() called while already running. Ignoring.\n");
            return false;
        }

        Log(LogTag + "Starting AR.");

        _sceneConfiguredForVideo = _sceneConfiguredForVideoWaitingMessageLogged = false;

        // Check rendering device.
        string renderDevice = SystemInfo.graphicsDeviceVersion;
        _useNativeGLTexturing = !renderDevice.StartsWith("Direct") && UseNativeGLTexturingIfAvailable;
        if (_useNativeGLTexturing) {
            Log(LogTag + "Render device: " + renderDevice + ", using native GL texturing.");
        } else {
            Log(LogTag + "Render device: " + renderDevice + ", using Unity texturing.");
        }

        CreateClearCamera();

        // Retrieve video configuration, and append any required per-platform overrides.
        // For native GL texturing we need monoplanar video; iOS and Android default to biplanar format.
        string videoConfiguration0;
        string videoConfiguration1;
        switch (Application.platform) {
            case RuntimePlatform.OSXEditor:
            case RuntimePlatform.OSXPlayer:
                videoConfiguration0 = videoConfigurationMacOSX0;
                videoConfiguration1 = videoConfigurationMacOSX1;
                if (_useNativeGLTexturing || !AllowNonRGBVideo) {
                    if (videoConfiguration0.IndexOf("-device=QuickTime7") != -1 || videoConfiguration0.IndexOf("-device=QUICKTIME") != -1) videoConfiguration0 += " -pixelformat=BGRA";
                    if (videoConfiguration1.IndexOf("-device=QuickTime7") != -1 || videoConfiguration1.IndexOf("-device=QUICKTIME") != -1) videoConfiguration1 += " -pixelformat=BGRA";
                }
                break;
            case RuntimePlatform.WindowsEditor:
            case RuntimePlatform.WindowsPlayer:
                videoConfiguration0 = videoConfigurationWindows0;
                videoConfiguration1 = videoConfigurationWindows1;
                if (_useNativeGLTexturing || !AllowNonRGBVideo) {
                    if (videoConfiguration0.IndexOf("-device=WinMF") != -1) videoConfiguration0 += " -format=BGRA";
                    if (videoConfiguration1.IndexOf("-device=WinMF") != -1) videoConfiguration1 += " -format=BGRA";
                }
                break;
            case RuntimePlatform.Android:
                videoConfiguration0 = videoConfigurationAndroid0 + " -cachedir=\"" + Application.temporaryCachePath + "\""  + (_useNativeGLTexturing || !AllowNonRGBVideo ? " -format=RGBA" : "");
                videoConfiguration1 = videoConfigurationAndroid1 + " -cachedir=\"" + Application.temporaryCachePath + "\""  + (_useNativeGLTexturing || !AllowNonRGBVideo ? " -format=RGBA" : "");
                break;
            case RuntimePlatform.IPhonePlayer:
                videoConfiguration0 = videoConfigurationiOS0 + (_useNativeGLTexturing || !AllowNonRGBVideo ? " -format=BGRA" : "");
                videoConfiguration1 = videoConfigurationiOS1 + (_useNativeGLTexturing || !AllowNonRGBVideo ? " -format=BGRA" : "");
                break;
            case RuntimePlatform.MetroPlayerX86:
            case RuntimePlatform.MetroPlayerX64:
            case RuntimePlatform.MetroPlayerARM:
                videoConfiguration0 = videoConfigurationWindowsStore0;
                videoConfiguration1 = videoConfigurationWindowsStore1;
                break;
            default:
                videoConfiguration0 = "";
                videoConfiguration1 = "";
                break;
        }

        // Load the default camera parameters.
        TextAsset ta;
        byte[] cparam0 = null;
        byte[] cparam1 = null;
        byte[] transL2R = null;
        ta = Resources.Load("ardata/" + videoCParamName0, typeof(TextAsset)) as TextAsset;
        if (ta == null) {
            // Error - the camera_para.dat file isn't in the right place
            Log(LogTag + "StartAR(): Error: Camera parameters file not found at Resources/ardata/" + videoCParamName0 + ".bytes");
            return (false);
        }
        cparam0 = ta.bytes;
        if (VideoIsStereo) {
            ta = Resources.Load("ardata/" + videoCParamName1, typeof(TextAsset)) as TextAsset;
            if (ta == null) {
                // Error - the camera_para.dat file isn't in the right place
                Log(LogTag + "StartAR(): Error: Camera parameters file not found at Resources/ardata/" + videoCParamName1 + ".bytes");
                return (false);
            }
            cparam1 = ta.bytes;
            ta = Resources.Load("ardata/" + transL2RName, typeof(TextAsset)) as TextAsset;
            if (ta == null) {
                // Error - the transL2R.dat file isn't in the right place
                Log(LogTag + "StartAR(): Error: The stereo calibration file not found at Resources/ardata/" + transL2RName + ".bytes");
                return (false);
            }
            transL2R = ta.bytes;
        }

        // Begin video capture and marker detection.
        if (!VideoIsStereo) {
            Log(LogTag + "Starting ARToolKit video with vconf '" + videoConfiguration0 + "'.");
            //_running = PluginFunctions.arwStartRunning(videoConfiguration, cparaName, nearPlane, farPlane);
            _running = PluginFunctions.arwStartRunningB(videoConfiguration0, cparam0, cparam0.Length, NearPlane, FarPlane);
        } else {
            Log(LogTag + "Starting ARToolKit video with vconfL '" + videoConfiguration0 + "', vconfR '" + videoConfiguration1 + "'.");
            //_running = PluginFunctions.arwStartRunningStereo(vconfL, cparaNameL, vconfR, cparaNameR, transL2RName, nearPlane, farPlane);
            _running = PluginFunctions.arwStartRunningStereoB(videoConfiguration0, cparam0, cparam0.Length, videoConfiguration1, cparam1, cparam1.Length, transL2R, transL2R.Length, NearPlane, FarPlane);

        }

        if (!_running) {
            Log(LogTag + "Error starting running");
            ARW_ERROR error = (ARW_ERROR)PluginFunctions.arwGetError();
            if (error == ARW_ERROR.ARW_ERROR_DEVICE_UNAVAILABLE) {
                showGUIErrorDialogContent = "Unable to start AR tracking. The camera may be in use by another application.";
            } else {
                showGUIErrorDialogContent = "Unable to start AR tracking. Please check that you have a camera connected.";
            }
            showGUIErrorDialog = true;
            return false;
        }

        // After calling arwStartRunningB/arwStartRunningStereoB, set ARToolKit configuration.
        Log(LogTag + "Setting ARToolKit tracking settings.");
        VideoThreshold = currentThreshold;
        VideoThresholdMode = currentThresholdMode;
        LabelingMode = currentLabelingMode;
        BorderSize = currentBorderSize;
        PatternDetectionMode = currentPatternDetectionMode;
        MatrixCodeType = currentMatrixCodeType;
        ImageProcMode = currentImageProcMode;
        NFTMultiMode = currentNFTMultiMode;

        // Remaining Unity setup happens in UpdateAR().
        return true;
    }
    public override void OnInspectorGUI()
    {
        ARController arcontroller = (ARController)target;

        if (arcontroller == null)
        {
            return;
        }


        EditorGUILayout.LabelField("Version", "ARToolKit " + arcontroller.Version);


        EditorGUILayout.Separator();

        showVideoOptions = EditorGUILayout.Foldout(showVideoOptions, "Video Options");
        if (showVideoOptions)
        {
            arcontroller.videoCParamName0           = EditorGUILayout.TextField("Camera parameters", arcontroller.videoCParamName0);
            arcontroller.videoConfigurationWindows0 = EditorGUILayout.TextField("Video config. (Windows)", arcontroller.videoConfigurationWindows0);
            arcontroller.videoConfigurationMacOSX0  = EditorGUILayout.TextField("Video config. (Mac OS X)", arcontroller.videoConfigurationMacOSX0);
            arcontroller.videoConfigurationiOS0     = EditorGUILayout.TextField("Video config. (iOS)", arcontroller.videoConfigurationiOS0);
            arcontroller.videoConfigurationAndroid0 = EditorGUILayout.TextField("Video config. (Android)", arcontroller.videoConfigurationAndroid0);
            arcontroller.BackgroundLayer0           = EditorGUILayout.LayerField("Layer", arcontroller.BackgroundLayer0);

            arcontroller.VideoIsStereo = EditorGUILayout.Toggle("Video source is stereo", arcontroller.VideoIsStereo);
            if (arcontroller.VideoIsStereo)
            {
                arcontroller.videoCParamName1           = EditorGUILayout.TextField("Camera parameters (R)", arcontroller.videoCParamName1);
                arcontroller.videoConfigurationWindows1 = EditorGUILayout.TextField("Video config.(R) (Windows)", arcontroller.videoConfigurationWindows1);
                arcontroller.videoConfigurationMacOSX1  = EditorGUILayout.TextField("Video config.(R) (Mac OS X)", arcontroller.videoConfigurationMacOSX1);
                arcontroller.videoConfigurationiOS1     = EditorGUILayout.TextField("Video config.(R) (iOS)", arcontroller.videoConfigurationiOS1);
                arcontroller.videoConfigurationAndroid1 = EditorGUILayout.TextField("Video config.(R) (Android)", arcontroller.videoConfigurationAndroid1);
                arcontroller.BackgroundLayer1           = EditorGUILayout.LayerField("Layer (R)", arcontroller.BackgroundLayer1);
                arcontroller.transL2RName = EditorGUILayout.TextField("Stereo parameters", arcontroller.transL2RName);
            }

            arcontroller.UseNativeGLTexturingIfAvailable = EditorGUILayout.Toggle("Use native GL texturing (if available)", arcontroller.UseNativeGLTexturingIfAvailable);
            if (arcontroller.UseNativeGLTexturingIfAvailable)
            {
                EditorGUI.BeginDisabledGroup(true);
                EditorGUILayout.Toggle("Allow non-RGB video internally.", false);
                EditorGUI.EndDisabledGroup();
            }
            else
            {
                arcontroller.AllowNonRGBVideo = EditorGUILayout.Toggle("Allow non-RGB video internally.", arcontroller.AllowNonRGBVideo);
            }


            ContentMode currentContentMode = arcontroller.ContentMode;
            ContentMode newContentMode     = (ContentMode)EditorGUILayout.EnumPopup("Content mode", currentContentMode);
            if (newContentMode != currentContentMode)
            {
                arcontroller.ContentMode = newContentMode;
            }
            arcontroller.ContentRotate90 = EditorGUILayout.Toggle("Rotate 90 deg.", arcontroller.ContentRotate90);
            arcontroller.ContentFlipV    = EditorGUILayout.Toggle("Flip vertically", arcontroller.ContentFlipV);
            arcontroller.ContentFlipH    = EditorGUILayout.Toggle("Flip horizontally.", arcontroller.ContentFlipH);
        }

        EditorGUILayout.Separator();

        arcontroller.NearPlane = EditorGUILayout.FloatField("Near plane", arcontroller.NearPlane);
        arcontroller.FarPlane  = EditorGUILayout.FloatField("Far plane", arcontroller.FarPlane);

        EditorGUILayout.Separator();

        showThresholdOptions = EditorGUILayout.Foldout(showThresholdOptions, "Threshold Options");
        if (showThresholdOptions)
        {
            // Threshold mode selection
            ARController.ARToolKitThresholdMode currentThreshMode = arcontroller.VideoThresholdMode;
            ARController.ARToolKitThresholdMode newThreshMode     = (ARController.ARToolKitThresholdMode)EditorGUILayout.EnumPopup("Mode:", currentThreshMode);
            if (newThreshMode != currentThreshMode)
            {
                arcontroller.VideoThresholdMode = newThreshMode;
            }

            // Info about the selected mode
            EditorGUILayout.LabelField("", ARController.ThresholdModeDescriptions[newThreshMode]);

            // Show threshold slider only in manual mode
            if (newThreshMode == ARController.ARToolKitThresholdMode.Manual)
            {
                int currentThreshold = arcontroller.VideoThreshold;
                //int newThreshold = UnityEngine.Mathf.Clamp(EditorGUILayout.IntField("Threshold: ", currentThreshold), 0, 255);
                int newThreshold = EditorGUILayout.IntSlider("Threshold: ", currentThreshold, 0, 255);
                if (newThreshold != currentThreshold)
                {
                    arcontroller.VideoThreshold = newThreshold;
                }
            }
        }

        EditorGUILayout.Separator();

        showSquareTrackingOptions = EditorGUILayout.Foldout(showSquareTrackingOptions, "Square Tracking Options");
        if (showSquareTrackingOptions)
        {
            int currentTemplateSize = arcontroller.TemplateSize;
            int newTemplateSize     = EditorGUILayout.IntField("Template size: ", currentTemplateSize);
            if (newTemplateSize != currentTemplateSize && newTemplateSize >= 16 && newTemplateSize <= 64)
            {
                arcontroller.TemplateSize = newTemplateSize;
            }
            int currentTemplateCountMax = arcontroller.TemplateCountMax;
            int newTemplateCountMax     = EditorGUILayout.IntField("Template count max.: ", currentTemplateCountMax);
            if (newTemplateCountMax != currentTemplateCountMax && newTemplateCountMax > 0)
            {
                arcontroller.TemplateCountMax = newTemplateCountMax;
            }

            // Labeling mode selection.
            ARController.ARToolKitLabelingMode currentLabelingMode = arcontroller.LabelingMode;
            ARController.ARToolKitLabelingMode newLabelingMode     = (ARController.ARToolKitLabelingMode)EditorGUILayout.EnumPopup("Marker borders:", currentLabelingMode);
            if (newLabelingMode != currentLabelingMode)
            {
                arcontroller.LabelingMode = newLabelingMode;
            }

            // Border size selection.
            float currentBorderSize = arcontroller.BorderSize;
            float newBorderSize     = UnityEngine.Mathf.Clamp(EditorGUILayout.FloatField("Border size:", currentBorderSize), 0.0f, 0.5f);
            if (newBorderSize != currentBorderSize)
            {
                arcontroller.BorderSize = newBorderSize;
            }

            // Pattern detection mode selection.
            ARController.ARToolKitPatternDetectionMode currentPatternDetectionMode = arcontroller.PatternDetectionMode;
            ARController.ARToolKitPatternDetectionMode newPatternDetectionMode     = (ARController.ARToolKitPatternDetectionMode)EditorGUILayout.EnumPopup("Pattern detection mode:", currentPatternDetectionMode);
            if (newPatternDetectionMode != currentPatternDetectionMode)
            {
                arcontroller.PatternDetectionMode = newPatternDetectionMode;
            }

            // Matrix code type selection (only when in one of the matrix modes).
            if (newPatternDetectionMode == ARController.ARToolKitPatternDetectionMode.AR_MATRIX_CODE_DETECTION ||
                newPatternDetectionMode == ARController.ARToolKitPatternDetectionMode.AR_TEMPLATE_MATCHING_COLOR_AND_MATRIX ||
                newPatternDetectionMode == ARController.ARToolKitPatternDetectionMode.AR_TEMPLATE_MATCHING_MONO_AND_MATRIX)
            {
                ARController.ARToolKitMatrixCodeType currentMatrixCodeType = arcontroller.MatrixCodeType;
                ARController.ARToolKitMatrixCodeType newMatrixCodeType     = (ARController.ARToolKitMatrixCodeType)EditorGUILayout.EnumPopup("Matrix code type:", currentMatrixCodeType);
                if (newMatrixCodeType != currentMatrixCodeType)
                {
                    arcontroller.MatrixCodeType = newMatrixCodeType;
                }
            }

            // Image processing mode selection.
            ARController.ARToolKitImageProcMode currentImageProcMode = arcontroller.ImageProcMode;
            ARController.ARToolKitImageProcMode newImageProcMode     = (ARController.ARToolKitImageProcMode)EditorGUILayout.EnumPopup("Image processing mode:", currentImageProcMode);
            if (newImageProcMode != currentImageProcMode)
            {
                arcontroller.ImageProcMode = newImageProcMode;
            }
        }

        EditorGUILayout.Separator();

        showNFTTrackingOptions = EditorGUILayout.Foldout(showNFTTrackingOptions, "NFT Tracking Options");
        if (showNFTTrackingOptions)
        {
            arcontroller.NFTMultiMode = EditorGUILayout.Toggle("Multi-page mode", arcontroller.NFTMultiMode);
        }

        EditorGUILayout.Separator();
        showApplicationOptions = EditorGUILayout.Foldout(showApplicationOptions, "Application Options");
        if (showApplicationOptions)
        {
            arcontroller.AutoStartAR = EditorGUILayout.Toggle("Auto-start AR.", arcontroller.AutoStartAR);
            if (arcontroller.AutoStartAR)
            {
                EditorGUILayout.HelpBox("ARController.StartAR() will be called during MonoBehavior.Start().", MessageType.Info);
            }
            else
            {
                EditorGUILayout.HelpBox("ARController.StartAR() will not be called during MonoBehavior.Start(); you must call it yourself.", MessageType.Info);
            }
            arcontroller.QuitOnEscOrBack = EditorGUILayout.Toggle("Quit on [Esc].", arcontroller.QuitOnEscOrBack);
            if (arcontroller.QuitOnEscOrBack)
            {
                EditorGUILayout.HelpBox("The [esc] key (Windows, OS X) or the [Back] button (Android) will quit the app.", MessageType.Info);
            }
            else
            {
                EditorGUILayout.HelpBox("The [esc] key (Windows, OS X) or the [Back] button (Android) will be ignored.", MessageType.Info);
            }
        }
    }
Esempio n. 3
0
    public override void OnInspectorGUI()
    {
        ARController arController = (ARController)target;

        if (arController == null)
        {
            return;
        }

        EditorGUILayout.LabelField("Version", "ARToolKit " + arController.Version);

        EditorGUILayout.Separator();
        if (cameras == null)
        {
            cameras = ARToolKitAssetManager.GetCameras();
        }
        cameraCount = (cameras == null ? 0 : cameras.Length);

        if (!arController.VideoIsStereo)
        {
            if (cameraCount <= 0)
            {
                arController.videoCparamOverride0 = false;
            }
            EditorGUI.BeginDisabledGroup(cameraCount <= 0);
            arController.videoCparamOverride0 = !EditorGUILayout.Toggle("Use automatic camera parameters", !arController.videoCparamOverride0);
            EditorGUI.EndDisabledGroup();
            if (!arController.videoCparamOverride0)
            {
                arController.videoCParamName0 = "";
            }
            else
            {
                arController.EditorCameraIndex = EditorGUILayout.Popup("Camera Parameter", Math.Min(cameraCount - 1, arController.EditorCameraIndex), cameras);
                if (string.Compare(cameras[arController.EditorCameraIndex], arController.videoCParamName0, StringComparison.Ordinal) != 0)
                {
                    arController.videoCParamName0 = cameras[arController.EditorCameraIndex];
                }
            }
            if (cameraCount <= 0)
            {
                EditorGUILayout.HelpBox("No camera parameters found.", MessageType.Info);
            }
        }
        else
        {
            if (cameraCount <= 0)
            {
                arController.videoCparamOverride0 = false;
            }
            EditorGUI.BeginDisabledGroup(cameraCount <= 0);
            arController.videoCparamOverride0 = !EditorGUILayout.Toggle("Use automatic camera parameters (L)", !arController.videoCparamOverride0);
            EditorGUI.EndDisabledGroup();
            if (!arController.videoCparamOverride0)
            {
                arController.videoCParamName0 = "";
            }
            else
            {
                arController.EditorCameraIndex = EditorGUILayout.Popup("Camera Parameter (L)", Math.Min(cameraCount - 1, arController.EditorCameraIndex), cameras);
                if (string.Compare(cameras[arController.EditorCameraIndex], arController.videoCParamName0, StringComparison.Ordinal) != 0)
                {
                    arController.videoCParamName0 = cameras[arController.EditorCameraIndex];
                }
            }
            if (cameraCount <= 0)
            {
                arController.videoCparamOverride1 = false;
            }
            EditorGUI.BeginDisabledGroup(cameraCount <= 0);
            arController.videoCparamOverride1 = !EditorGUILayout.Toggle("Use automatic camera parameters (R)", !arController.videoCparamOverride1);
            EditorGUI.EndDisabledGroup();
            if (!arController.videoCparamOverride1)
            {
                arController.videoCParamName1 = "";
            }
            else
            {
                arController.EditorCameraIndexR = EditorGUILayout.Popup("Camera Parameter (R)", Math.Max(cameraCount - 1, arController.EditorCameraIndexR), cameras);
                if (string.Compare(cameras[arController.EditorCameraIndexR], arController.videoCParamName1, StringComparison.Ordinal) != 0)
                {
                    arController.videoCParamName1 = cameras[arController.EditorCameraIndexR];
                }
            }
            if (cameraCount <= 0)
            {
                EditorGUILayout.HelpBox("No camera parameters found.", MessageType.Info);
            }
        }

        showVideoConfiguration = EditorGUILayout.Foldout(showVideoConfiguration, "Video Configuration");
        if (showVideoConfiguration)
        {
            if (!arController.VideoIsStereo)
            {
                arController.videoConfigurationAndroid0      = EditorGUILayout.TextField("Android", arController.videoConfigurationAndroid0);
                arController.videoConfigurationiOS0          = EditorGUILayout.TextField("iOS", arController.videoConfigurationiOS0);
                arController.videoConfigurationLinux0        = EditorGUILayout.TextField("Linux", arController.videoConfigurationLinux0);
                arController.videoConfigurationMacOSX0       = EditorGUILayout.TextField("macOS", arController.videoConfigurationMacOSX0);
                arController.videoConfigurationWindows0      = EditorGUILayout.TextField("Windows", arController.videoConfigurationWindows0);
                arController.videoConfigurationWindowsStore0 = EditorGUILayout.TextField("Windows Store", arController.videoConfigurationWindowsStore0);
            }
            else
            {
                arController.videoConfigurationAndroid0      = EditorGUILayout.TextField("Android (L)", arController.videoConfigurationAndroid0);
                arController.videoConfigurationAndroid1      = EditorGUILayout.TextField("Android (R)", arController.videoConfigurationAndroid1);
                arController.videoConfigurationiOS0          = EditorGUILayout.TextField("iOS (L)", arController.videoConfigurationiOS0);
                arController.videoConfigurationiOS1          = EditorGUILayout.TextField("iOS (R)", arController.videoConfigurationiOS1);
                arController.videoConfigurationLinux0        = EditorGUILayout.TextField("Linux (L)", arController.videoConfigurationLinux0);
                arController.videoConfigurationLinux1        = EditorGUILayout.TextField("Linux (R)", arController.videoConfigurationLinux1);
                arController.videoConfigurationMacOSX0       = EditorGUILayout.TextField("macOS (L)", arController.videoConfigurationMacOSX0);
                arController.videoConfigurationMacOSX1       = EditorGUILayout.TextField("macOS (R)", arController.videoConfigurationMacOSX1);
                arController.videoConfigurationWindows0      = EditorGUILayout.TextField("Windows (L)", arController.videoConfigurationWindows0);
                arController.videoConfigurationWindows1      = EditorGUILayout.TextField("Windows (R)", arController.videoConfigurationWindows1);
                arController.videoConfigurationWindowsStore0 = EditorGUILayout.TextField("Windows Store (L)", arController.videoConfigurationWindowsStore0);
                arController.videoConfigurationWindowsStore1 = EditorGUILayout.TextField("Windows Store (R)", arController.videoConfigurationWindowsStore1);
            }
            arController.VideoIsStereo = EditorGUILayout.Toggle("Stereo Video Input", arController.VideoIsStereo);
            EditorGUILayout.HelpBox("Check this option if you plan to use two cameras to track the environment. This is not stereoscopic rendering. Note: You will have to configure both left (L) and right (R) cameras separately.", MessageType.Info);
        }

        showVideoOptions = EditorGUILayout.Foldout(showVideoOptions, "Video Background");
        if (showVideoOptions)
        {
            arController.BackgroundLayer0 = EditorGUILayout.LayerField("Background Layer", arController.BackgroundLayer0);

            ContentMode currentContentMode = arController.ContentMode;
            ContentMode newContentMode     = (ContentMode)EditorGUILayout.EnumPopup("Content Mode", currentContentMode);
            if (newContentMode != currentContentMode)
            {
                arController.ContentMode = newContentMode;
            }
            arController.ContentRotate90 = EditorGUILayout.Toggle("Rotate 90° Clockwise", arController.ContentRotate90);
            arController.ContentFlipV    = EditorGUILayout.Toggle("Flip Vertically", arController.ContentFlipV);
            arController.ContentFlipH    = EditorGUILayout.Toggle("Flip Horizontally", arController.ContentFlipH);
        }

        showTwoDTrackingOptions = EditorGUILayout.Foldout(showTwoDTrackingOptions, "2D Tracking Options");
        if (showTwoDTrackingOptions)
        {
            int[]    values = { 1, 2, 3, 4, 5, 6, 7, 8 };
            String[] names  = { "1", "2", "3", "4", "5", "6", "7", "8" };
            arController.TwoDTrackerMaxMarkerCount = EditorGUILayout.IntPopup("Maximum simultaneous trackers:", arController.TwoDTrackerMaxMarkerCount, names, values);
        }

        showSquareTrackingOptions = EditorGUILayout.Foldout(showSquareTrackingOptions, "Square Tracking Options");
        if (showSquareTrackingOptions)
        {
            // Threshold mode selection
            ARController.ARToolKitThresholdMode currentThreshMode = arController.VideoThresholdMode;
            ARController.ARToolKitThresholdMode newThreshMode     = (ARController.ARToolKitThresholdMode)EditorGUILayout.EnumPopup("Mode", currentThreshMode);
            if (newThreshMode != currentThreshMode)
            {
                arController.VideoThresholdMode = newThreshMode;
            }
            EditorGUILayout.HelpBox(ARController.ThresholdModeDescriptions[newThreshMode], MessageType.Info);

            // Show threshold slider only in manual or bracketing modes.
            if (newThreshMode == ARController.ARToolKitThresholdMode.Manual || newThreshMode == ARController.ARToolKitThresholdMode.Bracketing)
            {
                int currentThreshold = arController.VideoThreshold;
                int newThreshold     = EditorGUILayout.IntSlider("Threshold", currentThreshold, 0, 255);
                if (newThreshold != currentThreshold)
                {
                    arController.VideoThreshold = newThreshold;
                }
            }
        }

        showApplicationOptions = EditorGUILayout.Foldout(showApplicationOptions, "Additional Options");
        if (showApplicationOptions)
        {
            arController.AutoStartAR = EditorGUILayout.Toggle("Auto-Start AR.", arController.AutoStartAR);
            if (arController.AutoStartAR)
            {
                EditorGUILayout.HelpBox("ARController.StartAR() will be called during MonoBehavior.Start().", MessageType.Info);
            }
            else
            {
                EditorGUILayout.HelpBox("ARController.StartAR() will not be called during MonoBehavior.Start(); you must call it yourself.", MessageType.Warning);
            }

            arController.QuitOnEscOrBack = EditorGUILayout.Toggle("Quit on [Esc].", arController.QuitOnEscOrBack);
            if (arController.QuitOnEscOrBack)
            {
                EditorGUILayout.HelpBox("The [esc] key (Windows, macOS) or the [Back] button (Android) will quit the app.", MessageType.Info);
            }
            else
            {
                EditorGUILayout.HelpBox("The [esc] key (Windows, macOS) or the [Back] button (Android) will be ignored.", MessageType.Warning);
            }

            arController.LogLevel = (ARController.AR_LOG_LEVEL)EditorGUILayout.EnumPopup("Native Log Level", arController.LogLevel);
        }
    }