diff --git a/assignment-client/src/entities/EntityTreeSendThread.cpp b/assignment-client/src/entities/EntityTreeSendThread.cpp index 03014bae6a..11e4d533fb 100644 --- a/assignment-client/src/entities/EntityTreeSendThread.cpp +++ b/assignment-client/src/entities/EntityTreeSendThread.cpp @@ -175,7 +175,7 @@ bool EntityTreeSendThread::addAncestorsToExtraFlaggedEntities(const QUuid& filte return parentWasNew || ancestorsWereNew; } - // since we didn't have a parent niether of our parents or ancestors could be new additions + // since we didn't have a parent, neither of our parents or ancestors could be new additions return false; } @@ -204,7 +204,9 @@ bool EntityTreeSendThread::addDescendantsToExtraFlaggedEntities(const QUuid& fil return hasNewChild || hasNewDescendants; } -void EntityTreeSendThread::startNewTraversal(const ViewFrustum& view, EntityTreeElementPointer root, int32_t lodLevelOffset, bool usesViewFrustum) { +void EntityTreeSendThread::startNewTraversal(const ViewFrustum& view, EntityTreeElementPointer root, int32_t lodLevelOffset, + bool usesViewFrustum) { + DiffTraversal::Type type = _traversal.prepareNewTraversal(view, root, lodLevelOffset, usesViewFrustum); // there are three types of traversal: // @@ -423,12 +425,19 @@ bool EntityTreeSendThread::traverseTreeAndBuildNextPacketPayload(EncodeBitstream uint64_t sendTime = usecTimestampNow(); auto nodeData = static_cast(params.nodeData); nodeData->stats.encodeStarted(); + auto entityNode = _node.toStrongRef(); + auto entityNodeData = static_cast(entityNode->getLinkedData()); while(!_sendQueue.empty()) { PrioritizedEntity queuedItem = _sendQueue.top(); EntityItemPointer entity = queuedItem.getEntity(); if (entity) { // Only send entities that match the jsonFilters, but keep track of everything we've tried to send so we don't try to send it again - if (entity->matchesJSONFilters(jsonFilters)) { + bool entityMatchesFilters = entity->matchesJSONFilters(jsonFilters); + if (entityMatchesFilters || entityNodeData->isEntityFlaggedAsExtra(entity->getID())) { + if (!jsonFilters.isEmpty() && entityMatchesFilters) { + // Record explicitly filtered-in entity so that extra entities can be flagged. + entityNodeData->insertSentFilteredEntity(entity->getID()); + } OctreeElement::AppendState appendEntityState = entity->appendEntityData(&_packetData, params, _extraEncodeData); if (appendEntityState != OctreeElement::COMPLETED) { diff --git a/assignment-client/src/entities/EntityTreeSendThread.h b/assignment-client/src/entities/EntityTreeSendThread.h index 49901491ff..a96a18494d 100644 --- a/assignment-client/src/entities/EntityTreeSendThread.h +++ b/assignment-client/src/entities/EntityTreeSendThread.h @@ -38,7 +38,8 @@ private: bool addAncestorsToExtraFlaggedEntities(const QUuid& filteredEntityID, EntityItem& entityItem, EntityNodeData& nodeData); bool addDescendantsToExtraFlaggedEntities(const QUuid& filteredEntityID, EntityItem& entityItem, EntityNodeData& nodeData); - void startNewTraversal(const ViewFrustum& viewFrustum, EntityTreeElementPointer root, int32_t lodLevelOffset, bool usesViewFrustum); + void startNewTraversal(const ViewFrustum& viewFrustum, EntityTreeElementPointer root, int32_t lodLevelOffset, + bool usesViewFrustum); bool traverseTreeAndBuildNextPacketPayload(EncodeBitstreamParams& params, const QJsonObject& jsonFilters) override; void preDistributionProcessing() override; diff --git a/cmake/externals/wasapi/CMakeLists.txt b/cmake/externals/wasapi/CMakeLists.txt index 4437024962..4c0ffaf88f 100644 --- a/cmake/externals/wasapi/CMakeLists.txt +++ b/cmake/externals/wasapi/CMakeLists.txt @@ -6,8 +6,8 @@ if (WIN32) include(ExternalProject) ExternalProject_Add( ${EXTERNAL_NAME} - URL http://hifi-public.s3.amazonaws.com/dependencies/qtaudio_wasapi9.zip - URL_MD5 94f4765bdbcd53cd099f349ae031e769 + URL http://hifi-public.s3.amazonaws.com/dependencies/qtaudio_wasapi10.zip + URL_MD5 4f40e49715a420fb67b45b9cee19052c CONFIGURE_COMMAND "" BUILD_COMMAND "" INSTALL_COMMAND "" diff --git a/domain-server/resources/web/js/shared.js b/domain-server/resources/web/js/shared.js index a5dd522a53..66159209ea 100644 --- a/domain-server/resources/web/js/shared.js +++ b/domain-server/resources/web/js/shared.js @@ -146,187 +146,256 @@ function sendUpdatePlaceRequest(id, path, domainID, clearDomainID, onSuccess, on }); } +var pendingDomainRequest = null; +function getDomainFromAPI(callback) { + if (pendingDomainRequest !== null) { + pendingDomainRequest.success(callback); + pendingDomainRequest.error(function() { callback({ status: 'fail' }) }); + return pendingDomainRequest; + } + + if (callback === undefined) { + callback = function() {}; + } + + var domainID = Settings.data.values.metaverse.id; + if (domainID === null || domainID === undefined || domainID === '') { + callback({ status: 'fail' }); + return null; + } + + pendingDomainRequest = $.ajax({ + url: "/api/domains/" + domainID, + dataType: 'json', + success: function(data) { + pendingDomainRequest = null; + + if (data.status === 'success') { + DomainInfo = data.domain; + } else { + DomainInfo = null; + } + callback(data); + }, + error: function() { + pendingDomainRequest = null; + + DomainInfo = null; + callback({ status: 'fail' }); + } + }); + + return pendingDomainRequest; +} + function chooseFromHighFidelityPlaces(accessToken, forcePathTo, onSuccessfullyAdded) { if (accessToken) { var loadingDialog = showLoadingDialog(Strings.ADD_PLACE_LOADING_DIALOG); - $.ajax("/api/places", { - dataType: 'json', - jsonp: false, - success: function(data) { - if (data.status == 'success') { - var modal_buttons = { - cancel: { - label: Strings.ADD_PLACE_CANCEL_BUTTON, - className: 'add-place-cancel-button btn-default' - } - }; - - var dialog; - var modal_body; - - if (data.data.places.length) { - var places_by_id = {}; - - modal_body = $('
'); - - modal_body.append($("

Choose a place name that you own or register a new place name

")); - - var currentDomainIDType = getCurrentDomainIDType(); - if (currentDomainIDType === DOMAIN_ID_TYPE_TEMP) { - var warning = "
"; - warning += "If you choose a place name it will replace your current temporary place name."; - warning += "
"; - modal_body.append(warning); - } - - // setup a select box for the returned places - modal_body.append($("")); - place_select = $(""); - _.each(data.data.places, function(place) { - places_by_id[place.id] = place; - place_select.append(""); - }) - modal_body.append(place_select); - modal_body.append($("")); - - if (forcePathTo === undefined || forcePathTo === null) { - var path = "
"; - path += ""; - path += ""; - path += "
"; - modal_body.append($(path)); - } - - var place_select = modal_body.find("#place-name-select") - place_select.change(function(ev) { - var warning = modal_body.find("#place-name-warning"); - var place = places_by_id[$(this).val()]; - if (place === undefined || place.pointee === null) { - warning.hide(); - } else { - warning.show(); + function loadPlaces() { + $.ajax("/api/places", { + dataType: 'json', + jsonp: false, + success: function(data) { + if (data.status == 'success') { + var modal_buttons = { + cancel: { + label: Strings.ADD_PLACE_CANCEL_BUTTON, + className: 'add-place-cancel-button btn-default' } - }); - place_select.trigger('change'); + }; - modal_buttons["success"] = { - label: Strings.ADD_PLACE_CONFIRM_BUTTON, - className: 'add-place-confirm-button btn btn-primary', - callback: function() { - var placeID = $('#place-name-select').val(); - // set the place ID on the form - $(Settings.place_ID_SELECTOR).val(placeID).change(); + var dialog; + var modal_body; - if (forcePathTo === undefined || forcePathTo === null) { - var placePath = $('#place-path-input').val(); + if (data.data.places.length) { + var places_by_id = {}; + + modal_body = $('
'); + + modal_body.append($("

Choose a place name that you own or register a new place name

")); + + var currentDomainIDType = getCurrentDomainIDType(); + if (currentDomainIDType === DOMAIN_ID_TYPE_TEMP) { + var warning = "
"; + warning += "If you choose a place name it will replace your current temporary place name."; + warning += "
"; + modal_body.append(warning); + } + + // setup a select box for the returned places + modal_body.append($("")); + place_select = $(""); + _.each(data.data.places, function(place) { + places_by_id[place.id] = place; + place_select.append(""); + }) + modal_body.append(place_select); + modal_body.append($("")); + + if (forcePathTo === undefined || forcePathTo === null) { + var path = "
"; + path += ""; + path += ""; + path += "
"; + modal_body.append($(path)); + } + + var place_select = modal_body.find("#place-name-select") + place_select.change(function(ev) { + var warning = modal_body.find("#place-name-warning"); + var place = places_by_id[$(this).val()]; + if (place === undefined || place.pointee === null) { + warning.hide(); } else { - var placePath = forcePathTo; + warning.show(); } + }); + place_select.trigger('change'); - $('.add-place-confirm-button').attr('disabled', 'disabled'); - $('.add-place-confirm-button').html(Strings.ADD_PLACE_CONFIRM_BUTTON_PENDING); - $('.add-place-cancel-button').attr('disabled', 'disabled'); + modal_buttons["success"] = { + label: Strings.ADD_PLACE_CONFIRM_BUTTON, + className: 'add-place-confirm-button btn btn-primary', + callback: function() { + var placeID = $('#place-name-select').val(); + // set the place ID on the form + $(Settings.place_ID_SELECTOR).val(placeID).change(); - function finalizeSaveDomainID(domainID) { - var jsonSettings = { - metaverse: { - id: domainID + if (forcePathTo === undefined || forcePathTo === null) { + var placePath = $('#place-path-input').val(); + } else { + var placePath = forcePathTo; + } + + $('.add-place-confirm-button').attr('disabled', 'disabled'); + $('.add-place-confirm-button').html(Strings.ADD_PLACE_CONFIRM_BUTTON_PENDING); + $('.add-place-cancel-button').attr('disabled', 'disabled'); + + function finalizeSaveDomainID(domainID) { + var jsonSettings = { + metaverse: { + id: domainID + } + } + var dialog = showLoadingDialog("Waiting for Domain Server to restart..."); + $.ajax('/settings.json', { + data: JSON.stringify(jsonSettings), + contentType: 'application/json', + type: 'POST' + }).done(function(data) { + if (data.status == "success") { + waitForDomainServerRestart(function() { + dialog.modal('hide'); + if (onSuccessfullyAdded) { + onSuccessfullyAdded(places_by_id[placeID].name, domainID); + } + }); + } else { + bootbox.alert("Failed to add place"); + } + }).fail(function() { + bootbox.alert("Failed to add place"); + }); + } + + // If domainID is not specified, the current domain id will be used. + function finishSettingUpPlace(domainID) { + sendUpdatePlaceRequest( + placeID, + placePath, + domainID, + false, + function(data) { + dialog.modal('hide') + if (domainID) { + $(Settings.DOMAIN_ID_SELECTOR).val(domainID).change(); + finalizeSaveDomainID(domainID); + } else { + if (onSuccessfullyAdded) { + onSuccessfullyAdded(places_by_id[placeID].name); + } + } + }, + function(data) { + $('.add-place-confirm-button').removeAttr('disabled'); + $('.add-place-confirm-button').html(Strings.ADD_PLACE_CONFIRM_BUTTON); + $('.add-place-cancel-button').removeAttr('disabled'); + bootbox.alert(Strings.ADD_PLACE_UNKNOWN_ERROR); + } + ); + } + + function maybeCreateNewDomainID() { + console.log("Maybe creating domain id", currentDomainIDType) + if (currentDomainIDType === DOMAIN_ID_TYPE_FULL) { + finishSettingUpPlace(); + } else { + sendCreateDomainRequest(function(domainID) { + console.log("Created domain", domainID); + finishSettingUpPlace(domainID); + }, function() { + $('.add-place-confirm-button').removeAttr('disabled'); + $('.add-place-confirm-button').html(Strings.ADD_PLACE_CONFIRM_BUTTON); + $('.add-place-cancel-button').removeAttr('disabled'); + bootbox.alert(Strings.ADD_PLACE_UNKNOWN_ERROR); + bootbox.alert("FAIL"); + }); } } - var dialog = showLoadingDialog("Waiting for Domain Server to restart..."); - $.ajax('/settings.json', { - data: JSON.stringify(jsonSettings), - contentType: 'application/json', - type: 'POST' - }).done(function(data) { - if (data.status == "success") { - waitForDomainServerRestart(function() { - dialog.modal('hide'); - if (onSuccessfullyAdded) { - onSuccessfullyAdded(places_by_id[placeID].name, domainID); - } - }); - } else { - bootbox.alert("Failed to add place"); - } - }).fail(function() { - bootbox.alert("Failed to add place"); - }); - } - function finishSettingUpPlace(domainID) { - sendUpdatePlaceRequest( - placeID, - placePath, - domainID, - false, - function(data) { - $(Settings.DOMAIN_ID_SELECTOR).val(domainID).change(); - dialog.modal('hide') - if (domainID) { - finalizeSaveDomainID(domainID); - } else { - if (onSuccessfullyAdded) { - onSuccessfullyAdded(places_by_id[placeID].name); - } - } - }, - function(data) { - $('.add-place-confirm-button').removeAttr('disabled'); - $('.add-place-confirm-button').html(Strings.ADD_PLACE_CONFIRM_BUTTON); - $('.add-place-cancel-button').removeAttr('disabled'); - bootbox.alert(Strings.ADD_PLACE_UNKNOWN_ERROR); - } - ); - } + maybeCreateNewDomainID(); - if (currentDomainIDType === DOMAIN_ID_TYPE_FULL) { - finishSettingUpPlace(); - } else { - sendCreateDomainRequest(function(domainID) { - console.log("Created domain", domainID); - finishSettingUpPlace(domainID); - }, function() { - $('.add-place-confirm-button').removeAttr('disabled'); - $('.add-place-confirm-button').html(Strings.ADD_PLACE_CONFIRM_BUTTON); - $('.add-place-cancel-button').removeAttr('disabled'); - bootbox.alert(Strings.ADD_PLACE_UNKNOWN_ERROR); - bootbox.alert("FAIL"); - }); + return false; } - - return false; } + } else { + modal_buttons["success"] = { + label: Strings.ADD_PLACE_NO_PLACES_BUTTON, + callback: function() { + window.open(URLs.METAVERSE_URL + "/user/places", '_blank'); + } + } + modal_body = Strings.ADD_PLACE_NO_PLACES_MESSAGE; } + + dialog = bootbox.dialog({ + title: Strings.ADD_PLACE_TITLE, + message: modal_body, + closeButton: false, + buttons: modal_buttons + }); } else { - modal_buttons["success"] = { - label: Strings.ADD_PLACE_NO_PLACES_BUTTON, - callback: function() { - window.open(URLs.METAVERSE_URL + "/user/places", '_blank'); - } - } - modal_body = Strings.ADD_PLACE_NO_PLACES_MESSAGE; + bootbox.alert(Strings.ADD_PLACE_UNABLE_TO_LOAD_ERROR); } - - dialog = bootbox.dialog({ - title: Strings.ADD_PLACE_TITLE, - message: modal_body, - closeButton: false, - buttons: modal_buttons - }); - } else { + }, + error: function() { bootbox.alert(Strings.ADD_PLACE_UNABLE_TO_LOAD_ERROR); + }, + complete: function() { + loadingDialog.modal('hide'); } - }, - error: function() { - bootbox.alert(Strings.ADD_PLACE_UNABLE_TO_LOAD_ERROR); - }, - complete: function() { - loadingDialog.modal('hide'); - } - }); + }); + } + + var domainType = getCurrentDomainIDType(); + if (domainType !== DOMAIN_ID_TYPE_UNKNOWN) { + loadPlaces(); + } else { + getDomainFromAPI(function(data) { + if (data.status === 'success') { + var domainType = getCurrentDomainIDType(); + loadPlaces(); + } else { + loadingDialog.modal('hide'); + bootbox.confirm("We were not able to load your domain information from the Metaverse. Would you like to retry?", function(response) { + if (response) { + chooseFromHighFidelityPlaces(accessToken, forcePathTo, onSuccessfullyAdded); + } + }); + } + }) + } } else { bootbox.alert({ diff --git a/domain-server/resources/web/settings/js/settings.js b/domain-server/resources/web/settings/js/settings.js index 1224b724da..7f99b367a3 100644 --- a/domain-server/resources/web/settings/js/settings.js +++ b/domain-server/resources/web/settings/js/settings.js @@ -980,20 +980,6 @@ function placeTableRowForPlaceObject(place) { return placeTableRow(place.name, placePathOrIndex, false, place.id); } -function getDomainFromAPI(callback) { - var domainID = Settings.data.values.metaverse.id; - $.ajax({ - url: "/api/domains/" + domainID, - dataType: 'json', - success: function(data) { - callback(data); - }, - error: function() { - callback({ status: 'fail' }); - } - }); -} - function reloadDomainInfo() { $('#' + Settings.PLACES_TABLE_ID + " tbody tr").not('.headers').remove(); @@ -1010,7 +996,6 @@ function reloadDomainInfo() { // check if we have owner_places (for a real domain) or a name (for a temporary domain) if (data.status == "success") { $('.domain-loading-hide').show(); - DomainInfo = data.domain; if (data.domain.owner_places) { // add a table row for each of these names _.each(data.domain.owner_places, function(place){ @@ -1043,7 +1028,6 @@ function reloadDomainInfo() { appendAddButtonToPlacesTable(); } else { - DomainInfo = null; $('.domain-loading-error').show(); } }) diff --git a/domain-server/resources/web/wizard/js/wizard.js b/domain-server/resources/web/wizard/js/wizard.js index 24a82402a6..1af3f305b7 100644 --- a/domain-server/resources/web/wizard/js/wizard.js +++ b/domain-server/resources/web/wizard/js/wizard.js @@ -58,6 +58,7 @@ $(document).ready(function(){ reloadSettings(function(success) { if (success) { + getDomainFromAPI(); setupWizardSteps(); updatePlaceNameDisplay(); updateUsernameDisplay(); diff --git a/domain-server/src/DomainServerSettingsManager.cpp b/domain-server/src/DomainServerSettingsManager.cpp index 43262a1a81..6c50e5245d 100644 --- a/domain-server/src/DomainServerSettingsManager.cpp +++ b/domain-server/src/DomainServerSettingsManager.cpp @@ -29,6 +29,7 @@ #include #include #include +#include #include //for KillAvatarReason #include #include "DomainServerNodeData.h" @@ -43,12 +44,7 @@ const QString DESCRIPTION_COLUMNS_KEY = "columns"; const QString SETTINGS_VIEWPOINT_KEY = "viewpoint"; -static Setting::Handle JSON_SETTING_VERSION("json-settings/version", 0.0); - -DomainServerSettingsManager::DomainServerSettingsManager() : - _descriptionArray(), - _configMap() -{ +DomainServerSettingsManager::DomainServerSettingsManager() { // load the description object from the settings description QFile descriptionFile(QCoreApplication::applicationDirPath() + SETTINGS_DESCRIPTION_RELATIVE_PATH); descriptionFile.open(QIODevice::ReadOnly); @@ -100,12 +96,34 @@ void DomainServerSettingsManager::processSettingsRequestPacket(QSharedPointer JSON_SETTING_VERSION("json-settings/version", 0.0); + if (JSON_SETTING_VERSION.isSet()) { + auto version = JSON_SETTING_VERSION.get(); + *versionVariant = version; + persistToFile(); + QFile::remove(settingsFilename()); + } + } // What settings version were we before and what are we using now? // Do we need to do any re-mapping? - double oldVersion = JSON_SETTING_VERSION.get(); + double oldVersion = versionVariant->toDouble(); if (oldVersion != _descriptionVersion) { const QString ALLOWED_USERS_SETTINGS_KEYPATH = "security.allowed_users"; @@ -137,12 +155,6 @@ void DomainServerSettingsManager::setupConfigMap(const QStringList& argumentList QVariant* restrictedAccess = _configMap.valueForKeyPath(RESTRICTED_ACCESS_SETTINGS_KEYPATH, true); *restrictedAccess = QVariant(true); - - // write the new settings to the json file - persistToFile(); - - // reload the master and user config so that the merged config is right - _configMap.loadMasterAndUserConfig(_argumentList); } } @@ -172,12 +184,6 @@ void DomainServerSettingsManager::setupConfigMap(const QStringList& argumentList *entityServerVariant = entityServerMap; } - - // write the new settings to the json file - persistToFile(); - - // reload the master and user config so that the merged config is right - _configMap.loadMasterAndUserConfig(_argumentList); } } @@ -195,12 +201,6 @@ void DomainServerSettingsManager::setupConfigMap(const QStringList& argumentList qDebug() << "Migrating plaintext password to SHA256 hash in domain-server settings."; *passwordVariant = QCryptographicHash::hash(plaintextPassword.toUtf8(), QCryptographicHash::Sha256).toHex(); - - // write the new settings to file - persistToFile(); - - // reload the master and user config so the merged config is correct - _configMap.loadMasterAndUserConfig(_argumentList); } } @@ -283,19 +283,6 @@ void DomainServerSettingsManager::setupConfigMap(const QStringList& argumentList packPermissions(); } - if (oldVersion < 1.7) { - // This was prior to the removal of the master config file - // So we write the merged config to the user config file, and stop reading from the user config file - - qDebug() << "Migrating merged config to user config file. The master config file is deprecated."; - - // replace the user config by the merged config - _configMap.getConfig() = _configMap.getMergedConfig(); - - // persist the new config so the user config file has the correctly merged config - persistToFile(); - } - if (oldVersion < 1.8) { unpackPermissions(); // This was prior to addition of domain content replacement, add that to localhost permissions by default @@ -316,16 +303,16 @@ void DomainServerSettingsManager::setupConfigMap(const QStringList& argumentList QVariant* wizardCompletedOnce = _configMap.valueForKeyPath(WIZARD_COMPLETED_ONCE, true); *wizardCompletedOnce = QVariant(true); - - // write the new settings to the json file - persistToFile(); } + + // write the current description version to our settings + *versionVariant = _descriptionVersion; + + // write the new settings to the json file + persistToFile(); } unpackPermissions(); - - // write the current description version to our settings - JSON_SETTING_VERSION.set(_descriptionVersion); } QVariantMap& DomainServerSettingsManager::getDescriptorsMap() { @@ -1289,9 +1276,6 @@ bool DomainServerSettingsManager::recurseJSONObjectAndOverwriteSettings(const QJ } } - // re-merge the user and master configs after a settings change - _configMap.mergeMasterAndUserConfigs(); - return needRestart; } diff --git a/interface/resources/qml/hifi/AssetServer.qml b/interface/resources/qml/hifi/AssetServer.qml index 5358ad1adc..37c3c2adab 100644 --- a/interface/resources/qml/hifi/AssetServer.qml +++ b/interface/resources/qml/hifi/AssetServer.qml @@ -16,10 +16,10 @@ import Qt.labs.settings 1.0 import "../styles-uit" import "../controls-uit" as HifiControls -import "../windows" +import "../windows" as Windows import "../dialogs" -ScrollingWindow { +Windows.ScrollingWindow { id: root objectName: "AssetServer" title: "Asset Browser" diff --git a/interface/resources/qml/hifi/NameCard.qml b/interface/resources/qml/hifi/NameCard.qml index fcfff02b72..775bae7969 100644 --- a/interface/resources/qml/hifi/NameCard.qml +++ b/interface/resources/qml/hifi/NameCard.qml @@ -432,7 +432,8 @@ Item { anchors.verticalCenter: nameCardRemoveConnectionImage.verticalCenter x: 240 onClicked: { - AddressManager.goToUser(thisNameCard.userName); + console.log("Vist user button clicked."); // Remove after debugging. + AddressManager.goToUser(thisNameCard.userName, false); UserActivityLogger.palAction("go_to_user", thisNameCard.userName); } } @@ -594,7 +595,10 @@ Item { // the avatar goes into fly mode rather than falling. However, that is not exposed to Javascript right now. // FIXME: it would be nice if this used the same teleport steps and smoothing as in the teleport.js script. // Note, however, that this script allows teleporting to a person in the air, while teleport.js is going to a grounded target. + // Position avatar 2 metres from the target in the direction that target avatar was facing. MyAvatar.position = Vec3.sum(avatar.position, Vec3.multiplyQbyV(avatar.orientation, {x: 0, y: 0, z: -2})); - MyAvatar.orientation = Quat.multiply(avatar.orientation, {y: 1}); + + // Rotate avatar on Y axis to face target avatar and cancel out any inherited roll and pitch. + MyAvatar.orientation = Quat.cancelOutRollAndPitch(Quat.multiply(avatar.orientation, {y: 1})); } } diff --git a/interface/resources/qml/hifi/Pal.qml b/interface/resources/qml/hifi/Pal.qml index 89f18e5ce2..efcf6ccfcf 100644 --- a/interface/resources/qml/hifi/Pal.qml +++ b/interface/resources/qml/hifi/Pal.qml @@ -827,7 +827,7 @@ Rectangle { hoverEnabled: enabled enabled: connectionsNameCard.selected && pal.activeTab == "connectionsTab" onClicked: { - AddressManager.goToUser(model.userName); + AddressManager.goToUser(model.userName, false); UserActivityLogger.palAction("go_to_user", model.userName); } onEntered: connectionsLocationData.color = hifi.colors.blueHighlight; diff --git a/interface/resources/qml/hifi/audio/Audio.qml b/interface/resources/qml/hifi/audio/Audio.qml index 12c2ec1835..87ddce49ca 100644 --- a/interface/resources/qml/hifi/audio/Audio.qml +++ b/interface/resources/qml/hifi/audio/Audio.qml @@ -213,8 +213,8 @@ Rectangle { anchors.right: parent.right peak: model.peak; anchors.verticalCenter: parent.verticalCenter - visible: (bar.currentIndex === 1 && selectedHMD && isVR) || - (bar.currentIndex === 0 && selectedDesktop && !isVR) && + visible: ((bar.currentIndex === 1 && isVR) || + (bar.currentIndex === 0 && !isVR)) && Audio.devices.input.peakValuesAvailable; } } diff --git a/interface/src/commerce/Ledger.cpp b/interface/src/commerce/Ledger.cpp index 904847cb5f..f607c923ee 100644 --- a/interface/src/commerce/Ledger.cpp +++ b/interface/src/commerce/Ledger.cpp @@ -90,7 +90,7 @@ void Ledger::buy(const QString& hfc_key, int cost, const QString& asset_id, cons signedSend("transaction", transactionString, hfc_key, "buy", "buySuccess", "buyFailure", controlled_failure); } -bool Ledger::receiveAt(const QString& hfc_key, const QString& old_key) { +bool Ledger::receiveAt(const QString& hfc_key, const QString& old_key, const QString& machine_fingerprint) { auto accountManager = DependencyManager::get(); if (!accountManager->isLoggedIn()) { qCWarning(commerce) << "Cannot set receiveAt when not logged in."; @@ -99,7 +99,13 @@ bool Ledger::receiveAt(const QString& hfc_key, const QString& old_key) { return false; // We know right away that we will fail, so tell the caller. } - signedSend("public_key", hfc_key.toUtf8(), old_key, "receive_at", "receiveAtSuccess", "receiveAtFailure"); + QJsonObject transaction; + transaction["hfc_key"] = hfc_key; + transaction["machine_fingerprint"] = machine_fingerprint; + QJsonDocument transactionDoc{ transaction }; + auto transactionString = transactionDoc.toJson(QJsonDocument::Compact); + + signedSend("transaction", transactionString, old_key, "receive_at", "receiveAtSuccess", "receiveAtFailure"); return true; // Note that there may still be an asynchronous signal of failure that callers might be interested in. } diff --git a/interface/src/commerce/Ledger.h b/interface/src/commerce/Ledger.h index 42eb0ffc49..54f3f780f3 100644 --- a/interface/src/commerce/Ledger.h +++ b/interface/src/commerce/Ledger.h @@ -26,7 +26,7 @@ class Ledger : public QObject, public Dependency { public: void buy(const QString& hfc_key, int cost, const QString& asset_id, const QString& inventory_key, const bool controlled_failure = false); - bool receiveAt(const QString& hfc_key, const QString& old_key); + bool receiveAt(const QString& hfc_key, const QString& old_key, const QString& machine_fingerprint); void balance(const QStringList& keys); void inventory(const QStringList& keys); void history(const QStringList& keys); diff --git a/interface/src/commerce/QmlCommerce.cpp b/interface/src/commerce/QmlCommerce.cpp index 9d07ddb4ab..f29e46d843 100644 --- a/interface/src/commerce/QmlCommerce.cpp +++ b/interface/src/commerce/QmlCommerce.cpp @@ -33,7 +33,7 @@ QmlCommerce::QmlCommerce(QQuickItem* parent) : OffscreenQmlDialog(parent) { connect(ledger.data(), &Ledger::updateCertificateStatus, this, &QmlCommerce::updateCertificateStatus); auto accountManager = DependencyManager::get(); - connect(accountManager.data(), &AccountManager::usernameChanged, [&]() { + connect(accountManager.data(), &AccountManager::usernameChanged, this, [&]() { setPassphrase(""); }); } diff --git a/interface/src/commerce/Wallet.cpp b/interface/src/commerce/Wallet.cpp index 85632ff8f1..9bc8dc9e43 100644 --- a/interface/src/commerce/Wallet.cpp +++ b/interface/src/commerce/Wallet.cpp @@ -16,6 +16,7 @@ #include "ui/ImageProvider.h" #include "scripting/HMDScriptingInterface.h" +#include #include #include #include @@ -541,7 +542,8 @@ bool Wallet::generateKeyPair() { // 2. It is maximally private, and we can step back from that later if desired. // 3. It maximally exercises all the machinery, so we are most likely to surface issues now. auto ledger = DependencyManager::get(); - return ledger->receiveAt(key, oldKey); + QString machineFingerprint = uuidStringWithoutCurlyBraces(FingerprintUtils::getMachineFingerprint()); + return ledger->receiveAt(key, oldKey, machineFingerprint); } QStringList Wallet::listPublicKeys() { diff --git a/interface/src/raypick/LaserPointer.cpp b/interface/src/raypick/LaserPointer.cpp index 75b43a251b..32dd74279b 100644 --- a/interface/src/raypick/LaserPointer.cpp +++ b/interface/src/raypick/LaserPointer.cpp @@ -25,7 +25,6 @@ LaserPointer::LaserPointer(const QVariant& rayProps, const RenderStateMap& rende _distanceScaleEnd(distanceScaleEnd), _rayPickUID(DependencyManager::get()->createRayPick(rayProps)) { - for (auto& state : _renderStates) { if (!enabled || state.first != _currentRenderState) { @@ -119,23 +118,25 @@ void LaserPointer::updateRenderState(const RenderState& renderState, const Inter qApp->getOverlays().editOverlay(renderState.getStartID(), startProps); } glm::vec3 endVec; - if (((defaultState || !_lockEnd) && _objectLockEnd.first.isNull()) || type == IntersectionType::HUD) { + if (((defaultState || !_lockEnd) && _lockEndObject.id.isNull()) || type == IntersectionType::HUD) { endVec = pickRay.origin + pickRay.direction * distance; } else { - if (!_objectLockEnd.first.isNull()) { + if (!_lockEndObject.id.isNull()) { glm::vec3 pos; glm::quat rot; glm::vec3 dim; glm::vec3 registrationPoint; - if (_objectLockEnd.second) { - pos = vec3FromVariant(qApp->getOverlays().getProperty(_objectLockEnd.first, "position").value); - rot = quatFromVariant(qApp->getOverlays().getProperty(_objectLockEnd.first, "rotation").value); - dim = vec3FromVariant(qApp->getOverlays().getProperty(_objectLockEnd.first, "dimensions").value); + if (_lockEndObject.isOverlay) { + pos = vec3FromVariant(qApp->getOverlays().getProperty(_lockEndObject.id, "position").value); + rot = quatFromVariant(qApp->getOverlays().getProperty(_lockEndObject.id, "rotation").value); + dim = vec3FromVariant(qApp->getOverlays().getProperty(_lockEndObject.id, "dimensions").value); registrationPoint = glm::vec3(0.5f); } else { - EntityItemProperties props = DependencyManager::get()->getEntityProperties(_objectLockEnd.first); - pos = props.getPosition(); - rot = props.getRotation(); + EntityItemProperties props = DependencyManager::get()->getEntityProperties(_lockEndObject.id); + glm::mat4 entityMat = createMatFromQuatAndPos(props.getRotation(), props.getPosition()); + glm::mat4 finalPosAndRotMat = entityMat * _lockEndObject.offsetMat; + pos = extractTranslation(finalPosAndRotMat); + rot = glmExtractRotation(finalPosAndRotMat); dim = props.getDimensions(); registrationPoint = props.getRegistrationPoint(); } @@ -209,7 +210,7 @@ void LaserPointer::update() { withReadLock([&] { RayPickResult prevRayPickResult = qApp->getRayPickManager().getPrevRayPickResult(_rayPickUID); if (_renderingEnabled && !_currentRenderState.empty() && _renderStates.find(_currentRenderState) != _renderStates.end() && - (prevRayPickResult.type != IntersectionType::NONE || _laserLength > 0.0f || !_objectLockEnd.first.isNull())) { + (prevRayPickResult.type != IntersectionType::NONE || _laserLength > 0.0f || !_lockEndObject.id.isNull())) { float distance = _laserLength > 0.0f ? _laserLength : prevRayPickResult.distance; updateRenderState(_renderStates[_currentRenderState], prevRayPickResult.type, distance, prevRayPickResult.objectID, prevRayPickResult.searchRay, false); disableRenderState(_defaultRenderStates[_currentRenderState].second); @@ -233,9 +234,11 @@ void LaserPointer::setLaserLength(const float laserLength) { }); } -void LaserPointer::setLockEndUUID(QUuid objectID, const bool isOverlay) { +void LaserPointer::setLockEndUUID(QUuid objectID, const bool isOverlay, const glm::mat4& offsetMat) { withWriteLock([&] { - _objectLockEnd = std::pair(objectID, isOverlay); + _lockEndObject.id = objectID; + _lockEndObject.isOverlay = isOverlay; + _lockEndObject.offsetMat = offsetMat; }); } diff --git a/interface/src/raypick/LaserPointer.h b/interface/src/raypick/LaserPointer.h index f2350c7199..896752a96e 100644 --- a/interface/src/raypick/LaserPointer.h +++ b/interface/src/raypick/LaserPointer.h @@ -21,6 +21,12 @@ class RayPickResult; +struct LockEndObject { + QUuid id { QUuid() }; + bool isOverlay { false }; + glm::mat4 offsetMat { glm::mat4() }; +}; + class RenderState { public: @@ -74,7 +80,7 @@ public: void setPrecisionPicking(const bool precisionPicking); void setLaserLength(const float laserLength); - void setLockEndUUID(QUuid objectID, const bool isOverlay); + void setLockEndUUID(QUuid objectID, const bool isOverlay, const glm::mat4& offsetMat = glm::mat4()); void setIgnoreItems(const QVector& ignoreItems) const; void setIncludeItems(const QVector& includeItems) const; @@ -91,7 +97,7 @@ private: bool _centerEndY; bool _lockEnd; bool _distanceScaleEnd; - std::pair _objectLockEnd { std::pair(QUuid(), false)}; + LockEndObject _lockEndObject; const QUuid _rayPickUID; diff --git a/interface/src/raypick/LaserPointerManager.cpp b/interface/src/raypick/LaserPointerManager.cpp index 9d58cc2587..45420d1488 100644 --- a/interface/src/raypick/LaserPointerManager.cpp +++ b/interface/src/raypick/LaserPointerManager.cpp @@ -113,9 +113,9 @@ void LaserPointerManager::setIncludeItems(const QUuid& uid, const QVector } } -void LaserPointerManager::setLockEndUUID(const QUuid& uid, const QUuid& objectID, const bool isOverlay) const { +void LaserPointerManager::setLockEndUUID(const QUuid& uid, const QUuid& objectID, const bool isOverlay, const glm::mat4& offsetMat) const { auto laserPointer = find(uid); if (laserPointer) { - laserPointer->setLockEndUUID(objectID, isOverlay); + laserPointer->setLockEndUUID(objectID, isOverlay, offsetMat); } } diff --git a/interface/src/raypick/LaserPointerManager.h b/interface/src/raypick/LaserPointerManager.h index e302318483..25089a291a 100644 --- a/interface/src/raypick/LaserPointerManager.h +++ b/interface/src/raypick/LaserPointerManager.h @@ -39,7 +39,7 @@ public: void setIgnoreItems(const QUuid& uid, const QVector& ignoreEntities) const; void setIncludeItems(const QUuid& uid, const QVector& includeEntities) const; - void setLockEndUUID(const QUuid& uid, const QUuid& objectID, const bool isOverlay) const; + void setLockEndUUID(const QUuid& uid, const QUuid& objectID, const bool isOverlay, const glm::mat4& offsetMat = glm::mat4()) const; void update(); diff --git a/interface/src/raypick/LaserPointerScriptingInterface.h b/interface/src/raypick/LaserPointerScriptingInterface.h index 19262e6e5d..986c53e24f 100644 --- a/interface/src/raypick/LaserPointerScriptingInterface.h +++ b/interface/src/raypick/LaserPointerScriptingInterface.h @@ -35,7 +35,7 @@ public slots: Q_INVOKABLE void setIgnoreItems(const QUuid& uid, const QScriptValue& ignoreEntities) const; Q_INVOKABLE void setIncludeItems(const QUuid& uid, const QScriptValue& includeEntities) const; - Q_INVOKABLE void setLockEndUUID(const QUuid& uid, const QUuid& objectID, bool isOverlay) const { qApp->getLaserPointerManager().setLockEndUUID(uid, objectID, isOverlay); } + Q_INVOKABLE void setLockEndUUID(const QUuid& uid, const QUuid& objectID, bool isOverlay, const glm::mat4& offsetMat = glm::mat4()) const { qApp->getLaserPointerManager().setLockEndUUID(uid, objectID, isOverlay, offsetMat); } private: static RenderState buildRenderState(const QVariantMap& propMap); diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index 0f2bce5ca4..acf7bf81d8 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -152,6 +152,7 @@ void Rig::overrideRoleAnimation(const QString& role, const QString& url, float f const float REFERENCE_FRAMES_PER_SECOND = 30.0f; float timeScale = fps / REFERENCE_FRAMES_PER_SECOND; auto clipNode = std::make_shared(role, url, firstFrame, lastFrame, timeScale, loop, false); + _roleAnimStates[role] = { role, url, fps, loop, firstFrame, lastFrame }; AnimNode::Pointer parent = node->getParent(); parent->replaceChild(node, clipNode); } else { @@ -1638,6 +1639,11 @@ void Rig::initAnimGraph(const QUrl& url) { _userAnimState = { UserAnimState::None, "", 30.0f, false, 0.0f, 0.0f }; overrideAnimation(origState.url, origState.fps, origState.loop, origState.firstFrame, origState.lastFrame); } + // restore the role animations we had before reset. + for (auto& roleAnimState : _roleAnimStates) { + auto roleState = roleAnimState.second; + overrideRoleAnimation(roleState.role, roleState.url, roleState.fps, roleState.loop, roleState.firstFrame, roleState.lastFrame); + } _animLoading = false; emit onLoadComplete(); diff --git a/libraries/animation/src/Rig.h b/libraries/animation/src/Rig.h index 18d49c5f1e..e9cc444bd4 100644 --- a/libraries/animation/src/Rig.h +++ b/libraries/animation/src/Rig.h @@ -335,8 +335,22 @@ protected: float firstFrame; float lastFrame; }; + + struct RoleAnimState { + RoleAnimState() {} + RoleAnimState(const QString& roleId, const QString& urlIn, float fpsIn, bool loopIn, float firstFrameIn, float lastFrameIn) : + role(roleId), url(urlIn), fps(fpsIn), loop(loopIn), firstFrame(firstFrameIn), lastFrame(lastFrameIn) {} + + QString role; + QString url; + float fps; + bool loop; + float firstFrame; + float lastFrame; + }; UserAnimState _userAnimState; + std::map _roleAnimStates; float _leftHandOverlayAlpha { 0.0f }; float _rightHandOverlayAlpha { 0.0f }; diff --git a/libraries/entities/src/DiffTraversal.cpp b/libraries/entities/src/DiffTraversal.cpp index 2f9423daa3..764c420197 100644 --- a/libraries/entities/src/DiffTraversal.cpp +++ b/libraries/entities/src/DiffTraversal.cpp @@ -142,7 +142,8 @@ DiffTraversal::DiffTraversal() { _path.reserve(MIN_PATH_DEPTH); } -DiffTraversal::Type DiffTraversal::prepareNewTraversal(const ViewFrustum& viewFrustum, EntityTreeElementPointer root, int32_t lodLevelOffset, bool usesViewFrustum) { +DiffTraversal::Type DiffTraversal::prepareNewTraversal(const ViewFrustum& viewFrustum, EntityTreeElementPointer root, + int32_t lodLevelOffset, bool usesViewFrustum) { assert(root); // there are three types of traversal: // diff --git a/libraries/entities/src/DiffTraversal.h b/libraries/entities/src/DiffTraversal.h index c26e48ae5f..eb7168356e 100644 --- a/libraries/entities/src/DiffTraversal.h +++ b/libraries/entities/src/DiffTraversal.h @@ -57,7 +57,8 @@ public: DiffTraversal(); - Type prepareNewTraversal(const ViewFrustum& viewFrustum, EntityTreeElementPointer root, int32_t lodLevelOffset, bool usesViewFrustum); + Type prepareNewTraversal(const ViewFrustum& viewFrustum, EntityTreeElementPointer root, int32_t lodLevelOffset, + bool usesViewFrustum); const ViewFrustum& getCurrentView() const { return _currentView.viewFrustum; } const ViewFrustum& getCompletedView() const { return _completedView.viewFrustum; } diff --git a/libraries/entities/src/EntityEditPacketSender.cpp b/libraries/entities/src/EntityEditPacketSender.cpp index e82ed82093..168b0cd446 100644 --- a/libraries/entities/src/EntityEditPacketSender.cpp +++ b/libraries/entities/src/EntityEditPacketSender.cpp @@ -93,27 +93,41 @@ void EntityEditPacketSender::queueEditEntityMessage(PacketType type, QByteArray bufferOut(NLPacket::maxPayloadSize(type), 0); - bool success; + OctreeElement::AppendState encodeResult = OctreeElement::PARTIAL; // start the loop assuming there's more to send auto nodeList = DependencyManager::get(); + + EntityPropertyFlags didntFitProperties; + EntityItemProperties propertiesCopy = properties; + if (properties.parentIDChanged() && properties.getParentID() == AVATAR_SELF_ID) { - EntityItemProperties propertiesCopy = properties; const QUuid myNodeID = nodeList->getSessionUUID(); propertiesCopy.setParentID(myNodeID); - success = EntityItemProperties::encodeEntityEditPacket(type, entityItemID, propertiesCopy, bufferOut); - } else { - success = EntityItemProperties::encodeEntityEditPacket(type, entityItemID, properties, bufferOut); } - if (success) { - #ifdef WANT_DEBUG - qCDebug(entities) << "calling queueOctreeEditMessage()..."; - qCDebug(entities) << " id:" << entityItemID; - qCDebug(entities) << " properties:" << properties; - #endif - queueOctreeEditMessage(type, bufferOut); - if (type == PacketType::EntityAdd && !properties.getCertificateID().isEmpty()) { - emit addingEntityWithCertificate(properties.getCertificateID(), DependencyManager::get()->getPlaceName()); + EntityPropertyFlags requestedProperties = propertiesCopy.getChangedProperties(); + + while (encodeResult == OctreeElement::PARTIAL) { + encodeResult = EntityItemProperties::encodeEntityEditPacket(type, entityItemID, propertiesCopy, bufferOut, requestedProperties, didntFitProperties); + + if (encodeResult != OctreeElement::NONE) { + #ifdef WANT_DEBUG + qCDebug(entities) << "calling queueOctreeEditMessage()..."; + qCDebug(entities) << " id:" << entityItemID; + qCDebug(entities) << " properties:" << properties; + #endif + queueOctreeEditMessage(type, bufferOut); + if (type == PacketType::EntityAdd && !properties.getCertificateID().isEmpty()) { + emit addingEntityWithCertificate(properties.getCertificateID(), DependencyManager::get()->getPlaceName()); + } } + + // if we still have properties to send, switch the message type to edit, and request only the packets that didn't fit + if (encodeResult != OctreeElement::COMPLETED) { + type = PacketType::EntityEdit; + requestedProperties = didntFitProperties; + } + + bufferOut.resize(NLPacket::maxPayloadSize(type)); // resize our output buffer for the next packet } } diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index f6f4e48a73..3f054e1ccb 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -83,7 +83,7 @@ EntityPropertyFlags EntityItem::getEntityProperties(EncodeBitstreamParams& param requestedProperties += PROP_ANGULAR_VELOCITY; requestedProperties += PROP_ACCELERATION; - requestedProperties += PROP_DIMENSIONS; // NOTE: PROP_RADIUS obsolete + requestedProperties += PROP_DIMENSIONS; requestedProperties += PROP_DENSITY; requestedProperties += PROP_GRAVITY; requestedProperties += PROP_DAMPING; @@ -241,7 +241,7 @@ OctreeElement::AppendState EntityItem::appendEntityData(OctreePacketData* packet APPEND_ENTITY_PROPERTY(PROP_ANGULAR_VELOCITY, getLocalAngularVelocity()); APPEND_ENTITY_PROPERTY(PROP_ACCELERATION, getAcceleration()); - APPEND_ENTITY_PROPERTY(PROP_DIMENSIONS, getDimensions()); // NOTE: PROP_RADIUS obsolete + APPEND_ENTITY_PROPERTY(PROP_DIMENSIONS, getDimensions()); APPEND_ENTITY_PROPERTY(PROP_DENSITY, getDensity()); APPEND_ENTITY_PROPERTY(PROP_GRAVITY, getGravity()); APPEND_ENTITY_PROPERTY(PROP_DAMPING, getDamping()); diff --git a/libraries/entities/src/EntityItemProperties.cpp b/libraries/entities/src/EntityItemProperties.cpp index f774b208c4..108fc14e30 100644 --- a/libraries/entities/src/EntityItemProperties.cpp +++ b/libraries/entities/src/EntityItemProperties.cpp @@ -1221,8 +1221,9 @@ void EntityItemProperties::entityPropertyFlagsFromScriptValue(const QScriptValue // // TODO: Implement support for script and visible properties. // -bool EntityItemProperties::encodeEntityEditPacket(PacketType command, EntityItemID id, const EntityItemProperties& properties, - QByteArray& buffer) { +OctreeElement::AppendState EntityItemProperties::encodeEntityEditPacket(PacketType command, EntityItemID id, const EntityItemProperties& properties, + QByteArray& buffer, EntityPropertyFlags requestedProperties, EntityPropertyFlags& didntFitProperties) { + OctreePacketData ourDataPacket(false, buffer.size()); // create a packetData object to add out packet details too. OctreePacketData* packetData = &ourDataPacket; // we want a pointer to this so we can use our APPEND_ENTITY_PROPERTY macro @@ -1264,17 +1265,8 @@ bool EntityItemProperties::encodeEntityEditPacket(PacketType command, EntityItem QByteArray encodedUpdateDelta = updateDeltaCoder; EntityPropertyFlags propertyFlags(PROP_LAST_ITEM); - EntityPropertyFlags requestedProperties = properties.getChangedProperties(); EntityPropertyFlags propertiesDidntFit = requestedProperties; - // TODO: we need to handle the multi-pass form of this, similar to how we handle entity data - // - // If we are being called for a subsequent pass at appendEntityData() that failed to completely encode this item, - // then our modelTreeElementExtraEncodeData should include data about which properties we need to append. - //if (modelTreeElementExtraEncodeData && modelTreeElementExtraEncodeData->includedItems.contains(getEntityItemID())) { - // requestedProperties = modelTreeElementExtraEncodeData->includedItems.value(getEntityItemID()); - //} - LevelDetails entityLevel = packetData->startLevel(); // Last Edited quint64 always first, before any other details, which allows us easy access to adjusting this @@ -1302,7 +1294,7 @@ bool EntityItemProperties::encodeEntityEditPacket(PacketType command, EntityItem int propertyCount = 0; bool headerFits = successIDFits && successTypeFits && successLastEditedFits - && successLastUpdatedFits && successPropertyFlagsFits; + && successLastUpdatedFits && successPropertyFlagsFits; int startOfEntityItemData = packetData->getUncompressedByteOffset(); @@ -1316,7 +1308,7 @@ bool EntityItemProperties::encodeEntityEditPacket(PacketType command, EntityItem APPEND_ENTITY_PROPERTY(PROP_SIMULATION_OWNER, properties._simulationOwner.toByteArray()); APPEND_ENTITY_PROPERTY(PROP_POSITION, properties.getPosition()); - APPEND_ENTITY_PROPERTY(PROP_DIMENSIONS, properties.getDimensions()); // NOTE: PROP_RADIUS obsolete + APPEND_ENTITY_PROPERTY(PROP_DIMENSIONS, properties.getDimensions()); APPEND_ENTITY_PROPERTY(PROP_ROTATION, properties.getRotation()); APPEND_ENTITY_PROPERTY(PROP_DENSITY, properties.getDensity()); APPEND_ENTITY_PROPERTY(PROP_VELOCITY, properties.getVelocity()); @@ -1472,6 +1464,7 @@ bool EntityItemProperties::encodeEntityEditPacket(PacketType command, EntityItem properties.getType() == EntityTypes::Sphere) { APPEND_ENTITY_PROPERTY(PROP_SHAPE, properties.getShape()); } + APPEND_ENTITY_PROPERTY(PROP_NAME, properties.getName()); APPEND_ENTITY_PROPERTY(PROP_COLLISION_SOUND_URL, properties.getCollisionSoundURL()); APPEND_ENTITY_PROPERTY(PROP_ACTION_DATA, properties.getActionData()); @@ -1522,12 +1515,7 @@ bool EntityItemProperties::encodeEntityEditPacket(PacketType command, EntityItem // If any part of the model items didn't fit, then the element is considered partial if (appendState != OctreeElement::COMPLETED) { - // TODO: handle mechanism for handling partial fitting data! - // add this item into our list for the next appendElementData() pass - //modelTreeElementExtraEncodeData->includedItems.insert(getEntityItemID(), propertiesDidntFit); - - // for now, if it's not complete, it's not successful - success = false; + didntFitProperties = propertiesDidntFit; } } @@ -1543,11 +1531,15 @@ bool EntityItemProperties::encodeEntityEditPacket(PacketType command, EntityItem } else { qCDebug(entities) << "ERROR - encoded edit message doesn't fit in output buffer."; success = false; + appendState = OctreeElement::NONE; // if we got here, then we didn't include the item + // maybe we should assert!!! } } else { packetData->discardSubTree(); } - return success; + + + return appendState; } QByteArray EntityItemProperties::getPackedNormals() const { @@ -1673,7 +1665,7 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_SIMULATION_OWNER, QByteArray, setSimulationOwner); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_POSITION, glm::vec3, setPosition); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_DIMENSIONS, glm::vec3, setDimensions); // NOTE: PROP_RADIUS obsolete + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_DIMENSIONS, glm::vec3, setDimensions); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_ROTATION, glm::quat, setRotation); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_DENSITY, float, setDensity); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_VELOCITY, glm::vec3, setVelocity); diff --git a/libraries/entities/src/EntityItemProperties.h b/libraries/entities/src/EntityItemProperties.h index a8bb063934..732dbdf69f 100644 --- a/libraries/entities/src/EntityItemProperties.h +++ b/libraries/entities/src/EntityItemProperties.h @@ -262,8 +262,8 @@ public: float getLocalRenderAlpha() const { return _localRenderAlpha; } void setLocalRenderAlpha(float value) { _localRenderAlpha = value; _localRenderAlphaChanged = true; } - static bool encodeEntityEditPacket(PacketType command, EntityItemID id, const EntityItemProperties& properties, - QByteArray& buffer); + static OctreeElement::AppendState encodeEntityEditPacket(PacketType command, EntityItemID id, const EntityItemProperties& properties, + QByteArray& buffer, EntityPropertyFlags requestedProperties, EntityPropertyFlags& didntFitProperties); static bool encodeEraseEntityMessage(const EntityItemID& entityItemID, QByteArray& buffer); diff --git a/libraries/entities/src/EntityPropertyFlags.h b/libraries/entities/src/EntityPropertyFlags.h index f0f22b0091..35d40b669a 100644 --- a/libraries/entities/src/EntityPropertyFlags.h +++ b/libraries/entities/src/EntityPropertyFlags.h @@ -21,8 +21,7 @@ enum EntityPropertyList { // these properties are supported by the EntityItem base class PROP_VISIBLE, PROP_POSITION, - PROP_RADIUS, // NOTE: PROP_RADIUS is obsolete and only included in old format streams - PROP_DIMENSIONS = PROP_RADIUS, + PROP_DIMENSIONS, PROP_ROTATION, PROP_DENSITY, PROP_VELOCITY, @@ -47,13 +46,13 @@ enum EntityPropertyList { PROP_ANGULAR_VELOCITY, PROP_ANGULAR_DAMPING, PROP_COLLISIONLESS, - PROP_DYNAMIC, + PROP_DYNAMIC, // 24 // property used by Light entity PROP_IS_SPOTLIGHT, PROP_DIFFUSE_COLOR, - PROP_AMBIENT_COLOR_UNUSED, - PROP_SPECULAR_COLOR_UNUSED, + PROP_AMBIENT_COLOR_UNUSED, // FIXME - No longer used, can remove and bump protocol + PROP_SPECULAR_COLOR_UNUSED, // FIXME - No longer used, can remove and bump protocol PROP_INTENSITY, // Previously PROP_CONSTANT_ATTENUATION PROP_LINEAR_ATTENUATION_UNUSED, PROP_QUADRATIC_ATTENUATION_UNUSED, @@ -61,30 +60,30 @@ enum EntityPropertyList { PROP_CUTOFF, // available to all entities - PROP_LOCKED, + PROP_LOCKED, // 34 PROP_TEXTURES, // used by Model entities - PROP_ANIMATION_SETTINGS, // used by Model entities - PROP_USER_DATA, // all entities + PROP_ANIMATION_SETTINGS_UNUSED, // FIXME - No longer used, can remove and bump protocol + PROP_USER_DATA, // all entities -- 37 PROP_SHAPE_TYPE, // used by Model + zones entities // used by ParticleEffect entities - PROP_MAX_PARTICLES, - PROP_LIFESPAN, + PROP_MAX_PARTICLES, // 39 + PROP_LIFESPAN, // 40 -- used by all entities PROP_EMIT_RATE, PROP_EMIT_SPEED, PROP_EMIT_STRENGTH, - PROP_EMIT_ACCELERATION, - PROP_PARTICLE_RADIUS, + PROP_EMIT_ACCELERATION, // FIXME - doesn't seem to get set in mark all changed???? + PROP_PARTICLE_RADIUS, // 45!! PROP_COMPOUND_SHAPE_URL, // used by Model + zones entities PROP_MARKETPLACE_ID, // all entities PROP_ACCELERATION, // all entities PROP_SIMULATION_OWNER, // formerly known as PROP_SIMULATOR_ID - PROP_NAME, // all entities + PROP_NAME, // all entities -- 50 PROP_COLLISION_SOUND_URL, PROP_RESTITUTION, - PROP_FRICTION, + PROP_FRICTION, // 53 PROP_VOXEL_VOLUME_SIZE, PROP_VOXEL_DATA, @@ -96,7 +95,7 @@ enum EntityPropertyList { // used by hyperlinks PROP_HREF, - PROP_DESCRIPTION, + PROP_DESCRIPTION, // 61 PROP_FACE_CAMERA, PROP_SCRIPT_TIMESTAMP, diff --git a/libraries/networking/src/AddressManager.cpp b/libraries/networking/src/AddressManager.cpp index 1a51440e91..b884dcba17 100644 --- a/libraries/networking/src/AddressManager.cpp +++ b/libraries/networking/src/AddressManager.cpp @@ -667,8 +667,11 @@ bool AddressManager::handleViewpoint(const QString& viewpointString, bool should qCDebug(networking) << "Orientation parsed from lookup string is invalid. Will not use for location change."; } } - - emit locationChangeRequired(newPosition, orientationChanged, newOrientation, shouldFace); + + emit locationChangeRequired(newPosition, orientationChanged, + LookupTrigger::VisitUserFromPAL ? cancelOutRollAndPitch(newOrientation): newOrientation, + shouldFace + ); } else { qCDebug(networking) << "Could not jump to position from lookup string because it has an invalid value."; @@ -732,13 +735,14 @@ bool AddressManager::setDomainInfo(const QString& hostname, quint16 port, Lookup return hostChanged; } -void AddressManager::goToUser(const QString& username) { +void AddressManager::goToUser(const QString& username, bool shouldMatchOrientation) { QString formattedUsername = QUrl::toPercentEncoding(username); - // for history storage handling we remember how this lookup was trigged - for a username it's always user input + // for history storage handling we remember how this lookup was triggered - for a username it's always user input QVariantMap requestParams; - requestParams.insert(LOOKUP_TRIGGER_KEY, static_cast(LookupTrigger::UserInput)); - + requestParams.insert(LOOKUP_TRIGGER_KEY, static_cast( + shouldMatchOrientation ? LookupTrigger::UserInput : LookupTrigger::VisitUserFromPAL + )); // this is a username - pull the captured name and lookup that user's location DependencyManager::get()->sendRequest(GET_USER_LOCATION.arg(formattedUsername), AccountManagerAuth::Optional, @@ -840,8 +844,8 @@ void AddressManager::addCurrentAddressToHistory(LookupTrigger trigger) { // and do not but it into the back stack _forwardStack.push(currentAddress()); } else { - if (trigger == LookupTrigger::UserInput) { - // anyime the user has manually looked up an address we know we should clear the forward stack + if (trigger == LookupTrigger::UserInput || trigger == LookupTrigger::VisitUserFromPAL) { + // anyime the user has actively triggered an address we know we should clear the forward stack _forwardStack.clear(); emit goForwardPossible(false); diff --git a/libraries/networking/src/AddressManager.h b/libraries/networking/src/AddressManager.h index 366fc5dfab..2f3d896509 100644 --- a/libraries/networking/src/AddressManager.h +++ b/libraries/networking/src/AddressManager.h @@ -54,7 +54,8 @@ public: DomainPathResponse, Internal, AttemptedRefresh, - Suggestions + Suggestions, + VisitUserFromPAL }; bool isConnected(); @@ -93,7 +94,7 @@ public slots: void goToLocalSandbox(QString path = "", LookupTrigger trigger = LookupTrigger::StartupFromSettings) { handleUrl(SANDBOX_HIFI_ADDRESS + path, trigger); } void goToEntry(LookupTrigger trigger = LookupTrigger::StartupFromSettings) { handleUrl(DEFAULT_HIFI_ADDRESS, trigger); } - void goToUser(const QString& username); + void goToUser(const QString& username, bool shouldMatchOrientation = true); void refreshPreviousLookup(); diff --git a/libraries/octree/src/OctreePacketData.cpp b/libraries/octree/src/OctreePacketData.cpp index 493dfdcff5..b5b4a161ef 100644 --- a/libraries/octree/src/OctreePacketData.cpp +++ b/libraries/octree/src/OctreePacketData.cpp @@ -68,8 +68,8 @@ bool OctreePacketData::append(const unsigned char* data, int length) { _dirty = true; } - const bool wantDebug = false; - if (wantDebug && !success) { + #ifdef WANT_DEBUG + if (!success) { qCDebug(octree) << "OctreePacketData::append(const unsigned char* data, int length) FAILING...."; qCDebug(octree) << " length=" << length; qCDebug(octree) << " _bytesAvailable=" << _bytesAvailable; @@ -77,6 +77,7 @@ bool OctreePacketData::append(const unsigned char* data, int length) { qCDebug(octree) << " _targetSize=" << _targetSize; qCDebug(octree) << " _bytesReserved=" << _bytesReserved; } + #endif return success; } @@ -647,6 +648,13 @@ void OctreePacketData::debugContent() { printf("\n"); } +void OctreePacketData::debugBytes() { + qCDebug(octree) << " _bytesAvailable=" << _bytesAvailable; + qCDebug(octree) << " _bytesInUse=" << _bytesInUse; + qCDebug(octree) << " _targetSize=" << _targetSize; + qCDebug(octree) << " _bytesReserved=" << _bytesReserved; +} + int OctreePacketData::unpackDataFromBytes(const unsigned char* dataBytes, QString& result) { uint16_t length; memcpy(&length, dataBytes, sizeof(length)); diff --git a/libraries/octree/src/OctreePacketData.h b/libraries/octree/src/OctreePacketData.h index ed6a49941b..37c171504b 100644 --- a/libraries/octree/src/OctreePacketData.h +++ b/libraries/octree/src/OctreePacketData.h @@ -240,6 +240,7 @@ public: /// displays contents for debugging void debugContent(); + void debugBytes(); static quint64 getCompressContentTime() { return _compressContentTime; } /// total time spent compressing content static quint64 getCompressContentCalls() { return _compressContentCalls; } /// total calls to compress content diff --git a/libraries/render-utils/src/DrawHaze.cpp b/libraries/render-utils/src/DrawHaze.cpp index 7cf07d8f33..f694a93033 100644 --- a/libraries/render-utils/src/DrawHaze.cpp +++ b/libraries/render-utils/src/DrawHaze.cpp @@ -140,7 +140,7 @@ void DrawHaze::run(const render::RenderContextPointer& renderContext, const Inpu gpu::StatePointer state = gpu::StatePointer(new gpu::State()); // Mask out haze on the tablet - PrepareStencil::testNoAA(*state); + PrepareStencil::testMask(*state); gpu::Shader::BindingSet slotBindings; slotBindings.insert(gpu::Shader::Binding(std::string("hazeBuffer"), HazeEffect_ParamsSlot)); diff --git a/libraries/render-utils/src/StencilMaskPass.cpp b/libraries/render-utils/src/StencilMaskPass.cpp index 0e0d8b56b3..f71111b64e 100644 --- a/libraries/render-utils/src/StencilMaskPass.cpp +++ b/libraries/render-utils/src/StencilMaskPass.cpp @@ -116,9 +116,9 @@ void PrepareStencil::drawBackground(gpu::State& state) { gpu::State::STENCIL_OP_REPLACE, gpu::State::STENCIL_OP_REPLACE, gpu::State::STENCIL_OP_KEEP)); } -// Pass if this area has NOT been marked as MASK +// Pass if this area has NOT been marked as MASK or anything containing MASK void PrepareStencil::testMask(gpu::State& state) { - state.setStencilTest(true, 0x00, gpu::State::StencilTest(STENCIL_MASK, 0xFF, gpu::NOT_EQUAL, + state.setStencilTest(true, 0x00, gpu::State::StencilTest(STENCIL_MASK, STENCIL_MASK, gpu::NOT_EQUAL, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_KEEP)); } diff --git a/libraries/render-utils/src/StencilMaskPass.h b/libraries/render-utils/src/StencilMaskPass.h index ddbf4a7ac0..fc258b607a 100644 --- a/libraries/render-utils/src/StencilMaskPass.h +++ b/libraries/render-utils/src/StencilMaskPass.h @@ -49,8 +49,8 @@ public: static void drawMask(gpu::State& state); static void drawBackground(gpu::State& state); - static void testNoAA(gpu::State& state); static void testMask(gpu::State& state); + static void testNoAA(gpu::State& state); static void testBackground(gpu::State& state); static void testShape(gpu::State& state); static void testMaskDrawShape(gpu::State& state); diff --git a/libraries/shared/src/HifiConfigVariantMap.cpp b/libraries/shared/src/HifiConfigVariantMap.cpp index d0fb14e104..59f660feee 100644 --- a/libraries/shared/src/HifiConfigVariantMap.cpp +++ b/libraries/shared/src/HifiConfigVariantMap.cpp @@ -91,32 +91,6 @@ QVariantMap HifiConfigVariantMap::mergeCLParametersWithJSONConfig(const QStringL return mergedMap; } -HifiConfigVariantMap::HifiConfigVariantMap() : - _userConfigFilename(), - _masterConfig(), - _userConfig(), - _mergedConfig() -{ - -} - -void HifiConfigVariantMap::loadMasterAndUserConfig(const QStringList& argumentList) { - // check if there is a master config file - const QString MASTER_CONFIG_FILE_OPTION = "--master-config"; - - int masterConfigIndex = argumentList.indexOf(MASTER_CONFIG_FILE_OPTION); - if (masterConfigIndex != -1) { - QString masterConfigFilepath = argumentList[masterConfigIndex + 1]; - - loadMapFromJSONFile(_masterConfig, masterConfigFilepath); - } - - // load the user config - that method replace loadMasterAndUserConfig after the 1.7 migration - loadConfig(argumentList); - - mergeMasterAndUserConfigs(); -} - void HifiConfigVariantMap::loadConfig(const QStringList& argumentList) { // load the user config const QString USER_CONFIG_FILE_OPTION = "--user-config"; @@ -172,14 +146,6 @@ void HifiConfigVariantMap::loadConfig(const QStringList& argumentList) { loadMapFromJSONFile(_userConfig, _userConfigFilename); } -void HifiConfigVariantMap::mergeMasterAndUserConfigs() { - // the merged config is initially matched to the master config - _mergedConfig = _masterConfig; - - // then we merge in anything missing from the user config - addMissingValuesToExistingMap(_mergedConfig, _userConfig); -} - void HifiConfigVariantMap::loadMapFromJSONFile(QVariantMap& existingMap, const QString& filename) { QFile configFile(filename); diff --git a/libraries/shared/src/HifiConfigVariantMap.h b/libraries/shared/src/HifiConfigVariantMap.h index cb6e92df96..ee248ec3d2 100644 --- a/libraries/shared/src/HifiConfigVariantMap.h +++ b/libraries/shared/src/HifiConfigVariantMap.h @@ -21,26 +21,19 @@ class HifiConfigVariantMap { public: static QVariantMap mergeCLParametersWithJSONConfig(const QStringList& argumentList); - HifiConfigVariantMap(); - void loadMasterAndUserConfig(const QStringList& argumentList); void loadConfig(const QStringList& argumentList); const QVariant value(const QString& key) const { return _userConfig.value(key); } QVariant* valueForKeyPath(const QString& keyPath, bool shouldCreateIfMissing = false) { return ::valueForKeyPath(_userConfig, keyPath, shouldCreateIfMissing); } - QVariantMap& getMergedConfig() { return _mergedConfig; } QVariantMap& getConfig() { return _userConfig; } - void mergeMasterAndUserConfigs(); - const QString& getUserConfigFilename() const { return _userConfigFilename; } private: QString _userConfigFilename; - QVariantMap _masterConfig; QVariantMap _userConfig; - QVariantMap _mergedConfig; void loadMapFromJSONFile(QVariantMap& existingMap, const QString& filename); void addMissingValuesToExistingMap(QVariantMap& existingMap, const QVariantMap& newMap); diff --git a/libraries/shared/src/SettingHelpers.cpp b/libraries/shared/src/SettingHelpers.cpp index dd301aa5aa..c6b8ac8f83 100644 --- a/libraries/shared/src/SettingHelpers.cpp +++ b/libraries/shared/src/SettingHelpers.cpp @@ -23,9 +23,15 @@ #include "SharedLogging.h" +const QSettings::Format JSON_FORMAT = QSettings::registerFormat("json", readJSONFile, writeJSONFile); + QSettings::SettingsMap jsonDocumentToVariantMap(const QJsonDocument& document); QJsonDocument variantMapToJsonDocument(const QSettings::SettingsMap& map); +QString settingsFilename() { + return QSettings().fileName(); +} + bool readJSONFile(QIODevice& device, QSettings::SettingsMap& map) { QJsonParseError jsonParseError; diff --git a/libraries/shared/src/SettingHelpers.h b/libraries/shared/src/SettingHelpers.h index 122e56957c..ad61897c65 100644 --- a/libraries/shared/src/SettingHelpers.h +++ b/libraries/shared/src/SettingHelpers.h @@ -14,12 +14,13 @@ #include +extern const QSettings::Format JSON_FORMAT; + +QString settingsFilename(); + bool readJSONFile(QIODevice& device, QSettings::SettingsMap& map); bool writeJSONFile(QIODevice& device, const QSettings::SettingsMap& map); -static const auto JSON_FORMAT = QSettings::registerFormat("json", readJSONFile, writeJSONFile); - void loadOldINIFile(QSettings& settings); - #endif // hifi_SettingHelpers_h diff --git a/scripts/system/controllers/controllerModules/farActionGrabEntity.js b/scripts/system/controllers/controllerModules/farActionGrabEntity.js index e34855d521..7d9a6dc1b5 100644 --- a/scripts/system/controllers/controllerModules/farActionGrabEntity.js +++ b/scripts/system/controllers/controllerModules/farActionGrabEntity.js @@ -13,12 +13,13 @@ makeDispatcherModuleParameters, MSECS_PER_SEC, HAPTIC_PULSE_STRENGTH, HAPTIC_PULSE_DURATION, PICK_MAX_DISTANCE, COLORS_GRAB_SEARCHING_HALF_SQUEEZE, COLORS_GRAB_SEARCHING_FULL_SQUEEZE, COLORS_GRAB_DISTANCE_HOLD, DEFAULT_SEARCH_SPHERE_DISTANCE, TRIGGER_OFF_VALUE, TRIGGER_ON_VALUE, ZERO_VEC, ensureDynamic, - getControllerWorldLocation, projectOntoEntityXYPlane, ContextOverlay, HMD, Reticle, Overlays, isPointingAtUI + getControllerWorldLocation, projectOntoEntityXYPlane, ContextOverlay, HMD, Reticle, Overlays, isPointingAtUI, Xform, getEntityParents */ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); Script.include("/~/system/libraries/controllers.js"); +Script.include("/~/system/libraries/Xform.js"); (function() { var PICK_WITH_HAND_RAY = true; @@ -113,18 +114,71 @@ Script.include("/~/system/libraries/controllers.js"); ]; var MARGIN = 25; + + function TargetObject(entityID, entityProps) { + this.entityID = entityID; + this.entityProps = entityProps; + this.targetEntityID = null; + this.targetEntityProps = null; + this.previousCollisionStatus = null; + this.madeDynamic = null; + + this.makeDynamic = function() { + if (this.targetEntityID) { + var newProps = { + dynamic: true, + collisionless: true + }; + this.previousCollisionStatus = this.targetEntityProps.collisionless; + Entities.editEntity(this.targetEntityID, newProps); + this.madeDynamic = true; + } + }; + + this.restoreTargetEntityOriginalProps = function() { + if (this.madeDynamic) { + var props = {}; + props.dynamic = false; + props.collisionless = this.previousCollisionStatus; + var zeroVector = {x: 0, y: 0, z:0}; + props.localVelocity = zeroVector; + props.localRotation = zeroVector; + Entities.editEntity(this.targetEntityID, props); + } + }; + + this.getTargetEntity = function() { + var parentPropsLength = this.parentProps.length; + if (parentPropsLength !== 0) { + var targetEntity = { + id: this.parentProps[parentPropsLength - 1].id, + props: this.parentProps[parentPropsLength - 1]}; + this.targetEntityID = targetEntity.id; + this.targetEntityProps = targetEntity.props; + return targetEntity; + } + this.targetEntityID = this.entityID; + this.targetEntityProps = this.entityProps; + return { + id: this.entityID, + props: this.entityProps}; + }; + } + function FarActionGrabEntity(hand) { this.hand = hand; this.grabbedThingID = null; + this.targetObject = null; this.actionID = null; // action this script created... + this.entityToLockOnto = null; this.entityWithContextOverlay = false; this.contextOverlayTimer = false; this.previousCollisionStatus = false; + this.locked = false; this.reticleMinX = MARGIN; this.reticleMaxX; this.reticleMinY = MARGIN; this.reticleMaxY; - this.madeDynamic = false; var ACTION_TTL = 15; // seconds @@ -158,9 +212,25 @@ Script.include("/~/system/libraries/controllers.js"); LaserPointers.enableLaserPointer(laserPointerID); LaserPointers.setRenderState(laserPointerID, mode); if (this.distanceHolding || this.distanceRotating) { - LaserPointers.setLockEndUUID(laserPointerID, this.grabbedThingID, this.grabbedIsOverlay); + if (!this.locked) { + // calculate offset + var targetProps = Entities.getEntityProperties(this.targetObject.entityID, [ + "position", + "rotation" + ]); + var zeroVector = { x: 0, y: 0, z:0, w: 0 }; + var intersection = controllerData.rayPicks[this.hand].intersection; + var intersectionMat = new Xform(zeroVector, intersection); + var modelMat = new Xform(targetProps.rotation, targetProps.position); + var modelMatInv = modelMat.inv(); + var xformMat = Xform.mul(modelMatInv, intersectionMat); + var offsetMat = Mat4.createFromRotAndTrans(xformMat.rot, xformMat.pos); + LaserPointers.setLockEndUUID(laserPointerID, this.targetObject.entityID, this.grabbedIsOverlay, offsetMat); + this.locked = true; + } } else { LaserPointers.setLockEndUUID(laserPointerID, null, false); + this.locked = false; } }; @@ -339,21 +409,15 @@ Script.include("/~/system/libraries/controllers.js"); var args = [this.hand === RIGHT_HAND ? "right" : "left", MyAvatar.sessionUUID]; Entities.callEntityMethod(this.grabbedThingID, "releaseGrab", args); - - if (this.madeDynamic) { - var props = {}; - props.dynamic = false; - props.collisionless = this.previousCollisionStatus; - props.localVelocity = {x: 0, y: 0, z: 0}; - props.localRotation = {x: 0, y: 0, z: 0}; - Entities.editEntity(this.grabbedThingID, props); - this.madeDynamic = false; + if (this.targetObject) { + this.targetObject.restoreTargetEntityOriginalProps(); } this.actionID = null; this.grabbedThingID = null; + this.targetObject = null; }; - this.updateRecommendedArea = function() { + this.updateRecommendedArea = function() { var dims = Controller.getViewportDimensions(); this.reticleMaxX = dims.x - MARGIN; this.reticleMaxY = dims.y - MARGIN; @@ -503,17 +567,18 @@ Script.include("/~/system/libraries/controllers.js"); "userData", "locked", "type" ]); + this.targetObject = new TargetObject(entityID, targetProps); + this.targetObject.parentProps = getEntityParents(targetProps); if (entityID !== this.entityWithContextOverlay) { this.destroyContextOverlay(); } + var targetEntity = this.targetObject.getTargetEntity(); + entityID = targetEntity.id; + targetProps = targetEntity.props; if (entityIsGrabbable(targetProps)) { if (!entityIsDistanceGrabbable(targetProps)) { - targetProps.dynamic = true; - this.previousCollisionStatus = targetProps.collisionless; - targetProps.collisionless = true; - Entities.editEntity(entityID, targetProps); - this.madeDynamic = true; + this.targetObject.makeDynamic(); } if (!this.distanceRotating) { diff --git a/scripts/system/controllers/grab.js b/scripts/system/controllers/grab.js index a1846e7ad7..eaa15ee3b1 100644 --- a/scripts/system/controllers/grab.js +++ b/scripts/system/controllers/grab.js @@ -315,6 +315,10 @@ Grabber.prototype.pressEvent = function(event) { return; } + if (event.isAlt || event.isMeta) { + return; + } + if (Overlays.getOverlayAtPoint(Reticle.position) > 0) { // the mouse is pointing at an overlay; don't look for entities underneath the overlay. return; diff --git a/scripts/system/edit.js b/scripts/system/edit.js index e76a02b6f5..ba84ffd716 100644 --- a/scripts/system/edit.js +++ b/scripts/system/edit.js @@ -1333,7 +1333,7 @@ function sortSelectedEntities(selected) { return sortedEntities; } -function recursiveDelete(entities, childrenList) { +function recursiveDelete(entities, childrenList, deletedIDs) { var entitiesLength = entities.length; for (var i = 0; i < entitiesLength; i++) { var entityID = entities[i]; @@ -1346,6 +1346,7 @@ function recursiveDelete(entities, childrenList) { properties: initialProperties, children: grandchildrenList }); + deletedIDs.push(entityID); Entities.deleteEntity(entityID); } } @@ -1413,6 +1414,8 @@ function parentSelectedEntities() { } function deleteSelectedEntities() { if (SelectionManager.hasSelection()) { + var deletedIDs = []; + selectedParticleEntityID = null; particleExplorerTool.destroyWebView(); SelectionManager.saveProperties(); @@ -1423,16 +1426,22 @@ function deleteSelectedEntities() { var initialProperties = SelectionManager.savedProperties[entityID]; var children = Entities.getChildrenIDs(entityID); var childList = []; - recursiveDelete(children, childList); + recursiveDelete(children, childList, deletedIDs); savedProperties.push({ entityID: entityID, properties: initialProperties, children: childList }); + deletedIDs.push(entityID); Entities.deleteEntity(entityID); } SelectionManager.clearSelections(); pushCommandForSelections([], savedProperties); + + entityListTool.webView.emitScriptEvent(JSON.stringify({ + type: "deleted", + ids: deletedIDs + })); } } diff --git a/scripts/system/html/js/entityList.js b/scripts/system/html/js/entityList.js index ea79750154..7b25e66c67 100644 --- a/scripts/system/html/js/entityList.js +++ b/scripts/system/html/js/entityList.js @@ -286,7 +286,6 @@ function loaded() { } elDelete.onclick = function() { EventBridge.emitWebEvent(JSON.stringify({ type: 'delete' })); - refreshEntities(); } document.addEventListener("keydown", function (keyDownEvent) { @@ -362,6 +361,12 @@ function loaded() { updateSelectedEntities(data.selectedIDs); resize(); } + } else if (data.type === "deleted") { + for (i = 0, length = data.ids.length; i < length; i++) { + delete entities[data.ids[i]]; + entityList.remove("id", data.ids[i]); + } + refreshFooter(); } }); setTimeout(refreshEntities, 1000); diff --git a/scripts/system/html/js/marketplacesInject.js b/scripts/system/html/js/marketplacesInject.js index 4b127baef8..dc0c610ace 100644 --- a/scripts/system/html/js/marketplacesInject.js +++ b/scripts/system/html/js/marketplacesInject.js @@ -89,7 +89,7 @@ window.location = "https://clara.io/library?gameCheck=true&public=true"; }); $('#exploreHifiMarketplace').on('click', function () { - window.location = "http://www.highfidelity.com/marketplace"; + window.location = metaverseServerURL + "/marketplace"; }); } @@ -612,9 +612,9 @@ var HIFI_ITEM_PAGE = 3; var pageType = DIRECTORY; - if (location.href.indexOf("highfidelity.com/") !== -1) { pageType = HIFI; } + if (location.href.indexOf(metaverseServerURL + "/") !== -1) { pageType = HIFI; } if (location.href.indexOf("clara.io/") !== -1) { pageType = CLARA; } - if (location.href.indexOf("highfidelity.com/marketplace/items/") !== -1) { pageType = HIFI_ITEM_PAGE; } + if (location.href.indexOf(metaverseServerURL + "/marketplace/items/") !== -1) { pageType = HIFI_ITEM_PAGE; } injectCommonCode(pageType === DIRECTORY); switch (pageType) { diff --git a/scripts/system/libraries/cloneEntityUtils.js b/scripts/system/libraries/cloneEntityUtils.js index 777504b16d..63b161eb80 100644 --- a/scripts/system/libraries/cloneEntityUtils.js +++ b/scripts/system/libraries/cloneEntityUtils.js @@ -8,8 +8,7 @@ /* global entityIsCloneable:true, getGrabbableData:true, cloneEntity:true, propsAreCloneDynamic:true, Script, propsAreCloneDynamic:true, Entities*/ -Script.include("/~/system/controllers/controllerDispatcherUtils.js"); - +Script.include("/~/system/libraries/controllerDispatcherUtils.js"); // Object assign polyfill if (typeof Object.assign !== 'function') { diff --git a/scripts/system/libraries/controllerDispatcherUtils.js b/scripts/system/libraries/controllerDispatcherUtils.js index cd3f1a711f..fb6de0e683 100644 --- a/scripts/system/libraries/controllerDispatcherUtils.js +++ b/scripts/system/libraries/controllerDispatcherUtils.js @@ -35,12 +35,14 @@ propsArePhysical:true, controllerDispatcherPluginsNeedSort:true, projectOntoXYPlane:true, + getChildrenProps:true, projectOntoEntityXYPlane:true, projectOntoOverlayXYPlane:true, entityHasActions:true, ensureDynamic:true, findGroupParent:true, BUMPER_ON_VALUE:true, + getEntityParents:true, findHandChildEntities:true, TEAR_AWAY_DISTANCE:true, TEAR_AWAY_COUNT:true, @@ -306,6 +308,23 @@ findGroupParent = function (controllerData, targetProps) { return targetProps; }; +getEntityParents = function(targetProps) { + var parentProperties = []; + while (targetProps.parentID && + targetProps.parentID !== Uuid.NULL && + Entities.getNestableType(targetProps.parentID) == "entity") { + var parentProps = Entities.getEntityProperties(targetProps.parentID, DISPATCHER_PROPERTIES); + if (!parentProps) { + break; + } + parentProps.id = targetProps.parentID; + targetProps = parentProps; + parentProperties.push(parentProps); + } + + return parentProperties; +}; + findHandChildEntities = function(hand) { // find children of avatar's hand joint diff --git a/tests/octree/src/OctreeTests.cpp b/tests/octree/src/OctreeTests.cpp index 64d68e8e13..81300a1293 100644 --- a/tests/octree/src/OctreeTests.cpp +++ b/tests/octree/src/OctreeTests.cpp @@ -74,7 +74,7 @@ void OctreeTests::propertyFlagsTests() { EntityPropertyFlags props; props.setHasProperty(PROP_VISIBLE); props.setHasProperty(PROP_POSITION); - props.setHasProperty(PROP_RADIUS); + props.setHasProperty(PROP_DIMENSIONS); props.setHasProperty(PROP_MODEL_URL); props.setHasProperty(PROP_COMPOUND_SHAPE_URL); props.setHasProperty(PROP_ROTATION);