Merge branch 'master' of github.com:highfidelity/hifi into raypick-avatars

This commit is contained in:
Seth Alves 2016-07-11 18:02:18 -07:00
commit 85cc5f8956
21 changed files with 574 additions and 303 deletions

View file

@ -389,8 +389,6 @@ void DomainServer::setupNodeListAndAssignments() {
const QVariant* idValueVariant = valueForKeyPath(settingsMap, METAVERSE_DOMAIN_ID_KEY_PATH);
if (idValueVariant) {
nodeList->setSessionUUID(idValueVariant->toString());
} else {
nodeList->setSessionUUID(QUuid::createUuid()); // Use random UUID
}
connect(nodeList.data(), &LimitedNodeList::nodeAdded, this, &DomainServer::nodeAdded);

View file

@ -0,0 +1,23 @@
{
"name": "Touchscreen to Actions",
"channels": [
{ "from": "Touchscreen.GesturePinchOut", "to": "Actions.BoomOut", "filters": [ { "type": "scale", "scale": 0.02 } ] },
{ "from": "Touchscreen.GesturePinchIn", "to": "Actions.BoomIn", "filters": [ { "type": "scale", "scale": 0.02 } ] },
{ "from": { "makeAxis" : [
[ "Touchscreen.DragLeft" ],
[ "Touchscreen.DragRight" ]
]
},
"to": "Actions.Yaw", "filters": [ { "type": "scale", "scale": 0.12 } ]
},
{ "from": { "makeAxis" : [
[ "Touchscreen.DragUp" ],
[ "Touchscreen.DragDown" ]
]
},
"to": "Actions.Pitch", "filters": [ { "type": "scale", "scale": 0.04 } ]
}
]
}

0
interface/resources/qml/controls-uit/ComboBox.qml Executable file → Normal file
View file

0
interface/resources/qml/controls-uit/SpinBox.qml Executable file → Normal file
View file

View file

View file

View file

@ -957,8 +957,12 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) :
return DependencyManager::get<OffscreenUi>()->navigationFocused() ? 1 : 0;
});
// Setup the keyboardMouseDevice and the user input mapper with the default bindings
// Setup the _keyboardMouseDevice, _touchscreenDevice and the user input mapper with the default bindings
userInputMapper->registerDevice(_keyboardMouseDevice->getInputDevice());
// if the _touchscreenDevice is not supported it will not be registered
if (_touchscreenDevice) {
userInputMapper->registerDevice(_touchscreenDevice->getInputDevice());
}
// force the model the look at the correct directory (weird order of operations issue)
scriptEngines->setScriptsLocation(scriptEngines->getScriptsLocation());
@ -1600,6 +1604,9 @@ void Application::initializeUi() {
if (KeyboardMouseDevice::NAME == inputPlugin->getName()) {
_keyboardMouseDevice = std::dynamic_pointer_cast<KeyboardMouseDevice>(inputPlugin);
}
if (TouchscreenDevice::NAME == inputPlugin->getName()) {
_touchscreenDevice = std::dynamic_pointer_cast<TouchscreenDevice>(inputPlugin);
}
}
_window->setMenuBar(new Menu());
@ -2096,6 +2103,9 @@ bool Application::event(QEvent* event) {
case QEvent::TouchUpdate:
touchUpdateEvent(static_cast<QTouchEvent*>(event));
return true;
case QEvent::Gesture:
touchGestureEvent((QGestureEvent*)event);
return true;
case QEvent::Wheel:
wheelEvent(static_cast<QWheelEvent*>(event));
return true;
@ -2727,6 +2737,9 @@ void Application::touchUpdateEvent(QTouchEvent* event) {
if (_keyboardMouseDevice->isActive()) {
_keyboardMouseDevice->touchUpdateEvent(event);
}
if (_touchscreenDevice->isActive()) {
_touchscreenDevice->touchUpdateEvent(event);
}
}
void Application::touchBeginEvent(QTouchEvent* event) {
@ -2745,6 +2758,9 @@ void Application::touchBeginEvent(QTouchEvent* event) {
if (_keyboardMouseDevice->isActive()) {
_keyboardMouseDevice->touchBeginEvent(event);
}
if (_touchscreenDevice->isActive()) {
_touchscreenDevice->touchBeginEvent(event);
}
}
@ -2762,10 +2778,19 @@ void Application::touchEndEvent(QTouchEvent* event) {
if (_keyboardMouseDevice->isActive()) {
_keyboardMouseDevice->touchEndEvent(event);
}
if (_touchscreenDevice->isActive()) {
_touchscreenDevice->touchEndEvent(event);
}
// put any application specific touch behavior below here..
}
void Application::touchGestureEvent(QGestureEvent* event) {
if (_touchscreenDevice->isActive()) {
_touchscreenDevice->touchGestureEvent(event);
}
}
void Application::wheelEvent(QWheelEvent* event) const {
_altPressed = false;
_controllerScriptingInterface->emitWheelEvent(event); // send events to any registered scripts

View file

@ -29,6 +29,7 @@
#include <EntityEditPacketSender.h>
#include <EntityTreeRenderer.h>
#include <input-plugins/KeyboardMouseDevice.h>
#include <input-plugins/TouchscreenDevice.h>
#include <OctreeQuery.h>
#include <PhysicalEntitySimulation.h>
#include <PhysicsEngine.h>
@ -403,6 +404,7 @@ private:
void touchBeginEvent(QTouchEvent* event);
void touchEndEvent(QTouchEvent* event);
void touchUpdateEvent(QTouchEvent* event);
void touchGestureEvent(QGestureEvent* event);
void wheelEvent(QWheelEvent* event) const;
void dropEvent(QDropEvent* event);
@ -455,6 +457,7 @@ private:
std::shared_ptr<controller::StateController> _applicationStateDevice; // Default ApplicationDevice reflecting the state of different properties of the session
std::shared_ptr<KeyboardMouseDevice> _keyboardMouseDevice; // Default input device, the good old keyboard mouse and maybe touchpad
std::shared_ptr<TouchscreenDevice> _touchscreenDevice; // the good old touchscreen
SimpleMovingAverage _avatarSimsPerSecond {10};
int _avatarSimsPerSecondReport {0};
quint64 _lastAvatarSimsPerSecondUpdate {0};

View file

@ -43,7 +43,6 @@ ModelOverlay::ModelOverlay(const ModelOverlay* modelOverlay) :
void ModelOverlay::update(float deltatime) {
if (_updateModel) {
_updateModel = false;
_model->setSnapModelToCenter(true);
_model->setScaleToFit(true, getDimensions());
_model->setRotation(getRotation());
@ -87,7 +86,6 @@ void ModelOverlay::render(RenderArgs* args) {
void ModelOverlay::setProperties(const QVariantMap& properties) {
auto position = getPosition();
auto rotation = getRotation();
auto scale = getDimensions();
Volume3DOverlay::setProperties(properties);
@ -95,14 +93,7 @@ void ModelOverlay::setProperties(const QVariantMap& properties) {
_updateModel = true;
}
if (scale != getDimensions()) {
auto newScale = getDimensions();
if (newScale.x <= 0 || newScale.y <= 0 || newScale.z <= 0) {
setDimensions(scale);
} else {
_updateModel = true;
}
}
auto urlValue = properties["url"];
if (urlValue.isValid() && urlValue.canConvert<QString>()) {

View file

@ -43,6 +43,7 @@ int GLWidget::getDeviceHeight() const {
void GLWidget::initializeGL() {
setAttribute(Qt::WA_AcceptTouchEvents);
grabGesture(Qt::PinchGesture);
setAcceptDrops(true);
// Note, we *DO NOT* want Qt to automatically swap buffers for us. This results in the "ringing" bug mentioned in WL#19514 when we're throttling the framerate.
setAutoBufferSwap(false);
@ -81,6 +82,7 @@ bool GLWidget::event(QEvent* event) {
case QEvent::TouchBegin:
case QEvent::TouchEnd:
case QEvent::TouchUpdate:
case QEvent::Gesture:
case QEvent::Wheel:
case QEvent::DragEnter:
case QEvent::Drop:

View file

@ -13,11 +13,13 @@
#include <plugins/PluginManager.h>
#include "KeyboardMouseDevice.h"
#include "TouchscreenDevice.h"
// TODO migrate to a DLL model where plugins are discovered and loaded at runtime by the PluginManager class
InputPluginList getInputPlugins() {
InputPlugin* PLUGIN_POOL[] = {
new KeyboardMouseDevice(),
new TouchscreenDevice(),
nullptr
};

View file

@ -19,6 +19,7 @@
#include <NumericalConstants.h>
const QString KeyboardMouseDevice::NAME = "Keyboard/Mouse";
bool KeyboardMouseDevice::_enableTouch = true;
void KeyboardMouseDevice::pluginUpdate(float deltaTime, const controller::InputCalibrationData& inputCalibrationData) {
auto userInputMapper = DependencyManager::get<controller::UserInputMapper>();
@ -79,7 +80,7 @@ void KeyboardMouseDevice::mouseReleaseEvent(QMouseEvent* event) {
// if we pressed and released at the same location within a small time window, then create a "_CLICKED"
// input for this button we might want to add some small tolerance to this so if you do a small drag it
// till counts as a clicked.
// still counts as a click.
static const int CLICK_TIME = USECS_PER_MSEC * 500; // 500 ms to click
if (!_mouseMoved && (usecTimestampNow() - _mousePressTime < CLICK_TIME)) {
_inputDevice->_buttonPressedMap.insert(_inputDevice->makeInput((Qt::MouseButton) event->button(), true).getChannel());
@ -132,18 +133,23 @@ glm::vec2 evalAverageTouchPoints(const QList<QTouchEvent::TouchPoint>& points) {
}
void KeyboardMouseDevice::touchBeginEvent(const QTouchEvent* event) {
if (_enableTouch) {
_isTouching = event->touchPointStates().testFlag(Qt::TouchPointPressed);
_lastTouch = evalAverageTouchPoints(event->touchPoints());
_lastTouchTime = _clock.now();
}
}
void KeyboardMouseDevice::touchEndEvent(const QTouchEvent* event) {
if (_enableTouch) {
_isTouching = false;
_lastTouch = evalAverageTouchPoints(event->touchPoints());
_lastTouchTime = _clock.now();
}
}
void KeyboardMouseDevice::touchUpdateEvent(const QTouchEvent* event) {
if (_enableTouch) {
auto currentPos = evalAverageTouchPoints(event->touchPoints());
_lastTouchTime = _clock.now();
@ -161,6 +167,7 @@ void KeyboardMouseDevice::touchUpdateEvent(const QTouchEvent* event) {
_lastTouch = currentPos;
}
}
controller::Input KeyboardMouseDevice::InputDevice::makeInput(Qt::Key code) const {
auto shortCode = (uint16_t)(code & KEYBOARD_MASK);
@ -247,4 +254,3 @@ QString KeyboardMouseDevice::InputDevice::getDefaultMappingConfig() const {
static const QString MAPPING_JSON = PathUtils::resourcesPath() + "/controllers/keyboardMouse.json";
return MAPPING_JSON;
}

View file

@ -85,6 +85,8 @@ public:
void wheelEvent(QWheelEvent* event);
static void enableTouch(bool enableTouch) { _enableTouch = enableTouch; }
static const QString NAME;
protected:
@ -122,6 +124,8 @@ protected:
bool _isTouching = false;
std::chrono::high_resolution_clock _clock;
std::chrono::high_resolution_clock::time_point _lastTouchTime;
static bool _enableTouch;
};
#endif // hifi_KeyboardMouseDevice_h

View file

@ -0,0 +1,132 @@
//
// TouchscreenDevice.cpp
// input-plugins/src/input-plugins
//
// Created by Triplelexx on 01/31/16.
// Copyright 2016 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "TouchscreenDevice.h"
#include "KeyboardMouseDevice.h"
#include <QtGui/QTouchEvent>
#include <QGestureEvent>
#include <QGuiApplication>
#include <QWindow>
#include <QScreen>
#include <controllers/UserInputMapper.h>
#include <PathUtils.h>
#include <NumericalConstants.h>
const QString TouchscreenDevice::NAME = "Touchscreen";
bool TouchscreenDevice::isSupported() const {
for (auto touchDevice : QTouchDevice::devices()) {
if (touchDevice->type() == QTouchDevice::TouchScreen) {
return true;
}
}
return false;
}
void TouchscreenDevice::pluginUpdate(float deltaTime, const controller::InputCalibrationData& inputCalibrationData) {
auto userInputMapper = DependencyManager::get<controller::UserInputMapper>();
userInputMapper->withLock([&, this]() {
_inputDevice->update(deltaTime, inputCalibrationData);
});
float distanceScaleX, distanceScaleY;
if (_touchPointCount == 1) {
if (_firstTouchVec.x < _currentTouchVec.x) {
distanceScaleX = (_currentTouchVec.x - _firstTouchVec.x) / _screenDPIScale.x;
_inputDevice->_axisStateMap[_inputDevice->makeInput(TOUCH_AXIS_X_POS).getChannel()] = distanceScaleX;
} else if (_firstTouchVec.x > _currentTouchVec.x) {
distanceScaleX = (_firstTouchVec.x - _currentTouchVec.x) / _screenDPIScale.x;
_inputDevice->_axisStateMap[_inputDevice->makeInput(TOUCH_AXIS_X_NEG).getChannel()] = distanceScaleX;
}
// Y axis is inverted, positive is pointing up the screen
if (_firstTouchVec.y > _currentTouchVec.y) {
distanceScaleY = (_firstTouchVec.y - _currentTouchVec.y) / _screenDPIScale.y;
_inputDevice->_axisStateMap[_inputDevice->makeInput(TOUCH_AXIS_Y_POS).getChannel()] = distanceScaleY;
} else if (_firstTouchVec.y < _currentTouchVec.y) {
distanceScaleY = (_currentTouchVec.y - _firstTouchVec.y) / _screenDPIScale.y;
_inputDevice->_axisStateMap[_inputDevice->makeInput(TOUCH_AXIS_Y_NEG).getChannel()] = distanceScaleY;
}
} else if (_touchPointCount == 2) {
if (_pinchScale > _lastPinchScale && _pinchScale != 0) {
_inputDevice->_axisStateMap[_inputDevice->makeInput(TOUCH_GESTURE_PINCH_POS).getChannel()] = 1.0f;
} else if (_pinchScale != 0) {
_inputDevice->_axisStateMap[_inputDevice->makeInput(TOUCH_GESTURE_PINCH_NEG).getChannel()] = 1.0f;
}
_lastPinchScale = _pinchScale;
}
}
void TouchscreenDevice::InputDevice::update(float deltaTime, const controller::InputCalibrationData& inputCalibrationData) {
_axisStateMap.clear();
}
void TouchscreenDevice::InputDevice::focusOutEvent() {
}
void TouchscreenDevice::touchBeginEvent(const QTouchEvent* event) {
const QTouchEvent::TouchPoint& point = event->touchPoints().at(0);
_firstTouchVec = glm::vec2(point.pos().x(), point.pos().y());
KeyboardMouseDevice::enableTouch(false);
QScreen* eventScreen = event->window()->screen();
if (_screenDPI != eventScreen->physicalDotsPerInch()) {
_screenDPIScale.x = (float)eventScreen->physicalDotsPerInchX();
_screenDPIScale.y = (float)eventScreen->physicalDotsPerInchY();
_screenDPI = eventScreen->physicalDotsPerInch();
}
}
void TouchscreenDevice::touchEndEvent(const QTouchEvent* event) {
_touchPointCount = 0;
KeyboardMouseDevice::enableTouch(true);
}
void TouchscreenDevice::touchUpdateEvent(const QTouchEvent* event) {
const QTouchEvent::TouchPoint& point = event->touchPoints().at(0);
_currentTouchVec = glm::vec2(point.pos().x(), point.pos().y());
_touchPointCount = event->touchPoints().count();
}
void TouchscreenDevice::touchGestureEvent(const QGestureEvent* event) {
if (QGesture* gesture = event->gesture(Qt::PinchGesture)) {
QPinchGesture* pinch = static_cast<QPinchGesture*>(gesture);
_pinchScale = pinch->totalScaleFactor();
}
}
controller::Input TouchscreenDevice::InputDevice::makeInput(TouchscreenDevice::TouchAxisChannel axis) const {
return controller::Input(_deviceID, axis, controller::ChannelType::AXIS);
}
controller::Input TouchscreenDevice::InputDevice::makeInput(TouchscreenDevice::TouchGestureAxisChannel gesture) const {
return controller::Input(_deviceID, gesture, controller::ChannelType::AXIS);
}
controller::Input::NamedVector TouchscreenDevice::InputDevice::getAvailableInputs() const {
using namespace controller;
static QVector<Input::NamedPair> availableInputs;
static std::once_flag once;
std::call_once(once, [&] {
availableInputs.append(Input::NamedPair(makeInput(TOUCH_AXIS_X_POS), "DragRight"));
availableInputs.append(Input::NamedPair(makeInput(TOUCH_AXIS_X_NEG), "DragLeft"));
availableInputs.append(Input::NamedPair(makeInput(TOUCH_AXIS_Y_POS), "DragUp"));
availableInputs.append(Input::NamedPair(makeInput(TOUCH_AXIS_Y_NEG), "DragDown"));
availableInputs.append(Input::NamedPair(makeInput(TOUCH_GESTURE_PINCH_POS), "GesturePinchOut"));
availableInputs.append(Input::NamedPair(makeInput(TOUCH_GESTURE_PINCH_NEG), "GesturePinchIn"));
});
return availableInputs;
}
QString TouchscreenDevice::InputDevice::getDefaultMappingConfig() const {
static const QString MAPPING_JSON = PathUtils::resourcesPath() + "/controllers/touchscreen.json";
return MAPPING_JSON;
}

View file

@ -0,0 +1,84 @@
//
// TouchscreenDevice.h
// input-plugins/src/input-plugins
//
// Created by Triplelexx on 1/31/16.
// Copyright 2016 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#ifndef hifi_TouchscreenDevice_h
#define hifi_TouchscreenDevice_h
#include <controllers/InputDevice.h>
#include "InputPlugin.h"
#include <QtGui/qtouchdevice.h>
class QTouchEvent;
class QGestureEvent;
class TouchscreenDevice : public InputPlugin {
Q_OBJECT
public:
enum TouchAxisChannel {
TOUCH_AXIS_X_POS = 0,
TOUCH_AXIS_X_NEG,
TOUCH_AXIS_Y_POS,
TOUCH_AXIS_Y_NEG,
};
enum TouchGestureAxisChannel {
TOUCH_GESTURE_PINCH_POS = TOUCH_AXIS_Y_NEG + 1,
TOUCH_GESTURE_PINCH_NEG,
};
// Plugin functions
virtual bool isSupported() const override;
virtual const QString& getName() const override { return NAME; }
virtual void pluginFocusOutEvent() override { _inputDevice->focusOutEvent(); }
virtual void pluginUpdate(float deltaTime, const controller::InputCalibrationData& inputCalibrationData) override;
void touchBeginEvent(const QTouchEvent* event);
void touchEndEvent(const QTouchEvent* event);
void touchUpdateEvent(const QTouchEvent* event);
void touchGestureEvent(const QGestureEvent* event);
static const QString NAME;
protected:
class InputDevice : public controller::InputDevice {
public:
InputDevice() : controller::InputDevice("Touchscreen") {}
private:
// Device functions
virtual controller::Input::NamedVector getAvailableInputs() const override;
virtual QString getDefaultMappingConfig() const override;
virtual void update(float deltaTime, const controller::InputCalibrationData& inputCalibrationData) override;
virtual void focusOutEvent() override;
controller::Input makeInput(TouchAxisChannel axis) const;
controller::Input makeInput(TouchGestureAxisChannel gesture) const;
friend class TouchscreenDevice;
};
public:
const std::shared_ptr<InputDevice>& getInputDevice() const { return _inputDevice; }
protected:
qreal _lastPinchScale;
qreal _pinchScale;
qreal _screenDPI;
glm::vec2 _screenDPIScale;
glm::vec2 _firstTouchVec;
glm::vec2 _currentTouchVec;
int _touchPointCount;
std::shared_ptr<InputDevice> _inputDevice { std::make_shared<InputDevice>() };
};
#endif // hifi_TouchscreenDevice_h

View file

@ -522,7 +522,6 @@ SharedNodePointer LimitedNodeList::addOrUpdateNode(const QUuid& uuid, NodeType_t
const HifiSockAddr& publicSocket, const HifiSockAddr& localSocket,
const NodePermissions& permissions,
const QUuid& connectionSecret) {
QReadLocker readLocker(&_nodeMutex);
NodeHash::const_iterator it = _nodeHash.find(uuid);
if (it != _nodeHash.end()) {

View file

@ -131,7 +131,7 @@ public:
std::function<void(Node*)> linkedDataCreateCallback;
size_t size() const { QReadLocker readLock(&_nodeMutex); return _nodeHash.size(); }
size_t size() const { return _nodeHash.size(); }
SharedNodePointer nodeWithUUID(const QUuid& nodeUUID);
@ -287,7 +287,7 @@ protected:
QUuid _sessionUUID;
NodeHash _nodeHash;
mutable QReadWriteLock _nodeMutex;
QReadWriteLock _nodeMutex;
udt::Socket _nodeSocket;
QUdpSocket* _dtlsSocket;
HifiSockAddr _localSockAddr;

View file

@ -513,16 +513,9 @@ void NodeList::processDomainServerConnectionTokenPacket(QSharedPointer<ReceivedM
void NodeList::processDomainServerList(QSharedPointer<ReceivedMessage> message) {
if (_domainHandler.getSockAddr().isNull()) {
qWarning() << "IGNORING DomainList packet while not connected to a Domain Server";
// refuse to process this packet if we aren't currently connected to the DS
return;
}
QUuid domainHandlerUUID = _domainHandler.getUUID();
QUuid messageSourceUUID = message->getSourceID();
if (!domainHandlerUUID.isNull() && domainHandlerUUID != messageSourceUUID) {
qWarning() << "IGNORING DomainList packet from" << messageSourceUUID << "while connected to" << domainHandlerUUID;
return;
}
// this is a packet from the domain server, reset the count of un-replied check-ins
_numNoReplyDomainCheckIns = 0;

View file

@ -26,9 +26,11 @@ var WANT_DEBUG_SEARCH_NAME = null;
// these tune time-averaging and "on" value for analog trigger
//
var SPARK_MODEL_SCALE_FACTOR = 0.75;
var TRIGGER_SMOOTH_RATIO = 0.1; // Time averaging of trigger - 0.0 disables smoothing
var TRIGGER_ON_VALUE = 0.4; // Squeezed just enough to activate search or near grab
var TRIGGER_GRAB_VALUE = 0.75; // Squeezed far enough to complete distant grab
var TRIGGER_GRAB_VALUE = 0.85; // Squeezed far enough to complete distant grab
var TRIGGER_OFF_VALUE = 0.15;
var BUMPER_ON_VALUE = 0.5;
@ -43,6 +45,10 @@ var DRAW_GRAB_BOXES = false;
var DRAW_HAND_SPHERES = false;
var DROP_WITHOUT_SHAKE = false;
var EQUIP_SPHERE_COLOR = { red: 179, green: 120, blue: 211 };
var EQUIP_SPHERE_ALPHA = 0.15;
var EQUIP_SPHERE_SCALE_FACTOR = 0.65;
//
// distant manipulation
//
@ -72,11 +78,14 @@ var LINE_ENTITY_DIMENSIONS = {
var LINE_LENGTH = 500;
var PICK_MAX_DISTANCE = 500; // max length of pick-ray
var EQUIP_RADIUS_EMBIGGEN_FACTOR = 1.1;
//
// near grabbing
//
var EQUIP_RADIUS = 0.1; // radius used for palm vs equip-hotspot for equipping.
var EQUIP_HOTSPOT_RENDER_RADIUS = 0.3; // radius used for palm vs equip-hotspot for rendering hot-spots
var NEAR_GRABBING_ACTION_TIMEFRAME = 0.05; // how quickly objects move to their new position
@ -179,9 +188,7 @@ CONTROLLER_STATE_MACHINE[STATE_OFF] = {
};
CONTROLLER_STATE_MACHINE[STATE_SEARCHING] = {
name: "searching",
updateMethod: "search",
enterMethod: "searchEnter",
exitMethod: "searchExit"
updateMethod: "search"
};
CONTROLLER_STATE_MACHINE[STATE_DISTANCE_HOLDING] = {
name: "distance_holding",
@ -261,7 +268,8 @@ function propsArePhysical(props) {
var EXTERNALLY_MANAGED_2D_MINOR_MODE = true;
var EDIT_SETTING = "io.highfidelity.isEditting";
function isEditing() {
return EXTERNALLY_MANAGED_2D_MINOR_MODE && Settings.getValue(EDIT_SETTING);
var actualSettingValue = Settings.getValue(EDIT_SETTING) === "false" ? false : !!Settings.getValue(EDIT_SETTING);
return EXTERNALLY_MANAGED_2D_MINOR_MODE && actualSettingValue;
}
function isIn2DMode() {
// In this version, we make our own determination of whether we're aimed a HUD element,
@ -398,6 +406,8 @@ function MyController(hand) {
this.entityPropertyCache = new EntityPropertiesCache();
this.equipOverlayInfoSetMap = {};
var _this = this;
var suppressedIn2D = [STATE_OFF, STATE_SEARCHING];
@ -545,7 +555,7 @@ function MyController(hand) {
this.overlayLineOn = function (closePoint, farPoint, color) {
if (this.overlayLine === null) {
var lineProperties = {
lineWidth: 5,
glow: 1.0,
start: closePoint,
end: farPoint,
color: color,
@ -877,7 +887,6 @@ function MyController(hand) {
};
this.off = function () {
if (this.triggerSmoothedReleased()) {
this.waitForTriggerRelease = false;
}
@ -887,158 +896,122 @@ function MyController(hand) {
this.startingHandRotation = Controller.getPoseValue(controllerHandInput).rotation;
if (this.triggerSmoothedSqueezed()) {
this.setState(STATE_SEARCHING, "trigger squeeze detected");
return;
}
}
this.entityPropertyCache.clear();
this.entityPropertyCache.findEntities(this.getHandPosition(), EQUIP_HOTSPOT_RENDER_RADIUS);
var candidateEntities = this.entityPropertyCache.getEntities();
var potentialEquipHotspot = this.chooseBestEquipHotspot(candidateEntities);
if (!this.waitForTriggerRelease) {
this.updateEquipHaptics(potentialEquipHotspot);
}
var nearEquipHotspots = this.chooseNearEquipHotspots(candidateEntities, EQUIP_HOTSPOT_RENDER_RADIUS);
this.updateEquipHotspotRendering(nearEquipHotspots, potentialEquipHotspot);
};
this.createHotspots = function() {
var _this = this;
this.clearEquipHaptics = function () {
this.prevPotentialEquipHotspot = null;
};
var HAND_EQUIP_SPHERE_COLOR = { red: 90, green: 255, blue: 90 };
var HAND_EQUIP_SPHERE_ALPHA = 0.7;
var HAND_EQUIP_SPHERE_RADIUS = 0.01;
var HAND_GRAB_SPHERE_COLOR = { red: 90, green: 90, blue: 255 };
var HAND_GRAB_SPHERE_ALPHA = 0.3;
var HAND_GRAB_SPHERE_RADIUS = NEAR_GRAB_RADIUS;
var EQUIP_SPHERE_COLOR = { red: 90, green: 255, blue: 90 };
var EQUIP_SPHERE_ALPHA = 0.3;
var GRAB_BOX_COLOR = { red: 90, green: 90, blue: 255 };
var GRAB_BOX_ALPHA = 0.1;
this.hotspotOverlays = [];
var overlay;
if (DRAW_HAND_SPHERES) {
// add tiny green sphere around the palm.
var handPosition = this.getHandPosition();
overlay = Overlays.addOverlay("sphere", {
position: handPosition,
size: HAND_EQUIP_SPHERE_RADIUS * 2,
color: HAND_EQUIP_SPHERE_COLOR,
alpha: HAND_EQUIP_SPHERE_ALPHA,
solid: true,
visible: true,
ignoreRayIntersection: true,
drawInFront: false
});
this.hotspotOverlays.push({
entityID: undefined,
overlay: overlay,
type: "hand"
});
// add larger blue sphere around the palm.
overlay = Overlays.addOverlay("sphere", {
position: handPosition,
size: HAND_GRAB_SPHERE_RADIUS * 2,
color: HAND_GRAB_SPHERE_COLOR,
alpha: HAND_GRAB_SPHERE_ALPHA,
solid: true,
visible: true,
ignoreRayIntersection: true,
drawInFront: false
});
this.hotspotOverlays.push({
entityID: undefined,
overlay: overlay,
type: "hand",
localPosition: {x: 0, y: 0, z: 0}
});
this.updateEquipHaptics = function (potentialEquipHotspot) {
if (potentialEquipHotspot && !this.prevPotentialEquipHotspot ||
!potentialEquipHotspot && this.prevPotentialEquipHotspot) {
Controller.triggerShortHapticPulse(0.5, this.hand);
}
this.prevPotentialEquipHotspot = potentialEquipHotspot;
};
// find entities near the avatar that might be equipable.
this.entityPropertyCache.clear();
this.entityPropertyCache.findEntities(MyAvatar.position, HOTSPOT_DRAW_DISTANCE);
if (DRAW_GRAB_BOXES) {
// add blue box overlays for grabbable entities.
this.entityPropertyCache.getEntities().forEach(function(entityID) {
var props = _this.entityPropertyCache.getProps(entityID);
if (_this.entityIsGrabbable(entityID)) {
var overlay = Overlays.addOverlay("cube", {
rotation: props.rotation,
position: props.position,
size: props.dimensions,
color: GRAB_BOX_COLOR,
alpha: GRAB_BOX_ALPHA,
solid: true,
visible: true,
ignoreRayIntersection: true,
drawInFront: false
});
_this.hotspotOverlays.push({
entityID: entityID,
overlay: overlay,
type: "near",
localPosition: {x: 0, y: 0, z: 0}
});
}
});
this.clearEquipHotspotRendering = function () {
var keys = Object.keys(this.equipOverlayInfoSetMap);
for (var i = 0; i < keys.length; i++) {
var overlayInfoSet = this.equipOverlayInfoSetMap[keys[i]];
this.deleteOverlayInfoSet(overlayInfoSet);
}
this.equipOverlayInfoSetMap = {};
};
// add green spheres for each equippable hotspot.
flatten(this.entityPropertyCache.getEntities().map(function(entityID) {
return _this.collectEquipHotspots(entityID);
})).filter(function(hotspot) {
return _this.hotspotIsEquippable(hotspot);
}).forEach(function(hotspot) {
var overlay = Overlays.addOverlay("sphere", {
this.createOverlayInfoSet = function (hotspot, timestamp) {
var overlayInfoSet = {
timestamp: timestamp,
entityID: hotspot.entityID,
localPosition: hotspot.localPosition,
hotspot: hotspot,
overlays: []
};
var diameter = hotspot.radius * 2;
overlayInfoSet.overlays.push(Overlays.addOverlay("sphere", {
position: hotspot.worldPosition,
size: hotspot.radius * 2,
rotation: {x: 0, y: 0, z: 0, w: 1},
dimensions: diameter * EQUIP_SPHERE_SCALE_FACTOR,
color: EQUIP_SPHERE_COLOR,
alpha: EQUIP_SPHERE_ALPHA,
solid: true,
visible: true,
ignoreRayIntersection: true,
drawInFront: false
});
_this.hotspotOverlays.push({
entityID: hotspot.entityID,
overlay: overlay,
type: "equip",
localPosition: hotspot.localPosition
}));
return overlayInfoSet;
};
this.updateOverlayInfoSet = function (overlayInfoSet, timestamp, potentialEquipHotspot) {
overlayInfoSet.timestamp = timestamp;
var diameter = overlayInfoSet.hotspot.radius * 2;
// embiggen the overlays if it maches the potentialEquipHotspot
if (potentialEquipHotspot && overlayInfoSet.entityID == potentialEquipHotspot.entityID &&
Vec3.equal(overlayInfoSet.localPosition, potentialEquipHotspot.localPosition)) {
diameter = diameter * EQUIP_RADIUS_EMBIGGEN_FACTOR;
}
var props = _this.entityPropertyCache.getProps(overlayInfoSet.entityID);
var entityXform = new Xform(props.rotation, props.position);
var position = entityXform.xformPoint(overlayInfoSet.localPosition);
overlayInfoSet.overlays.forEach(function (overlay) {
Overlays.editOverlay(overlay, {
position: position,
rotation: props.rotation,
dimensions: diameter * EQUIP_SPHERE_SCALE_FACTOR
});
});
};
this.updateHotspots = function() {
var _this = this;
var props;
this.hotspotOverlays.forEach(function(overlayInfo) {
if (overlayInfo.type === "hand") {
Overlays.editOverlay(overlayInfo.overlay, { position: _this.getHandPosition() });
} else if (overlayInfo.type === "equip") {
_this.entityPropertyCache.updateEntity(overlayInfo.entityID);
props = _this.entityPropertyCache.getProps(overlayInfo.entityID);
var entityXform = new Xform(props.rotation, props.position);
Overlays.editOverlay(overlayInfo.overlay, {
position: entityXform.xformPoint(overlayInfo.localPosition),
rotation: props.rotation
this.deleteOverlayInfoSet = function (overlayInfoSet) {
overlayInfoSet.overlays.forEach(function (overlay) {
Overlays.deleteOverlay(overlay);
});
} else if (overlayInfo.type === "near") {
_this.entityPropertyCache.updateEntity(overlayInfo.entityID);
props = _this.entityPropertyCache.getProps(overlayInfo.entityID);
Overlays.editOverlay(overlayInfo.overlay, { position: props.position, rotation: props.rotation });
};
this.updateEquipHotspotRendering = function (hotspots, potentialEquipHotspot) {
var now = Date.now();
var _this = this;
hotspots.forEach(function (hotspot) {
var overlayInfoSet = _this.equipOverlayInfoSetMap[hotspot.key];
if (overlayInfoSet) {
_this.updateOverlayInfoSet(overlayInfoSet, now, potentialEquipHotspot);
} else {
_this.equipOverlayInfoSetMap[hotspot.key] = _this.createOverlayInfoSet(hotspot, now);
}
});
};
this.destroyHotspots = function() {
this.hotspotOverlays.forEach(function(overlayInfo) {
Overlays.deleteOverlay(overlayInfo.overlay);
});
this.hotspotOverlays = [];
};
this.searchEnter = function() {
this.createHotspots();
};
this.searchExit = function() {
this.destroyHotspots();
// delete sets with old timestamps.
var keys = Object.keys(this.equipOverlayInfoSetMap);
for (var i = 0; i < keys.length; i++) {
var overlayInfoSet = this.equipOverlayInfoSetMap[keys[i]];
if (overlayInfoSet.timestamp !== now) {
this.deleteOverlayInfoSet(overlayInfoSet);
delete this.equipOverlayInfoSetMap[keys[i]];
}
}
};
// Performs ray pick test from the hand controller into the world
@ -1106,6 +1079,7 @@ function MyController(hand) {
// returns a list of all equip-hotspots assosiated with this entity.
// @param {UUID} entityID
// @returns {Object[]} array of objects with the following fields.
// * key {string} a string that can be used to uniquely identify this hotspot
// * entityID {UUID}
// * localPosition {Vec3} position of the hotspot in object space.
// * worldPosition {vec3} position of the hotspot in world space.
@ -1123,6 +1097,7 @@ function MyController(hand) {
var hotspot = equipHotspotsProps[i];
if (hotspot.position && hotspot.radius && hotspot.joints) {
result.push({
key: entityID.toString() + i.toString(),
entityID: entityID,
localPosition: hotspot.position,
worldPosition: entityXform.xformPoint(hotspot.position),
@ -1135,6 +1110,7 @@ function MyController(hand) {
var wearableProps = this.entityPropertyCache.getWearableProps(entityID);
if (wearableProps && wearableProps.joints) {
result.push({
key: entityID.toString() + "0",
entityID: entityID,
localPosition: {x: 0, y: 0, z: 0},
worldPosition: entityXform.pos,
@ -1275,12 +1251,36 @@ function MyController(hand) {
return true;
};
this.chooseNearEquipHotspots = function (candidateEntities, distance) {
var equippableHotspots = flatten(candidateEntities.map(function (entityID) {
return _this.collectEquipHotspots(entityID);
})).filter(function (hotspot) {
return (_this.hotspotIsEquippable(hotspot) &&
Vec3.distance(hotspot.worldPosition, _this.getHandPosition()) < hotspot.radius + distance);
});
return equippableHotspots;
};
this.chooseBestEquipHotspot = function (candidateEntities) {
var DISTANCE = 0;
var equippableHotspots = this.chooseNearEquipHotspots(candidateEntities, DISTANCE);
if (equippableHotspots.length > 0) {
// sort by distance
equippableHotspots.sort(function (a, b) {
var aDistance = Vec3.distance(a.worldPosition, this.getHandPosition());
var bDistance = Vec3.distance(b.worldPosition, this.getHandPosition());
return aDistance - bDistance;
});
return equippableHotspots[0];
} else {
return null;
}
};
this.search = function () {
var _this = this;
var name;
this.updateHotspots();
this.grabbedEntity = null;
this.isInitialGrab = false;
this.shouldResetParentOnRelease = false;
@ -1298,27 +1298,13 @@ function MyController(hand) {
this.entityPropertyCache.findEntities(handPosition, NEAR_GRAB_RADIUS);
var candidateEntities = this.entityPropertyCache.getEntities();
var equippableHotspots = flatten(candidateEntities.map(function(entityID) {
return _this.collectEquipHotspots(entityID);
})).filter(function(hotspot) {
return _this.hotspotIsEquippable(hotspot) && Vec3.distance(hotspot.worldPosition, handPosition) < hotspot.radius;
});
var entity;
if (equippableHotspots.length > 0) {
// sort by distance
equippableHotspots.sort(function(a, b) {
var aDistance = Vec3.distance(a.worldPosition, handPosition);
var bDistance = Vec3.distance(b.worldPosition, handPosition);
return aDistance - bDistance;
});
var potentialEquipHotspot = this.chooseBestEquipHotspot(candidateEntities);
if (potentialEquipHotspot) {
if (this.triggerSmoothedGrab()) {
this.grabbedHotspot = equippableHotspots[0];
this.grabbedEntity = equippableHotspots[0].entityID;
this.grabbedHotspot = potentialEquipHotspot;
this.grabbedEntity = potentialEquipHotspot.entityID;
this.setState(STATE_HOLD, "eqipping '" + this.entityPropertyCache.getProps(this.grabbedEntity).name + "'");
return;
} else {
// TODO: highlight the equippable object?
}
}
@ -1339,6 +1325,7 @@ function MyController(hand) {
this.intersectionDistance = 0;
}
var entity;
if (grabbableEntities.length > 0) {
// sort by distance
grabbableEntities.sort(function (a, b) {
@ -1354,11 +1341,10 @@ function MyController(hand) {
this.setState(STATE_NEAR_TRIGGER, "near trigger '" + name + "'");
return;
} else {
// TODO: highlight the near-triggerable object?
// potentialNearTriggerEntity = entity;
}
} else {
if (this.triggerSmoothedGrab()) {
var props = this.entityPropertyCache.getProps(entity);
var grabProps = this.entityPropertyCache.getGrabProps(entity);
var refCount = grabProps.refCount ? grabProps.refCount : 0;
@ -1373,10 +1359,9 @@ function MyController(hand) {
this.setState(STATE_NEAR_GRABBING, "near grab '" + name + "'");
return;
} else {
// TODO: highlight the grabbable object?
// potentialNearGrabEntity = entity;
}
}
return;
}
if (rayPickInfo.entityID) {
@ -1388,7 +1373,7 @@ function MyController(hand) {
this.setState(STATE_FAR_TRIGGER, "far trigger '" + name + "'");
return;
} else {
// TODO: highlight the far-triggerable object?
// potentialFarTriggerEntity = entity;
}
} else if (this.entityIsDistanceGrabbable(rayPickInfo.entityID, handPosition)) {
if (this.triggerSmoothedGrab() && !isEditing()) {
@ -1396,11 +1381,16 @@ function MyController(hand) {
this.setState(STATE_DISTANCE_HOLDING, "distance hold '" + name + "'");
return;
} else {
// TODO: highlight the far-grabbable object?
// potentialFarGrabEntity = entity;
}
}
}
this.updateEquipHaptics(potentialEquipHotspot);
var nearEquipHotspots = this.chooseNearEquipHotspots(candidateEntities, EQUIP_HOTSPOT_RENDER_RADIUS);
this.updateEquipHotspotRendering(nearEquipHotspots, potentialEquipHotspot);
// search line visualizations
if (USE_ENTITY_LINES_FOR_SEARCHING === true) {
this.lineOn(rayPickInfo.searchRay.origin,
@ -1428,6 +1418,9 @@ function MyController(hand) {
this.distanceHoldingEnter = function () {
this.clearEquipHotspotRendering();
this.clearEquipHaptics();
// controller pose is in avatar frame
var avatarControllerPose =
Controller.getPoseValue((this.hand === RIGHT_HAND) ? Controller.Standard.RightHand : Controller.Standard.LeftHand);
@ -1703,6 +1696,10 @@ function MyController(hand) {
this.overlayLineOff();
this.dropGestureReset();
this.clearEquipHaptics();
this.clearEquipHotspotRendering();
Controller.triggerShortHapticPulse(1.0, this.hand);
if (this.entityActivated) {
var saveGrabbedID = this.grabbedEntity;
@ -1920,10 +1917,18 @@ function MyController(hand) {
};
this.nearTriggerEnter = function () {
this.clearEquipHotspotRendering();
this.clearEquipHaptics();
Controller.triggerShortHapticPulse(1.0, this.hand);
this.callEntityMethodOnGrabbed("startNearTrigger");
};
this.farTriggerEnter = function () {
this.clearEquipHotspotRendering();
this.clearEquipHaptics();
this.callEntityMethodOnGrabbed("startFarTrigger");
};

View file

@ -38,6 +38,7 @@
#include <plugins/PluginManager.h>
#include <input-plugins/InputPlugin.h>
#include <input-plugins/KeyboardMouseDevice.h>
#include <input-plugins/TouchscreenDevice.h>
#include <controllers/ScriptingInterface.h>
#include <DependencyManager.h>
@ -144,6 +145,9 @@ int main(int argc, char** argv) {
if (name == KeyboardMouseDevice::NAME) {
userInputMapper->registerDevice(std::dynamic_pointer_cast<KeyboardMouseDevice>(inputPlugin)->getInputDevice());
}
if (name == TouchscreenDevice::NAME) {
userInputMapper->registerDevice(std::dynamic_pointer_cast<TouchscreenDevice>(inputPlugin)->getInputDevice());
}
inputPlugin->pluginUpdate(0, calibrationData);
}
rootContext->setContextProperty("Controllers", new MyControllerScriptingInterface());