mirror of
https://github.com/JulianGro/overte.git
synced 2025-04-07 19:23:04 +02:00
Merge branch 'master' into loginInitiative2
This commit is contained in:
commit
4116f6ca9a
24 changed files with 536 additions and 129 deletions
|
@ -110,6 +110,8 @@ public:
|
|||
void setBaseDisplayName(const QString& baseDisplayName) { _baseDisplayName = baseDisplayName; }
|
||||
bool getRequestsDomainListData() { return _requestsDomainListData; }
|
||||
void setRequestsDomainListData(bool requesting) { _requestsDomainListData = requesting; }
|
||||
bool getPrevRequestsDomainListData() { return _prevRequestsDomainListData; }
|
||||
void setPrevRequestsDomainListData(bool requesting) { _prevRequestsDomainListData = requesting; }
|
||||
|
||||
const ConicalViewFrustums& getViewFrustums() const { return _currentViewFrustums; }
|
||||
|
||||
|
@ -176,6 +178,7 @@ private:
|
|||
int _recentOtherAvatarsOutOfView { 0 };
|
||||
QString _baseDisplayName{}; // The santized key used in determinging unique sessionDisplayName, so that we can remove from dictionary.
|
||||
bool _requestsDomainListData { false };
|
||||
bool _prevRequestsDomainListData{ false };
|
||||
|
||||
AvatarTraits::TraitVersions _lastReceivedTraitVersions;
|
||||
TraitsCheckTimestamp _lastReceivedTraitsChange;
|
||||
|
|
|
@ -268,6 +268,7 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node)
|
|||
// When this is true, the AvatarMixer will send Avatar data to a client
|
||||
// about avatars they've ignored or that are out of view
|
||||
bool PALIsOpen = nodeData->getRequestsDomainListData();
|
||||
bool PALWasOpen = nodeData->getPrevRequestsDomainListData();
|
||||
|
||||
// When this is true, the AvatarMixer will send Avatar data to a client about avatars that have ignored them
|
||||
bool getsAnyIgnored = PALIsOpen && destinationNode->getCanKick();
|
||||
|
@ -392,6 +393,20 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node)
|
|||
|
||||
sortedAvatars.push(SortableAvatar(avatarNodeData, avatarNode, lastEncodeTime));
|
||||
}
|
||||
|
||||
// If Avatar A's PAL WAS open but is no longer open, AND
|
||||
// Avatar A should be ignoring Avatar B...
|
||||
if (PALWasOpen && !PALIsOpen && shouldIgnore) {
|
||||
// ...send a Kill Packet to Node A, instructing Node A to kill Avatar B,
|
||||
// then have Node A cleanup the killed Node B.
|
||||
auto packet = NLPacket::create(PacketType::KillAvatar, NUM_BYTES_RFC4122_UUID + sizeof(KillAvatarReason), true);
|
||||
packet->write(avatarNode->getUUID().toRfc4122());
|
||||
packet->writePrimitive(KillAvatarReason::AvatarIgnored);
|
||||
nodeList->sendPacket(std::move(packet), *destinationNode);
|
||||
nodeData->cleanupKilledNode(avatarNode->getUUID(), avatarNode->getLocalID());
|
||||
}
|
||||
|
||||
nodeData->setPrevRequestsDomainListData(PALIsOpen);
|
||||
}
|
||||
|
||||
// loop through our sorted avatars and allocate our bandwidth to them accordingly
|
||||
|
|
|
@ -1,4 +1,22 @@
|
|||
{
|
||||
"backPlate": {
|
||||
"dimensions": {
|
||||
"x": 0.723600000888109207,
|
||||
"y": 0.022600000724196434,
|
||||
"z": 0.2474999976158142
|
||||
},
|
||||
"position": {
|
||||
"x": -0.3292800903320312,
|
||||
"y": 0.004300000742077827,
|
||||
"z": -0.055427663803100586
|
||||
},
|
||||
"rotation": {
|
||||
"w": 1.000,
|
||||
"x": 0.000,
|
||||
"y": 0.000,
|
||||
"z": 0.000
|
||||
}
|
||||
},
|
||||
"anchor": {
|
||||
"dimensions": {
|
||||
"x": 0.023600000888109207,
|
||||
|
|
|
@ -70,6 +70,15 @@ ScrollingWindow {
|
|||
}
|
||||
}
|
||||
|
||||
var useKeyboardPreference = findPreference("User Interface", "Use Virtual Keyboard");
|
||||
var keyboardInputPreference = findPreference("User Interface", "Keyboard laser / mallets");
|
||||
if (useKeyboardPreference && keyboardInputPreference) {
|
||||
keyboardInputPreference.visible = useKeyboardPreference.value;
|
||||
useKeyboardPreference.valueChanged.connect(function() {
|
||||
keyboardInputPreference.visible = useKeyboardPreference.value;
|
||||
});
|
||||
}
|
||||
|
||||
if (sections.length) {
|
||||
// Default sections to expanded/collapsed as appropriate for dialog.
|
||||
if (sections.length === 1) {
|
||||
|
@ -112,4 +121,32 @@ ScrollingWindow {
|
|||
onClicked: root.restoreAll()
|
||||
}
|
||||
}
|
||||
|
||||
function findPreference(category, name) {
|
||||
var section = null;
|
||||
var preference = null;
|
||||
var i;
|
||||
|
||||
// Find category section.
|
||||
i = 0;
|
||||
while (!section && i < sections.length) {
|
||||
if (sections[i].name === category) {
|
||||
section = sections[i];
|
||||
}
|
||||
i++;
|
||||
}
|
||||
|
||||
// Find named preference.
|
||||
if (section) {
|
||||
i = 0;
|
||||
while (!preference && i < section.preferences.length) {
|
||||
if (section.preferences[i].preference && section.preferences[i].preference.name === name) {
|
||||
preference = section.preferences[i];
|
||||
}
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
return preference;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,6 +19,7 @@ Preference {
|
|||
property bool value: false
|
||||
Component.onCompleted: {
|
||||
checkBox.checked = preference.value;
|
||||
value = checkBox.checked;
|
||||
preference.value = Qt.binding(function(){ return checkBox.checked; });
|
||||
value = checkBox.checked;
|
||||
}
|
||||
|
|
|
@ -20,6 +20,11 @@ Preference {
|
|||
|
||||
property int value: 0
|
||||
|
||||
readonly property int visibleBottomPadding: 3
|
||||
readonly property int invisibleBottomPadding: 0
|
||||
readonly property int indentLeftMargin: 20
|
||||
readonly property int nonindentLeftMargin: 0
|
||||
|
||||
Component.onCompleted: {
|
||||
value = preference.value;
|
||||
repeater.itemAt(preference.value).checked = true;
|
||||
|
@ -46,24 +51,24 @@ Preference {
|
|||
preference.save();
|
||||
}
|
||||
|
||||
RalewaySemiBold {
|
||||
id: heading
|
||||
size: hifi.fontSizes.inputLabel
|
||||
text: preference.heading
|
||||
color: hifi.colors.lightGrayText
|
||||
visible: text !== ""
|
||||
bottomPadding: heading.visible ? visibleBottomPadding : invisibleBottomPadding
|
||||
}
|
||||
|
||||
Column {
|
||||
id: control
|
||||
anchors {
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
bottom: parent.bottom
|
||||
top: heading.visible ? heading.bottom : heading.top
|
||||
leftMargin: preference.indented ? indentLeftMargin : nonindentLeftMargin
|
||||
}
|
||||
spacing: 3
|
||||
|
||||
RalewaySemiBold {
|
||||
id: heading
|
||||
size: hifi.fontSizes.inputLabel
|
||||
text: preference.heading
|
||||
color: hifi.colors.lightGrayText
|
||||
visible: text !== ""
|
||||
bottomPadding: 3
|
||||
}
|
||||
|
||||
Repeater {
|
||||
id: repeater
|
||||
model: preference.items.length
|
||||
|
|
|
@ -138,6 +138,15 @@ Item {
|
|||
}
|
||||
}
|
||||
|
||||
var useKeyboardPreference = findPreference("User Interface", "Use Virtual Keyboard");
|
||||
var keyboardInputPreference = findPreference("User Interface", "Keyboard laser / mallets");
|
||||
if (useKeyboardPreference && keyboardInputPreference) {
|
||||
keyboardInputPreference.visible = useKeyboardPreference.value;
|
||||
useKeyboardPreference.valueChanged.connect(function() {
|
||||
keyboardInputPreference.visible = useKeyboardPreference.value;
|
||||
});
|
||||
}
|
||||
|
||||
if (sections.length) {
|
||||
// Default sections to expanded/collapsed as appropriate for dialog.
|
||||
if (sections.length === 1) {
|
||||
|
|
BIN
interface/resources/sounds/keySound.mp3
Normal file
BIN
interface/resources/sounds/keySound.mp3
Normal file
Binary file not shown.
|
@ -5746,7 +5746,44 @@ void Application::updateSecondaryCameraViewFrustum() {
|
|||
}
|
||||
|
||||
ViewFrustum secondaryViewFrustum;
|
||||
if (camera->mirrorProjection && !camera->attachedEntityId.isNull()) {
|
||||
if (camera->portalProjection && !camera->attachedEntityId.isNull() && !camera->portalEntranceEntityId.isNull()) {
|
||||
auto entityScriptingInterface = DependencyManager::get<EntityScriptingInterface>();
|
||||
EntityItemPointer portalEntrance = qApp->getEntities()->getTree()->findEntityByID(camera->portalEntranceEntityId);
|
||||
EntityItemPointer portalExit = qApp->getEntities()->getTree()->findEntityByID(camera->attachedEntityId);
|
||||
|
||||
glm::vec3 portalEntrancePropertiesPosition = portalEntrance->getWorldPosition();
|
||||
glm::quat portalEntrancePropertiesRotation = portalEntrance->getWorldOrientation();
|
||||
glm::mat4 worldFromPortalEntranceRotation = glm::mat4_cast(portalEntrancePropertiesRotation);
|
||||
glm::mat4 worldFromPortalEntranceTranslation = glm::translate(portalEntrancePropertiesPosition);
|
||||
glm::mat4 worldFromPortalEntrance = worldFromPortalEntranceTranslation * worldFromPortalEntranceRotation;
|
||||
glm::mat4 portalEntranceFromWorld = glm::inverse(worldFromPortalEntrance);
|
||||
|
||||
glm::vec3 portalExitPropertiesPosition = portalExit->getWorldPosition();
|
||||
glm::quat portalExitPropertiesRotation = portalExit->getWorldOrientation();
|
||||
glm::vec3 portalExitPropertiesDimensions = portalExit->getScaledDimensions();
|
||||
glm::vec3 halfPortalExitPropertiesDimensions = 0.5f * portalExitPropertiesDimensions;
|
||||
|
||||
glm::mat4 worldFromPortalExitRotation = glm::mat4_cast(portalExitPropertiesRotation);
|
||||
glm::mat4 worldFromPortalExitTranslation = glm::translate(portalExitPropertiesPosition);
|
||||
glm::mat4 worldFromPortalExit = worldFromPortalExitTranslation * worldFromPortalExitRotation;
|
||||
|
||||
glm::vec3 mainCameraPositionWorld = getCamera().getPosition();
|
||||
glm::vec3 mainCameraPositionPortalEntrance = vec3(portalEntranceFromWorld * vec4(mainCameraPositionWorld, 1.0f));
|
||||
mainCameraPositionPortalEntrance = vec3(-mainCameraPositionPortalEntrance.x, mainCameraPositionPortalEntrance.y,
|
||||
-mainCameraPositionPortalEntrance.z);
|
||||
glm::vec3 portalExitCameraPositionWorld = vec3(worldFromPortalExit * vec4(mainCameraPositionPortalEntrance, 1.0f));
|
||||
|
||||
secondaryViewFrustum.setPosition(portalExitCameraPositionWorld);
|
||||
secondaryViewFrustum.setOrientation(portalExitPropertiesRotation);
|
||||
|
||||
float nearClip = mainCameraPositionPortalEntrance.z + portalExitPropertiesDimensions.z * 2.0f;
|
||||
// `mainCameraPositionPortalEntrance` should technically be `mainCameraPositionPortalExit`,
|
||||
// but the values are the same.
|
||||
glm::vec3 upperRight = halfPortalExitPropertiesDimensions - mainCameraPositionPortalEntrance;
|
||||
glm::vec3 bottomLeft = -halfPortalExitPropertiesDimensions - mainCameraPositionPortalEntrance;
|
||||
glm::mat4 frustum = glm::frustum(bottomLeft.x, upperRight.x, bottomLeft.y, upperRight.y, nearClip, camera->farClipPlaneDistance);
|
||||
secondaryViewFrustum.setProjection(frustum);
|
||||
} else if (camera->mirrorProjection && !camera->attachedEntityId.isNull()) {
|
||||
auto entityScriptingInterface = DependencyManager::get<EntityScriptingInterface>();
|
||||
auto entityProperties = entityScriptingInterface->getEntityProperties(camera->attachedEntityId);
|
||||
glm::vec3 mirrorPropertiesPosition = entityProperties.getPosition();
|
||||
|
|
|
@ -33,6 +33,7 @@ public:
|
|||
|
||||
void configure(const Config& config) {
|
||||
_attachedEntityId = config.attachedEntityId;
|
||||
_portalEntranceEntityId = config.portalEntranceEntityId;
|
||||
_position = config.position;
|
||||
_orientation = config.orientation;
|
||||
_vFoV = config.vFoV;
|
||||
|
@ -40,7 +41,60 @@ public:
|
|||
_farClipPlaneDistance = config.farClipPlaneDistance;
|
||||
_textureWidth = config.textureWidth;
|
||||
_textureHeight = config.textureHeight;
|
||||
_mirrorProjection = config.mirrorProjection;
|
||||
_mirrorProjection = config.mirrorProjection;
|
||||
_portalProjection = config.portalProjection;
|
||||
}
|
||||
|
||||
void setPortalProjection(ViewFrustum& srcViewFrustum) {
|
||||
if (_portalEntranceEntityId.isNull() || _attachedEntityId.isNull()) {
|
||||
qWarning() << "ERROR: Cannot set portal projection for SecondaryCamera without an attachedEntityId AND portalEntranceEntityId set.";
|
||||
return;
|
||||
}
|
||||
|
||||
EntityItemPointer portalEntrance = qApp->getEntities()->getTree()->findEntityByID(_portalEntranceEntityId);
|
||||
if (!portalEntrance) {
|
||||
qWarning() << "ERROR: Cannot get EntityItemPointer for portalEntrance.";
|
||||
return;
|
||||
}
|
||||
|
||||
EntityItemPointer portalExit = qApp->getEntities()->getTree()->findEntityByID(_attachedEntityId);
|
||||
if (!portalExit) {
|
||||
qWarning() << "ERROR: Cannot get EntityItemPointer for portalExit.";
|
||||
return;
|
||||
}
|
||||
|
||||
glm::vec3 portalEntrancePropertiesPosition = portalEntrance->getWorldPosition();
|
||||
glm::quat portalEntrancePropertiesRotation = portalEntrance->getWorldOrientation();
|
||||
glm::mat4 worldFromPortalEntranceRotation = glm::mat4_cast(portalEntrancePropertiesRotation);
|
||||
glm::mat4 worldFromPortalEntranceTranslation = glm::translate(portalEntrancePropertiesPosition);
|
||||
glm::mat4 worldFromPortalEntrance = worldFromPortalEntranceTranslation * worldFromPortalEntranceRotation;
|
||||
glm::mat4 portalEntranceFromWorld = glm::inverse(worldFromPortalEntrance);
|
||||
|
||||
glm::vec3 portalExitPropertiesPosition = portalExit->getWorldPosition();
|
||||
glm::quat portalExitPropertiesRotation = portalExit->getWorldOrientation();
|
||||
glm::vec3 portalExitPropertiesDimensions = portalExit->getScaledDimensions();
|
||||
glm::vec3 halfPortalExitPropertiesDimensions = 0.5f * portalExitPropertiesDimensions;
|
||||
|
||||
glm::mat4 worldFromPortalExitRotation = glm::mat4_cast(portalExitPropertiesRotation);
|
||||
glm::mat4 worldFromPortalExitTranslation = glm::translate(portalExitPropertiesPosition);
|
||||
glm::mat4 worldFromPortalExit = worldFromPortalExitTranslation * worldFromPortalExitRotation;
|
||||
|
||||
glm::vec3 mainCameraPositionWorld = qApp->getCamera().getPosition();
|
||||
glm::vec3 mainCameraPositionPortalEntrance = vec3(portalEntranceFromWorld * vec4(mainCameraPositionWorld, 1.0f));
|
||||
mainCameraPositionPortalEntrance = vec3(-mainCameraPositionPortalEntrance.x, mainCameraPositionPortalEntrance.y,
|
||||
-mainCameraPositionPortalEntrance.z);
|
||||
glm::vec3 portalExitCameraPositionWorld = vec3(worldFromPortalExit * vec4(mainCameraPositionPortalEntrance, 1.0f));
|
||||
|
||||
srcViewFrustum.setPosition(portalExitCameraPositionWorld);
|
||||
srcViewFrustum.setOrientation(portalExitPropertiesRotation);
|
||||
|
||||
float nearClip = mainCameraPositionPortalEntrance.z + portalExitPropertiesDimensions.z * 2.0f;
|
||||
// `mainCameraPositionPortalEntrance` should technically be `mainCameraPositionPortalExit`,
|
||||
// but the values are the same.
|
||||
glm::vec3 upperRight = halfPortalExitPropertiesDimensions - mainCameraPositionPortalEntrance;
|
||||
glm::vec3 bottomLeft = -halfPortalExitPropertiesDimensions - mainCameraPositionPortalEntrance;
|
||||
glm::mat4 frustum = glm::frustum(bottomLeft.x, upperRight.x, bottomLeft.y, upperRight.y, nearClip, _farClipPlaneDistance);
|
||||
srcViewFrustum.setProjection(frustum);
|
||||
}
|
||||
|
||||
void setMirrorProjection(ViewFrustum& srcViewFrustum) {
|
||||
|
@ -109,7 +163,17 @@ public:
|
|||
|
||||
auto srcViewFrustum = args->getViewFrustum();
|
||||
if (_mirrorProjection) {
|
||||
setMirrorProjection(srcViewFrustum);
|
||||
if (_portalProjection) {
|
||||
qWarning() << "ERROR: You can't set both _portalProjection and _mirrorProjection";
|
||||
} else {
|
||||
setMirrorProjection(srcViewFrustum);
|
||||
}
|
||||
} else if (_portalProjection) {
|
||||
if (_mirrorProjection) {
|
||||
qWarning() << "ERROR: You can't set both _portalProjection and _mirrorProjection";
|
||||
} else {
|
||||
setPortalProjection(srcViewFrustum);
|
||||
}
|
||||
} else {
|
||||
if (!_attachedEntityId.isNull()) {
|
||||
EntityItemPointer attachedEntity = qApp->getEntities()->getTree()->findEntityByID(_attachedEntityId);
|
||||
|
@ -141,6 +205,7 @@ protected:
|
|||
|
||||
private:
|
||||
QUuid _attachedEntityId;
|
||||
QUuid _portalEntranceEntityId;
|
||||
glm::vec3 _position;
|
||||
glm::quat _orientation;
|
||||
float _vFoV;
|
||||
|
@ -149,6 +214,7 @@ private:
|
|||
int _textureWidth;
|
||||
int _textureHeight;
|
||||
bool _mirrorProjection;
|
||||
bool _portalProjection;
|
||||
EntityPropertyFlags _attachedEntityPropertyFlags;
|
||||
};
|
||||
|
||||
|
|
|
@ -20,14 +20,17 @@
|
|||
class SecondaryCameraJobConfig : public render::Task::Config { // Exposes secondary camera parameters to JavaScript.
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(QUuid attachedEntityId MEMBER attachedEntityId NOTIFY dirty) // entity whose properties define camera position and orientation
|
||||
Q_PROPERTY(QUuid portalEntranceEntityId MEMBER portalEntranceEntityId NOTIFY dirty) // entity whose properties define a portal's entrance position and orientation
|
||||
Q_PROPERTY(glm::vec3 position READ getPosition WRITE setPosition) // of viewpoint to render from
|
||||
Q_PROPERTY(glm::quat orientation READ getOrientation WRITE setOrientation) // of viewpoint to render from
|
||||
Q_PROPERTY(float vFoV MEMBER vFoV NOTIFY dirty) // Secondary camera's vertical field of view. In degrees.
|
||||
Q_PROPERTY(float nearClipPlaneDistance MEMBER nearClipPlaneDistance NOTIFY dirty) // Secondary camera's near clip plane distance. In meters.
|
||||
Q_PROPERTY(float farClipPlaneDistance MEMBER farClipPlaneDistance NOTIFY dirty) // Secondary camera's far clip plane distance. In meters.
|
||||
Q_PROPERTY(bool mirrorProjection MEMBER mirrorProjection NOTIFY dirty) // Flag to use attached mirror entity to build frustum for the mirror and set mirrored camera position/orientation.
|
||||
Q_PROPERTY(bool portalProjection MEMBER portalProjection NOTIFY dirty) // Flag to use attached portal entity to build frustum for the portal and set portal camera position/orientation.
|
||||
public:
|
||||
QUuid attachedEntityId;
|
||||
QUuid portalEntranceEntityId;
|
||||
glm::vec3 position;
|
||||
glm::quat orientation;
|
||||
float vFoV { DEFAULT_FIELD_OF_VIEW_DEGREES };
|
||||
|
@ -36,6 +39,7 @@ public:
|
|||
int textureWidth { TextureCache::DEFAULT_SPECTATOR_CAM_WIDTH };
|
||||
int textureHeight { TextureCache::DEFAULT_SPECTATOR_CAM_HEIGHT };
|
||||
bool mirrorProjection { false };
|
||||
bool portalProjection { false };
|
||||
|
||||
SecondaryCameraJobConfig() : render::Task::Config(false) {}
|
||||
signals:
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
#include "KeyboardScriptingInterface.h"
|
||||
#include "ui/Keyboard.h"
|
||||
|
||||
bool KeyboardScriptingInterface::isRaised() {
|
||||
bool KeyboardScriptingInterface::isRaised() const {
|
||||
return DependencyManager::get<Keyboard>()->isRaised();
|
||||
}
|
||||
|
||||
|
@ -20,8 +20,7 @@ void KeyboardScriptingInterface::setRaised(bool raised) {
|
|||
DependencyManager::get<Keyboard>()->setRaised(raised);
|
||||
}
|
||||
|
||||
|
||||
bool KeyboardScriptingInterface::isPassword() {
|
||||
bool KeyboardScriptingInterface::isPassword() const {
|
||||
return DependencyManager::get<Keyboard>()->isPassword();
|
||||
}
|
||||
|
||||
|
@ -33,6 +32,38 @@ void KeyboardScriptingInterface::loadKeyboardFile(const QString& keyboardFile) {
|
|||
DependencyManager::get<Keyboard>()->loadKeyboardFile(keyboardFile);
|
||||
}
|
||||
|
||||
bool KeyboardScriptingInterface::getUse3DKeyboard() {
|
||||
bool KeyboardScriptingInterface::getUse3DKeyboard() const {
|
||||
return DependencyManager::get<Keyboard>()->getUse3DKeyboard();
|
||||
}
|
||||
|
||||
void KeyboardScriptingInterface::disableRightMallet() {
|
||||
DependencyManager::get<Keyboard>()->disableRightMallet();
|
||||
}
|
||||
|
||||
void KeyboardScriptingInterface::disableLeftMallet() {
|
||||
DependencyManager::get<Keyboard>()->disableLeftMallet();
|
||||
}
|
||||
|
||||
void KeyboardScriptingInterface::enableRightMallet() {
|
||||
DependencyManager::get<Keyboard>()->enableRightMallet();
|
||||
}
|
||||
|
||||
void KeyboardScriptingInterface::enableLeftMallet() {
|
||||
DependencyManager::get<Keyboard>()->enableLeftMallet();
|
||||
}
|
||||
|
||||
void KeyboardScriptingInterface::setLeftHandLaser(unsigned int leftHandLaser) {
|
||||
DependencyManager::get<Keyboard>()->setLeftHandLaser(leftHandLaser);
|
||||
}
|
||||
|
||||
void KeyboardScriptingInterface::setRightHandLaser(unsigned int rightHandLaser) {
|
||||
DependencyManager::get<Keyboard>()->setRightHandLaser(rightHandLaser);
|
||||
}
|
||||
|
||||
bool KeyboardScriptingInterface::getPreferMalletsOverLasers() const {
|
||||
return DependencyManager::get<Keyboard>()->getPreferMalletsOverLasers();
|
||||
}
|
||||
|
||||
bool KeyboardScriptingInterface::containsID(OverlayID overlayID) const {
|
||||
return DependencyManager::get<Keyboard>()->containsID(overlayID);
|
||||
}
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
#include <QtCore/QUuid>
|
||||
|
||||
#include "DependencyManager.h"
|
||||
#include "ui/overlays/Overlay.h"
|
||||
|
||||
/**jsdoc
|
||||
* The Keyboard API provides facilities to use 3D Physical keyboard.
|
||||
|
@ -27,22 +28,33 @@
|
|||
* @property {bool} raised - <code>true</code> If the keyboard is visible <code>false</code> otherwise
|
||||
* @property {bool} password - <code>true</code> Will show * instead of characters in the text display <code>false</code> otherwise
|
||||
*/
|
||||
|
||||
class KeyboardScriptingInterface : public QObject, public Dependency {
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(bool raised READ isRaised WRITE setRaised)
|
||||
Q_PROPERTY(bool password READ isPassword WRITE setPassword)
|
||||
Q_PROPERTY(bool use3DKeyboard READ getUse3DKeyboard);
|
||||
Q_PROPERTY(bool use3DKeyboard READ getUse3DKeyboard CONSTANT);
|
||||
Q_PROPERTY(bool preferMalletsOverLasers READ getPreferMalletsOverLasers CONSTANT)
|
||||
|
||||
public:
|
||||
KeyboardScriptingInterface() = default;
|
||||
~KeyboardScriptingInterface() = default;
|
||||
Q_INVOKABLE void loadKeyboardFile(const QString& string);
|
||||
|
||||
Q_INVOKABLE void enableLeftMallet();
|
||||
Q_INVOKABLE void enableRightMallet();
|
||||
Q_INVOKABLE void disableLeftMallet();
|
||||
Q_INVOKABLE void disableRightMallet();
|
||||
Q_INVOKABLE void setLeftHandLaser(unsigned int leftHandLaser);
|
||||
Q_INVOKABLE void setRightHandLaser(unsigned int rightHandLaser);
|
||||
Q_INVOKABLE bool containsID(OverlayID overlayID) const;
|
||||
private:
|
||||
bool isRaised();
|
||||
bool getPreferMalletsOverLasers() const;
|
||||
bool isRaised() const;
|
||||
void setRaised(bool raised);
|
||||
|
||||
bool isPassword();
|
||||
bool isPassword() const;
|
||||
void setPassword(bool password);
|
||||
|
||||
bool getUse3DKeyboard();
|
||||
bool getUse3DKeyboard() const;
|
||||
};
|
||||
#endif
|
||||
|
|
|
@ -351,6 +351,12 @@ void Keyboard::raiseKeyboardAnchor(bool raise) const {
|
|||
};
|
||||
|
||||
overlays.editOverlay(_textDisplay.overlayID, textDisplayProperties);
|
||||
|
||||
auto backPlateOverlay = std::dynamic_pointer_cast<Cube3DOverlay>(overlays.getOverlay(_backPlate.overlayID));
|
||||
|
||||
if (backPlateOverlay) {
|
||||
backPlateOverlay->setVisible(raise);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -382,6 +388,17 @@ void Keyboard::scaleKeyboard(float sensorToWorldScale) {
|
|||
};
|
||||
|
||||
overlays.editOverlay(_textDisplay.overlayID, textDisplayProperties);
|
||||
|
||||
|
||||
glm::vec3 backPlateScaledDimensions = _backPlate.dimensions * sensorToWorldScale;
|
||||
glm::vec3 backPlateScaledLocalPosition = _backPlate.localPosition * sensorToWorldScale;
|
||||
|
||||
QVariantMap backPlateProperties {
|
||||
{ "localPosition", vec3toVariant(backPlateScaledLocalPosition) },
|
||||
{ "dimensions", vec3toVariant(backPlateScaledDimensions) }
|
||||
};
|
||||
|
||||
overlays.editOverlay(_backPlate.overlayID, backPlateProperties);
|
||||
}
|
||||
|
||||
void Keyboard::startLayerSwitchTimer() {
|
||||
|
@ -391,7 +408,7 @@ void Keyboard::startLayerSwitchTimer() {
|
|||
}
|
||||
}
|
||||
|
||||
bool Keyboard::isLayerSwitchTimerFinished() {
|
||||
bool Keyboard::isLayerSwitchTimerFinished() const {
|
||||
if (_layerSwitchTimer) {
|
||||
return (_layerSwitchTimer->remainingTime() <= 0);
|
||||
}
|
||||
|
@ -433,6 +450,16 @@ void Keyboard::setResetKeyboardPositionOnRaise(bool reset) {
|
|||
_resetKeyboardPositionOnRaise = reset;
|
||||
});
|
||||
}
|
||||
void Keyboard::setPreferMalletsOverLasers(bool preferMalletsOverLasers) {
|
||||
_preferMalletsOverLasersSettingLock.withWriteLock([&] {
|
||||
_preferMalletsOverLasers.set(preferMalletsOverLasers);
|
||||
});
|
||||
}
|
||||
|
||||
bool Keyboard::getPreferMalletsOverLasers() const {
|
||||
return _preferMalletsOverLasersSettingLock.resultWithReadLock<bool>([&] {
|
||||
return _preferMalletsOverLasers.get();
|
||||
});
|
||||
}
|
||||
|
||||
void Keyboard::switchToLayer(int layerIndex) {
|
||||
|
@ -469,15 +496,21 @@ void Keyboard::switchToLayer(int layerIndex) {
|
|||
}
|
||||
}
|
||||
|
||||
bool Keyboard::shouldProcessOverlayAndPointerEvent(const PointerEvent& event, const OverlayID& overlayID) const {
|
||||
return (shouldProcessPointerEvent(event) && shouldProcessOverlay(overlayID));
|
||||
}
|
||||
|
||||
bool Keyboard::shouldProcessPointerEvent(const PointerEvent& event) const {
|
||||
bool preferMalletsOverLasers = getPreferMalletsOverLasers();
|
||||
unsigned int pointerID = event.getID();
|
||||
bool isStylusEvent = (pointerID == _leftHandStylus || pointerID == _rightHandStylus);
|
||||
bool isLaserEvent = (pointerID == _leftHandLaser || pointerID == _rightHandLaser);
|
||||
return ((isStylusEvent && preferMalletsOverLasers) || (isLaserEvent && !preferMalletsOverLasers));
|
||||
}
|
||||
|
||||
void Keyboard::handleTriggerBegin(const OverlayID& overlayID, const PointerEvent& event) {
|
||||
if (_keyboardLayers.empty() || !isLayerSwitchTimerFinished()) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto pointerID = event.getID();
|
||||
auto buttonType = event.getButton();
|
||||
|
||||
if ((pointerID != _leftHandStylus && pointerID != _rightHandStylus) || buttonType != PointerEvent::PrimaryButton) {
|
||||
if (!shouldProcessOverlayAndPointerEvent(event, overlayID) || buttonType != PointerEvent::PrimaryButton) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -491,8 +524,10 @@ void Keyboard::handleTriggerBegin(const OverlayID& overlayID, const PointerEvent
|
|||
Key& key = search.value();
|
||||
|
||||
if (key.timerFinished()) {
|
||||
unsigned int pointerID = event.getID();
|
||||
auto handIndex = (pointerID == _leftHandStylus || pointerID == _leftHandLaser)
|
||||
? controller::Hand::LEFT : controller::Hand::RIGHT;
|
||||
|
||||
auto handIndex = (pointerID == _leftHandStylus) ? controller::Hand::LEFT : controller::Hand::RIGHT;
|
||||
auto userInputMapper = DependencyManager::get<UserInputMapper>();
|
||||
userInputMapper->triggerHapticPulse(PULSE_STRENGTH, PULSE_DURATION, handIndex);
|
||||
|
||||
|
@ -560,19 +595,28 @@ void Keyboard::handleTriggerBegin(const OverlayID& overlayID, const PointerEvent
|
|||
QCoreApplication::postEvent(QCoreApplication::instance(), pressEvent);
|
||||
QCoreApplication::postEvent(QCoreApplication::instance(), releaseEvent);
|
||||
|
||||
key.startTimer(KEY_PRESS_TIMEOUT_MS);
|
||||
if (!getPreferMalletsOverLasers()) {
|
||||
key.startTimer(KEY_PRESS_TIMEOUT_MS);
|
||||
}
|
||||
auto selection = DependencyManager::get<SelectionScriptingInterface>();
|
||||
selection->addToSelectedItemsList(KEY_PRESSED_HIGHLIGHT, "overlay", overlayID);
|
||||
}
|
||||
}
|
||||
|
||||
void Keyboard::handleTriggerEnd(const OverlayID& overlayID, const PointerEvent& event) {
|
||||
if (_keyboardLayers.empty() || !isLayerSwitchTimerFinished()) {
|
||||
return;
|
||||
}
|
||||
void Keyboard::setLeftHandLaser(unsigned int leftHandLaser) {
|
||||
_handLaserLock.withWriteLock([&] {
|
||||
_leftHandLaser = leftHandLaser;
|
||||
});
|
||||
}
|
||||
|
||||
auto pointerID = event.getID();
|
||||
if (pointerID != _leftHandStylus && pointerID != _rightHandStylus) {
|
||||
void Keyboard::setRightHandLaser(unsigned int rightHandLaser) {
|
||||
_handLaserLock.withWriteLock([&] {
|
||||
_rightHandLaser = rightHandLaser;
|
||||
});
|
||||
}
|
||||
|
||||
void Keyboard::handleTriggerEnd(const OverlayID& overlayID, const PointerEvent& event) {
|
||||
if (!shouldProcessOverlayAndPointerEvent(event, overlayID)) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -592,7 +636,7 @@ void Keyboard::handleTriggerEnd(const OverlayID& overlayID, const PointerEvent&
|
|||
}
|
||||
|
||||
key.setIsPressed(false);
|
||||
if (key.timerFinished()) {
|
||||
if (key.timerFinished() && getPreferMalletsOverLasers()) {
|
||||
key.startTimer(KEY_PRESS_TIMEOUT_MS);
|
||||
}
|
||||
|
||||
|
@ -601,13 +645,7 @@ void Keyboard::handleTriggerEnd(const OverlayID& overlayID, const PointerEvent&
|
|||
}
|
||||
|
||||
void Keyboard::handleTriggerContinue(const OverlayID& overlayID, const PointerEvent& event) {
|
||||
if (_keyboardLayers.empty() || !isLayerSwitchTimerFinished()) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto pointerID = event.getID();
|
||||
|
||||
if (pointerID != _leftHandStylus && pointerID != _rightHandStylus) {
|
||||
if (!shouldProcessOverlayAndPointerEvent(event, overlayID)) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -621,10 +659,11 @@ void Keyboard::handleTriggerContinue(const OverlayID& overlayID, const PointerEv
|
|||
Key& key = search.value();
|
||||
Overlays& overlays = qApp->getOverlays();
|
||||
|
||||
if (!key.isPressed()) {
|
||||
if (!key.isPressed() && getPreferMalletsOverLasers()) {
|
||||
auto base3DOverlay = std::dynamic_pointer_cast<Base3DOverlay>(overlays.getOverlay(overlayID));
|
||||
|
||||
if (base3DOverlay) {
|
||||
unsigned int pointerID = event.getID();
|
||||
auto pointerManager = DependencyManager::get<PointerManager>();
|
||||
auto pickResult = pointerManager->getPrevPickResult(pointerID);
|
||||
auto stylusPickResult = std::dynamic_pointer_cast<StylusPickResult>(pickResult);
|
||||
|
@ -645,13 +684,7 @@ void Keyboard::handleTriggerContinue(const OverlayID& overlayID, const PointerEv
|
|||
}
|
||||
|
||||
void Keyboard::handleHoverBegin(const OverlayID& overlayID, const PointerEvent& event) {
|
||||
if (_keyboardLayers.empty() || !isLayerSwitchTimerFinished()) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto pointerID = event.getID();
|
||||
|
||||
if (pointerID != _leftHandStylus && pointerID != _rightHandStylus) {
|
||||
if (!shouldProcessOverlayAndPointerEvent(event, overlayID)) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -667,13 +700,7 @@ void Keyboard::handleHoverBegin(const OverlayID& overlayID, const PointerEvent&
|
|||
}
|
||||
|
||||
void Keyboard::handleHoverEnd(const OverlayID& overlayID, const PointerEvent& event) {
|
||||
if (_keyboardLayers.empty() || !isLayerSwitchTimerFinished()) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto pointerID = event.getID();
|
||||
|
||||
if (pointerID != _leftHandStylus && pointerID != _rightHandStylus) {
|
||||
if (!shouldProcessOverlayAndPointerEvent(event, overlayID)) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -765,6 +792,27 @@ void Keyboard::loadKeyboardFile(const QString& keyboardFile) {
|
|||
anchor.originalDimensions = dimensions;
|
||||
_anchor = anchor;
|
||||
|
||||
QJsonObject backPlateObject = jsonObject["backPlate"].toObject();
|
||||
|
||||
QVariantMap backPlateProperties {
|
||||
{ "name", "backPlate"},
|
||||
{ "isSolid", true },
|
||||
{ "visible", true },
|
||||
{ "grabbable", false },
|
||||
{ "alpha", 0.0 },
|
||||
{ "ignoreRayIntersection", false},
|
||||
{ "dimensions", backPlateObject["dimensions"].toVariant() },
|
||||
{ "position", backPlateObject["position"].toVariant() },
|
||||
{ "orientation", backPlateObject["rotation"].toVariant() },
|
||||
{ "parentID", _anchor.overlayID }
|
||||
};
|
||||
|
||||
BackPlate backPlate;
|
||||
backPlate.overlayID = overlays.addOverlay("cube", backPlateProperties);
|
||||
backPlate.dimensions = vec3FromVariant(backPlateObject["dimensions"].toVariant());
|
||||
backPlate.localPosition = vec3FromVariant(overlays.getProperty(backPlate.overlayID, "localPosition").value);
|
||||
_backPlate = backPlate;
|
||||
|
||||
const QJsonArray& keyboardLayers = jsonObject["layers"].toArray();
|
||||
int keyboardLayerCount = keyboardLayers.size();
|
||||
_keyboardLayers.reserve(keyboardLayerCount);
|
||||
|
@ -871,12 +919,17 @@ void Keyboard::loadKeyboardFile(const QString& keyboardFile) {
|
|||
request->send();
|
||||
}
|
||||
|
||||
|
||||
OverlayID Keyboard::getAnchorID() {
|
||||
return _ignoreItemsLock.resultWithReadLock<OverlayID>([&] {
|
||||
return _anchor.overlayID;
|
||||
});
|
||||
}
|
||||
|
||||
bool Keyboard::shouldProcessOverlay(const OverlayID& overlayID) const {
|
||||
return (!_keyboardLayers.empty() && isLayerSwitchTimerFinished() && overlayID != _backPlate.overlayID);
|
||||
}
|
||||
|
||||
QVector<OverlayID> Keyboard::getKeysID() {
|
||||
return _ignoreItemsLock.resultWithReadLock<QVector<OverlayID>>([&] {
|
||||
return _itemsToIgnore;
|
||||
|
@ -894,6 +947,7 @@ void Keyboard::clearKeyboardKeys() {
|
|||
|
||||
overlays.deleteOverlay(_anchor.overlayID);
|
||||
overlays.deleteOverlay(_textDisplay.overlayID);
|
||||
overlays.deleteOverlay(_backPlate.overlayID);
|
||||
|
||||
_keyboardLayers.clear();
|
||||
|
||||
|
@ -903,10 +957,38 @@ void Keyboard::clearKeyboardKeys() {
|
|||
}
|
||||
|
||||
void Keyboard::enableStylus() {
|
||||
if (getPreferMalletsOverLasers()) {
|
||||
enableRightMallet();
|
||||
enableLeftMallet();
|
||||
}
|
||||
}
|
||||
|
||||
void Keyboard::enableRightMallet() {
|
||||
auto pointerManager = DependencyManager::get<PointerManager>();
|
||||
pointerManager->setRenderState(_leftHandStylus, "events on");
|
||||
pointerManager->enablePointer(_leftHandStylus);
|
||||
pointerManager->setRenderState(_rightHandStylus, "events on");
|
||||
pointerManager->enablePointer(_rightHandStylus);
|
||||
|
||||
}
|
||||
|
||||
void Keyboard::enableLeftMallet() {
|
||||
auto pointerManager = DependencyManager::get<PointerManager>();
|
||||
pointerManager->setRenderState(_leftHandStylus, "events on");
|
||||
pointerManager->enablePointer(_leftHandStylus);
|
||||
}
|
||||
|
||||
void Keyboard::disableLeftMallet() {
|
||||
auto pointerManager = DependencyManager::get<PointerManager>();
|
||||
pointerManager->setRenderState(_leftHandStylus, "events off");
|
||||
pointerManager->disablePointer(_leftHandStylus);
|
||||
}
|
||||
|
||||
void Keyboard::disableRightMallet() {
|
||||
auto pointerManager = DependencyManager::get<PointerManager>();
|
||||
pointerManager->setRenderState(_rightHandStylus, "events off");
|
||||
pointerManager->disablePointer(_rightHandStylus);
|
||||
}
|
||||
|
||||
bool Keyboard::containsID(OverlayID overlayID) const {
|
||||
return resultWithReadLock<bool>([&] {
|
||||
return _itemsToIgnore.contains(overlayID) || _backPlate.overlayID == overlayID;
|
||||
});
|
||||
}
|
||||
|
|
|
@ -87,7 +87,8 @@ private:
|
|||
std::shared_ptr<QTimer> _timer { std::make_shared<QTimer>() };
|
||||
};
|
||||
|
||||
class Keyboard : public Dependency, public QObject, public ReadWriteLockable {
|
||||
class Keyboard : public QObject, public Dependency, public ReadWriteLockable {
|
||||
Q_OBJECT
|
||||
public:
|
||||
Keyboard();
|
||||
void createKeyboard();
|
||||
|
@ -97,9 +98,20 @@ public:
|
|||
void setResetKeyboardPositionOnRaise(bool reset);
|
||||
bool isPassword() const;
|
||||
void setPassword(bool password);
|
||||
void enableRightMallet();
|
||||
void enableLeftMallet();
|
||||
void disableRightMallet();
|
||||
void disableLeftMallet();
|
||||
|
||||
void setLeftHandLaser(unsigned int leftHandLaser);
|
||||
void setRightHandLaser(unsigned int rightHandLaser);
|
||||
|
||||
void setPreferMalletsOverLasers(bool preferMalletsOverLasers);
|
||||
bool getPreferMalletsOverLasers() const;
|
||||
|
||||
bool getUse3DKeyboard() const;
|
||||
void setUse3DKeyboard(bool use);
|
||||
bool containsID(OverlayID overlayID) const;
|
||||
|
||||
void loadKeyboardFile(const QString& keyboardFile);
|
||||
QVector<OverlayID> getKeysID();
|
||||
|
@ -119,6 +131,12 @@ private:
|
|||
glm::vec3 originalDimensions;
|
||||
};
|
||||
|
||||
struct BackPlate {
|
||||
OverlayID overlayID;
|
||||
glm::vec3 dimensions;
|
||||
glm::vec3 localPosition;
|
||||
};
|
||||
|
||||
struct TextDisplay {
|
||||
float lineHeight;
|
||||
OverlayID overlayID;
|
||||
|
@ -128,35 +146,44 @@ private:
|
|||
|
||||
void raiseKeyboard(bool raise) const;
|
||||
void raiseKeyboardAnchor(bool raise) const;
|
||||
|
||||
void setLayerIndex(int layerIndex);
|
||||
void enableStylus();
|
||||
void disableStylus();
|
||||
|
||||
void setLayerIndex(int layerIndex);
|
||||
void clearKeyboardKeys();
|
||||
void switchToLayer(int layerIndex);
|
||||
void updateTextDisplay();
|
||||
bool shouldProcessOverlayAndPointerEvent(const PointerEvent& event, const OverlayID& overlayID) const;
|
||||
bool shouldProcessPointerEvent(const PointerEvent& event) const;
|
||||
bool shouldProcessOverlay(const OverlayID& overlayID) const;
|
||||
|
||||
void startLayerSwitchTimer();
|
||||
bool isLayerSwitchTimerFinished();
|
||||
bool isLayerSwitchTimerFinished() const;
|
||||
|
||||
bool _raised { false };
|
||||
bool _resetKeyboardPositionOnRaise { true };
|
||||
bool _password { false };
|
||||
bool _capsEnabled { false };
|
||||
int _layerIndex { 0 };
|
||||
Setting::Handle<bool> _preferMalletsOverLasers { "preferMalletsOverLaser", true };
|
||||
unsigned int _leftHandStylus { 0 };
|
||||
unsigned int _rightHandStylus { 0 };
|
||||
unsigned int _leftHandLaser { 0 };
|
||||
unsigned int _rightHandLaser { 0 };
|
||||
SharedSoundPointer _keySound { nullptr };
|
||||
std::shared_ptr<QTimer> _layerSwitchTimer { std::make_shared<QTimer>() };
|
||||
|
||||
mutable ReadWriteLockable _use3DKeyboardLock;
|
||||
mutable ReadWriteLockable _handLaserLock;
|
||||
mutable ReadWriteLockable _preferMalletsOverLasersSettingLock;
|
||||
mutable ReadWriteLockable _ignoreItemsLock;
|
||||
Setting::Handle<bool> _use3DKeyboard { "use3DKeyboard", true };
|
||||
|
||||
QString _typedCharacters;
|
||||
TextDisplay _textDisplay;
|
||||
Anchor _anchor;
|
||||
BackPlate _backPlate;
|
||||
|
||||
mutable ReadWriteLockable _ignoreItemsLock;
|
||||
QVector<OverlayID> _itemsToIgnore;
|
||||
std::vector<QHash<OverlayID, Key>> _keyboardLayers;
|
||||
};
|
||||
|
|
|
@ -107,12 +107,6 @@ void setupPreferences() {
|
|||
preferences->addPreference(preference);
|
||||
}
|
||||
|
||||
{
|
||||
auto getter = []()->bool { return qApp->getPreferStylusOverLaser(); };
|
||||
auto setter = [](bool value) { qApp->setPreferStylusOverLaser(value); };
|
||||
preferences->addPreference(new CheckPreference(UI_CATEGORY, "Prefer Stylus Over Laser", getter, setter));
|
||||
}
|
||||
|
||||
{
|
||||
static const QString RETICLE_ICON_NAME = { Cursor::Manager::getIconName(Cursor::Icon::RETICLE) };
|
||||
auto getter = []()->bool { return qApp->getPreferredCursor() == RETICLE_ICON_NAME; };
|
||||
|
@ -121,15 +115,38 @@ void setupPreferences() {
|
|||
}
|
||||
|
||||
{
|
||||
auto getter = []()->bool { return DependencyManager::get<Keyboard>()->getUse3DKeyboard(); };
|
||||
auto setter = [](bool value) { DependencyManager::get<Keyboard>()->setUse3DKeyboard(value); };
|
||||
auto getter = []()->bool { return qApp->getMiniTabletEnabled(); };
|
||||
auto setter = [](bool value) { qApp->setMiniTabletEnabled(value); };
|
||||
preferences->addPreference(new CheckPreference(UI_CATEGORY, "Use mini tablet", getter, setter));
|
||||
}
|
||||
|
||||
{
|
||||
auto getter = []()->int { return DependencyManager::get<Keyboard>()->getUse3DKeyboard(); };
|
||||
auto setter = [](int value) { DependencyManager::get<Keyboard>()->setUse3DKeyboard(value); };
|
||||
preferences->addPreference(new CheckPreference(UI_CATEGORY, "Use Virtual Keyboard", getter, setter));
|
||||
}
|
||||
|
||||
{
|
||||
auto getter = []()->bool { return qApp->getMiniTabletEnabled(); };
|
||||
auto setter = [](bool value) { qApp->setMiniTabletEnabled(value); };
|
||||
preferences->addPreference(new CheckPreference(UI_CATEGORY, "Use mini tablet", getter, setter));
|
||||
auto getter = []()->bool { return DependencyManager::get<Keyboard>()->getPreferMalletsOverLasers() ? 1 : 0; };
|
||||
auto setter = [](bool value) { return DependencyManager::get<Keyboard>()->setPreferMalletsOverLasers((bool)value); };
|
||||
auto preference = new RadioButtonsPreference(UI_CATEGORY, "Keyboard laser / mallets", getter, setter);
|
||||
QStringList items;
|
||||
items << "Lasers" << "Mallets";
|
||||
preference->setItems(items);
|
||||
preference->setIndented(true);
|
||||
preferences->addPreference(preference);
|
||||
}
|
||||
|
||||
|
||||
{
|
||||
auto getter = []()->int { return qApp->getPreferStylusOverLaser() ? 1 : 0; };
|
||||
auto setter = [](int value) { qApp->setPreferStylusOverLaser((bool)value); };
|
||||
auto preference = new RadioButtonsPreference(UI_CATEGORY, "Tablet stylys / laser", getter, setter);
|
||||
QStringList items;
|
||||
items << "Lasers" << "Stylus";
|
||||
preference->setHeading("Tablet Input Mechanism");
|
||||
preference->setItems(items);
|
||||
preferences->addPreference(preference);
|
||||
}
|
||||
|
||||
static const QString VIEW_CATEGORY{ "View" };
|
||||
|
@ -151,15 +168,14 @@ void setupPreferences() {
|
|||
preferences->addPreference(preference);
|
||||
}
|
||||
|
||||
|
||||
// FIXME: Remove setting completely or make available through JavaScript API?
|
||||
/*
|
||||
// FIXME: Remove setting completely or make available through JavaScript API?
|
||||
{
|
||||
auto getter = []()->bool { return qApp->getPreferAvatarFingerOverStylus(); };
|
||||
auto setter = [](bool value) { qApp->setPreferAvatarFingerOverStylus(value); };
|
||||
preferences->addPreference(new CheckPreference(UI_CATEGORY, "Prefer Avatar Finger Over Stylus", getter, setter));
|
||||
}
|
||||
*/
|
||||
}*/
|
||||
|
||||
// Snapshots
|
||||
static const QString SNAPSHOTS { "Snapshots" };
|
||||
{
|
||||
|
|
|
@ -556,6 +556,7 @@ void ModelOverlay::locationChanged(bool tellPhysics) {
|
|||
if (_model && _model->isActive()) {
|
||||
_model->setRotation(getWorldOrientation());
|
||||
_model->setTranslation(getWorldPosition());
|
||||
_updateModel = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -539,7 +539,7 @@ RayToOverlayIntersectionResult Overlays::findRayIntersectionVector(const PickRay
|
|||
bool bestIsFront = false;
|
||||
bool bestIsTablet = false;
|
||||
auto tabletIDs = qApp->getTabletIDs();
|
||||
const QVector<OverlayID> keyboardKeysToDiscard = DependencyManager::get<Keyboard>()->getKeysID();
|
||||
|
||||
QMutexLocker locker(&_mutex);
|
||||
RayToOverlayIntersectionResult result;
|
||||
QMapIterator<OverlayID, Overlay::Pointer> i(_overlaysWorld);
|
||||
|
@ -549,8 +549,7 @@ RayToOverlayIntersectionResult Overlays::findRayIntersectionVector(const PickRay
|
|||
auto thisOverlay = std::dynamic_pointer_cast<Base3DOverlay>(i.value());
|
||||
|
||||
if ((overlaysToDiscard.size() > 0 && overlaysToDiscard.contains(thisID)) ||
|
||||
(overlaysToInclude.size() > 0 && !overlaysToInclude.contains(thisID)) ||
|
||||
(keyboardKeysToDiscard.size() > 0 && keyboardKeysToDiscard.contains(thisID))) {
|
||||
(overlaysToInclude.size() > 0 && !overlaysToInclude.contains(thisID))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
|
@ -364,6 +364,7 @@ class RadioButtonsPreference : public IntPreference {
|
|||
Q_OBJECT
|
||||
Q_PROPERTY(QString heading READ getHeading CONSTANT)
|
||||
Q_PROPERTY(QStringList items READ getItems CONSTANT)
|
||||
Q_PROPERTY(bool indented READ getIndented CONSTANT)
|
||||
public:
|
||||
RadioButtonsPreference(const QString& category, const QString& name, Getter getter, Setter setter)
|
||||
: IntPreference(category, name, getter, setter) { }
|
||||
|
@ -373,10 +374,13 @@ public:
|
|||
const QStringList& getItems() { return _items; }
|
||||
void setHeading(const QString& heading) { _heading = heading; }
|
||||
void setItems(const QStringList& items) { _items = items; }
|
||||
bool getIndented() const { return _indented; }
|
||||
void setIndented(const bool indented) { _indented = indented; }
|
||||
|
||||
protected:
|
||||
QString _heading;
|
||||
QStringList _items;
|
||||
bool _indented { false };
|
||||
};
|
||||
#endif
|
||||
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
LEFT_HAND, RIGHT_HAND, NEAR_GRAB_PICK_RADIUS, DEFAULT_SEARCH_SPHERE_DISTANCE, DISPATCHER_PROPERTIES,
|
||||
getGrabPointSphereOffset, HMD, MyAvatar, Messages, findHandChildEntities, Picks, PickType, Pointers,
|
||||
PointerManager, getGrabPointSphereOffset, HMD, MyAvatar, Messages, findHandChildEntities, Picks, PickType, Pointers,
|
||||
PointerManager, print, Selection, DISPATCHER_HOVERING_LIST, DISPATCHER_HOVERING_STYLE
|
||||
PointerManager, print, Selection, DISPATCHER_HOVERING_LIST, DISPATCHER_HOVERING_STYLE, Keyboard
|
||||
*/
|
||||
|
||||
controllerDispatcherPlugins = {};
|
||||
|
@ -463,6 +463,7 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js");
|
|||
distanceScaleEnd: true,
|
||||
hand: LEFT_HAND
|
||||
});
|
||||
Keyboard.setLeftHandLaser(this.leftPointer);
|
||||
this.rightPointer = this.pointerManager.createPointer(false, PickType.Ray, {
|
||||
joint: "_CAMERA_RELATIVE_CONTROLLER_RIGHTHAND",
|
||||
filter: Picks.PICK_OVERLAYS | Picks.PICK_ENTITIES | Picks.PICK_INCLUDE_NONCOLLIDABLE,
|
||||
|
@ -473,6 +474,7 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js");
|
|||
distanceScaleEnd: true,
|
||||
hand: RIGHT_HAND
|
||||
});
|
||||
Keyboard.setRightHandLaser(this.rightPointer);
|
||||
this.leftHudPointer = this.pointerManager.createPointer(true, PickType.Ray, {
|
||||
joint: "_CAMERA_RELATIVE_CONTROLLER_LEFTHAND",
|
||||
filter: Picks.PICK_HUD,
|
||||
|
|
|
@ -128,7 +128,7 @@ Script.include("/~/system/libraries/controllers.js");
|
|||
}
|
||||
}
|
||||
|
||||
var WEB_DISPLAY_STYLUS_DISTANCE = 0.5;
|
||||
const WEB_DISPLAY_STYLUS_DISTANCE = (Keyboard.raised && Keyboard.preferMalletsOverLasers) ? 0.2 : 0.5;
|
||||
var nearStylusTarget = isNearStylusTarget(stylusTargets, WEB_DISPLAY_STYLUS_DISTANCE * sensorScaleFactor);
|
||||
|
||||
if (nearStylusTarget.length !== 0) {
|
||||
|
@ -152,9 +152,13 @@ Script.include("/~/system/libraries/controllers.js");
|
|||
|
||||
if (isUsingStylus && this.processStylus(controllerData)) {
|
||||
Pointers.enablePointer(this.pointer);
|
||||
this.hand === RIGHT_HAND ? Keyboard.disableRightMallet() : Keyboard.disableLeftMallet();
|
||||
return makeRunningValues(true, [], []);
|
||||
} else {
|
||||
Pointers.disablePointer(this.pointer);
|
||||
if (Keyboard.raised && Keyboard.preferMalletsOverLasers) {
|
||||
this.hand === RIGHT_HAND ? Keyboard.enableRightMallet() : Keyboard.enableLeftMallet();
|
||||
}
|
||||
return makeRunningValues(false, [], []);
|
||||
}
|
||||
};
|
||||
|
|
|
@ -14,11 +14,21 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js");
|
|||
Script.include("/~/system/libraries/controllers.js");
|
||||
|
||||
(function() {
|
||||
const intersectionType = {
|
||||
None: 0,
|
||||
WebOverlay: 1,
|
||||
WebEntity: 2,
|
||||
HifiKeyboard: 3,
|
||||
Overlay: 4,
|
||||
HifiTablet: 5,
|
||||
};
|
||||
|
||||
function WebSurfaceLaserInput(hand) {
|
||||
this.hand = hand;
|
||||
this.otherHand = this.hand === RIGHT_HAND ? LEFT_HAND : RIGHT_HAND;
|
||||
this.running = false;
|
||||
this.ignoredObjects = [];
|
||||
this.intersectedType = intersectionType["None"];
|
||||
|
||||
this.parameters = makeDispatcherModuleParameters(
|
||||
160,
|
||||
|
@ -115,7 +125,7 @@ Script.include("/~/system/libraries/controllers.js");
|
|||
this.ignoredObjects = [];
|
||||
};
|
||||
|
||||
this.isPointingAtTriggerable = function(controllerData, triggerPressed, checkEntitiesOnly) {
|
||||
this.getInteractableType = function(controllerData, triggerPressed, checkEntitiesOnly) {
|
||||
// allow pointing at tablet, unlocked web entities, or web overlays automatically without pressing trigger,
|
||||
// but for pointing at locked web entities or non-web overlays user must be pressing trigger
|
||||
var intersection = controllerData.rayPicks[this.hand];
|
||||
|
@ -124,18 +134,29 @@ Script.include("/~/system/libraries/controllers.js");
|
|||
if ((HMD.tabletID && objectID === HMD.tabletID) ||
|
||||
(HMD.tabletScreenID && objectID === HMD.tabletScreenID) ||
|
||||
(HMD.homeButtonID && objectID === HMD.homeButtonID)) {
|
||||
return true;
|
||||
return intersectionType["HifiTablet"];
|
||||
} else {
|
||||
var overlayType = Overlays.getOverlayType(objectID);
|
||||
return overlayType === "web3d" || triggerPressed;
|
||||
var type = intersectionType["None"];
|
||||
if (Keyboard.containsID(objectID) && !Keyboard.preferMalletsOverLasers) {
|
||||
type = intersectionType["HifiKeyboard"];
|
||||
} else if (overlayType === "web3d") {
|
||||
type = intersectionType["WebOverlay"];
|
||||
} else if (triggerPressed) {
|
||||
type = intersectionType["Overlay"];
|
||||
}
|
||||
|
||||
return type;
|
||||
}
|
||||
} else if (intersection.type === Picks.INTERSECTED_ENTITY) {
|
||||
var entityProperties = Entities.getEntityProperties(objectID, DISPATCHER_PROPERTIES);
|
||||
var entityType = entityProperties.type;
|
||||
var isLocked = entityProperties.locked;
|
||||
return entityType === "Web" && (!isLocked || triggerPressed);
|
||||
if (entityType === "Web" && (!isLocked || triggerPressed)) {
|
||||
return intersectionType["WebEntity"];
|
||||
}
|
||||
}
|
||||
return false;
|
||||
return intersectionType["None"];
|
||||
};
|
||||
|
||||
this.deleteContextOverlay = function() {
|
||||
|
@ -152,9 +173,9 @@ Script.include("/~/system/libraries/controllers.js");
|
|||
}
|
||||
};
|
||||
|
||||
this.updateAllwaysOn = function() {
|
||||
this.updateAlwaysOn = function(type) {
|
||||
var PREFER_STYLUS_OVER_LASER = "preferStylusOverLaser";
|
||||
this.parameters.handLaser.allwaysOn = !Settings.getValue(PREFER_STYLUS_OVER_LASER, false);
|
||||
this.parameters.handLaser.alwaysOn = (!Settings.getValue(PREFER_STYLUS_OVER_LASER, false) || type === intersectionType["HifiKeyboard"]);
|
||||
};
|
||||
|
||||
this.getDominantHand = function() {
|
||||
|
@ -164,19 +185,28 @@ Script.include("/~/system/libraries/controllers.js");
|
|||
this.dominantHandOverride = false;
|
||||
|
||||
this.isReady = function(controllerData) {
|
||||
var otherModuleRunning = this.getOtherModule().running;
|
||||
otherModuleRunning = otherModuleRunning && this.getDominantHand() !== this.hand; // Auto-swap to dominant hand.
|
||||
var isTriggerPressed = controllerData.triggerValues[this.hand] > TRIGGER_OFF_VALUE &&
|
||||
controllerData.triggerValues[this.otherHand] <= TRIGGER_OFF_VALUE;
|
||||
var allowThisModule = !otherModuleRunning || isTriggerPressed;
|
||||
var type = this.getInteractableType(controllerData, isTriggerPressed, false);
|
||||
|
||||
if ((allowThisModule && this.isPointingAtTriggerable(controllerData, isTriggerPressed, false)) && !this.grabModuleWantsNearbyOverlay(controllerData)) {
|
||||
this.updateAllwaysOn();
|
||||
if (isTriggerPressed) {
|
||||
this.dominantHandOverride = true; // Override dominant hand.
|
||||
this.getOtherModule().dominantHandOverride = false;
|
||||
if (type !== intersectionType["None"] && !this.grabModuleWantsNearbyOverlay(controllerData)) {
|
||||
if (type === intersectionType["WebOverlay"] || type === intersectionType["WebEntity"] || type === intersectionType["HifiTablet"]) {
|
||||
var otherModuleRunning = this.getOtherModule().running;
|
||||
otherModuleRunning = otherModuleRunning && this.getDominantHand() !== this.hand; // Auto-swap to dominant hand.
|
||||
var allowThisModule = !otherModuleRunning || isTriggerPressed;
|
||||
|
||||
if (!allowThisModule) {
|
||||
return makeRunningValues(true, [], []);
|
||||
}
|
||||
|
||||
if (isTriggerPressed) {
|
||||
this.dominantHandOverride = true; // Override dominant hand.
|
||||
this.getOtherModule().dominantHandOverride = false;
|
||||
}
|
||||
}
|
||||
if (this.parameters.handLaser.allwaysOn || isTriggerPressed) {
|
||||
|
||||
this.updateAlwaysOn(type);
|
||||
if (this.parameters.handLaser.alwaysOn || isTriggerPressed) {
|
||||
return makeRunningValues(true, [], []);
|
||||
}
|
||||
}
|
||||
|
@ -187,33 +217,37 @@ Script.include("/~/system/libraries/controllers.js");
|
|||
return makeRunningValues(false, [], []);
|
||||
};
|
||||
|
||||
this.run = function(controllerData, deltaTime) {
|
||||
this.shouldThisModuleRun = function(controllerData) {
|
||||
var otherModuleRunning = this.getOtherModule().running;
|
||||
otherModuleRunning = otherModuleRunning && this.getDominantHand() !== this.hand; // Auto-swap to dominant hand.
|
||||
otherModuleRunning = otherModuleRunning || this.getOtherModule().dominantHandOverride; // Override dominant hand.
|
||||
var grabModuleNeedsToRun = this.grabModuleWantsNearbyOverlay(controllerData);
|
||||
// only allow for non-near grab
|
||||
var allowThisModule = !otherModuleRunning && !grabModuleNeedsToRun;
|
||||
var isTriggerPressed = controllerData.triggerValues[this.hand] > TRIGGER_OFF_VALUE;
|
||||
var laserOn = isTriggerPressed || this.parameters.handLaser.allwaysOn;
|
||||
return !otherModuleRunning && !grabModuleNeedsToRun;
|
||||
};
|
||||
|
||||
this.run = function(controllerData, deltaTime) {
|
||||
this.addObjectToIgnoreList(controllerData);
|
||||
if (allowThisModule) {
|
||||
if (isTriggerPressed && !this.isPointingAtTriggerable(controllerData, isTriggerPressed, true)) {
|
||||
// if trigger is down + not pointing at a web entity, keep running web surface laser
|
||||
var type = this.getInteractableType(controllerData, isTriggerPressed, false);
|
||||
var isTriggerPressed = controllerData.triggerValues[this.hand] > TRIGGER_OFF_VALUE;
|
||||
var laserOn = isTriggerPressed || this.parameters.handLaser.alwaysOn;
|
||||
this.addObjectToIgnoreList(controllerData);
|
||||
|
||||
if (type === intersectionType["HifiTablet"] && laserOn) {
|
||||
if (this.shouldThisModuleRun(controllerData)) {
|
||||
this.running = true;
|
||||
return makeRunningValues(true, [], []);
|
||||
} else if (laserOn && this.isPointingAtTriggerable(controllerData, isTriggerPressed, false)) {
|
||||
// if trigger is down + pointing at a web entity/overlay, keep running web surface laser
|
||||
this.running = true;
|
||||
return makeRunningValues(true, [], []);
|
||||
} else {
|
||||
this.deleteContextOverlay();
|
||||
this.running = false;
|
||||
this.dominantHandOverride = false;
|
||||
return makeRunningValues(false, [], []);
|
||||
}
|
||||
} else if ((type === intersectionType["WebOverlay"] || type === intersectionType["WebEntity"]) && laserOn) { // auto laser on WebEntities andWebOverlays
|
||||
if (this.shouldThisModuleRun(controllerData)) {
|
||||
this.running = true;
|
||||
return makeRunningValues(true, [], []);
|
||||
}
|
||||
} else if ((type === intersectionType["HifiKeyboard"] && laserOn) || type === intersectionType["Overlay"]) {
|
||||
this.running = true;
|
||||
return makeRunningValues(true, [], []);
|
||||
}
|
||||
// if module needs to stop from near grabs or other modules are running, stop it.
|
||||
|
||||
this.deleteContextOverlay();
|
||||
this.running = false;
|
||||
this.dominantHandOverride = false;
|
||||
|
|
|
@ -181,14 +181,14 @@ makeLaserLockInfo = function(targetID, isOverlay, hand, offset) {
|
|||
};
|
||||
};
|
||||
|
||||
makeLaserParams = function(hand, allwaysOn) {
|
||||
if (allwaysOn === undefined) {
|
||||
allwaysOn = false;
|
||||
makeLaserParams = function(hand, alwaysOn) {
|
||||
if (alwaysOn === undefined) {
|
||||
alwaysOn = false;
|
||||
}
|
||||
|
||||
return {
|
||||
hand: hand,
|
||||
allwaysOn: allwaysOn
|
||||
alwaysOn: alwaysOn
|
||||
};
|
||||
};
|
||||
|
||||
|
|
|
@ -145,7 +145,7 @@ Pointer = function(hudLayer, pickType, pointerData) {
|
|||
mode = "hold";
|
||||
} else if (triggerClicks[this.hand]) {
|
||||
mode = "full";
|
||||
} else if (triggerValues[this.hand] > TRIGGER_ON_VALUE || this.allwaysOn) {
|
||||
} else if (triggerValues[this.hand] > TRIGGER_ON_VALUE || this.alwaysOn) {
|
||||
mode = "half";
|
||||
}
|
||||
}
|
||||
|
@ -171,7 +171,7 @@ PointerManager = function() {
|
|||
var index = laserParams.hand;
|
||||
if (index < this.pointers.length && index >= 0) {
|
||||
this.pointers[index].makeVisible();
|
||||
this.pointers[index].allwaysOn = laserParams.allwaysOn;
|
||||
this.pointers[index].alwaysOn = laserParams.alwaysOn;
|
||||
}
|
||||
};
|
||||
|
||||
|
|
Loading…
Reference in a new issue