Merge branch 'master' of https://github.com/highfidelity/hifi into blackProp

This commit is contained in:
Sam Gateau 2019-03-05 17:48:58 -08:00
commit 206bc0addb
37 changed files with 491 additions and 381 deletions

Binary file not shown.

View file

@ -6,7 +6,7 @@ import QtQuick.Layouts 1.3
import TabletScriptingInterface 1.0
import "."
import stylesUit 1.0
import stylesUit 1.0 as HifiStylesUit
import "../audio" as HifiAudio
Item {
@ -49,44 +49,116 @@ Item {
}
Item {
width: 150
height: 50
id: rightContainer
width: clockItem.width > loginItem.width ? clockItem.width + clockAmPmTextMetrics.width :
loginItem.width + clockAmPmTextMetrics.width
height: parent.height
anchors.top: parent.top
anchors.topMargin: 15
anchors.right: parent.right
anchors.rightMargin: 30
anchors.verticalCenter: parent.verticalCenter
anchors.rightMargin: 20
anchors.bottom: parent.bottom
ColumnLayout {
anchors.fill: parent
function timeChanged() {
var date = new Date();
clockTime.text = date.toLocaleTimeString(Qt.locale("en_US"), "h:mm ap");
var regex = /[\sa-zA-z]+/;
clockTime.text = clockTime.text.replace(regex, "");
clockAmPm.text = date.toLocaleTimeString(Qt.locale("en_US"), "ap");
}
RalewaySemiBold {
text: Account.loggedIn ? qsTr("Log out") : qsTr("Log in")
horizontalAlignment: Text.AlignRight
Layout.alignment: Qt.AlignRight
font.pixelSize: 20
color: "#afafaf"
Timer {
interval: 1000; running: true; repeat: true;
onTriggered: rightContainer.timeChanged();
}
Item {
id: clockAmPmItem
width: clockAmPmTextMetrics.width
height: clockAmPmTextMetrics.height
anchors.top: parent.top
anchors.right: parent.right
TextMetrics {
id: clockAmPmTextMetrics
text: clockAmPm.text
font: clockAmPm.font
}
RalewaySemiBold {
visible: Account.loggedIn
height: Account.loggedIn ? parent.height/2 - parent.spacing/2 : 0
text: Account.loggedIn ? "[" + tabletRoot.usernameShort + "]" : ""
horizontalAlignment: Text.AlignRight
Layout.alignment: Qt.AlignRight
font.pixelSize: 20
Text {
anchors.left: parent.left
id: clockAmPm
anchors.right: parent.right
font.capitalization: Font.AllUppercase
font.pixelSize: 12
font.family: "Rawline"
color: "#afafaf"
}
}
MouseArea {
anchors.fill: parent
onClicked: {
if (!Account.loggedIn) {
DialogsManager.showLoginDialog()
} else {
Account.logOut()
Item {
id: clockItem
width: clockTimeTextMetrics.width
height: clockTimeTextMetrics.height
anchors {
top: parent.top
topMargin: -10
right: clockAmPmItem.left
rightMargin: 5
}
TextMetrics {
id: clockTimeTextMetrics
text: clockTime.text
font: clockTime.font
}
Text {
anchors.top: parent.top
anchors.right: parent.right
id: clockTime
font.bold: false
font.pixelSize: 36
font.family: "Rawline"
color: "#afafaf"
}
}
Item {
id: loginItem
width: loginTextMetrics.width
height: loginTextMetrics.height
anchors {
bottom: parent.bottom
bottomMargin: 10
right: clockAmPmItem.left
rightMargin: 5
}
Text {
id: loginText
anchors.right: parent.right
text: Account.loggedIn ? tabletRoot.usernameShort : qsTr("Log in")
horizontalAlignment: Text.AlignRight
Layout.alignment: Qt.AlignRight
font.pixelSize: 18
font.family: "Rawline"
color: "#afafaf"
}
TextMetrics {
id: loginTextMetrics
text: loginText.text
font: loginText.font
}
MouseArea {
anchors.fill: parent
onClicked: {
if (!Account.loggedIn) {
DialogsManager.showLoginDialog();
}
}
}
}
Component.onCompleted: {
rightContainer.timeChanged();
}
}
}

View file

@ -178,10 +178,10 @@ Rectangle {
function setUsername(newUsername) {
username = newUsername;
usernameShort = newUsername.substring(0, 8);
usernameShort = newUsername.substring(0, 14);
if (newUsername.length > 8) {
usernameShort = usernameShort + "..."
if (newUsername.length > 14) {
usernameShort = usernameShort + "..."
}
}

View file

@ -0,0 +1,20 @@
//
// Rawline.qml
//
// Created by Wayne Chen on 25 Feb 2019
// Copyright 2019 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
//
import QtQuick 2.7
Text {
id: root
property real size: 32
font.pixelSize: size
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignLeft
font.family: "Rawline"
}

View file

@ -0,0 +1,20 @@
//
// Rawline.qml
//
// Created by Wayne Chen on 25 Feb 2019
// Copyright 2019 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
//
import QtQuick 2.7
Text {
id: root
property real size: 32
font.pixelSize: size
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignLeft
font.family: "Rawline"
}

View file

@ -16,8 +16,8 @@
#include <QObject>
/**jsdoc
* The <code>HifiAbout</code> API provides information about the version of Interface that is currently running. It also
* provides the ability to open a Web page in an Interface browser window.
* The <code>HifiAbout</code> API provides information about the version of Interface that is currently running. It also
* has the functionality to open a web page in an Interface browser window.
*
* @namespace HifiAbout
*
@ -30,9 +30,9 @@
* @property {string} qtVersion - The Qt version used in Interface that is currently running. <em>Read-only.</em>
*
* @example <caption>Report build information for the version of Interface currently running.</caption>
* print("HiFi build date: " + HifiAbout.buildDate); // 11 Feb 2019
* print("HiFi version: " + HifiAbout.buildVersion); // 0.78.0
* print("Qt version: " + HifiAbout.qtVersion); // 5.10.1
* print("HiFi build date: " + HifiAbout.buildDate); // Returns the build date of the version of Interface currently running on your machine.
* print("HiFi version: " + HifiAbout.buildVersion); // Returns the build version of Interface currently running on your machine.
* print("Qt version: " + HifiAbout.qtVersion); // Returns the Qt version details of the version of Interface currently running on your machine.
*/
class AboutUtil : public QObject {
@ -52,9 +52,9 @@ public:
public slots:
/**jsdoc
* Display a Web page in an Interface browser window.
* Display a web page in an Interface browser window.
* @function HifiAbout.openUrl
* @param {string} url - The URL of the Web page to display.
* @param {string} url - The URL of the web page you want to view in Interface.
*/
void openUrl(const QString &url) const;
private:

View file

@ -1061,6 +1061,10 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
connect(PluginManager::getInstance().data(), &PluginManager::inputDeviceRunningChanged,
controllerScriptingInterface, &controller::ScriptingInterface::updateRunningInputDevices);
EntityTree::setEntityClicksCapturedOperator([this] {
return _controllerScriptingInterface->areEntityClicksCaptured();
});
_entityClipboard->createRootElement();
#ifdef Q_OS_WIN
@ -1075,6 +1079,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
QFontDatabase::addApplicationFont(PathUtils::resourcesPath() + "fonts/FiraSans-SemiBold.ttf");
QFontDatabase::addApplicationFont(PathUtils::resourcesPath() + "fonts/Raleway-Light.ttf");
QFontDatabase::addApplicationFont(PathUtils::resourcesPath() + "fonts/Raleway-Regular.ttf");
QFontDatabase::addApplicationFont(PathUtils::resourcesPath() + "fonts/rawline-500.ttf");
QFontDatabase::addApplicationFont(PathUtils::resourcesPath() + "fonts/Raleway-Bold.ttf");
QFontDatabase::addApplicationFont(PathUtils::resourcesPath() + "fonts/Raleway-SemiBold.ttf");
QFontDatabase::addApplicationFont(PathUtils::resourcesPath() + "fonts/Cairo-SemiBold.ttf");
@ -4391,7 +4396,6 @@ void Application::mouseMoveEvent(QMouseEvent* event) {
if (compositor.getReticleVisible() || !isHMDMode() || !compositor.getReticleOverDesktop() ||
getOverlays().getOverlayAtPoint(glm::vec2(transformedPos.x(), transformedPos.y())) != UNKNOWN_ENTITY_ID) {
getEntities()->mouseMoveEvent(&mappedEvent);
getOverlays().mouseMoveEvent(&mappedEvent);
}
_controllerScriptingInterface->emitMouseMoveEvent(&mappedEvent); // send events to any registered scripts
@ -4425,14 +4429,8 @@ void Application::mousePressEvent(QMouseEvent* event) {
#endif
QMouseEvent mappedEvent(event->type(), transformedPos, event->screenPos(), event->button(), event->buttons(), event->modifiers());
std::pair<float, QUuid> entityResult;
if (!_controllerScriptingInterface->areEntityClicksCaptured()) {
entityResult = getEntities()->mousePressEvent(&mappedEvent);
}
std::pair<float, QUuid> overlayResult = getOverlays().mousePressEvent(&mappedEvent);
QUuid focusedEntity = entityResult.first < overlayResult.first ? entityResult.second : overlayResult.second;
setKeyboardFocusEntity(getEntities()->wantsKeyboardFocus(focusedEntity) ? focusedEntity : UNKNOWN_ENTITY_ID);
QUuid result = getEntities()->mousePressEvent(&mappedEvent);
setKeyboardFocusEntity(getEntities()->wantsKeyboardFocus(result) ? result : UNKNOWN_ENTITY_ID);
_controllerScriptingInterface->emitMousePressEvent(&mappedEvent); // send events to any registered scripts
@ -4471,11 +4469,7 @@ void Application::mouseDoublePressEvent(QMouseEvent* event) {
transformedPos,
event->screenPos(), event->button(),
event->buttons(), event->modifiers());
if (!_controllerScriptingInterface->areEntityClicksCaptured()) {
getEntities()->mouseDoublePressEvent(&mappedEvent);
}
getOverlays().mouseDoublePressEvent(&mappedEvent);
getEntities()->mouseDoublePressEvent(&mappedEvent);
// if one of our scripts have asked to capture this event, then stop processing it
if (_controllerScriptingInterface->isMouseCaptured()) {
@ -4500,7 +4494,6 @@ void Application::mouseReleaseEvent(QMouseEvent* event) {
event->buttons(), event->modifiers());
getEntities()->mouseReleaseEvent(&mappedEvent);
getOverlays().mouseReleaseEvent(&mappedEvent);
_controllerScriptingInterface->emitMouseReleaseEvent(&mappedEvent); // send events to any registered scripts
@ -6950,7 +6943,7 @@ void Application::clearDomainOctreeDetails(bool clearAll) {
});
// reset the model renderer
clearAll ? getEntities()->clear() : getEntities()->clearNonLocalEntities();
clearAll ? getEntities()->clear() : getEntities()->clearDomainAndNonOwnedEntities();
auto skyStage = DependencyManager::get<SceneScriptingInterface>()->getSkyStage();

View file

@ -62,8 +62,8 @@ public:
* <tr><th>Value</th><th>Meaning</th><th>Description</th></tr>
* </thead>
* <tbody>
* <tr><td><code>0</code></td><td>Not logged in</td><td>The user isn't logged in.</td></tr>
* <tr><td><code>1</code></td><td>Not set up</td><td>The user's wallet isn't set up.</td></tr>
* <tr><td><code>0</code></td><td>Not logged in</td><td>The user is not logged in.</td></tr>
* <tr><td><code>1</code></td><td>Not set up</td><td>The user's wallet has not been set up.</td></tr>
* <tr><td><code>2</code></td><td>Pre-existing</td><td>There is a wallet present on the server but not one
* locally.</td></tr>
* <tr><td><code>3</code></td><td>Conflicting</td><td>There is a wallet present on the server plus one present locally,
@ -73,8 +73,8 @@ public:
* <tr><td><code>5</code></td><td>Ready</td><td>The wallet is ready for use.</td></tr>
* </tbody>
* </table>
* <p>Wallets used to be stored locally but now they're stored on the server, unless the computer once had a wallet stored
* locally in which case the wallet may be present in both places.</p>
* <p>Wallets used to be stored locally but now they're only stored on the server. A wallet is present in both places if
* your computer previously stored its information locally.</p>
* @typedef {number} WalletScriptingInterface.WalletStatus
*/
enum WalletStatus {

View file

@ -172,7 +172,10 @@ void LaserPointer::RenderState::update(const glm::vec3& origin, const glm::vec3&
properties.setVisible(true);
properties.setIgnorePickIntersection(doesPathIgnorePicks());
QVector<float> widths;
widths.append(getLineWidth() * parentScale);
float width = getLineWidth() * parentScale;
widths.append(width);
widths.append(width);
properties.setStrokeWidths(widths);
DependencyManager::get<EntityScriptingInterface>()->editEntity(getPathID(), properties);
}
}

View file

@ -115,8 +115,8 @@ DownloadInfoResult::DownloadInfoResult() :
/**jsdoc
* Information on the assets currently being downloaded and pending download.
* @typedef {object} AccountServices.DownloadInfoResult
* @property {number[]} downloading - The percentage complete for each asset currently being downloaded.
* @property {number} pending - The number of assets waiting to be download.
* @property {number[]} downloading - The download percentage remaining of each asset currently downloading.
* @property {number} pending - The number of assets pending download.
*/
QScriptValue DownloadInfoResultToScriptValue(QScriptEngine* engine, const DownloadInfoResult& result) {
QScriptValue object = engine->newObject();

View file

@ -38,19 +38,19 @@ class AccountServicesScriptingInterface : public QObject {
Q_OBJECT
/**jsdoc
* The <code>AccountServices</code> API provides functions related to user connectivity, visibility, and asset download
* progress.
* The <code>AccountServices</code> API provides functions that give information on user connectivity, visibility, and
* asset download progress.
*
* @hifi-interface
* @hifi-client-entity
* @hifi-avatar
*
* @namespace AccountServices
* @property {string} username - The user name if the user is logged in, otherwise <code>"Unknown user"</code>.
* <em>Read-only.</em>
* @property {string} username - The user name of the user logged in. If there is no user logged in, it is
* <code>"Unknown user"</code>. <em>Read-only.</em>
* @property {boolean} loggedIn - <code>true</code> if the user is logged in, otherwise <code>false</code>.
* <em>Read-only.</em>
* @property {string} findableBy - The user's visibility to other people:<br />
* @property {string} findableBy - The user's visibility to other users:<br />
* <code>"none"</code> - user appears offline.<br />
* <code>"friends"</code> - user is visible only to friends.<br />
* <code>"connections"</code> - user is visible to friends and connections.<br />
@ -74,23 +74,23 @@ public:
public slots:
/**jsdoc
* Get information on the progress of downloading assets in the domain.
* Gets information on the download progress of assets in the domain.
* @function AccountServices.getDownloadInfo
* @returns {AccountServices.DownloadInfoResult} Information on the progress of assets download.
* @returns {AccountServices.DownloadInfoResult} Information on the download progress of assets.
*/
DownloadInfoResult getDownloadInfo();
/**jsdoc
* Cause a {@link AccountServices.downloadInfoChanged|downloadInfoChanged} signal to be triggered with information on the
* current progress of the download of assets in the domain.
* Triggers a {@link AccountServices.downloadInfoChanged|downloadInfoChanged} signal with information on the current
* download progress of the assets in the domain.
* @function AccountServices.updateDownloadInfo
*/
void updateDownloadInfo();
/**jsdoc
* Check whether the user is logged in.
* Checks whether the user is logged in.
* @function AccountServices.isLoggedIn
* @returns {boolean} <code>true</code> if the user is logged in, <code>false</code> otherwise.
* @returns {boolean} <code>true</code> if the user is logged in, <code>false</code> if not.
* @example <caption>Report whether you are logged in.</caption>
* var isLoggedIn = AccountServices.isLoggedIn();
* print("You are logged in: " + isLoggedIn); // true or false
@ -98,9 +98,9 @@ public slots:
bool isLoggedIn();
/**jsdoc
* Prompts the user to log in (the login dialog is displayed) if they're not already logged in.
* The function returns the login status of the user and prompts the user to log in (with a login dialog) if they're not already logged in.
* @function AccountServices.checkAndSignalForAccessToken
* @returns {boolean} <code>true</code> if the user is already logged in, <code>false</code> otherwise.
* @returns {boolean} <code>true</code> if the user is logged in, <code>false</code> if not.
*/
bool checkAndSignalForAccessToken();
@ -140,7 +140,7 @@ signals:
/**jsdoc
* Triggered when the username logged in with changes, i.e., when the user logs in or out.
* @function AccountServices.myUsernameChanged
* @param {string} username - The username logged in with if the user is logged in, otherwise <code>""</code>.
* @param {string} username - The user name of the user logged in. If there is no user logged in, it is <code>""</code>.
* @returns {Signal}
* @example <caption>Report when your username changes.</caption>
* AccountServices.myUsernameChanged.connect(function (username) {
@ -150,9 +150,9 @@ signals:
void myUsernameChanged(const QString& username);
/**jsdoc
* Triggered when the progress of the download of assets for the domain changes.
* Triggered when the download progress of the assets in the domain changes.
* @function AccountServices.downloadInfoChanged
* @param {AccountServices.DownloadInfoResult} downloadInfo - Information on the progress of assets download.
* @param {AccountServices.DownloadInfoResult} downloadInfo - Information on the download progress of assets.
* @returns {Signal}
*/
void downloadInfoChanged(DownloadInfoResult info);
@ -186,7 +186,7 @@ signals:
/**jsdoc
* Triggered when the login status of the user changes.
* @function AccountServices.loggedInChanged
* @param {boolean} loggedIn - <code>true</code> if the user is logged in, otherwise <code>false</code>.
* @param {boolean} loggedIn - <code>true</code> if the user is logged in, <code>false</code> if not.
* @returns {Signal}
* @example <caption>Report when your login status changes.</caption>
* AccountServices.loggedInChanged.connect(function(loggedIn) {

View file

@ -41,8 +41,8 @@ public:
*
* @property {WalletScriptingInterface.WalletStatus} walletStatus - The status of the user's wallet. <em>Read-only.</em>
* @property {boolean} limitedCommerce - <code>true</code> if Interface is running in limited commerce mode. In limited commerce
* mode, certain Interface functionality is disabled, e.g., users can't buy non-free items from the Marketplace. The Oculus
* Store version of Interface runs in limited commerce mode. <em>Read-only.</em>
* mode, certain Interface functionalities are disabled, e.g., users can't buy items that are not free from the Marketplace.
* The Oculus Store version of Interface runs in limited commerce mode. <em>Read-only.</em>
*/
class WalletScriptingInterface : public QObject, public Dependency {
Q_OBJECT
@ -55,16 +55,16 @@ public:
WalletScriptingInterface();
/**jsdoc
* Check and update the user's wallet status.
* Checks and updates the user's wallet status.
* @function WalletScriptingInterface.refreshWalletStatus
*/
Q_INVOKABLE void refreshWalletStatus();
/**jsdoc
* Get the current status of the user's wallet.
* Gets the current status of the user's wallet.
* @function WalletScriptingInterface.getWalletStatus
* @returns {WalletScriptingInterface.WalletStatus}
* @example <caption>Two ways to report your wallet status.</caption>
* @example <caption>Use two methods to report your wallet's status.</caption>
* print("Wallet status: " + WalletScriptingInterface.walletStatus); // Same value as next line.
* print("Wallet status: " + WalletScriptingInterface.getWalletStatus());
*/
@ -74,11 +74,11 @@ public:
* Check that a certified avatar entity is owned by the avatar whose entity it is. The result of the check is provided via
* the {@link WalletScriptingInterface.ownershipVerificationSuccess|ownershipVerificationSuccess} and
* {@link WalletScriptingInterface.ownershipVerificationFailed|ownershipVerificationFailed} signals.<br />
* <strong>Warning:</strong> Neither of these signals fire if the entity is not an avatar entity or it's not a certified
* entity.
* <strong>Warning:</strong> Neither of these signals are triggered if the entity is not an avatar entity or is not
* certified.
* @function WalletScriptingInterface.proveAvatarEntityOwnershipVerification
* @param {Uuid} entityID - The ID of the avatar entity to check.
* @example <caption>Check ownership of all nearby certified avatar entities.</caption>
* @param {Uuid} entityID - The avatar entity's ID.
* @example <caption>Check the ownership of all nearby certified avatar entities.</caption>
* // Set up response handling.
* function ownershipSuccess(entityID) {
* print("Ownership test succeeded for: " + entityID);
@ -118,7 +118,7 @@ public:
signals:
/**jsdoc
* Triggered when the status of the user's wallet changes.
* Triggered when the user's wallet status changes.
* @function WalletScriptingInterface.walletStatusChanged
* @returns {Signal}
* @example <caption>Report when your wallet status changes, e.g., when you log in and out.</caption>
@ -136,7 +136,7 @@ signals:
void limitedCommerceChanged();
/**jsdoc
* Triggered when the user rezzes a certified entity but the user's wallet is not ready and so the certified location of the
* Triggered when the user rezzes a certified entity but the user's wallet is not ready. So the certified location of the
* entity cannot be updated in the metaverse.
* @function WalletScriptingInterface.walletNotSetup
* @returns {Signal}

View file

@ -43,14 +43,6 @@ std::unordered_map<QString, QString> Overlays::_entityToOverlayTypes;
std::unordered_map<QString, QString> Overlays::_overlayToEntityTypes;
Overlays::Overlays() {
auto pointerManager = DependencyManager::get<PointerManager>();
connect(pointerManager.data(), &PointerManager::hoverBeginOverlay, this, &Overlays::hoverEnterPointerEvent);
connect(pointerManager.data(), &PointerManager::hoverContinueOverlay, this, &Overlays::hoverOverPointerEvent);
connect(pointerManager.data(), &PointerManager::hoverEndOverlay, this, &Overlays::hoverLeavePointerEvent);
connect(pointerManager.data(), &PointerManager::triggerBeginOverlay, this, &Overlays::mousePressPointerEvent);
connect(pointerManager.data(), &PointerManager::triggerContinueOverlay, this, &Overlays::mouseMovePointerEvent);
connect(pointerManager.data(), &PointerManager::triggerEndOverlay, this, &Overlays::mouseReleasePointerEvent);
ADD_TYPE_MAP(Box, cube);
ADD_TYPE_MAP(Sphere, sphere);
_overlayToEntityTypes["rectangle3d"] = "Shape";
@ -81,13 +73,23 @@ void Overlays::cleanupAllOverlays() {
void Overlays::init() {
auto entityScriptingInterface = DependencyManager::get<EntityScriptingInterface>().data();
auto pointerManager = DependencyManager::get<PointerManager>();
connect(pointerManager.data(), &PointerManager::hoverBeginOverlay, entityScriptingInterface , &EntityScriptingInterface::hoverEnterEntity);
connect(pointerManager.data(), &PointerManager::hoverContinueOverlay, entityScriptingInterface, &EntityScriptingInterface::hoverOverEntity);
connect(pointerManager.data(), &PointerManager::hoverEndOverlay, entityScriptingInterface, &EntityScriptingInterface::hoverLeaveEntity);
connect(pointerManager.data(), &PointerManager::triggerBeginOverlay, entityScriptingInterface, &EntityScriptingInterface::mousePressOnEntity);
connect(pointerManager.data(), &PointerManager::triggerContinueOverlay, entityScriptingInterface, &EntityScriptingInterface::mouseMoveOnEntity);
connect(pointerManager.data(), &PointerManager::triggerEndOverlay, entityScriptingInterface, &EntityScriptingInterface::mouseReleaseOnEntity);
auto pointerManager = DependencyManager::get<PointerManager>().data();
connect(pointerManager, &PointerManager::hoverBeginOverlay, entityScriptingInterface , &EntityScriptingInterface::hoverEnterEntity);
connect(pointerManager, &PointerManager::hoverContinueOverlay, entityScriptingInterface, &EntityScriptingInterface::hoverOverEntity);
connect(pointerManager, &PointerManager::hoverEndOverlay, entityScriptingInterface, &EntityScriptingInterface::hoverLeaveEntity);
connect(pointerManager, &PointerManager::triggerBeginOverlay, entityScriptingInterface, &EntityScriptingInterface::mousePressOnEntity);
connect(pointerManager, &PointerManager::triggerContinueOverlay, entityScriptingInterface, &EntityScriptingInterface::mouseMoveOnEntity);
connect(pointerManager, &PointerManager::triggerEndOverlay, entityScriptingInterface, &EntityScriptingInterface::mouseReleaseOnEntity);
connect(entityScriptingInterface, &EntityScriptingInterface::mousePressOnEntity, this, &Overlays::mousePressOnPointerEvent);
connect(entityScriptingInterface, &EntityScriptingInterface::mousePressOffEntity, this, &Overlays::mousePressOffPointerEvent);
connect(entityScriptingInterface, &EntityScriptingInterface::mouseDoublePressOnEntity, this, &Overlays::mouseDoublePressOnPointerEvent);
connect(entityScriptingInterface, &EntityScriptingInterface::mouseDoublePressOffEntity, this, &Overlays::mouseDoublePressOffPointerEvent);
connect(entityScriptingInterface, &EntityScriptingInterface::mouseReleaseOnEntity, this, &Overlays::mouseReleasePointerEvent);
connect(entityScriptingInterface, &EntityScriptingInterface::mouseMoveOnEntity, this, &Overlays::mouseMovePointerEvent);
connect(entityScriptingInterface, &EntityScriptingInterface::hoverEnterEntity , this, &Overlays::hoverEnterPointerEvent);
connect(entityScriptingInterface, &EntityScriptingInterface::hoverOverEntity, this, &Overlays::hoverOverPointerEvent);
connect(entityScriptingInterface, &EntityScriptingInterface::hoverLeaveEntity, this, &Overlays::hoverLeavePointerEvent);
}
void Overlays::update(float deltatime) {
@ -413,32 +415,13 @@ EntityItemProperties Overlays::convertOverlayToEntityProperties(QVariantMap& ove
overlayProps["dimensions"] = vec3toVariant(ratio * dimensions);
}
if (add || overlayProps.contains("rotation")) {
glm::quat rotation;
{
auto iter = overlayProps.find("rotation");
if (iter != overlayProps.end()) {
rotation = quatFromVariant(iter.value());
} else if (!add) {
EntityPropertyFlags desiredProperties;
desiredProperties += PROP_ROTATION;
rotation = DependencyManager::get<EntityScriptingInterface>()->getEntityProperties(id, desiredProperties).getRotation();
}
}
if (add && !overlayProps.contains("rotation") && !overlayProps.contains("localRotation")) {
overlayProps["rotation"] = quatToVariant(glm::angleAxis(-(float)M_PI_2, Vectors::RIGHT));
} else if (overlayProps.contains("rotation")) {
glm::quat rotation = quatFromVariant(overlayProps["rotation"]);
overlayProps["rotation"] = quatToVariant(glm::angleAxis(-(float)M_PI_2, rotation * Vectors::RIGHT) * rotation);
}
if (add || overlayProps.contains("localRotation")) {
glm::quat rotation;
{
auto iter = overlayProps.find("localRotation");
if (iter != overlayProps.end()) {
rotation = quatFromVariant(iter.value());
} else if (!add) {
EntityPropertyFlags desiredProperties;
desiredProperties += PROP_LOCAL_ROTATION;
rotation = DependencyManager::get<EntityScriptingInterface>()->getEntityProperties(id, desiredProperties).getLocalRotation();
}
}
} else if (overlayProps.contains("localRotation")) {
glm::quat rotation = quatFromVariant(overlayProps["localRotation"]);
overlayProps["localRotation"] = quatToVariant(glm::angleAxis(-(float)M_PI_2, rotation * Vectors::RIGHT) * rotation);
}
@ -1178,7 +1161,7 @@ bool Overlays::isAddedOverlay(const QUuid& id) {
}
void Overlays::sendMousePressOnOverlay(const QUuid& id, const PointerEvent& event) {
mousePressPointerEvent(id, event);
mousePressOnPointerEvent(id, event);
}
void Overlays::sendMouseReleaseOnOverlay(const QUuid& id, const PointerEvent& event) {
@ -1225,57 +1208,66 @@ float Overlays::height() {
return offscreenUi->getWindow()->size().height();
}
static uint32_t toPointerButtons(const QMouseEvent& event) {
uint32_t buttons = 0;
buttons |= event.buttons().testFlag(Qt::LeftButton) ? PointerEvent::PrimaryButton : 0;
buttons |= event.buttons().testFlag(Qt::RightButton) ? PointerEvent::SecondaryButton : 0;
buttons |= event.buttons().testFlag(Qt::MiddleButton) ? PointerEvent::TertiaryButton : 0;
return buttons;
}
static PointerEvent::Button toPointerButton(const QMouseEvent& event) {
switch (event.button()) {
case Qt::LeftButton:
return PointerEvent::PrimaryButton;
case Qt::RightButton:
return PointerEvent::SecondaryButton;
case Qt::MiddleButton:
return PointerEvent::TertiaryButton;
default:
return PointerEvent::NoButtons;
}
}
RayToOverlayIntersectionResult getPrevPickResult() {
RayToOverlayIntersectionResult overlayResult;
overlayResult.intersects = false;
auto pickResult = DependencyManager::get<PickManager>()->getPrevPickResultTyped<RayPickResult>(DependencyManager::get<EntityTreeRenderer>()->getMouseRayPickID());
if (pickResult) {
overlayResult.intersects = pickResult->type == IntersectionType::LOCAL_ENTITY;
if (overlayResult.intersects) {
overlayResult.intersection = pickResult->intersection;
overlayResult.distance = pickResult->distance;
overlayResult.surfaceNormal = pickResult->surfaceNormal;
overlayResult.overlayID = pickResult->objectID;
overlayResult.extraInfo = pickResult->extraInfo;
void Overlays::mousePressOnPointerEvent(const QUuid& id, const PointerEvent& event) {
auto keyboard = DependencyManager::get<Keyboard>();
// Do not send keyboard key event to scripts to prevent malignant scripts from gathering what you typed
if (!keyboard->getKeyIDs().contains(id)) {
auto entity = DependencyManager::get<EntityTreeRenderer>()->getEntity(id);
if (entity && entity->isLocalEntity()) {
emit mousePressOnOverlay(id, event);
}
}
return overlayResult;
}
PointerEvent Overlays::calculateOverlayPointerEvent(const QUuid& id, const PickRay& ray,
const RayToOverlayIntersectionResult& rayPickResult, QMouseEvent* event,
PointerEvent::EventType eventType) {
glm::vec2 pos2D = RayPick::projectOntoEntityXYPlane(id, rayPickResult.intersection);
return PointerEvent(eventType, PointerManager::MOUSE_POINTER_ID, pos2D, rayPickResult.intersection, rayPickResult.surfaceNormal,
ray.direction, toPointerButton(*event), toPointerButtons(*event), event->modifiers());
void Overlays::mousePressOffPointerEvent() {
emit mousePressOffOverlay();
}
void Overlays::mouseDoublePressOnPointerEvent(const QUuid& id, const PointerEvent& event) {
auto keyboard = DependencyManager::get<Keyboard>();
// Do not send keyboard key event to scripts to prevent malignant scripts from gathering what you typed
if (!keyboard->getKeyIDs().contains(id)) {
auto entity = DependencyManager::get<EntityTreeRenderer>()->getEntity(id);
if (entity && entity->isLocalEntity()) {
emit mouseDoublePressOnOverlay(id, event);
}
}
}
void Overlays::mouseDoublePressOffPointerEvent() {
emit mouseDoublePressOffOverlay();
}
void Overlays::mouseReleasePointerEvent(const QUuid& id, const PointerEvent& event) {
auto keyboard = DependencyManager::get<Keyboard>();
// Do not send keyboard key event to scripts to prevent malignant scripts from gathering what you typed
if (!keyboard->getKeyIDs().contains(id)) {
auto entity = DependencyManager::get<EntityTreeRenderer>()->getEntity(id);
if (entity && entity->isLocalEntity()) {
emit mouseReleaseOnOverlay(id, event);
}
}
}
void Overlays::mouseMovePointerEvent(const QUuid& id, const PointerEvent& event) {
auto keyboard = DependencyManager::get<Keyboard>();
// Do not send keyboard key event to scripts to prevent malignant scripts from gathering what you typed
if (!keyboard->getKeyIDs().contains(id)) {
auto entity = DependencyManager::get<EntityTreeRenderer>()->getEntity(id);
if (entity && entity->isLocalEntity()) {
emit mouseMoveOnOverlay(id, event);
}
}
}
void Overlays::hoverEnterPointerEvent(const QUuid& id, const PointerEvent& event) {
auto keyboard = DependencyManager::get<Keyboard>();
// Do not send keyboard key event to scripts to prevent malignant scripts from gathering what you typed
if (!keyboard->getKeyIDs().contains(id)) {
emit hoverEnterOverlay(id, event);
auto entity = DependencyManager::get<EntityTreeRenderer>()->getEntity(id);
if (entity && entity->isLocalEntity()) {
emit hoverEnterOverlay(id, event);
}
}
}
@ -1283,7 +1275,10 @@ void Overlays::hoverOverPointerEvent(const QUuid& id, const PointerEvent& event)
auto keyboard = DependencyManager::get<Keyboard>();
// Do not send keyboard key event to scripts to prevent malignant scripts from gathering what you typed
if (!keyboard->getKeyIDs().contains(id)) {
emit hoverOverOverlay(id, event);
auto entity = DependencyManager::get<EntityTreeRenderer>()->getEntity(id);
if (entity && entity->isLocalEntity()) {
emit hoverOverOverlay(id, event);
}
}
}
@ -1291,113 +1286,10 @@ void Overlays::hoverLeavePointerEvent(const QUuid& id, const PointerEvent& event
auto keyboard = DependencyManager::get<Keyboard>();
// Do not send keyboard key event to scripts to prevent malignant scripts from gathering what you typed
if (!keyboard->getKeyIDs().contains(id)) {
emit hoverLeaveOverlay(id, event);
}
}
std::pair<float, QUuid> Overlays::mousePressEvent(QMouseEvent* event) {
PerformanceTimer perfTimer("Overlays::mousePressEvent");
PickRay ray = qApp->computePickRay(event->x(), event->y());
RayToOverlayIntersectionResult rayPickResult = getPrevPickResult();
if (rayPickResult.intersects) {
_currentClickingOnOverlayID = rayPickResult.overlayID;
PointerEvent pointerEvent = calculateOverlayPointerEvent(_currentClickingOnOverlayID, ray, rayPickResult, event, PointerEvent::Press);
mousePressPointerEvent(_currentClickingOnOverlayID, pointerEvent);
return { rayPickResult.distance, rayPickResult.overlayID };
}
emit mousePressOffOverlay();
return { FLT_MAX, UNKNOWN_ENTITY_ID };
}
void Overlays::mousePressPointerEvent(const QUuid& id, const PointerEvent& event) {
auto keyboard = DependencyManager::get<Keyboard>();
// Do not send keyboard key event to scripts to prevent malignant scripts from gathering what you typed
if (!keyboard->getKeyIDs().contains(id)) {
emit mousePressOnOverlay(id, event);
}
}
bool Overlays::mouseDoublePressEvent(QMouseEvent* event) {
PerformanceTimer perfTimer("Overlays::mouseDoublePressEvent");
PickRay ray = qApp->computePickRay(event->x(), event->y());
RayToOverlayIntersectionResult rayPickResult = getPrevPickResult();
if (rayPickResult.intersects) {
_currentClickingOnOverlayID = rayPickResult.overlayID;
auto pointerEvent = calculateOverlayPointerEvent(_currentClickingOnOverlayID, ray, rayPickResult, event, PointerEvent::Press);
emit mouseDoublePressOnOverlay(_currentClickingOnOverlayID, pointerEvent);
return true;
}
emit mouseDoublePressOffOverlay();
return false;
}
bool Overlays::mouseReleaseEvent(QMouseEvent* event) {
PerformanceTimer perfTimer("Overlays::mouseReleaseEvent");
PickRay ray = qApp->computePickRay(event->x(), event->y());
RayToOverlayIntersectionResult rayPickResult = getPrevPickResult();
if (rayPickResult.intersects) {
auto pointerEvent = calculateOverlayPointerEvent(rayPickResult.overlayID, ray, rayPickResult, event, PointerEvent::Release);
mouseReleasePointerEvent(rayPickResult.overlayID, pointerEvent);
}
_currentClickingOnOverlayID = UNKNOWN_ENTITY_ID;
return false;
}
void Overlays::mouseReleasePointerEvent(const QUuid& id, const PointerEvent& event) {
auto keyboard = DependencyManager::get<Keyboard>();
// Do not send keyboard key event to scripts to prevent malignant scripts from gathering what you typed
if (!keyboard->getKeyIDs().contains(id)) {
emit mouseReleaseOnOverlay(id, event);
}
}
bool Overlays::mouseMoveEvent(QMouseEvent* event) {
PerformanceTimer perfTimer("Overlays::mouseMoveEvent");
PickRay ray = qApp->computePickRay(event->x(), event->y());
RayToOverlayIntersectionResult rayPickResult = getPrevPickResult();
if (rayPickResult.intersects) {
auto pointerEvent = calculateOverlayPointerEvent(rayPickResult.overlayID, ray, rayPickResult, event, PointerEvent::Move);
mouseMovePointerEvent(rayPickResult.overlayID, pointerEvent);
// If previously hovering over a different overlay then leave hover on that overlay.
if (_currentHoverOverOverlayID != UNKNOWN_ENTITY_ID && rayPickResult.overlayID != _currentHoverOverOverlayID) {
auto pointerEvent = calculateOverlayPointerEvent(_currentHoverOverOverlayID, ray, rayPickResult, event, PointerEvent::Move);
hoverLeavePointerEvent(_currentHoverOverOverlayID, pointerEvent);
auto entity = DependencyManager::get<EntityTreeRenderer>()->getEntity(id);
if (entity && entity->isLocalEntity()) {
emit hoverLeaveOverlay(id, event);
}
// If hovering over a new overlay then enter hover on that overlay.
if (rayPickResult.overlayID != _currentHoverOverOverlayID) {
hoverEnterPointerEvent(rayPickResult.overlayID, pointerEvent);
}
// Hover over current overlay.
hoverOverPointerEvent(rayPickResult.overlayID, pointerEvent);
_currentHoverOverOverlayID = rayPickResult.overlayID;
} else {
// If previously hovering an overlay then leave hover.
if (_currentHoverOverOverlayID != UNKNOWN_ENTITY_ID) {
auto pointerEvent = calculateOverlayPointerEvent(_currentHoverOverOverlayID, ray, rayPickResult, event, PointerEvent::Move);
hoverLeavePointerEvent(_currentHoverOverOverlayID, pointerEvent);
_currentHoverOverOverlayID = UNKNOWN_ENTITY_ID;
}
}
return false;
}
void Overlays::mouseMovePointerEvent(const QUuid& id, const PointerEvent& event) {
auto keyboard = DependencyManager::get<Keyboard>();
// Do not send keyboard key event to scripts to prevent malignant scripts from gathering what you typed
if (!keyboard->getKeyIDs().contains(id)) {
emit mouseMoveOnOverlay(id, event);
}
}

View file

@ -112,11 +112,6 @@ public:
const QVector<EntityItemID>& discard,
bool visibleOnly = false, bool collidableOnly = false);
std::pair<float, QUuid> mousePressEvent(QMouseEvent* event);
bool mouseDoublePressEvent(QMouseEvent* event);
bool mouseReleaseEvent(QMouseEvent* event);
bool mouseMoveEvent(QMouseEvent* event);
void cleanupAllOverlays();
mutable QScriptEngine _scriptEngine;
@ -719,9 +714,6 @@ private:
PointerEvent calculateOverlayPointerEvent(const QUuid& id, const PickRay& ray, const RayToOverlayIntersectionResult& rayPickResult,
QMouseEvent* event, PointerEvent::EventType eventType);
QUuid _currentClickingOnOverlayID;
QUuid _currentHoverOverOverlayID;
static QString entityToOverlayType(const QString& type);
static QString overlayToEntityType(const QString& type);
static std::unordered_map<QString, QString> _entityToOverlayTypes;
@ -732,12 +724,17 @@ private:
EntityItemProperties convertOverlayToEntityProperties(QVariantMap& overlayProps, std::pair<glm::quat, bool>& rotationToSave, const QString& type, bool add, const QUuid& id = QUuid());
private slots:
void mousePressPointerEvent(const QUuid& id, const PointerEvent& event);
void mouseMovePointerEvent(const QUuid& id, const PointerEvent& event);
void mousePressOnPointerEvent(const QUuid& id, const PointerEvent& event);
void mousePressOffPointerEvent();
void mouseDoublePressOnPointerEvent(const QUuid& id, const PointerEvent& event);
void mouseDoublePressOffPointerEvent();
void mouseReleasePointerEvent(const QUuid& id, const PointerEvent& event);
void mouseMovePointerEvent(const QUuid& id, const PointerEvent& event);
void hoverEnterPointerEvent(const QUuid& id, const PointerEvent& event);
void hoverOverPointerEvent(const QUuid& id, const PointerEvent& event);
void hoverLeavePointerEvent(const QUuid& id, const PointerEvent& event);
};
#define ADD_TYPE_MAP(entity, overlay) \

View file

@ -123,6 +123,42 @@ void AnimClip::copyFromNetworkAnim() {
const int animFrameCount = animModel.animationFrames.size();
_anim.resize(animFrameCount);
// find the size scale factor for translation in the animation.
const int avatarHipsParentIndex = avatarSkeleton->getParentIndex(avatarSkeleton->nameToJointIndex("Hips"));
const int animHipsParentIndex = animSkeleton.getParentIndex(animSkeleton.nameToJointIndex("Hips"));
const AnimPose& avatarHipsAbsoluteDefaultPose = avatarSkeleton->getAbsoluteDefaultPose(avatarSkeleton->nameToJointIndex("Hips"));
const AnimPose& animHipsAbsoluteDefaultPose = animSkeleton.getAbsoluteDefaultPose(animSkeleton.nameToJointIndex("Hips"));
// the get the units and the heights for the animation and the avatar
const float avatarUnitScale = extractScale(avatarSkeleton->getGeometryOffset()).y;
const float animationUnitScale = extractScale(animModel.offset).y;
const float avatarHeightInMeters = avatarUnitScale * avatarHipsAbsoluteDefaultPose.trans().y;
const float animHeightInMeters = animationUnitScale * animHipsAbsoluteDefaultPose.trans().y;
// get the parent scales for the avatar and the animation
float avatarHipsParentScale = 1.0f;
if (avatarHipsParentIndex >= 0) {
const AnimPose& avatarHipsParentAbsoluteDefaultPose = avatarSkeleton->getAbsoluteDefaultPose(avatarHipsParentIndex);
avatarHipsParentScale = avatarHipsParentAbsoluteDefaultPose.scale().y;
}
float animHipsParentScale = 1.0f;
if (animHipsParentIndex >= 0) {
const AnimPose& animationHipsParentAbsoluteDefaultPose = animSkeleton.getAbsoluteDefaultPose(animHipsParentIndex);
animHipsParentScale = animationHipsParentAbsoluteDefaultPose.scale().y;
}
const float EPSILON = 0.0001f;
float boneLengthScale = 1.0f;
// compute the ratios for the units, the heights in meters, and the parent scales
if ((fabsf(animHeightInMeters) > EPSILON) && (animationUnitScale > EPSILON) && (animHipsParentScale > EPSILON)) {
const float avatarToAnimationHeightRatio = avatarHeightInMeters / animHeightInMeters;
const float unitsRatio = 1.0f / (avatarUnitScale / animationUnitScale);
const float parentScaleRatio = 1.0f / (avatarHipsParentScale / animHipsParentScale);
boneLengthScale = avatarToAnimationHeightRatio * unitsRatio * parentScaleRatio;
}
for (int frame = 0; frame < animFrameCount; frame++) {
const HFMAnimationFrame& animFrame = animModel.animationFrames[frame];
@ -162,7 +198,6 @@ void AnimClip::copyFromNetworkAnim() {
avatarSkeleton->convertAbsoluteRotationsToRelative(avatarRotations);
_anim[frame].reserve(avatarJointCount);
for (int avatarJointIndex = 0; avatarJointIndex < avatarJointCount; avatarJointIndex++) {
const AnimPose& avatarDefaultPose = avatarSkeleton->getRelativeDefaultPose(avatarJointIndex);
@ -177,11 +212,6 @@ void AnimClip::copyFromNetworkAnim() {
// retarget translation from animation to avatar
const glm::vec3& animZeroTrans = animModel.animationFrames[0].translations[animJointIndex];
float boneLengthScale = 1.0f;
const float EPSILON = 0.0001f;
if (fabsf(glm::length(animZeroTrans)) > EPSILON) {
boneLengthScale = glm::length(avatarDefaultPose.trans()) / glm::length(animZeroTrans);
}
relativeTranslation = avatarDefaultPose.trans() + boneLengthScale * (animTrans - animZeroTrans);
} else {
// This joint is NOT in the animation at all.

View file

@ -298,10 +298,8 @@ void AnimInverseKinematics::solve(const AnimContext& context, const std::vector<
}
// harvest accumulated rotations and apply the average
for (int i = 0; i < (int)_relativePoses.size(); ++i) {
if (i == _hipsIndex) {
continue; // don't apply accumulators to hips
}
// don't apply accumulators to hips, or parents of hips
for (int i = (_hipsIndex+1); i < (int)_relativePoses.size(); ++i) {
if (_rotationAccumulators[i].size() > 0) {
_relativePoses[i].rot() = _rotationAccumulators[i].getAverage();
_rotationAccumulators[i].clear();

View file

@ -17,6 +17,9 @@
#include "AnimationLogging.h"
AnimSkeleton::AnimSkeleton(const HFMModel& hfmModel) {
_geometryOffset = hfmModel.offset;
// convert to std::vector of joints
std::vector<HFMJoint> joints;
joints.reserve(hfmModel.joints.size());

View file

@ -36,6 +36,7 @@ public:
const AnimPoseVec& getRelativeDefaultPoses() const { return _relativeDefaultPoses; }
const AnimPose& getAbsoluteDefaultPose(int jointIndex) const;
const AnimPoseVec& getAbsoluteDefaultPoses() const { return _absoluteDefaultPoses; }
const glm::mat4& getGeometryOffset() const { return _geometryOffset; }
// get pre transform which should include FBX pre potations
const AnimPose& getPreRotationPose(int jointIndex) const;
@ -84,6 +85,7 @@ protected:
std::vector<int> _mirrorMap;
QHash<QString, int> _jointIndicesByName;
std::vector<std::vector<HFMCluster>> _clusterBindMatrixOriginalValues;
glm::mat4 _geometryOffset;
// no copies
AnimSkeleton(const AnimSkeleton&) = delete;

View file

@ -73,14 +73,14 @@ EntityTreeRenderer::EntityTreeRenderer(bool wantScripts, AbstractViewStateInterf
_currentHoverOverEntityID = UNKNOWN_ENTITY_ID;
_currentClickingOnEntityID = UNKNOWN_ENTITY_ID;
auto entityScriptingInterface = DependencyManager::get<EntityScriptingInterface>();
auto entityScriptingInterface = DependencyManager::get<EntityScriptingInterface>().data();
auto pointerManager = DependencyManager::get<PointerManager>();
connect(pointerManager.data(), &PointerManager::hoverBeginEntity, entityScriptingInterface.data(), &EntityScriptingInterface::hoverEnterEntity);
connect(pointerManager.data(), &PointerManager::hoverContinueEntity, entityScriptingInterface.data(), &EntityScriptingInterface::hoverOverEntity);
connect(pointerManager.data(), &PointerManager::hoverEndEntity, entityScriptingInterface.data(), &EntityScriptingInterface::hoverLeaveEntity);
connect(pointerManager.data(), &PointerManager::triggerBeginEntity, entityScriptingInterface.data(), &EntityScriptingInterface::mousePressOnEntity);
connect(pointerManager.data(), &PointerManager::triggerContinueEntity, entityScriptingInterface.data(), &EntityScriptingInterface::mouseMoveOnEntity);
connect(pointerManager.data(), &PointerManager::triggerEndEntity, entityScriptingInterface.data(), &EntityScriptingInterface::mouseReleaseOnEntity);
connect(pointerManager.data(), &PointerManager::hoverBeginEntity, entityScriptingInterface, &EntityScriptingInterface::hoverEnterEntity);
connect(pointerManager.data(), &PointerManager::hoverContinueEntity, entityScriptingInterface, &EntityScriptingInterface::hoverOverEntity);
connect(pointerManager.data(), &PointerManager::hoverEndEntity, entityScriptingInterface, &EntityScriptingInterface::hoverLeaveEntity);
connect(pointerManager.data(), &PointerManager::triggerBeginEntity, entityScriptingInterface, &EntityScriptingInterface::mousePressOnEntity);
connect(pointerManager.data(), &PointerManager::triggerContinueEntity, entityScriptingInterface, &EntityScriptingInterface::mouseMoveOnEntity);
connect(pointerManager.data(), &PointerManager::triggerEndEntity, entityScriptingInterface, &EntityScriptingInterface::mouseReleaseOnEntity);
// Forward mouse events to web entities
auto handlePointerEvent = [&](const QUuid& entityID, const PointerEvent& event) {
@ -93,10 +93,10 @@ EntityTreeRenderer::EntityTreeRenderer(bool wantScripts, AbstractViewStateInterf
QMetaObject::invokeMethod(thisEntity.get(), "handlePointerEvent", Q_ARG(const PointerEvent&, event));
}
};
connect(entityScriptingInterface.data(), &EntityScriptingInterface::mousePressOnEntity, this, handlePointerEvent);
connect(entityScriptingInterface.data(), &EntityScriptingInterface::mouseMoveOnEntity, this, handlePointerEvent);
connect(entityScriptingInterface.data(), &EntityScriptingInterface::mouseReleaseOnEntity, this, handlePointerEvent);
connect(entityScriptingInterface.data(), &EntityScriptingInterface::hoverEnterEntity, this, [&](const QUuid& entityID, const PointerEvent& event) {
connect(entityScriptingInterface, &EntityScriptingInterface::mousePressOnEntity, this, handlePointerEvent);
connect(entityScriptingInterface, &EntityScriptingInterface::mouseMoveOnEntity, this, handlePointerEvent);
connect(entityScriptingInterface, &EntityScriptingInterface::mouseReleaseOnEntity, this, handlePointerEvent);
connect(entityScriptingInterface, &EntityScriptingInterface::hoverEnterEntity, this, [&](const QUuid& entityID, const PointerEvent& event) {
std::shared_ptr<render::entities::WebEntityRenderer> thisEntity;
auto entity = getEntity(entityID);
if (entity && entity->isVisible() && entity->getType() == EntityTypes::Web) {
@ -106,8 +106,8 @@ EntityTreeRenderer::EntityTreeRenderer(bool wantScripts, AbstractViewStateInterf
QMetaObject::invokeMethod(thisEntity.get(), "hoverEnterEntity", Q_ARG(const PointerEvent&, event));
}
});
connect(entityScriptingInterface.data(), &EntityScriptingInterface::hoverOverEntity, this, handlePointerEvent);
connect(entityScriptingInterface.data(), &EntityScriptingInterface::hoverLeaveEntity, this, [&](const QUuid& entityID, const PointerEvent& event) {
connect(entityScriptingInterface, &EntityScriptingInterface::hoverOverEntity, this, handlePointerEvent);
connect(entityScriptingInterface, &EntityScriptingInterface::hoverLeaveEntity, this, [&](const QUuid& entityID, const PointerEvent& event) {
std::shared_ptr<render::entities::WebEntityRenderer> thisEntity;
auto entity = getEntity(entityID);
if (entity && entity->isVisible() && entity->getType() == EntityTypes::Web) {
@ -196,8 +196,8 @@ void EntityTreeRenderer::resetEntitiesScriptEngine() {
});
}
void EntityTreeRenderer::stopNonLocalEntityScripts() {
leaveNonLocalEntities();
void EntityTreeRenderer::stopDomainAndNonOwnedEntities() {
leaveDomainAndNonOwnedEntities();
// unload and stop the engine
if (_entitiesScriptEngine) {
QList<EntityItemID> entitiesWithEntityScripts = _entitiesScriptEngine->getListOfEntityScriptIDs();
@ -206,7 +206,7 @@ void EntityTreeRenderer::stopNonLocalEntityScripts() {
EntityItemPointer entityItem = getTree()->findEntityByEntityItemID(entityID);
if (entityItem) {
if (!entityItem->isLocalEntity()) {
if (!(entityItem->isLocalEntity() || (entityItem->isAvatarEntity() && entityItem->getOwningAvatarID() == getTree()->getMyAvatarSessionUUID()))) {
_entitiesScriptEngine->unloadEntityScript(entityID, true);
}
}
@ -214,8 +214,8 @@ void EntityTreeRenderer::stopNonLocalEntityScripts() {
}
}
void EntityTreeRenderer::clearNonLocalEntities() {
stopNonLocalEntityScripts();
void EntityTreeRenderer::clearDomainAndNonOwnedEntities() {
stopDomainAndNonOwnedEntities();
std::unordered_map<EntityItemID, EntityRendererPointer> savedEntities;
// remove all entities from the scene
@ -225,7 +225,7 @@ void EntityTreeRenderer::clearNonLocalEntities() {
for (const auto& entry : _entitiesInScene) {
const auto& renderer = entry.second;
const EntityItemPointer& entityItem = renderer->getEntity();
if (!entityItem->isLocalEntity()) {
if (!(entityItem->isLocalEntity() || (entityItem->isAvatarEntity() && entityItem->getOwningAvatarID() == getTree()->getMyAvatarSessionUUID()))) {
renderer->removeFromScene(scene, transaction);
} else {
savedEntities[entry.first] = entry.second;
@ -239,7 +239,7 @@ void EntityTreeRenderer::clearNonLocalEntities() {
_layeredZones.clearNonLocalLayeredZones();
OctreeProcessor::clearNonLocalEntities();
OctreeProcessor::clearDomainAndNonOwnedEntities();
}
void EntityTreeRenderer::clear() {
@ -655,22 +655,22 @@ bool EntityTreeRenderer::checkEnterLeaveEntities() {
return didUpdate;
}
void EntityTreeRenderer::leaveNonLocalEntities() {
void EntityTreeRenderer::leaveDomainAndNonOwnedEntities() {
if (_tree && !_shuttingDown) {
QVector<EntityItemID> currentLocalEntitiesInside;
QVector<EntityItemID> currentEntitiesInsideToSave;
foreach (const EntityItemID& entityID, _currentEntitiesInside) {
EntityItemPointer entityItem = getTree()->findEntityByEntityItemID(entityID);
if (!entityItem->isLocalEntity()) {
if (!(entityItem->isLocalEntity() || (entityItem->isAvatarEntity() && entityItem->getOwningAvatarID() == getTree()->getMyAvatarSessionUUID()))) {
emit leaveEntity(entityID);
if (_entitiesScriptEngine) {
_entitiesScriptEngine->callEntityScriptMethod(entityID, "leaveEntity");
}
} else {
currentLocalEntitiesInside.push_back(entityID);
currentEntitiesInsideToSave.push_back(entityID);
}
}
_currentEntitiesInside = currentLocalEntitiesInside;
_currentEntitiesInside = currentEntitiesInsideToSave;
forceRecheckEntities();
}
}
@ -792,11 +792,11 @@ static PointerEvent::Button toPointerButton(const QMouseEvent& event) {
}
}
std::pair<float, QUuid> EntityTreeRenderer::mousePressEvent(QMouseEvent* event) {
QUuid EntityTreeRenderer::mousePressEvent(QMouseEvent* event) {
// If we don't have a tree, or we're in the process of shutting down, then don't
// process these events.
if (!_tree || _shuttingDown) {
return { FLT_MAX, UNKNOWN_ENTITY_ID };
return UNKNOWN_ENTITY_ID;
}
PerformanceTimer perfTimer("EntityTreeRenderer::mousePressEvent");
@ -805,11 +805,13 @@ std::pair<float, QUuid> EntityTreeRenderer::mousePressEvent(QMouseEvent* event)
RayToEntityIntersectionResult rayPickResult = _getPrevRayPickResultOperator(_mouseRayPickID);
EntityItemPointer entity;
if (rayPickResult.intersects && (entity = getTree()->findEntityByID(rayPickResult.entityID))) {
auto properties = entity->getProperties();
QString urlString = properties.getHref();
QUrl url = QUrl(urlString, QUrl::StrictMode);
if (url.isValid() && !url.isEmpty()){
DependencyManager::get<AddressManager>()->handleLookupString(urlString);
if (!EntityTree::areEntityClicksCaptured()) {
auto properties = entity->getProperties();
QString urlString = properties.getHref();
QUrl url = QUrl(urlString, QUrl::StrictMode);
if (url.isValid() && !url.isEmpty()) {
DependencyManager::get<AddressManager>()->handleLookupString(urlString);
}
}
glm::vec2 pos2D = projectOntoEntityXYPlane(entity, ray, rayPickResult);
@ -827,10 +829,10 @@ std::pair<float, QUuid> EntityTreeRenderer::mousePressEvent(QMouseEvent* event)
_lastPointerEvent = pointerEvent;
_lastPointerEventValid = true;
return { rayPickResult.distance, rayPickResult.entityID };
return rayPickResult.entityID;
}
emit entityScriptingInterface->mousePressOffEntity();
return { FLT_MAX, UNKNOWN_ENTITY_ID };
return UNKNOWN_ENTITY_ID;
}
void EntityTreeRenderer::mouseDoublePressEvent(QMouseEvent* event) {

View file

@ -87,14 +87,14 @@ public:
virtual void init() override;
/// clears the tree
virtual void clearNonLocalEntities() override;
virtual void clearDomainAndNonOwnedEntities() override;
virtual void clear() override;
/// reloads the entity scripts, calling unload and preload
void reloadEntityScripts();
// event handles which may generate entity related events
std::pair<float, QUuid> mousePressEvent(QMouseEvent* event);
QUuid mousePressEvent(QMouseEvent* event);
void mouseReleaseEvent(QMouseEvent* event);
void mouseDoublePressEvent(QMouseEvent* event);
void mouseMoveEvent(QMouseEvent* event);
@ -170,7 +170,7 @@ private:
bool findBestZoneAndMaybeContainingEntities(QVector<EntityItemID>* entitiesContainingAvatar = nullptr);
bool applyLayeredZones();
void stopNonLocalEntityScripts();
void stopDomainAndNonOwnedEntities();
void checkAndCallPreload(const EntityItemID& entityID, bool reload = false, bool unloadFirst = false);
@ -179,7 +179,7 @@ private:
QScriptValueList createEntityArgs(const EntityItemID& entityID);
bool checkEnterLeaveEntities();
void leaveNonLocalEntities();
void leaveDomainAndNonOwnedEntities();
void leaveAllEntities();
void forceRecheckEntities();

View file

@ -78,13 +78,14 @@ OctreeElementPointer EntityTree::createNewElement(unsigned char* octalCode) {
return std::static_pointer_cast<OctreeElement>(newElement);
}
void EntityTree::eraseNonLocalEntities() {
void EntityTree::eraseDomainAndNonOwnedEntities() {
emit clearingEntities();
if (_simulation) {
// local entities are not in the simulation, so we clear ALL
_simulation->clearEntities();
}
this->withWriteLock([&] {
QHash<EntityItemID, EntityItemPointer> savedEntities;
// NOTE: lock the Tree first, then lock the _entityMap.
@ -93,10 +94,10 @@ void EntityTree::eraseNonLocalEntities() {
foreach(EntityItemPointer entity, _entityMap) {
EntityTreeElementPointer element = entity->getElement();
if (element) {
element->cleanupNonLocalEntities();
element->cleanupDomainAndNonOwnedEntities();
}
if (entity->isLocalEntity()) {
if (entity->isLocalEntity() || (entity->isAvatarEntity() && entity->getOwningAvatarID() == getMyAvatarSessionUUID())) {
savedEntities[entity->getEntityItemID()] = entity;
} else {
int32_t spaceIndex = entity->getSpaceIndex();
@ -114,15 +115,16 @@ void EntityTree::eraseNonLocalEntities() {
{
QWriteLocker locker(&_needsParentFixupLock);
QVector<EntityItemWeakPointer> localEntitiesNeedsParentFixup;
QVector<EntityItemWeakPointer> needParentFixup;
foreach (EntityItemWeakPointer entityItem, _needsParentFixup) {
if (!entityItem.expired() && entityItem.lock()->isLocalEntity()) {
localEntitiesNeedsParentFixup.push_back(entityItem);
auto entity = entityItem.lock();
if (entity && (entity->isLocalEntity() || (entity->isAvatarEntity() && entity->getOwningAvatarID() == getMyAvatarSessionUUID()))) {
needParentFixup.push_back(entityItem);
}
}
_needsParentFixup = localEntitiesNeedsParentFixup;
_needsParentFixup = needParentFixup;
}
}
@ -2972,6 +2974,7 @@ QStringList EntityTree::getJointNames(const QUuid& entityID) const {
std::function<QObject*(const QUuid&)> EntityTree::_getEntityObjectOperator = nullptr;
std::function<QSizeF(const QUuid&, const QString&)> EntityTree::_textSizeOperator = nullptr;
std::function<bool()> EntityTree::_areEntityClicksCapturedOperator = nullptr;
QObject* EntityTree::getEntityObject(const QUuid& id) {
if (_getEntityObjectOperator) {
@ -2987,6 +2990,13 @@ QSizeF EntityTree::textSize(const QUuid& id, const QString& text) {
return QSizeF(0.0f, 0.0f);
}
bool EntityTree::areEntityClicksCaptured() {
if (_areEntityClicksCapturedOperator) {
return _areEntityClicksCapturedOperator();
}
return false;
}
void EntityTree::updateEntityQueryAACubeWorker(SpatiallyNestablePointer object, EntityEditPacketSender* packetSender,
MovingEntitiesOperator& moveOperator, bool force, bool tellServer) {
// if the queryBox has changed, tell the entity-server

View file

@ -75,7 +75,7 @@ public:
}
virtual void eraseNonLocalEntities() override;
virtual void eraseDomainAndNonOwnedEntities() override;
virtual void eraseAllOctreeElements(bool createNewRoot = true) override;
virtual void readBitstreamToTree(const unsigned char* bitstream,
@ -255,6 +255,7 @@ public:
QByteArray computeNonce(const QString& certID, const QString ownerKey);
bool verifyNonce(const QString& certID, const QString& nonce, EntityItemID& id);
QUuid getMyAvatarSessionUUID() { return _myAvatar ? _myAvatar->getSessionUUID() : QUuid(); }
void setMyAvatar(std::shared_ptr<AvatarData> myAvatar) { _myAvatar = myAvatar; }
void swapStaleProxies(std::vector<int>& proxies) { proxies.swap(_staleProxies); }
@ -268,6 +269,9 @@ public:
static void setTextSizeOperator(std::function<QSizeF(const QUuid&, const QString&)> textSizeOperator) { _textSizeOperator = textSizeOperator; }
static QSizeF textSize(const QUuid& id, const QString& text);
static void setEntityClicksCapturedOperator(std::function<bool()> areEntityClicksCapturedOperator) { _areEntityClicksCapturedOperator = areEntityClicksCapturedOperator; }
static bool areEntityClicksCaptured();
std::map<QString, QString> getNamedPaths() const { return _namedPaths; }
void updateEntityQueryAACube(SpatiallyNestablePointer object, EntityEditPacketSender* packetSender,
@ -378,6 +382,7 @@ private:
static std::function<QObject*(const QUuid&)> _getEntityObjectOperator;
static std::function<QSizeF(const QUuid&, const QString&)> _textSizeOperator;
static std::function<bool()> _areEntityClicksCapturedOperator;
std::vector<int32_t> _staleProxies;

View file

@ -697,11 +697,11 @@ EntityItemPointer EntityTreeElement::getEntityWithEntityItemID(const EntityItemI
return foundEntity;
}
void EntityTreeElement::cleanupNonLocalEntities() {
void EntityTreeElement::cleanupDomainAndNonOwnedEntities() {
withWriteLock([&] {
EntityItems savedEntities;
foreach(EntityItemPointer entity, _entityItems) {
if (!entity->isLocalEntity()) {
if (!(entity->isLocalEntity() || (entity->isAvatarEntity() && entity->getOwningAvatarID() == getTree()->getMyAvatarSessionUUID()))) {
entity->preDelete();
entity->_element = NULL;
} else {

View file

@ -190,7 +190,7 @@ public:
EntityItemPointer getEntityWithEntityItemID(const EntityItemID& id) const;
void getEntitiesInside(const AACube& box, QVector<EntityItemPointer>& foundEntities);
void cleanupNonLocalEntities();
void cleanupDomainAndNonOwnedEntities();
void cleanupEntities(); /// called by EntityTree on cleanup this will free all entities
bool removeEntityItem(EntityItemPointer entity, bool deletion = false);

View file

@ -164,7 +164,7 @@ protected:
int _lastKnownCurrentFrame{-1};
glm::u8vec3 _color;
glm::vec3 _modelScale;
glm::vec3 _modelScale { 1.0f };
QString _modelURL;
bool _relayParentJoints;
bool _groupCulled { false };

View file

@ -167,7 +167,6 @@ glm::mat4 getGlobalTransform(const QMultiMap<QString, QString>& _connectionParen
}
}
}
return globalTransform;
}
@ -436,6 +435,8 @@ HFMModel* FBXSerializer::extractHFMModel(const QVariantHash& mapping, const QStr
hfmModel.originalURL = url;
float unitScaleFactor = 1.0f;
glm::quat upAxisZRotation;
bool applyUpAxisZRotation = false;
glm::vec3 ambientColor;
QString hifiGlobalNodeID;
unsigned int meshIndex = 0;
@ -473,11 +474,22 @@ HFMModel* FBXSerializer::extractHFMModel(const QVariantHash& mapping, const QStr
if (subobject.name == propertyName) {
static const QVariant UNIT_SCALE_FACTOR = QByteArray("UnitScaleFactor");
static const QVariant AMBIENT_COLOR = QByteArray("AmbientColor");
static const QVariant UP_AXIS = QByteArray("UpAxis");
const auto& subpropName = subobject.properties.at(0);
if (subpropName == UNIT_SCALE_FACTOR) {
unitScaleFactor = subobject.properties.at(index).toFloat();
} else if (subpropName == AMBIENT_COLOR) {
ambientColor = getVec3(subobject.properties, index);
} else if (subpropName == UP_AXIS) {
constexpr int UP_AXIS_Y = 1;
constexpr int UP_AXIS_Z = 2;
int upAxis = subobject.properties.at(index).toInt();
if (upAxis == UP_AXIS_Y) {
// No update necessary, y up is the default
} else if (upAxis == UP_AXIS_Z) {
upAxisZRotation = glm::angleAxis(glm::radians(-90.0f), glm::vec3(1.0f, 0.0f, 0.0f));
applyUpAxisZRotation = true;
}
}
}
}
@ -1269,9 +1281,11 @@ HFMModel* FBXSerializer::extractHFMModel(const QVariantHash& mapping, const QStr
joint.geometricScaling = fbxModel.geometricScaling;
joint.isSkeletonJoint = fbxModel.isLimbNode;
hfmModel.hasSkeletonJoints = (hfmModel.hasSkeletonJoints || joint.isSkeletonJoint);
if (applyUpAxisZRotation && joint.parentIndex == -1) {
joint.rotation *= upAxisZRotation;
joint.translation = upAxisZRotation * joint.translation;
}
glm::quat combinedRotation = joint.preRotation * joint.rotation * joint.postRotation;
if (joint.parentIndex == -1) {
joint.transform = hfmModel.offset * glm::translate(joint.translation) * joint.preTransform *
glm::mat4_cast(combinedRotation) * joint.postTransform;
@ -1664,6 +1678,14 @@ HFMModel* FBXSerializer::extractHFMModel(const QVariantHash& mapping, const QStr
}
}
if (applyUpAxisZRotation) {
hfmModelPtr->meshExtents.transform(glm::mat4_cast(upAxisZRotation));
hfmModelPtr->bindExtents.transform(glm::mat4_cast(upAxisZRotation));
for (auto &mesh : hfmModelPtr->meshes) {
mesh.modelTransform *= glm::mat4_cast(upAxisZRotation);
mesh.meshExtents.transform(glm::mat4_cast(upAxisZRotation));
}
}
return hfmModelPtr;
}

View file

@ -353,16 +353,19 @@ QSharedPointer<Resource> ResourceCache::getResource(const QUrl& url, const QUrl&
// We've seen this extra info before
resource = resourcesWithExtraHashIter.value().lock();
} else if (resourcesWithExtraHash.size() > 0.0f) {
// We haven't seen this extra info before, but we've already downloaded the resource. We need a new copy of this object (with any old hash).
resource = createResourceCopy(resourcesWithExtraHash.begin().value().lock());
resource->setExtra(extra);
resource->setExtraHash(extraHash);
resource->setSelf(resource);
resource->setCache(this);
resource->moveToThread(qApp->thread());
connect(resource.data(), &Resource::updateSize, this, &ResourceCache::updateTotalSize);
resourcesWithExtraHash.insert(extraHash, resource);
resource->ensureLoading();
auto oldResource = resourcesWithExtraHash.begin().value().lock();
if (oldResource) {
// We haven't seen this extra info before, but we've already downloaded the resource. We need a new copy of this object (with any old hash).
resource = createResourceCopy(oldResource);
resource->setExtra(extra);
resource->setExtraHash(extraHash);
resource->setSelf(resource);
resource->setCache(this);
resource->moveToThread(qApp->thread());
connect(resource.data(), &Resource::updateSize, this, &ResourceCache::updateTotalSize);
resourcesWithExtraHash.insert(extraHash, resource);
resource->ensureLoading();
}
}
}
if (resource) {

View file

@ -31,7 +31,6 @@ using namespace udt;
using namespace std::chrono;
Connection::Connection(Socket* parentSocket, HifiSockAddr destination, std::unique_ptr<CongestionControl> congestionControl) :
QObject(parentSocket),
_parentSocket(parentSocket),
_destination(destination),
_congestionControl(move(congestionControl))

View file

@ -251,7 +251,10 @@ Connection* Socket::findOrCreateConnection(const HifiSockAddr& sockAddr, bool fi
auto congestionControl = _ccFactory->create();
congestionControl->setMaxBandwidth(_maxBandwidth);
auto connection = std::unique_ptr<Connection>(new Connection(this, sockAddr, std::move(congestionControl)));
if (QThread::currentThread() != thread()) {
qCDebug(networking) << "Moving new Connection to NodeList thread";
connection->moveToThread(thread());
}
// allow higher-level classes to find out when connections have completed a handshake
QObject::connect(connection.get(), &Connection::receiverHandshakeRequestComplete,
this, &Socket::clientHandshakeRequestComplete);

View file

@ -149,7 +149,7 @@ public:
OctreeElementPointer getRoot() { return _rootElement; }
virtual void eraseNonLocalEntities() { _isDirty = true; };
virtual void eraseDomainAndNonOwnedEntities() { _isDirty = true; };
virtual void eraseAllOctreeElements(bool createNewRoot = true);
virtual void readBitstreamToTree(const unsigned char* bitstream, uint64_t bufferSizeBytes, ReadBitstreamToTreeParams& args);

View file

@ -198,10 +198,10 @@ void OctreeProcessor::processDatagram(ReceivedMessage& message, SharedNodePointe
}
void OctreeProcessor::clearNonLocalEntities() {
void OctreeProcessor::clearDomainAndNonOwnedEntities() {
if (_tree) {
_tree->withWriteLock([&] {
_tree->eraseNonLocalEntities();
_tree->eraseDomainAndNonOwnedEntities();
});
}
}

View file

@ -43,7 +43,7 @@ public:
virtual void init();
/// clears the tree
virtual void clearNonLocalEntities();
virtual void clearDomainAndNonOwnedEntities();
virtual void clear();
float getAverageElementsPerPacket() const { return _elementsPerPacket.getAverage(); }

View file

@ -1346,14 +1346,19 @@ void Model::updateRig(float deltaTime, glm::mat4 parentTransform) {
}
void Model::computeMeshPartLocalBounds() {
for (auto& part : _modelMeshRenderItems) {
const Model::MeshState& state = _meshStates.at(part->_meshIndex);
if (_useDualQuaternionSkinning) {
part->computeAdjustedLocalBound(state.clusterDualQuaternions);
} else {
part->computeAdjustedLocalBound(state.clusterMatrices);
}
render::Transaction transaction;
auto meshStates = _meshStates;
for (auto renderItem : _modelMeshRenderItemIDs) {
transaction.updateItem<ModelMeshPartPayload>(renderItem, [this, meshStates](ModelMeshPartPayload& data) {
const Model::MeshState& state = meshStates.at(data._meshIndex);
if (_useDualQuaternionSkinning) {
data.computeAdjustedLocalBound(state.clusterDualQuaternions);
} else {
data.computeAdjustedLocalBound(state.clusterMatrices);
}
});
}
AbstractViewStateInterface::instance()->getMain3DScene()->enqueueTransaction(transaction);
}
// virtual

View file

@ -976,7 +976,9 @@ void ScriptEngine::addEventHandler(const EntityItemID& entityID, const QString&
using PointerHandler = std::function<void(const EntityItemID&, const PointerEvent&)>;
auto makePointerHandler = [this](QString eventName) -> PointerHandler {
return [this, eventName](const EntityItemID& entityItemID, const PointerEvent& event) {
forwardHandlerCall(entityItemID, eventName, { entityItemID.toScriptValue(this), event.toScriptValue(this) });
if (!EntityTree::areEntityClicksCaptured()) {
forwardHandlerCall(entityItemID, eventName, { entityItemID.toScriptValue(this), event.toScriptValue(this) });
}
};
};

View file

@ -30,6 +30,20 @@
100,
makeLaserParams((this.hand + HUD_LASER_OFFSET), false));
this.getFarGrab = function () {
return getEnabledModuleByName(this.hand === RIGHT_HAND ? ("RightFarGrabEntity") : ("LeftFarGrabEntity"));
}
this.farGrabActive = function () {
var farGrab = this.getFarGrab();
// farGrab will be null if module isn't loaded.
if (farGrab) {
return farGrab.targetIsNull();
} else {
return false;
}
};
this.getOtherHandController = function() {
return (this.hand === RIGHT_HAND) ? Controller.Standard.LeftHand : Controller.Standard.RightHand;
};
@ -79,7 +93,7 @@
this.isReady = function (controllerData) {
var otherModuleRunning = this.getOtherModule().running;
if (!otherModuleRunning && HMD.active) {
if (!otherModuleRunning && HMD.active && !this.farGrabActive()) {
if (this.processLaser(controllerData)) {
this.running = true;
return ControllerDispatcherUtils.makeRunningValues(true, [], []);

View file

@ -37,6 +37,20 @@ Script.include("/~/system/libraries/controllers.js");
100,
makeLaserParams(hand, true));
this.getFarGrab = function () {
return getEnabledModuleByName(this.hand === RIGHT_HAND ? ("RightFarGrabEntity") : ("LeftFarGrabEntity"));
};
this.farGrabActive = function () {
var farGrab = this.getFarGrab();
// farGrab will be null if module isn't loaded.
if (farGrab) {
return farGrab.targetIsNull();
} else {
return false;
}
};
this.grabModuleWantsNearbyOverlay = function(controllerData) {
if (controllerData.triggerValues[this.hand] > TRIGGER_ON_VALUE || controllerData.secondaryValues[this.hand] > BUMPER_ON_VALUE) {
var nearGrabName = this.hand === RIGHT_HAND ? "RightNearParentingGrabOverlay" : "LeftNearParentingGrabOverlay";
@ -184,7 +198,12 @@ Script.include("/~/system/libraries/controllers.js");
this.dominantHandOverride = false;
this.isReady = function(controllerData) {
this.isReady = function (controllerData) {
// Trivial rejection for when FarGrab is active.
if (this.farGrabActive()) {
return makeRunningValues(false, [], []);
}
var isTriggerPressed = controllerData.triggerValues[this.hand] > TRIGGER_OFF_VALUE &&
controllerData.triggerValues[this.otherHand] <= TRIGGER_OFF_VALUE;
var type = this.getInteractableType(controllerData, isTriggerPressed, false);

View file

@ -418,13 +418,11 @@ resizeTablet = function (width, newParentJointIndex, sensorToWorldScaleOverride)
var HOME_BUTTON_Z_OFFSET = (tabletDepth / 1.9) * sensorScaleOffsetOverride;
Entities.editEntity(HMD.homeButtonID, {
localPosition: { x: HOME_BUTTON_X_OFFSET, y: HOME_BUTTON_Y_OFFSET, z: -HOME_BUTTON_Z_OFFSET },
localRotation: { x: 0, y: 1, z: 0, w: 0 },
dimensions: { x: homeButtonDim, y: homeButtonDim, z: homeButtonDim }
});
Entities.editEntity(HMD.homeButtonHighlightID, {
localPosition: { x: -HOME_BUTTON_X_OFFSET, y: HOME_BUTTON_Y_OFFSET, z: -HOME_BUTTON_Z_OFFSET },
localRotation: { x: 0, y: 1, z: 0, w: 0 },
dimensions: { x: homeButtonDim, y: homeButtonDim, z: homeButtonDim }
});
};
@ -482,13 +480,11 @@ reparentAndScaleTablet = function(width, reparentProps) {
var HOME_BUTTON_Z_OFFSET = (tabletDepth / 1.9) * sensorScaleOffsetOverride;
Entities.editEntity(HMD.homeButtonID, {
localPosition: { x: HOME_BUTTON_X_OFFSET, y: HOME_BUTTON_Y_OFFSET, z: -HOME_BUTTON_Z_OFFSET },
localRotation: { x: 0, y: 1, z: 0, w: 0 },
dimensions: { x: homeButtonDim, y: homeButtonDim, z: homeButtonDim }
});
Entities.editEntity(HMD.homeButtonHighlightID, {
localPosition: { x: -HOME_BUTTON_X_OFFSET, y: HOME_BUTTON_Y_OFFSET, z: -HOME_BUTTON_Z_OFFSET },
localRotation: { x: 0, y: 1, z: 0, w: 0 },
dimensions: { x: homeButtonDim, y: homeButtonDim, z: homeButtonDim }
});
}