227 lines
7.9 KiB
C++
227 lines
7.9 KiB
C++
//
|
|
// VROInputPresenter.h
|
|
// ViroRenderer
|
|
//
|
|
// Copyright © 2017 Viro Media. All rights reserved.
|
|
//
|
|
|
|
#ifndef VROInputPresenter_H
|
|
#define VROInputPresenter_H
|
|
|
|
#include <memory>
|
|
#include <atomic>
|
|
#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<VRONode>();
|
|
}
|
|
|
|
~VROInputPresenter() {}
|
|
|
|
std::shared_ptr<VRONode> getRootNode(){
|
|
return _rootNode;
|
|
}
|
|
|
|
void setEventDelegate(std::shared_ptr<VROEventDelegate> delegate){
|
|
_eventDelegateWeak = delegate;
|
|
}
|
|
|
|
virtual void onHover(int source, std::shared_ptr<VRONode> node, bool isHovering, std::vector<float> position) {
|
|
passert_thread();
|
|
|
|
std::shared_ptr<VROEventDelegate> delegate = getDelegate();
|
|
if (delegate != nullptr && delegate->isEventEnabled(VROEventDelegate::EventAction::OnHover)){
|
|
delegate->onHover(source, node, isHovering, position);
|
|
}
|
|
}
|
|
|
|
virtual void onClick(int source, std::shared_ptr<VRONode> node, ClickState clickState, std::vector<float> position) {
|
|
passert_thread();
|
|
|
|
std::shared_ptr<VROEventDelegate> delegate = getDelegate();
|
|
if (delegate != nullptr && delegate->isEventEnabled(VROEventDelegate::EventAction::OnClick)){
|
|
delegate->onClick(source, node, clickState, position);
|
|
}
|
|
}
|
|
|
|
virtual void onTouch(int source, std::shared_ptr<VRONode> node, TouchState state, float x, float y){
|
|
passert_thread();
|
|
|
|
std::shared_ptr<VROEventDelegate> 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<VRONode> node,
|
|
VROVector3f rotation, VROVector3f position, VROVector3f forwardVec) {
|
|
passert_thread();
|
|
_lastKnownForward.store(forwardVec);
|
|
std::shared_ptr<VROEventDelegate> 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<VROEventDelegate> delegate = getDelegate();
|
|
if (delegate != nullptr && delegate->isEventEnabled(VROEventDelegate::EventAction::OnControllerStatus)){
|
|
delegate->onControllerStatus(source, status);
|
|
}
|
|
}
|
|
|
|
virtual void onSwipe(int source, std::shared_ptr<VRONode> node, SwipeState swipeState) {
|
|
passert_thread();
|
|
|
|
std::shared_ptr<VROEventDelegate> delegate = getDelegate();
|
|
if (delegate != nullptr && delegate->isEventEnabled(VROEventDelegate::EventAction::OnSwipe)){
|
|
delegate->onSwipe(source, node, swipeState);
|
|
}
|
|
}
|
|
|
|
virtual void onScroll(int source, std::shared_ptr<VRONode> node, float x, float y) {
|
|
passert_thread();
|
|
|
|
std::shared_ptr<VROEventDelegate> delegate = getDelegate();
|
|
if (delegate != nullptr && delegate->isEventEnabled(VROEventDelegate::EventAction::OnScroll)){
|
|
delegate->onScroll(source, node, x, y);
|
|
}
|
|
}
|
|
|
|
virtual void onGazeHit(int source, std::shared_ptr<VRONode> node, const VROHitTestResult &hit) {
|
|
//No-op
|
|
}
|
|
|
|
virtual void onDrag(int source, std::shared_ptr<VRONode> node, VROVector3f newPosition) {
|
|
passert_thread();
|
|
|
|
std::shared_ptr<VROEventDelegate> delegate = getDelegate();
|
|
if (delegate != nullptr && delegate->isEventEnabled(VROEventDelegate::EventAction::OnDrag)){
|
|
delegate->onDrag(source, node, newPosition);
|
|
}
|
|
}
|
|
|
|
virtual void onFuse(int source, std::shared_ptr<VRONode> node, float timeToFuseRatio) {
|
|
passert_thread();
|
|
if (_reticle == nullptr) {
|
|
return;
|
|
}
|
|
|
|
std::shared_ptr<VROEventDelegate> 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<VROReticle> getReticle() {
|
|
return _reticle;
|
|
}
|
|
|
|
void setReticle(std::shared_ptr<VROReticle> reticle){
|
|
_reticle = reticle;
|
|
_reticleInitialPositionSet = false;
|
|
}
|
|
|
|
VROVector3f getLastKnownForward(){
|
|
return _lastKnownForward.load();
|
|
}
|
|
|
|
void updateLastKnownForward(VROVector3f forward) {
|
|
_lastKnownForward.store(forward);
|
|
}
|
|
|
|
protected:
|
|
|
|
std::shared_ptr<VRONode> _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<VROEventDelegate> _eventDelegateWeak;
|
|
|
|
std::shared_ptr<VROReticle> _reticle;
|
|
bool _reticleInitialPositionSet;
|
|
std::atomic<VROVector3f> _lastKnownForward;
|
|
|
|
/*
|
|
Event delegate for triggering calls back to Controller_JNI.
|
|
*/
|
|
std::shared_ptr<VROEventDelegate> getDelegate(){
|
|
if (_eventDelegateWeak.expired()){
|
|
return nullptr;
|
|
}
|
|
return _eventDelegateWeak.lock();
|
|
}
|
|
|
|
};
|
|
#endif
|