Ejemplo n.º 1
0
    void Awake()
    {
        compass = compassBackground;
        if (isVRSeekbar)
        {
            instanceVR = this;
        }
        else
        {
            instance = this;
        }

        if (!instances.Contains(this))
        {
            instances.Add(this);
        }
    }
Ejemplo n.º 2
0
    void Update()
    {
        VRDevices.DetectDevices();

        //NOTE(Kristof): VR specific behaviour
        {
            if (XRSettings.enabled)
            {
                videoController.transform.position = Camera.main.transform.position;

                //NOTE(Lander): enable the highlight in the tutorial mode
                VRDevices.SetControllersTutorialMode(new[] { controllerLeft, controllerRight }, videoController.videoState == VideoController.VideoState.Intro);

                //NOTE(Kristof): Rotating the seekbar
                {
                    //NOTE(Kristof): Seekbar rotation is the same as the seekbar's angle on the circle
                    var seekbarAngle = Vector2.SignedAngle(new Vector2(Canvass.seekbar.transform.position.x, Canvass.seekbar.transform.position.z), Vector2.up);

                    var fov = Camera.main.fieldOfView;
                    //NOTE(Kristof): Camera rotation tells you to which angle on the circle the camera is looking towards
                    var cameraAngle = Camera.main.transform.eulerAngles.y;

                    //NOTE(Kristof): Calculate the absolute degree angle from the camera to the seekbar
                    var distanceLeft  = Mathf.Abs((cameraAngle - seekbarAngle + 360) % 360);
                    var distanceRight = Mathf.Abs((cameraAngle - seekbarAngle - 360) % 360);

                    var angle = Mathf.Min(distanceLeft, distanceRight);

                    if (isOutofView)
                    {
                        if (angle < 2.5f)
                        {
                            isOutofView = false;
                        }
                    }
                    else
                    {
                        if (angle > fov)
                        {
                            isOutofView = true;
                        }
                    }

                    if (isOutofView)
                    {
                        var newAngle = Mathf.LerpAngle(seekbarAngle, cameraAngle, 0.025f);

                        //NOTE(Kristof): Angle needs to be reversed, in Unity postive angles go clockwise while they go counterclockwise in the unit circle (cos and sin)
                        //NOTE(Kristof): We also need to add an offset of 90 degrees because in Unity 0 degrees is in front of you, in the unit circle it is (1,0) on the axis
                        var radianAngle = (-newAngle + 90) * Mathf.PI / 180;
                        var x           = 1.8f * Mathf.Cos(radianAngle);
                        var y           = Camera.main.transform.position.y - 2f;
                        var z           = 1.8f * Mathf.Sin(radianAngle);

                        Canvass.seekbar.transform.position    = new Vector3(x, y, z);
                        Canvass.seekbar.transform.eulerAngles = new Vector3(30, newAngle, 0);
                    }
                }

                //NOTE(Kristof): Rotating the Crosshair canvas
                {
                    Ray cameraRay = Camera.main.ViewportPointToRay(new Vector2(0.5f, 0.5f));
                    Canvass.crosshair.transform.position = cameraRay.GetPoint(90);
                    Canvass.crosshair.transform.LookAt(Camera.main.transform);
                }
            }
            else
            {
                Canvass.seekbar.gameObject.SetActive(false);
                Canvass.crosshair.gameObject.SetActive(false);
            }
        }

        //NOTE(Kristof): Controller specific behaviour
        {
            if (VRDevices.loadedControllerSet != VRDevices.LoadedControllerSet.NoControllers)
            {
                crosshair.enabled      = false;
                crosshairTimer.enabled = false;
            }
            else
            {
                crosshair.enabled      = true;
                crosshairTimer.enabled = true;
            }

            if (Input.mouseScrollDelta.y != 0)
            {
                Camera.main.fieldOfView = Mathf.Clamp(Camera.main.fieldOfView - Input.mouseScrollDelta.y * 5, 20, 120);
            }
        }

        Ray ray;

        //NOTE(Kristof): Deciding on which object the Ray will be based on
        {
            Ray cameraRay     = Camera.main.ViewportPointToRay(new Vector2(0.5f, 0.5f));
            Ray controllerRay = new Ray();

            const ulong ulTriggerValue = (ulong)1 << 33;

            if (trackedControllerLeft.controllerState.ulButtonPressed == controllerLeftOldState.ulButtonPressed + ulTriggerValue)
            {
                controllerRay = controllerLeft.GetComponent <Controller>().CastRay();
            }

            if (trackedControllerRight.controllerState.ulButtonPressed == controllerRightOldState.ulButtonPressed + ulTriggerValue)
            {
                controllerRay = controllerRight.GetComponent <Controller>().CastRay();
            }

            controllerLeftOldState  = trackedControllerLeft.controllerState;
            controllerRightOldState = trackedControllerRight.controllerState;

            if (VRDevices.loadedControllerSet > VRDevices.LoadedControllerSet.NoControllers)
            {
                ray = controllerRay;
            }
            else
            {
                ray = cameraRay;
            }
        }

        interacting = false;

        if (playerState == PlayerState.Watching)
        {
            if (Input.GetKeyDown(KeyCode.Space) && VRDevices.loadedSdk == VRDevices.LoadedSdk.None)
            {
                videoController.TogglePlay();
            }

            //Note(Simon): Interaction with points
            {
                var reversedRay = ray;
                //Note(Simon): Create a reversed raycast to find positions on the sphere with
                reversedRay.origin    = ray.GetPoint(100);
                reversedRay.direction = -ray.direction;

                RaycastHit hit;
                Physics.Raycast(reversedRay, out hit, 100, 1 << LayerMask.NameToLayer("interactionPoints"));

                var left  = controllerLeft.GetComponent <Controller>();
                var right = controllerRight.GetComponent <Controller>();

                float forwardAngle;
                //Note(lander): Turn the blips with the correct angle.
                {
                    if (left || right)
                    {
                        forwardAngle = right.compassAttached
                                                        ? right.transform.eulerAngles.y
                                                        : left.transform.eulerAngles.y;
                    }
                    else
                    {
                        forwardAngle = Seekbar.compass.transform.parent.localEulerAngles.y;
                    }
                }

                //NOTE(Kristof): The startpoints are removed in the for loop, so we need to loop in reverse
                for (var i = interactionPoints.Count - 1; i >= 0; i--)
                {
                    var point = interactionPoints[i];

                    var pointActive = point.startTime <= videoController.currentTime && point.endTime >= videoController.currentTime;
                    point.point.SetActive(pointActive);

                    var textMesh = point.point.GetComponentInChildren <TextMesh>();

                    // Note(Lander): highlight the untouched interaction points
                    if (!point.isStartPoint && pointActive && !point.isTouched)
                    {
                        if (textMesh != null)
                        {
                            textMesh.color = Color.black;
                        }

                        var blipAngle = point.point.transform.eulerAngles.y;

                        // TODO(Lander): Rely on a start position of a video instead
                        var angle = (XRSettings.enabled ? forwardAngle : 90) - blipAngle;

                        if (point.blip == null)
                        {
                            point.blip = Seekbar.CreateBlip(-angle, Instantiate(compassBlipPrefab));
                            remainingPoints++;
                        }
                        point.blip.transform.localEulerAngles = new Vector3(0, 0, angle);
                    }
                    else
                    {
                        if (textMesh != null)
                        {
                            textMesh.color = Color.white;
                        }
                        if (point.blip != null)
                        {
                            Destroy(point.blip);
                            point.blip = null;
                            remainingPoints--;
                        }
                    }

                    if (!point.isStartPoint && point.isTouched)
                    {
                        point.point.GetComponent <Renderer>().material.color = GRAY;
                    }
                    blipCounter.text = remainingPoints != 0
                                                ? remainingPoints.ToString()
                                                : "";

                    //NOTE(Lander): current point is hit with the raycast
                    if (hit.transform != null && hit.transform.gameObject == point.point)
                    {
                        //NOTE(Kristof): Interacting with controller
                        if (VRDevices.loadedControllerSet > VRDevices.LoadedControllerSet.NoControllers)
                        {
                            //NOTE(Kristof): The controllers only raycast on trigger down
                            //NOTE(Kristof): Interacting with StartPoints
                            if (point.isStartPoint)
                            {
                                videoController.videoState = VideoController.VideoState.Watching;
                                startPointGroup.SetActive(false);
                                Togglecanvasses();
                                interactionPoints.RemoveRange(0, 4);
                            }
                            //NOTE(Kristof): Interacting with InteractionPoints
                            else
                            {
                                point.panel.SetActive(!point.panel.activeSelf);

                                if (point.panel.activeSelf)
                                {
                                    activePoints++;
                                    point.isTouched = true;
                                }
                                else
                                {
                                    activePoints--;
                                }

                                videoController.Pause();

                                //NOTE(Kristof): Play the video when you deactivate the last point
                                if (activePoints == 0 && VideoController.autoResume)
                                {
                                    videoController.TogglePlay();
                                }
                            }
                        }
                        //NOTE(Kristof): Interacting without controllers
                        else
                        {
                            interacting = true;

                            if (timeToInteract < interactionTimer)
                            {
                                //NOTE(Kristof): Interacting with StartPoints
                                if (point.isStartPoint)
                                {
                                    videoController.videoState = VideoController.VideoState.Watching;
                                    startPointGroup.SetActive(false);
                                    Togglecanvasses();
                                }
                                //NOTE(Kristof): Interacting with InteractionPoints
                                else
                                {
                                    //NOTE(Kristof): Making a panel active
                                    if (!point.panel.activeSelf)
                                    {
                                        if (VRDevices.loadedSdk > VRDevices.LoadedSdk.None)
                                        {
                                            interactionTimer = -1;
                                            activePoints++;
                                        }
                                        else
                                        {
                                            //HACK(Kristof): Set to to double of timeToInteract to ensure no funky business happens (like disabling the panel right away)
                                            interactionTimer = timeToInteract * 2;
                                        }
                                        point.isTouched = true;
                                        point.panel.SetActive(true);
                                        videoController.Pause();
                                    }
                                    //NOTE(Kristof): Making a panel inactive
                                    //NOTE This only needs to be the done the same frame that the interactiontimer exceeds the timeToInteract, on this frame point.interactionTimer
                                    //NOTE will be between timeToInteract and timeToInteract + deltaTime
                                    //NOTE(Kristof): This condition will occasionally cause bugs (see HACK above)
                                    else if (timeToInteract < interactionTimer && interactionTimer < timeToInteract + Time.deltaTime)
                                    {
                                        point.panel.SetActive(false);
                                        activePoints--;

                                        interactionTimer = -1;

                                        if (activePoints == 0 && VideoController.autoResume)
                                        {
                                            videoController.TogglePlay();
                                        }
                                    }
                                }
                            }
                        }
                    }
                    //NOTE(Kristof): Gets executed for the point.panels that the user made active before but are not currently being interacted with (no hit)
                    else if (point.panel != null && point.panel.activeSelf)
                    {
                        //NOTE(Kristof): Disable all panels when using the Seekbar play button to resume play
                        if (videoController.playing)
                        {
                            point.panel.SetActive(false);
                            activePoints--;
                        }

                        //NOTE(Kristof): Video can resume if the user is not using VR
                        if (VRDevices.loadedSdk == VRDevices.LoadedSdk.None)
                        {
                            point.panel.SetActive(false);
                            videoController.TogglePlay();
                        }
                    }
                }
            }
        }

        if (playerState == PlayerState.Opening)
        {
            var panel = indexPanel.GetComponent <IndexPanel>();

            if (panel.answered)
            {
                var metaFilename = Path.Combine(Application.persistentDataPath, Path.Combine(panel.answerVideoId, SaveFile.metaFilename));
                if (OpenFile(metaFilename))
                {
                    Destroy(indexPanel);
                    playerState = PlayerState.Watching;
                    Canvass.modalBackground.SetActive(false);
                    if (VRDevices.loadedSdk > VRDevices.LoadedSdk.None)
                    {
                        EventManager.OnSpace();
                        videoPositions.Clear();
                    }
                }
                else
                {
                    Debug.Log("Couldn't open savefile");
                }
            }
        }

        //NOTE(Kristof): Interaction with UI
        {
            RaycastHit hit;
            Physics.Raycast(ray, out hit, 100, LayerMask.GetMask("UI", "WorldUI"));

            var controllerList = new List <Controller>
            {
                controllerLeft.GetComponent <Controller>(),
                controllerRight.GetComponent <Controller>()
            };

            //NOTE(Kristof): Looping over hittable UI scripts
            foreach (var hittable in hittables)
            {
                if (hittable == null)
                {
                    continue;
                }
                hittable.hitting  = false;
                hittable.hovering = false;

                //NOTE(Kristof): Checking for controller hover needs to happen independently of controller interactions
                foreach (var con in controllerList)
                {
                    if (con.uiHovering && con.hovered == hittable.gameObject)
                    {
                        hittable.hovering = true;
                    }
                }

                if (hit.transform != null && hit.transform.gameObject == hittable.gameObject)
                {
                    //NOTE(Kristof): Interacting with controller
                    if (VRDevices.loadedControllerSet > VRDevices.LoadedControllerSet.NoControllers)
                    {
                        hittable.hitting = true;
                    }
                    //NOTE(Kristof): Interacting without controllers
                    else
                    {
                        interacting       = true;
                        hittable.hovering = true;
                        if (interactionTimer >= timeToInteract)
                        {
                            interactionTimer = -1;
                            hittable.hitting = true;
                        }
                    }
                }
            }
        }

        //NOTE(Kristof): Interaction interactionTimer and Crosshair behaviour
        {
            if (interacting)
            {
                interactionTimer         += Time.deltaTime;
                crosshairTimer.fillAmount = interactionTimer / timeToInteract;
                crosshair.fillAmount      = 1 - (interactionTimer / timeToInteract);
            }
            else
            {
                interactionTimer          = 0;
                crosshairTimer.fillAmount = 0;
                crosshair.fillAmount      = 1;
            }
        }

        //NOTE(Kristof): Turning CameraRig
        {
            var controllers = new[]
            {
                controllerLeft.GetComponent <SteamVR_TrackedObject>(),
                controllerRight.GetComponent <SteamVR_TrackedObject>()
            };

            for (var index = 0; index < controllers.Length; index++)
            {
                var controller = controllers[index];
                if (controller.index > SteamVR_TrackedObject.EIndex.None)
                {
                    var device = SteamVR_Controller.Input((int)controller.index);

                    switch (VRDevices.loadedControllerSet)
                    {
                    case VRDevices.LoadedControllerSet.Oculus:
                    {
                        var touchpad = device.GetAxis();

                        if (-0.7f < touchpad.x && touchpad.x < 0.7f)
                        {
                            cameraRigMovable[index] = true;
                        }
                        else if (touchpad.x > 0.7f && cameraRigMovable[index])
                        {
                            cameraRig.transform.localEulerAngles += new Vector3(0, 30, 0);
                            cameraRigMovable[index] = false;
                        }
                        else if (touchpad.x < -0.7f && cameraRigMovable[index])
                        {
                            cameraRig.transform.localEulerAngles -= new Vector3(0, 30, 0);
                            cameraRigMovable[index] = false;
                        }

                        break;
                    }

                    case VRDevices.LoadedControllerSet.Vive:
                    {
                        var touchpad = device.GetAxis();
                        if (device.GetPressDown(SteamVR_Controller.ButtonMask.Touchpad))
                        {
                            if (touchpad.x > 0.7f && cameraRigMovable[index])
                            {
                                cameraRig.transform.localEulerAngles += new Vector3(0, 30, 0);
                                cameraRigMovable[index] = false;
                            }
                            else if (touchpad.x < -0.7f && cameraRigMovable[index])
                            {
                                cameraRig.transform.localEulerAngles -= new Vector3(0, 30, 0);
                                cameraRigMovable[index] = false;
                            }
                        }
                        else
                        {
                            cameraRigMovable[index] = true;
                        }

                        break;
                    }
                    }
                }
            }
        }
    }
Ejemplo n.º 3
0
    void Update()
    {
        //NOTE(Kristof): VR specific behaviour
        {
            if (XRSettings.enabled)
            {
                videoController.transform.position = Camera.main.transform.position;
                Canvass.seekbar.gameObject.SetActive(true);

                //NOTE(Kristof): Rotating the seekbar
                {
                    //NOTE(Kristof): Seekbar rotation is the same as the seekbar's angle on the circle
                    var seekbarAngle = Vector2.SignedAngle(new Vector2(Canvass.seekbar.transform.position.x, Canvass.seekbar.transform.position.z), Vector2.up);

                    var fov = Camera.main.fieldOfView;
                    //NOTE(Kristof): Camera rotation tells you to which angle on the circle the camera is looking towards
                    var cameraAngle = Camera.main.transform.eulerAngles.y;

                    //NOTE(Kristof): Calculate the absolute degree angle from the camera to the seekbar
                    var distanceLeft  = Mathf.Abs((cameraAngle - seekbarAngle + 360) % 360);
                    var distanceRight = Mathf.Abs((cameraAngle - seekbarAngle - 360) % 360);

                    var angle = Mathf.Min(distanceLeft, distanceRight);

                    if (isSeekbarOutOfView)
                    {
                        if (angle < 2.5f)
                        {
                            isSeekbarOutOfView = false;
                        }
                    }
                    else
                    {
                        if (angle > fov)
                        {
                            isSeekbarOutOfView = true;
                        }
                    }

                    if (isSeekbarOutOfView)
                    {
                        float newAngle = Mathf.LerpAngle(seekbarAngle, cameraAngle, 0.025f);

                        //NOTE(Kristof): Angle needs to be reversed, in Unity postive angles go clockwise while they go counterclockwise in the unit circle (cos and sin)
                        //NOTE(Kristof): We also need to add an offset of 90 degrees because in Unity 0 degrees is in front of you, in the unit circle it is (1,0) on the axis
                        float radianAngle = (-newAngle + 90) * Mathf.PI / 180;
                        float x           = 1.8f * Mathf.Cos(radianAngle);
                        float y           = Camera.main.transform.position.y - 2f;
                        float z           = 1.8f * Mathf.Sin(radianAngle);

                        Canvass.seekbar.transform.position    = new Vector3(x, y, z);
                        Canvass.seekbar.transform.eulerAngles = new Vector3(30, newAngle, 0);
                    }
                }
            }
            else
            {
                Canvass.seekbar.gameObject.SetActive(false);
            }
        }

        //NOTE(Simon): Zoom when not using HMD
        if (!XRSettings.enabled)
        {
            if (Input.mouseScrollDelta.y != 0)
            {
                Camera.main.fieldOfView = Mathf.Clamp(Camera.main.fieldOfView - Input.mouseScrollDelta.y * 5, 20, 120);
            }
        }

        Ray interactionpointRay = new Ray();

        //NOTE(Kristof): Deciding on which object the Ray will be based on
        //TODO(Simon): Prefers right over left controller
        {
            if (trackedControllerLeft != null && trackedControllerLeft.triggerPressed)
            {
                interactionpointRay = trackedControllerLeft.CastRay();
            }

            if (trackedControllerRight != null && trackedControllerRight.triggerPressed)
            {
                interactionpointRay = trackedControllerRight.CastRay();
            }

            if (VRDevices.loadedControllerSet == VRDevices.LoadedControllerSet.NoControllers && Input.GetMouseButtonUp(0))
            {
                interactionpointRay = Camera.main.ScreenPointToRay(Input.mousePosition);
            }
        }

        if (playerState == PlayerState.Watching)
        {
            if (Input.GetKeyDown(KeyCode.Space) && VRDevices.loadedSdk == VRDevices.LoadedSdk.None)
            {
                videoController.TogglePlay();
            }

            Seekbar.instance.RenderBlips(interactionPoints, trackedControllerLeft, trackedControllerRight);

            //Note(Simon): Interaction with points
            {
                var reversedRay = interactionpointRay.ReverseRay();
                //Note(Simon): Create a reversed raycast to find positions on the sphere with

                Physics.Raycast(reversedRay, out var hit, 100, 1 << LayerMask.NameToLayer("interactionPoints"));

                //NOTE(Simon): Update visible interactionpoints
                for (int i = 0; i < interactionPoints.Count; i++)
                {
                    bool pointActive = videoController.currentTime >= interactionPoints[i].startTime &&
                                       videoController.currentTime <= interactionPoints[i].endTime;
                    interactionPoints[i].point.SetActive(pointActive);
                    interactionPoints[i].point.GetComponentInChildren <MeshRenderer>(includeInactive: true).gameObject.SetActive(!interactionPoints[i].isSeen);
                }

                //NOTE(Simon): Interact with inactive interactionpoints
                if (activeInteractionPoint == null && hit.transform != null)
                {
                    var pointGO = hit.transform.gameObject;
                    InteractionPointPlayer point = null;

                    for (int i = 0; i < interactionPoints.Count; i++)
                    {
                        if (pointGO == interactionPoints[i].point)
                        {
                            point = interactionPoints[i];
                            break;
                        }
                    }

                    ActivateInteractionPoint(point);
                }

                //NOTE(Simon): Disable active interactionPoint if playback was started through seekbar
                if (videoController.playing && activeInteractionPoint != null)
                {
                    DeactivateActiveInteractionPoint();
                }
            }

            //NOTE(Simon): Handle mandatory interactionPoints
            {
                double timeToNextPause       = Double.MaxValue;
                var    interactionsInChapter = MandatoryInteractionsForTime(videoController.currentTime);

                //NOTE(Simon): Find the next unseen mandatory interaction in this chapter
                for (int i = 0; i < interactionsInChapter.Count; i++)
                {
                    if (!interactionsInChapter[i].isSeen && interactionsInChapter[i].endTime > videoController.currentTime)
                    {
                        timeToNextPause = interactionsInChapter[i].endTime - videoController.currentTime;
                        break;
                    }
                }

                //NOTE(Simon): Set the playbackspeed. Speed will get lower the closer to the next pause we are.
                if (timeToNextPause < mandatoryPauseFadeTime)
                {
                    float speed = (float)(timeToNextPause / mandatoryPauseFadeTime);
                    if (timeToNextPause < .05f)
                    {
                        speed = 0;
                    }

                    videoController.SetPlaybackSpeed(speed);
                    if (!mandatoryPauseActive)
                    {
                        ShowMandatoryInteractionMessage();
                        mandatoryPauseActive = true;
                    }
                }
                else
                {
                    if (mandatoryPauseActive)
                    {
                        mandatoryPauseActive = false;
                        HideMandatoryInteractionMessage();
                    }

                    videoController.SetPlaybackSpeed(1f);
                }
            }
        }

        if (playerState == PlayerState.Opening)
        {
            var panel = indexPanel.GetComponent <IndexPanel>();

            if (panel.answered)
            {
                var projectPath = Path.Combine(Application.persistentDataPath, panel.answerVideoId);
                if (OpenFile(projectPath))
                {
                    Destroy(indexPanel);
                    playerState = PlayerState.Watching;
                    Canvass.modalBackground.SetActive(false);
                    SetCanvasesActive(true);
                    chapterSelector = Instantiate(chapterSelectorPrefab, Canvass.main.transform, false).GetComponent <ChapterSelectorPanel>();

                    if (VRDevices.loadedSdk > VRDevices.LoadedSdk.None)
                    {
                        StartCoroutine(FadevideoCanvasOut(videoCanvas));
                        EventManager.OnSpace();
                        videoPositions.Clear();
                        Seekbar.ReattachCompass();
                    }
                }
                else
                {
                    Debug.LogError("Couldn't open savefile");
                }
            }
        }

        //NOTE(Simon): Interaction with Hittables
        {
            Physics.Raycast(interactionpointRay, out var hit, 100, LayerMask.GetMask("UI", "WorldUI"));

            var controllerList = new List <Controller>
            {
                trackedControllerLeft,
                trackedControllerRight
            };

            //NOTE(Simon): Reset all hittables
            foreach (var hittable in hittables)
            {
                if (hittable == null)
                {
                    continue;
                }

                //NOTE(Jitse): Check if a hittable is being held down
                if (!(controllerList[0].triggerDown || controllerList[1].triggerDown))
                {
                    hittable.hitting = false;
                }

                hittable.hovering = false;
            }

            //NOTE(Simon): Set hover state when hovered by controllers
            foreach (var con in controllerList)
            {
                if (con.uiHovering && con.hoveredGo != null)
                {
                    var hittable = con.hoveredGo.GetComponent <Hittable>();
                    if (hittable != null)
                    {
                        hittable.hovering = true;
                    }
                }
            }

            //NOTE(Simon): Set hitting and hovering in hittables
            if (hit.transform != null)
            {
                hit.transform.GetComponent <Hittable>().hitting = true;
            }
        }
    }