// // VROInputPresenter.h // ViroRenderer // // Copyright © 2017 Viro Media. All rights reserved. // #ifndef VROInputPresenter_H #define VROInputPresenter_H #include #include #include "VROEventDelegate.h" #include "VRORenderContext.h" #include "VROReticle.h" #include "VRONode.h" #include "VROMath.h" #include "VROInputType.h" #include "VROThreadRestricted.h" static const float kReticleSizeMultiple = 3; static const bool kDebugSceneBackgroundDistance = false; /* VROInputPresenter contains all UI view implementations to be displayed for a given VROInputController. */ class VROInputPresenter : public VROEventDelegate, public VROThreadRestricted { public: VROInputPresenter() : VROThreadRestricted(VROThreadName::Renderer) { _reticle = nullptr; _rootNode = std::make_shared(); } ~VROInputPresenter() {} std::shared_ptr getRootNode(){ return _rootNode; } void setEventDelegate(std::shared_ptr delegate){ _eventDelegateWeak = delegate; } virtual void onHover(int source, std::shared_ptr node, bool isHovering, std::vector position) { passert_thread(); std::shared_ptr delegate = getDelegate(); if (delegate != nullptr && delegate->isEventEnabled(VROEventDelegate::EventAction::OnHover)){ delegate->onHover(source, node, isHovering, position); } } virtual void onClick(int source, std::shared_ptr node, ClickState clickState, std::vector position) { passert_thread(); std::shared_ptr delegate = getDelegate(); if (delegate != nullptr && delegate->isEventEnabled(VROEventDelegate::EventAction::OnClick)){ delegate->onClick(source, node, clickState, position); } } virtual void onTouch(int source, std::shared_ptr node, TouchState state, float x, float y){ passert_thread(); std::shared_ptr delegate = getDelegate(); if (delegate != nullptr && delegate->isEventEnabled(VROEventDelegate::EventAction::OnTouch)){ delegate->onTouch(source, node, state, x, y); } } virtual void onMove(int source, std::shared_ptr node, VROVector3f rotation, VROVector3f position, VROVector3f forwardVec) { passert_thread(); _lastKnownForward.store(forwardVec); std::shared_ptr delegate = getDelegate(); if (delegate != nullptr && delegate->isEventEnabled(VROEventDelegate::EventAction::OnMove)){ delegate->onMove(source, node, rotation, position, forwardVec); } } virtual void onControllerStatus(int source, ControllerStatus status) { passert_thread(); std::shared_ptr delegate = getDelegate(); if (delegate != nullptr && delegate->isEventEnabled(VROEventDelegate::EventAction::OnControllerStatus)){ delegate->onControllerStatus(source, status); } } virtual void onSwipe(int source, std::shared_ptr node, SwipeState swipeState) { passert_thread(); std::shared_ptr delegate = getDelegate(); if (delegate != nullptr && delegate->isEventEnabled(VROEventDelegate::EventAction::OnSwipe)){ delegate->onSwipe(source, node, swipeState); } } virtual void onScroll(int source, std::shared_ptr node, float x, float y) { passert_thread(); std::shared_ptr delegate = getDelegate(); if (delegate != nullptr && delegate->isEventEnabled(VROEventDelegate::EventAction::OnScroll)){ delegate->onScroll(source, node, x, y); } } virtual void onGazeHit(int source, std::shared_ptr node, const VROHitTestResult &hit) { //No-op } virtual void onDrag(int source, std::shared_ptr node, VROVector3f newPosition) { passert_thread(); std::shared_ptr delegate = getDelegate(); if (delegate != nullptr && delegate->isEventEnabled(VROEventDelegate::EventAction::OnDrag)){ delegate->onDrag(source, node, newPosition); } } virtual void onFuse(int source, std::shared_ptr node, float timeToFuseRatio) { passert_thread(); if (_reticle == nullptr) { return; } std::shared_ptr delegate = getDelegate(); if (delegate != nullptr && delegate->isEventEnabled(VROEventDelegate::EventAction::OnFuse)) { delegate->onFuse(source, node, timeToFuseRatio); } // TimeToFuseRatio is (time that has passed since fuse began) / (total time to fuse). // When the timeToFuseRatio reaches 1, it is an indication that the node has been "onFused". if (timeToFuseRatio == kOnFuseReset) { _reticle->stopFuseAnimation(); } else { _reticle->animateFuse(1 - timeToFuseRatio); } } std::shared_ptr getReticle() { return _reticle; } void setReticle(std::shared_ptr reticle){ _reticle = reticle; _reticleInitialPositionSet = false; } VROVector3f getLastKnownForward(){ return _lastKnownForward.load(); } void updateLastKnownForward(VROVector3f forward) { _lastKnownForward.store(forward); } protected: std::shared_ptr _rootNode; void onReticleGazeHit(const VROHitTestResult &hit) { passert_thread(); if (_reticle == nullptr) { return; } float depth = -hit.getDistance(); if (!_reticle->isHeadlocked()) { _reticle->setPosition(hit.getLocation()); float worldPerScreen = hit.getCamera().getWorldPerScreen(depth); float radius = fabs(worldPerScreen) * kReticleSizeMultiple; _reticle->setRadius(radius); } else { // Lock the Reticle's position to the center of the screen // for fixed pointer mode (usually Cardboard). The reticle is // rendered as HUD object, with view matrix identity (e.g. it // always follows the headset) // Set the 'depth' of the reticle to the object it is hovering // over, then set the radius to compensate for that distance so // that the reticle stays the same size. The depth effectively // determines the difference in reticle position between the two // eyes. // Only use the background depth if this is our first time // positioning the reticle. Otherwise we maintain the current // reticle depth, to avoid reticle 'popping' that occurs when // the user moves from an actual focused object to the background. // The background has no 'actual' depth so this is ok. if (!_reticleInitialPositionSet || !hit.isBackgroundHit() || kDebugSceneBackgroundDistance) { _reticle->setPosition(VROVector3f(0, 0, depth)); _reticleInitialPositionSet = true; float worldPerScreen = hit.getCamera().getWorldPerScreen(depth); float radius = fabs(worldPerScreen) * kReticleSizeMultiple; _reticle->setRadius(radius); } } } private: std::weak_ptr _eventDelegateWeak; std::shared_ptr _reticle; bool _reticleInitialPositionSet; std::atomic _lastKnownForward; /* Event delegate for triggering calls back to Controller_JNI. */ std::shared_ptr getDelegate(){ if (_eventDelegateWeak.expired()){ return nullptr; } return _eventDelegateWeak.lock(); } }; #endif