Ejemplo n.º 1
0
    IEnumerator JobsSpawnerCoroutine()
    {
        // ensure that the headset is connected and ready before starting any recording
        var nextFrameAwaiter = new WaitForEndOfFrame();

        while (!FoveManager.IsHardwareConnected())
        {
            yield return(nextFrameAwaiter);
        }

        // if the recording rate is the same as the fove rendering rate,
        // we use a coroutine to be sure that recorded gazes are synchronized with frames
        if (recordingRate == RecordingRate._70FPS)
        {
            // Coroutines give us a bit more control over when the call happens, and also simplify the code
            // structure. However they are only ever called once per frame -- they processing to happen in
            // pieces, but they shouldn't be confused with threads.
            StartCoroutine(RecordDataCoroutine());
        }
        else // otherwise we just start a vsynch asynchronous thread
        {
            StartCoroutine(RecordFoveTransformCoroutine());
            collectThread = new Thread(CollectThreadFunc);
            collectThread.Start();
        }
    }
Ejemplo n.º 2
0
    static string getEyeClosedString(Fove.Eye eye)
    {
        var closedEyes = FoveManager.CheckEyesClosed();
        var eyeClosed  = (eye & closedEyes) != 0;

        return(eyeClosed.ToString());
    }
Ejemplo n.º 3
0
    private void UpdateFoveInterfaceMatrices(bool immediate)
    {
        var t = fove.transform;

        if (immediate)
        {
            // In the case of 120 FPS recording rate, we re-fetch the HMD latest pose
            // and localy recalculate the fove interface local transform
            var pose                = FoveManager.GetHMDPose(true);
            var isStanding          = fove.poseType == FoveInterface.PlayerPose.Standing;
            var hmdAdjustedPosition = (isStanding ? pose.standingPosition : pose.position).ToVector3();
            var localPos            = fove.fetchPosition? hmdAdjustedPosition : t.position;
            var localRot            = fove.fetchOrientation? pose.orientation.ToQuaternion() : t.rotation;

            var parentTransfo = t.parent != null ? t.parent.localToWorldMatrix : Matrix4x4.identity;
            var localTransfo  = Matrix4x4.TRS(localPos, localRot, t.localScale);

            lock (foveInterfaceTransfo)
            {
                foveInterfaceTransfo.HMDToWorld = parentTransfo * localTransfo;
                foveInterfaceTransfo.HMDToLocal = localTransfo;
            }
        }
        else
        {
            // no need to lock the object, we are in synchronize mode (access from the same thread)
            foveInterfaceTransfo.HMDToWorld = t.localToWorldMatrix;
            foveInterfaceTransfo.HMDToLocal = Matrix4x4.TRS(t.localPosition, t.localRotation, t.localScale);
        }
    }
Ejemplo n.º 4
0
 // Update is called once per frame
 void Update()
 {
     ManagerOrientText.text   = toFomattedString(FoveManager.GetHmdRotation().value);
     ManagerPosText.text      = toFomattedString(FoveManager.GetHmdPosition(false).value);
     ManagerGazeText.text     = toFomattedString(FoveManager.GetHmdCombinedGazeRay().value.direction);
     InterfaceOrientText.text = toFomattedString(fove.transform.rotation);
     InterfacePosText.text    = toFomattedString(fove.transform.position);
     InterfaceGazeText.text   = toFomattedString(fove.GetCombinedGazeRay().value.direction);
 }
Ejemplo n.º 5
0
    // Update is called once per frame
    void Update()
    {
        // IOD & IPD
        iodText.text = toMilliString(FoveManager.GetUserIOD());
        ipdText.text = toMilliString(FoveManager.GetUserIPD());

        // Eye radius
        UpdateEyeTexts(Fove.Eye.Left);
        UpdateEyeTexts(Fove.Eye.Right);
    }
Ejemplo n.º 6
0
 // Update is called once per frame
 void Update()
 {
     if (FoveManager.GetGazedObject() == gazedReference)
     {
         mat.color = Color.yellow;
     }
     else
     {
         mat.color = Color.gray;
     }
 }
Ejemplo n.º 7
0
    void Start()
    {
        var caps = Fove.ClientCapabilities.EyeTorsion
                   | Fove.ClientCapabilities.EyeballRadius
                   | Fove.ClientCapabilities.IrisRadius
                   | Fove.ClientCapabilities.PupilRadius
                   | Fove.ClientCapabilities.UserIOD
                   | Fove.ClientCapabilities.UserIPD;

        FoveManager.RegisterCapabilities(caps);
    }
Ejemplo n.º 8
0
    // Update is called once per frame
    void Update()
    {
        var pose     = FoveManager.GetHMDPose();
        var conv     = FoveManager.GetHMDGazeConvergence();
        var foveConv = fove.GetGazeConvergence();

        ManagerOrientText.text   = toFomattedString(pose.orientation.ToQuaternion());
        ManagerPosText.text      = toFomattedString(pose.position.ToVector3());
        ManagerGazeText.text     = toFomattedString(conv.ray.direction);
        InterfaceOrientText.text = toFomattedString(fove.transform.rotation);
        InterfacePosText.text    = toFomattedString(fove.transform.position);
        InterfaceGazeText.text   = toFomattedString(foveConv.ray.direction);
    }
Ejemplo n.º 9
0
    void Start()
    {
        var profileNames = FoveManager.ListProfiles().value;

        foreach (var profile in profileNames)
        {
            var item = GameObject.Instantiate(profileItemPrefab);
            item.transform.SetParent(layout.transform, false);

            var controller = item.GetComponent <ProfileItemController>();
            controller.ProfileName = profile;
            controllers.Add(controller);
        }
    }
Ejemplo n.º 10
0
    // Update is called once per frame
    void Update()
    {
        var gazeRay   = FoveManager.GetHmdCombinedGazeRay().value;
        var gazeDepth = FoveManager.GetCombinedGazeDepth().value;
        var dist      = useGazeConvergenceDepth ? gazeDepth : distance;

        if (float.IsInfinity(dist) || float.IsNaN(dist))
        {
            dist = distance;
        }

        transform.localPosition = gazeRay.origin + dist * gazeRay.direction;
        pointRenderer.enabled   = !FoveManager.IsEyeTrackingCalibrating();
    }
Ejemplo n.º 11
0
    // Update is called once per frame
    void Update()
    {
        if (Input.GetKeyDown(KeyCode.T))
        {
            FoveManager.TareOrientation();
        }
        if (Input.GetKeyDown(KeyCode.M))
        {
            calibOptions.method = (CalibrationMethod)(((int)calibOptions.method + 1) % Enum.GetNames(typeof(CalibrationMethod)).Length);
            RestartCalibrationIfRunning();
        }
        if (Input.GetKeyDown(KeyCode.C))
        {
            calibRenderer.enabled = !calibRenderer.enabled;
            RestartCalibrationIfRunning();
        }
        if (Input.GetKeyDown(KeyCode.E))
        {
            calibOptions.eyeByEye = (EyeByEyeCalibration)(((int)calibOptions.eyeByEye + 1) % Enum.GetNames(typeof(EyeByEyeCalibration)).Length);
            RestartCalibrationIfRunning();
        }
        if (Input.GetKeyDown(KeyCode.R))
        {
            calibOptions.restart = !calibOptions.restart;
            RestartCalibrationIfRunning();
        }
        if (Input.GetKeyDown(KeyCode.L))
        {
            calibOptions.lazy = !calibOptions.lazy;
            RestartCalibrationIfRunning();
        }
        if (Input.GetKeyDown(KeyCode.Space))
        {
            FoveManager.StartEyeTrackingCalibration(calibOptions);
        }
        if (Input.GetKeyDown(KeyCode.S))
        {
            FoveManager.StopEyeTrackingCalibration();
        }

        isCalibratedText.text  = "IsCalibrated: " + FoveManager.IsEyeTrackingCalibrated().value;
        isCalibratingText.text = "IsCalibrating: " + FoveManager.IsEyeTrackingCalibrating().value;
        stateText.text         = "Calibration State: " + FoveManager.GetEyeTrackingCalibrationState().value;
        lazyText.text          = "Lazy Calibration: " + calibOptions.lazy;
        restartText.text       = "Restart Calibration: " + calibOptions.restart;
        eyeByEyeText.text      = "Eye-by-eye Calibration: " + calibOptions.eyeByEye;
        methodText.text        = "Calibration Method: " + calibOptions.method;
        renderModeText.text    = "Custom Calibration Rendering: " + calibRenderer.enabled;
    }
Ejemplo n.º 12
0
    private void RestartCalibrationIfRunning()
    {
        if (!FoveManager.IsEyeTrackingCalibrating())
        {
            return;
        }

        var restartOpts = new CalibrationOptions
        {
            restart  = true,
            eyeByEye = calibOptions.eyeByEye,
            method   = calibOptions.method,
        };

        FoveManager.StartEyeTrackingCalibration(restartOpts);
    }
Ejemplo n.º 13
0
    // Use this for initialization
    void Start()
    {
        FoveManager.RegisterCapabilities(Fove.ClientCapabilities.EyesImage | Fove.ClientCapabilities.EyeShape);

        for (var i = 0; i < EyeShape.OutlinePointCount; ++i)
        {
            eyePointsLeft[i]  = (GameObject)Instantiate(eyePointPrefab, eyePointRootLeft, false);
            eyePointsRight[i] = (GameObject)Instantiate(eyePointPrefab, eyePointRootRight, false);

            var size = 7f;
            eyePointsLeft[i].transform.localScale  = size * new Vector3(1, -1, 1);
            eyePointsRight[i].transform.localScale = size * new Vector3(1, -1, 1);

            eyePointsLeft[i].GetComponentInChildren <Text>().text  = i.ToString();
            eyePointsRight[i].GetComponentInChildren <Text>().text = i.ToString();
        }
    }
Ejemplo n.º 14
0
    private void UpdateEyeTexts(Fove.Eye eye)
    {
        var isLeft    = eye == Fove.Eye.Left;
        var infoTexts = isLeft ? leftEyeInfoTexts : rightEyeInfoTexts;

        // eye closed or open
        infoTexts.stateText.text = getEyeStateString(eye);

        // Eye radius
        infoTexts.eyeText.text   = toMilliString(FoveManager.GetEyeballRadius(eye));
        infoTexts.irisText.text  = toMilliString(FoveManager.GetIrisRadius(eye));
        infoTexts.pupilText.text = toMilliString(FoveManager.GetPupilRadius(eye));

        var torsion     = FoveManager.GetEyeTorsion(eye);
        var torsionText = torsion.Failed ? torsion.error.ToString() : torsion.value.ToString("F2");

        infoTexts.torsionText.text = torsionText;
    }
Ejemplo n.º 15
0
    // Use this for initialization
    void Start()
    {
        FoveManager.RegisterCapabilities(Fove.ClientCapabilities.GazedObjectDetection);

        if (gazedReference == null)
        {
            gazedReference = gameObject;
        }

        var renderer = GetComponent <Renderer>();

        if (renderer != null)
        {
            mat = GetComponent <Renderer>().material;
        }
        else
        {
            mat = GetComponent <Image>().material;
        }
    }
Ejemplo n.º 16
0
    void UpdateEyeShape(Fove.Eye eye)
    {
        var shapeResult = FoveManager.GetEyeShape(eye);

        if (shapeResult.Failed)
        {
            Debug.LogWarning("Failed to retrieve eye shape");
            return;
        }

        var shapes    = shapeResult.value;
        var eyePoints = eye == Fove.Eye.Left ? eyePointsLeft : eyePointsRight;

        int i = 0;

        foreach (var point in shapes.Outline)
        {
            eyePoints[i++].transform.localPosition = new Vector3(point.x, point.y, 0);
        }
    }
Ejemplo n.º 17
0
    // Update is called once per frame
    void FixedUpdate()
    {
        var obj = FoveManager.GetGazedObject().value;

        if (obj == null)
        {
            return;
        }

        var rbody = obj.GetComponent <Rigidbody>();

        if (rbody == null)
        {
            return;
        }

        var gazeRay = fove.GetCombinedGazeRay().value;

        rbody.AddForce(25 * gazeRay.direction);
    }
Ejemplo n.º 18
0
    private void UpdateFoveInterfaceMatrices(bool immediate)
    {
        var t = fove.transform;

        var markKeyDown       = Input.GetKey(markFrameKey);
        var gazedObjectResult = FoveManager.GetGazedObject();
        var gazedObjectName   = new Result <string>(gazedObjectResult.value? gazedObjectResult.value.name : "", gazedObjectResult.error);

        if (immediate)
        {
            // In the case of 120 FPS recording rate, we re-fetch the HMD latest pose
            // and locally recalculate the fove interface local transform
            var isStanding          = fove.poseType == FoveInterface.PlayerPose.Standing;
            var hmdAdjustedPosition = FoveManager.GetHmdPosition(isStanding);
            var localPos            = fove.fetchPosition? hmdAdjustedPosition : t.position;
            var localRot            = fove.fetchOrientation? FoveManager.GetHmdRotation() : t.rotation;

            var parentTransfo = t.parent != null ? t.parent.localToWorldMatrix : Matrix4x4.identity;
            var localTransfo  = Matrix4x4.TRS(localPos, localRot, t.localScale);

            lock (unityThreadData)
            {
                unityThreadData.HMDToWorld     = parentTransfo * localTransfo;
                unityThreadData.HMDToLocal     = localTransfo;
                unityThreadData.markKeyDown    = markKeyDown;
                unityThreadData.gazedObject    = gazedObjectName;
                unityThreadData.HMDPosition    = localPos;
                unityThreadData.HMDOrientation = localRot;
            }
        }
        else
        {
            // no need to lock the object, we are in synchronize mode (access from the same thread)
            unityThreadData.HMDToWorld     = t.localToWorldMatrix;
            unityThreadData.HMDToLocal     = Matrix4x4.TRS(t.localPosition, t.localRotation, t.localScale);
            unityThreadData.markKeyDown    = markKeyDown;
            unityThreadData.gazedObject    = gazedObjectName;
            unityThreadData.HMDPosition    = t.localPosition;
            unityThreadData.HMDOrientation = t.localRotation;
        }
    }
Ejemplo n.º 19
0
    void validateSelectedItem(bool setCurrent)
    {
        var controller = controllers[selectedIndex];

        controller.ValidateName();

        var newProfileName = controller.GetEditValue();

        if (setCurrent && controller.ProfileName == newProfileName)         // it is a set
        {
            FoveManager.SetCurrentProfile(newProfileName);
        }
        else if (string.IsNullOrEmpty(controller.ProfileName))         // create case
        {
            FoveManager.CreateProfile(newProfileName);
        }
        else
        {
            FoveManager.RenameProfile(controller.ProfileName, newProfileName);
        }

        controller.ProfileName = newProfileName;
    }
Ejemplo n.º 20
0
    private Stopwatch stopwatch = new Stopwatch(); // Unity Time.time can't be use outside of main thread.

    // Use this for initialization.
    void Start()
    {
        stopwatch.Start();
        if (!Stopwatch.IsHighResolution)
        {
            Debug.LogWarning("High precision stopwatch is not supported on this machine. Recorded frame times may not be highly accurate.");
        }

        // Check to make sure that the FOVE interface variable is assigned. This prevents a ton of errors
        // from filling your log if you forget to assign the interface through the inspector.
        if (fove == null)
        {
            Debug.LogWarning("Forgot to assign a Fove interface to the FOVERecorder object.");
            enabled = false;
            return;
        }

        var caps = ClientCapabilities.EyeTracking;

        if (exportFields.GazeDepth)
        {
            caps |= ClientCapabilities.GazeDepth;
        }
        if (exportFields.PupilsRadius)
        {
            caps |= ClientCapabilities.PupilRadius;
        }
        if (exportFields.GazedObject)
        {
            caps |= ClientCapabilities.GazedObjectDetection;
        }
        if (exportFields.EyeTorsion)
        {
            caps |= ClientCapabilities.EyeTorsion;
        }
        if (exportFields.UserPresence)
        {
            caps |= ClientCapabilities.UserPresence;
        }
        if (exportFields.UserAttentionShift)
        {
            caps |= ClientCapabilities.UserAttentionShift;
        }
        if (exportFields.IPD)
        {
            caps |= ClientCapabilities.UserIPD;
        }
        if (exportFields.IOD)
        {
            caps |= ClientCapabilities.UserIOD;
        }
        if (exportFields.EyeballRadius)
        {
            caps |= ClientCapabilities.EyeballRadius;
        }
        if (exportFields.EyeShape)
        {
            caps |= ClientCapabilities.EyeShape;
        }
        if (exportFields.PupilShape)
        {
            caps |= ClientCapabilities.PupilShape;
        }

        FoveManager.RegisterCapabilities(caps);

        // We set the initial data slice capacity to the expected size + 1 so that we never waste time reallocating and
        // copying data under the hood. If the system ever requires more than a single extra entry, there is likely
        // a severe problem causing delays which should be addressed.
        dataSlice = new AggregatedData(writeAtDataCount + 1);

        // If overwrite is not set, then we need to make sure our selected file name is valid before proceeding.
        if (!Directory.Exists(OutputFolder))
        {
            Directory.CreateDirectory(OutputFolder);
        }
        {
            string testFileName = Path.Combine(OutputFolder, outputFileName + ".csv");
            if (!overwriteExistingFile)
            {
                int counter = 1;
                while (File.Exists(testFileName))
                {
                    testFileName = Path.Combine(OutputFolder, outputFileName + "_" + (counter++) + ".csv"); // e.g., "results_12.csv"
                }
            }
            outputFileName = testFileName;

            Debug.Log("Writing data to " + outputFileName);
        }

        dataToWrite.Enqueue(new DataHeaderSerializer(exportFields));

        // Create the write thread to call "WriteThreadFunc", and then start it.
        writeThread = new Thread(WriteThreadFunc);
        writeThread.Start();

        StartCoroutine(JobsSpawnerCoroutine());
    }
Ejemplo n.º 21
0
    private void RecordDatum(bool immediate)
    {
        // If recording is stopped (which is it by default), loop back around next frame.
        if (recordingStopped)
        {
            return;
        }

        if (!immediate)         // we run in the same thread as unity we can update the transformations in a synchronized way
        {
            UpdateFoveInterfaceMatrices(false);
        }

        Matrix4x4 transformMat;

        lock (foveInterfaceTransfo)
        {
            switch (raysCoordinateSpace)
            {
            case CoordinateSpace.World:
                transformMat = foveInterfaceTransfo.HMDToWorld;
                break;

            case CoordinateSpace.Local:
                transformMat = foveInterfaceTransfo.HMDToLocal;
                break;

            default:
                transformMat = Matrix4x4.identity;
                break;
            }
        }

        var leftEyeOffset  = FoveManager.GetLeftEyeOffset(immediate);
        var rightEyeOffset = FoveManager.GetRightEyeOffset(immediate);
        var leftEyeVector  = FoveManager.GetLeftEyeVector(immediate);
        var rightEyeVector = FoveManager.GetRightEyeVector(immediate);

        Ray leftRay, rightRay;

        Utils.CalculateGazeRays(ref transformMat, ref leftEyeVector, ref rightEyeVector, ref leftEyeOffset, ref rightEyeOffset, out leftRay, out rightRay);

        // If you add new fields, be sure to write them here.
        var datum = new RecordingDatum
        {
            frameTime = stopwatch.Elapsed.TotalSeconds,
            leftGaze  = leftRay,
            rightGaze = rightRay,
        };

        dataSlice.Add(datum);

        if (dataSlice.Count >= writeAtDataCount)
        {
            // Make sure we have exclusive access by locking the mutex, but only wait for up to 30 milliseconds.
            if (!writingDataMutex.WaitOne(30))
            {
                // If we got here, it means that we couldn't acquire exclusive access within the specified time
                // limit. Likely this means an error happened, but it could also mean that more data was being
                // written than it took to gather another set of data -- in which case you may need to extend the
                // timeout duration, though that will cause a noticeable frame skip in your application.

                // For now, the best thing we can do is continue the loop and try writing data again next frame.
                long excess = dataSlice.Count - writeAtDataCount;
                if (excess > 1)
                {
                    Debug.LogError("Data slice is " + excess + " entries over where it should be; this is" +
                                   "indicative of a major performance concern in the data recording and writing" +
                                   "process.");
                }
                return;
            }

            CheckForNullDataToWrite();

            // Move our current slice over to dataToWrite, and then create a new slice.
            dataToWrite = dataSlice;
            dataSlice   = new List <RecordingDatum>((int)(writeAtDataCount + 1));

            // Release our claim on the mutex.
            writingDataMutex.ReleaseMutex();

            if (!threadWaitHandle.Set())
            {
                Debug.LogError("Error setting the event to wake up the file writer thread");
            }
        }
    }
Ejemplo n.º 22
0
 void Update()
 {
     EyeImage.texture      = FoveManager.GetEyesImage();
     PositionImage.texture = FoveManager.GetPositionImage();
     MirrorImage.texture   = FoveManager.GetMirrorTexture();
 }
Ejemplo n.º 23
0
    void Start()
    {
        var caps = Fove.ClientCapabilities.EyesImage | Fove.ClientCapabilities.PositionImage;

        FoveManager.RegisterCapabilities(caps);
    }
Ejemplo n.º 24
0
 public void Calibration()
 {
     FoveManager.EnsureEyeTrackingCalibration();
 }
Ejemplo n.º 25
0
    void Update()
    {
        if (Input.GetKeyDown(KeyCode.Delete))
        {
            var controller = controllers[selectedIndex];
            FoveManager.DeleteProfile(controller.ProfileName);
            Destroy(controller.gameObject);
            controllers.RemoveAt(selectedIndex);
        }
        else if (Input.GetKeyDown(KeyCode.DownArrow))
        {
            validateSelectedItem(false);
            ++selectedIndex;
        }
        else if (Input.GetKeyDown(KeyCode.UpArrow))
        {
            validateSelectedItem(false);
            --selectedIndex;
        }
        else if (Input.GetKeyDown(KeyCode.F2))
        {
            controllers[selectedIndex].EnterNameEditMode();
        }
        else if (Input.GetKeyDown(KeyCode.Insert))
        {
            var item = GameObject.Instantiate(profileItemPrefab);
            item.transform.SetParent(layout.transform, false);

            var controller = item.GetComponent <ProfileItemController>();
            controller.EnterNameEditMode();
            controllers.Add(controller);

            selectedIndex = controllers.Count - 1;
        }
        else if (Input.GetKeyDown(KeyCode.Return))
        {
            validateSelectedItem(true);
        }

        if (controllers.Count == 0)
        {
            return;
        }

        // else if RENAME
        // else if CREATE
        selectedIndex = (selectedIndex + controllers.Count) % controllers.Count;

        string selectedProfile         = controllers[selectedIndex].ProfileName;
        string selectedProfileDataPath = "";

        var currProfile = FoveManager.GetCurrentProfile().value;

        if (!string.IsNullOrEmpty(selectedProfile))
        {
            selectedProfileDataPath = FoveManager.GetProfileDataPath(selectedProfile);
        }

        dataPathText.text = "Data Path: \"" + selectedProfileDataPath + "\"";

        for (int i = 0; i < controllers.Count; i++)
        {
            var controller = controllers[i];
            controller.isSelected = i == selectedIndex;
            controller.isCurrent  = controller.ProfileName == currProfile;
        }
    }
Ejemplo n.º 26
0
 // Use this for initialization
 void Start()
 {
     FoveManager.TareOrientation();
 }
Ejemplo n.º 27
0
    // Currently does not support editing of multiple objects at once.
    // I do not anticipate any problems with this since you should only
    // ever really have one per scene anyway.
    public sealed override void OnInspectorGUI()
    {
        // A decent style.  Light grey text inside a border.
        helpStyle           = new GUIStyle(GUI.skin.box);
        helpStyle.wordWrap  = true;
        helpStyle.alignment = TextAnchor.UpperLeft;

        helpStyle.normal.textColor = Color.red;

        // Update the serializedobject
        serializedObject.Update();

        // Cache the editor's playing state so we can prevent editing fields that shouldn't update during
        // a live play session.
        bool isPlaying = EditorApplication.isPlaying;

        EditorGUILayout.Space();
        EditorGUILayout.LabelField("Client fetches and sync:", EditorStyles.boldLabel);
        EditorGUI.BeginChangeCheck();
        {
            EditorGUI.indentLevel++;
            EditorGUILayout.PropertyField(_gaze, fetchGazeLabel);
            EditorGUILayout.PropertyField(_orientation, fetchOrientationLabel);
            EditorGUILayout.PropertyField(_position, fetchPositionLabel);
            EditorGUI.indentLevel--;
        }

        EditorGUILayout.Space();
        EditorGUILayout.PropertyField(_eyeTargets);
        EditorGUILayout.PropertyField(_poseType);

        GUI.enabled   = true;
        _showGazeCast = EditorGUILayout.Foldout(_showGazeCast, "Gaze Object Detection");
        if (_showGazeCast)
        {
            EditorGUI.indentLevel++;
            EditorGUILayout.PropertyField(_registerCameraObject, registerCameraLabel);
            EditorGUILayout.PropertyField(_gazeCastCullMask, castCullMaskLabel);
            EditorGUI.indentLevel--;
        }

        GUI.enabled       = true;
        _showCullingMasks = EditorGUILayout.Foldout(_showCullingMasks, "Per-Eye Culling Masks");
        if (_showCullingMasks)
        {
            EditorGUI.indentLevel++;
            EditorGUILayout.PropertyField(_cullMaskLeft);
            EditorGUILayout.PropertyField(_cullMaskRight);
            EditorGUI.indentLevel--;
        }

        GUI.enabled            = true;
        _showCompositorAttribs = EditorGUILayout.Foldout(_showCompositorAttribs, "Compositor options");
        if (_showCompositorAttribs)
        {
            GUI.enabled = !isPlaying;
            EditorGUI.indentLevel++;

            //EditorGUILayout.PropertyField(_compositorLayerType);
            EditorGUILayout.PropertyField(_compositorDisableTimewarp);
            //EditorGUILayout.PropertyField(_compositorDisableDistortion);

            EditorGUI.indentLevel--;
        }

        GUI.enabled = true;
        if (isPlaying && GUILayout.Button("Ensure calibration"))
        {
            Debug.Log("Manually triggering eye tracking calibration check from inspector...");
            FoveManager.StartEyeTrackingCalibration(new CalibrationOptions {
                lazy = true
            });
        }

        if (Application.targetFrameRate != -1)
        {
            GUILayout.Label(
                "WARNING: Your target framerate is set to " + Application.targetFrameRate + ". Having a target framerate can artificially slow down FOVE frame submission. We recommend disabling this."
                , helpStyle
                , GUILayout.ExpandWidth(true));
        }

        serializedObject.ApplyModifiedProperties();
    }
Ejemplo n.º 28
0
    static string getEyeStateString(Fove.Eye eye)
    {
        var state = FoveManager.GetEyeState(eye).value;

        return(state.ToString());
    }
Ejemplo n.º 29
0
    private void RecordDatum(bool immediate)
    {
        // If recording is stopped (which is it by default), loop back around next frame.
        if (!shouldRecord)
        {
            return;
        }

        if (!immediate) // we run in the same thread as unity we can update the transformations in a synchronized way
        {
            UpdateFoveInterfaceMatrices(false);
        }

        bool            frameMarked;
        Result <string> gazedObjectName;
        Matrix4x4       transformMat;
        Vector3         hmdPosition;
        Quaternion      hmdOrientation;

        lock (unityThreadData)
        {
            switch (gazeCoordinateSpace)
            {
            case CoordinateSpace.World:
                transformMat = unityThreadData.HMDToWorld;
                break;

            case CoordinateSpace.Local:
                transformMat = unityThreadData.HMDToLocal;
                break;

            default:
                transformMat = Matrix4x4.identity;
                break;
            }
            frameMarked     = unityThreadData.markKeyDown;
            gazedObjectName = unityThreadData.gazedObject;
            hmdPosition     = unityThreadData.HMDPosition;
            hmdOrientation  = unityThreadData.HMDOrientation;
        }

        var eyeOffsets = FoveManager.GetEyeOffsets();
        var eyeVectorL = FoveManager.GetHmdGazeVector(Eye.Left);
        var eyeVectorR = FoveManager.GetHmdGazeVector(Eye.Right);

        Stereo <Result <Ray> > eyeRays;

        eyeRays.left  = Utils.CalculateWorldGazeVector(ref transformMat, ref eyeOffsets.value.left, ref eyeVectorL);
        eyeRays.right = Utils.CalculateWorldGazeVector(ref transformMat, ref eyeOffsets.value.right, ref eyeVectorR);

        var gazeDepth   = FoveManager.GetCombinedGazeDepth();
        var combinedRay = FoveManager.GetHmdCombinedGazeRay();

        combinedRay.value.origin    = transformMat.MultiplyPoint(combinedRay.value.origin);
        combinedRay.value.direction = transformMat.MultiplyVector(combinedRay.value.direction).normalized;

        var pupilRadiusLeft  = FoveManager.GetPupilRadius(Eye.Left);
        var pupilRadiusRight = FoveManager.GetPupilRadius(Eye.Right);

        var eyeStateL = FoveManager.GetEyeState(Eye.Left);
        var eyeStateR = FoveManager.GetEyeState(Eye.Right);

        var eyeTorsionL = FoveManager.GetEyeTorsion(Eye.Left);
        var eyeTorsionR = FoveManager.GetEyeTorsion(Eye.Right);

        // If you add new fields, be sure to write them here.
        var datum = new Datum
        {
            AppTime            = (float)stopwatch.Elapsed.TotalSeconds,
            UserMark           = frameMarked,
            GazedObjectName    = gazedObjectName,
            HeadsetPosition    = new Result <Vector3>(hmdPosition),
            HeadsetOrientation = new Result <Quaternion>(hmdOrientation),
            CombinedRay        = combinedRay,
            GazeDepth          = gazeDepth,
            EyeRays            = eyeRays,
            EyesState          = new Stereo <Result <EyeState> >(eyeStateL, eyeStateR),
            PupilsRadius       = new Stereo <Result <float> >(pupilRadiusLeft, pupilRadiusRight),
            EyeTorsions        = new Stereo <Result <float> >(eyeTorsionL, eyeTorsionR),
            UserPresence       = FoveManager.IsUserPresent(),
            UserAttentionShift = FoveManager.IsUserShiftingAttention(),
            IPD           = FoveManager.GetUserIPD(),
            IOD           = FoveManager.GetUserIOD(),
            EyeballRadius = new Stereo <Result <float> >(FoveManager.GetEyeballRadius(Eye.Left), FoveManager.GetEyeballRadius(Eye.Right)),
            ScreenGaze    = new Stereo <Result <Vector2> >(FoveManager.GetGazeScreenPosition(Eye.Left), FoveManager.GetGazeScreenPosition(Eye.Right)),
            EyeShape      = new Stereo <Result <Fove.Unity.EyeShape> >(FoveManager.GetEyeShape(Eye.Left), FoveManager.GetEyeShape(Eye.Right)),
            PupilShape    = new Stereo <Result <Fove.Unity.PupilShape> >(FoveManager.GetPupilShape(Eye.Left), FoveManager.GetPupilShape(Eye.Right)),
        };

        dataSlice.Add(datum);

        if (dataSlice.Count >= writeAtDataCount)
        {
            flushData();
        }
    }
    // Update is called once per frame
    void Update()
    {
        var isVisible = spriteRenders[0].enabled || spriteRenders[1].enabled || canvas.enabled;

        // tick and pull calibration state
        var result = FoveManager.TickEyeTrackingCalibration(Time.deltaTime, isVisible);

        if (result.Failed)
        {
            Debug.LogError("Failed to tick calibration. Error:" + result.error);
            spriteRenders[0].enabled = false;
            spriteRenders[1].enabled = false;
            canvas.enabled           = false;
            return;
        }

        // update the target position
        var calibrationData = result.value;
        var targets         = calibrationData.targets;

        for (int i = 0; i < targets.ElementCount; i++)
        {
            var target         = calibrationData.targets[i];
            var spriteRenderer = spriteRenders[i];

            if (target.recommendedSize > 0)
            {
                spriteRenderer.enabled = true;
                spriteRenderer.transform.localPosition = target.position;
                spriteRenderer.transform.localScale    = 4f * target.recommendedSize * Vector3.one;
            }
            else
            {
                spriteRenderer.enabled = false;
            }
        }

        // update the text
        canvas.enabled = true;
        switch (calibrationData.state)
        {
        case Fove.CalibrationState.WaitingForUser:
            text.text = "Look at the target";
            break;

        case Fove.CalibrationState.ProcessingData:
            text.text = "Processing...";
            break;

        case Fove.CalibrationState.Successful_HighQuality:
            text.text = "Successful high quality!";
            break;

        case Fove.CalibrationState.Successful_MediumQuality:
            text.text = "Successful medium quality";
            break;

        case Fove.CalibrationState.Successful_LowQuality:
            text.text = "Successful low quality";
            break;

        case Fove.CalibrationState.Failed_Aborted:
            text.text = "Calibration Aborted";
            break;

        case Fove.CalibrationState.Failed_InaccurateData:
            text.text = "Calibration failed: Inaccurate data";
            break;

        case Fove.CalibrationState.Failed_NoRenderer:
            text.text = "Calibration failed: no renderer";
            break;

        case Fove.CalibrationState.Failed_NoUser:
            text.text = "Calibration failed: no user";
            break;

        case Fove.CalibrationState.Failed_Unknown:
            text.text = "Calibration failed: unknown issue";
            break;

        case Fove.CalibrationState.NotStarted:
        case Fove.CalibrationState.CollectingData:
        default:
            canvas.enabled = false;
            break;
        }

        if (calibrationData.state.IsSuccessful() || calibrationData.state.IsFailure())
        {
            timeSinceCompletion += Time.deltaTime;
            if (timeSinceCompletion > 1.5)
            {
                canvas.enabled = false;
            }
        }
        else
        {
            timeSinceCompletion = 0;
        }
    }