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<OctreeQueryNode*>(params.nodeData); nodeData->stats.encodeStarted(); + auto entityNode = _node.toStrongRef(); + auto entityNodeData = static_cast<EntityNodeData*>(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/content/index.shtml b/domain-server/resources/web/content/index.shtml index e1ba5499b6..0e48c1eff8 100644 --- a/domain-server/resources/web/content/index.shtml +++ b/domain-server/resources/web/content/index.shtml @@ -19,12 +19,13 @@ Upload an entities file (e.g.: models.json.gz) to replace the content of this domain.<br> Note: <strong>Your domain's content will be replaced by the content you upload</strong>, but the backup files of your domain's content will not immediately be changed. </p> - <p> - If your domain has any content that you would like to re-use at a later date, save a manual backup of your models.json.gz file, which is usually stored at the following paths:<br> - <pre>C:/Users/[username]/AppData/Roaming/High Fidelity/assignment-client/entities/models.json.gz</pre> - <pre>/Users/[username]/Library/Application Support/High Fidelity/assignment-client/entities/models.json.gz</pre> - <pre>/home/[username]/.local/share/High Fidelity/assignment-client/entities/models.json.gz</pre> - </p> + <p>If your domain has any content that you would like to re-use at a later date, save a manual backup of your models.json.gz file, which is usually stored at the following paths:</p> + <label class="control-label">Windows</label> + <pre>C:/Users/[username]/AppData/Roaming/High Fidelity/assignment-client/entities/models.json.gz</pre> + <label class="control-label">OSX</label> + <pre>/Users/[username]/Library/Application Support/High Fidelity/assignment-client/entities/models.json.gz</pre> + <label class="control-label">Linux</label> + <pre>/home/[username]/.local/share/High Fidelity/assignment-client/entities/models.json.gz</pre> <br> <input type="file" name="entities-file" class="form-control-file" accept=".json, .gz"> <br> 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 = $('<div>'); - - modal_body.append($("<p>Choose a place name that you own or <a href='" + URLs.METAVERSE_URL + "/user/places' target='_blank'>register a new place name</a></p>")); - - var currentDomainIDType = getCurrentDomainIDType(); - if (currentDomainIDType === DOMAIN_ID_TYPE_TEMP) { - var warning = "<div class='domain-loading-error alert alert-warning'>"; - warning += "If you choose a place name it will replace your current temporary place name."; - warning += "</div>"; - modal_body.append(warning); - } - - // setup a select box for the returned places - modal_body.append($("<label for='place-name-select'>Places</label>")); - place_select = $("<select id='place-name-select' class='form-control'></select>"); - _.each(data.data.places, function(place) { - places_by_id[place.id] = place; - place_select.append("<option value='" + place.id + "'>" + place.name + "</option>"); - }) - modal_body.append(place_select); - modal_body.append($("<p id='place-name-warning' class='warning-text' style='display: none'>This place name already points to a place or path. Saving this would overwrite the previous settings associated with it.</p>")); - - if (forcePathTo === undefined || forcePathTo === null) { - var path = "<div class='form-group'>"; - path += "<label for='place-path-input' class='control-label'>Path</label>"; - path += "<input type='text' id='place-path-input' class='form-control' value='/'>"; - path += "</div>"; - 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 = $('<div>'); + + modal_body.append($("<p>Choose a place name that you own or <a href='" + URLs.METAVERSE_URL + "/user/places' target='_blank'>register a new place name</a></p>")); + + var currentDomainIDType = getCurrentDomainIDType(); + if (currentDomainIDType === DOMAIN_ID_TYPE_TEMP) { + var warning = "<div class='domain-loading-error alert alert-warning'>"; + warning += "If you choose a place name it will replace your current temporary place name."; + warning += "</div>"; + modal_body.append(warning); + } + + // setup a select box for the returned places + modal_body.append($("<label for='place-name-select'>Places</label>")); + place_select = $("<select id='place-name-select' class='form-control'></select>"); + _.each(data.data.places, function(place) { + places_by_id[place.id] = place; + place_select.append("<option value='" + place.id + "'>" + place.name + "</option>"); + }) + modal_body.append(place_select); + modal_body.append($("<p id='place-name-warning' class='warning-text' style='display: none'>This place name already points to a place or path. Saving this would overwrite the previous settings associated with it.</p>")); + + if (forcePathTo === undefined || forcePathTo === null) { + var path = "<div class='form-group'>"; + path += "<label for='place-path-input' class='control-label'>Path</label>"; + path += "<input type='text' id='place-path-input' class='form-control' value='/'>"; + path += "</div>"; + 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 <NLPacketList.h> #include <NumericalConstants.h> #include <SettingHandle.h> +#include <SettingHelpers.h> #include <AvatarData.h> //for KillAvatarReason #include <FingerprintUtils.h> #include "DomainServerNodeData.h" @@ -43,12 +44,7 @@ const QString DESCRIPTION_COLUMNS_KEY = "columns"; const QString SETTINGS_VIEWPOINT_KEY = "viewpoint"; -static Setting::Handle<double> 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<Re void DomainServerSettingsManager::setupConfigMap(const QStringList& argumentList) { _argumentList = argumentList; - // after 1.7 we no longer use the master or merged configs - this is kept in place for migration - _configMap.loadMasterAndUserConfig(_argumentList); + _configMap.loadConfig(_argumentList); + + static const auto VERSION_SETTINGS_KEYPATH = "version"; + QVariant* versionVariant = _configMap.valueForKeyPath(VERSION_SETTINGS_KEYPATH); + + if (!versionVariant) { + versionVariant = _configMap.valueForKeyPath(VERSION_SETTINGS_KEYPATH, true); + *versionVariant = _descriptionVersion; + persistToFile(); + qDebug() << "No version in config file, setting to current version" << _descriptionVersion; + } + + { + // Backward compatibility migration code + // The config version used to be stored in a different file + // This moves it to the actual config file. + Setting::Handle<double> 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/resources/qml/hifi/commerce/wallet/WalletHome.qml b/interface/resources/qml/hifi/commerce/wallet/WalletHome.qml index 1fe0dcc58b..fd7ce0fdfd 100644 --- a/interface/resources/qml/hifi/commerce/wallet/WalletHome.qml +++ b/interface/resources/qml/hifi/commerce/wallet/WalletHome.qml @@ -196,7 +196,38 @@ Item { anchors.bottom: parent.bottom; anchors.left: parent.left; anchors.right: parent.right; - anchors.rightMargin: 24; + + Item { + visible: transactionHistoryModel.count === 0 && root.historyReceived; + anchors.centerIn: parent; + width: parent.width - 12; + height: parent.height; + + HifiControlsUit.Separator { + colorScheme: 1; + anchors.left: parent.left; + anchors.right: parent.right; + anchors.top: parent.top; + } + + RalewayRegular { + id: noActivityText; + text: "<b>The Wallet app is in closed Beta.</b><br><br>To request entry and <b>receive free HFC</b>, please contact " + + "<b>info@highfidelity.com</b> with your High Fidelity account username and the email address registered to that account."; + // Text size + size: 24; + // Style + color: hifi.colors.blueAccent; + anchors.left: parent.left; + anchors.leftMargin: 12; + anchors.right: parent.right; + anchors.rightMargin: 12; + anchors.verticalCenter: parent.verticalCenter; + height: paintedHeight; + wrapMode: Text.WordWrap; + horizontalAlignment: Text.AlignHCenter; + } + } ListView { id: transactionHistory; @@ -294,17 +325,6 @@ Item { } } } - - // This should never be visible (since you immediately get 100 HFC) - FiraSansRegular { - id: emptyTransationHistory; - size: 24; - visible: !transactionHistory.visible && root.historyReceived; - text: "Recent Activity Unavailable"; - anchors.fill: parent; - horizontalAlignment: Text.AlignHCenter; - verticalAlignment: Text.AlignVCenter; - } } } @@ -350,7 +370,9 @@ Item { } root.pendingCount = pendingCount; - transactionHistoryModel.insert(0, {"transaction_type": "pendingCount"}); + if (pendingCount > 0) { + transactionHistoryModel.insert(0, {"transaction_type": "pendingCount"}); + } } // diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 4f051697ad..b21588958e 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2693,7 +2693,7 @@ bool Application::importFromZIP(const QString& filePath) { qDebug() << "A zip file has been dropped in: " << filePath; QUrl empty; // handle Blocks download from Marketplace - if (filePath.contains("vr.google.com/downloads")) { + if (filePath.contains("poly.google.com/downloads")) { addAssetToWorldFromURL(filePath); } else { qApp->getFileDownloadInterface()->runUnzip(filePath, empty, true, true, false); @@ -6235,7 +6235,7 @@ void Application::addAssetToWorldFromURL(QString url) { if (url.contains("filename")) { filename = url.section("filename=", 1, 1); // Filename is in "?filename=" parameter at end of URL. } - if (url.contains("vr.google.com/downloads")) { + if (url.contains("poly.google.com/downloads")) { filename = url.section('/', -1); if (url.contains("noDownload")) { filename.remove(".zip?noDownload=false"); @@ -6270,7 +6270,7 @@ void Application::addAssetToWorldFromURLRequestFinished() { if (url.contains("filename")) { filename = url.section("filename=", 1, 1); // Filename is in "?filename=" parameter at end of URL. } - if (url.contains("vr.google.com/downloads")) { + if (url.contains("poly.google.com/downloads")) { filename = url.section('/', -1); if (url.contains("noDownload")) { filename.remove(".zip?noDownload=false"); diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index e8b800db69..4cf8ba6d4e 100644 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -179,6 +179,12 @@ void AvatarManager::updateOtherAvatars(float deltaTime) { const AvatarPriority& sortData = sortedAvatars.top(); const auto& avatar = std::static_pointer_cast<Avatar>(sortData.avatar); + bool ignoring = DependencyManager::get<NodeList>()->isPersonalMutingNode(avatar->getID()); + if (ignoring) { + sortedAvatars.pop(); + continue; + } + // for ALL avatars... if (_shouldRender) { avatar->ensureInScene(avatar, qApp->getMain3DScene()); diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 0fceba82f0..0dfcf93a65 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -135,7 +135,7 @@ MyAvatar::MyAvatar(QThread* thread) : connect(&domainHandler, &DomainHandler::settingsReceived, this, &MyAvatar::restrictScaleFromDomainSettings); // when we leave a domain we lift whatever restrictions that domain may have placed on our scale - connect(&domainHandler, &DomainHandler::disconnectedFromDomain, this, &MyAvatar::clearScaleRestriction); + connect(&domainHandler, &DomainHandler::disconnectedFromDomain, this, &MyAvatar::leaveDomain); _bodySensorMatrix = deriveBodyFromHMDSensor(); @@ -2189,6 +2189,7 @@ void MyAvatar::clampScaleChangeToDomainLimits(float desiredScale) { setTargetScale(clampedTargetScale); qCDebug(interfaceapp, "Changed scale to %f", (double)_targetScale); + emit(scaleChanged()); } float MyAvatar::getDomainMinScale() { @@ -2278,6 +2279,18 @@ void MyAvatar::restrictScaleFromDomainSettings(const QJsonObject& domainSettings settings.endGroup(); } +void MyAvatar::leaveDomain() { + clearScaleRestriction(); + saveAvatarScale(); +} + +void MyAvatar::saveAvatarScale() { + Settings settings; + settings.beginGroup("Avatar"); + settings.setValue("scale", _targetScale); + settings.endGroup(); +} + void MyAvatar::clearScaleRestriction() { _domainMinimumScale = MIN_AVATAR_SCALE; _domainMaximumScale = MAX_AVATAR_SCALE; diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 303a395a47..e4e8f8d02c 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -620,6 +620,10 @@ signals: void dominantHandChanged(const QString& hand); void sensorToWorldScaleChanged(float sensorToWorldScale); void attachmentsChanged(); + void scaleChanged(); + +private slots: + void leaveDomain(); private: @@ -637,6 +641,8 @@ private: virtual int parseDataFromBuffer(const QByteArray& buffer) override; virtual glm::vec3 getSkeletonPosition() const override; + void saveAvatarScale(); + glm::vec3 getScriptedMotorVelocity() const { return _scriptedMotorVelocity; } float getScriptedMotorTimescale() const { return _scriptedMotorTimescale; } QString getScriptedMotorFrame() const; 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<AccountManager>(); - connect(accountManager.data(), &AccountManager::usernameChanged, [&]() { + connect(accountManager.data(), &AccountManager::usernameChanged, this, [&]() { setPassphrase(""); }); } 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<RayPickScriptingInterface>()->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<EntityScriptingInterface>()->getEntityProperties(_objectLockEnd.first); - pos = props.getPosition(); - rot = props.getRotation(); + EntityItemProperties props = DependencyManager::get<EntityScriptingInterface>()->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<QUuid, bool>(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<QUuid>& ignoreItems) const; void setIncludeItems(const QVector<QUuid>& includeItems) const; @@ -91,7 +97,7 @@ private: bool _centerEndY; bool _lockEnd; bool _distanceScaleEnd; - std::pair<QUuid, bool> _objectLockEnd { std::pair<QUuid, bool>(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<QUuid> } } -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<QUuid>& ignoreEntities) const; void setIncludeItems(const QUuid& uid, const QVector<QUuid>& 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/interface/src/ui/overlays/Base3DOverlay.cpp b/interface/src/ui/overlays/Base3DOverlay.cpp index 42425932df..d157e29959 100644 --- a/interface/src/ui/overlays/Base3DOverlay.cpp +++ b/interface/src/ui/overlays/Base3DOverlay.cpp @@ -265,7 +265,7 @@ void Base3DOverlay::locationChanged(bool tellPhysics) { SpatiallyNestable::locationChanged(tellPhysics); // Force the actual update of the render transform through the notify call - notifyRenderTransformChange(); + notifyRenderVariableChange(); } void Base3DOverlay::parentDeleted() { @@ -275,18 +275,21 @@ void Base3DOverlay::parentDeleted() { void Base3DOverlay::update(float duration) { // In Base3DOverlay, if its location or bound changed, the renderTrasnformDirty flag is true. // then the correct transform used for rendering is computed in the update transaction and assigned. - if (_renderTransformDirty) { + if (_renderVariableDirty) { auto itemID = getRenderItemID(); if (render::Item::isValidID(itemID)) { // Capture the render transform value in game loop before auto latestTransform = evalRenderTransform(); - _renderTransformDirty = false; + bool latestVisible = getVisible(); + _renderVariableDirty = false; render::ScenePointer scene = qApp->getMain3DScene(); render::Transaction transaction; - transaction.updateItem<Overlay>(itemID, [latestTransform](Overlay& data) { + transaction.updateItem<Overlay>(itemID, [latestTransform, latestVisible](Overlay& data) { auto overlay3D = dynamic_cast<Base3DOverlay*>(&data); if (overlay3D) { + // TODO: overlays need to communicate all relavent render properties through transactions overlay3D->setRenderTransform(latestTransform); + overlay3D->setRenderVisible(latestVisible); } }); scene->enqueueTransaction(transaction); @@ -294,8 +297,8 @@ void Base3DOverlay::update(float duration) { } } -void Base3DOverlay::notifyRenderTransformChange() const { - _renderTransformDirty = true; +void Base3DOverlay::notifyRenderVariableChange() const { + _renderVariableDirty = true; } Transform Base3DOverlay::evalRenderTransform() { @@ -306,8 +309,17 @@ void Base3DOverlay::setRenderTransform(const Transform& transform) { _renderTransform = transform; } +void Base3DOverlay::setRenderVisible(bool visible) { + _renderVisible = visible; +} + SpatialParentTree* Base3DOverlay::getParentTree() const { auto entityTreeRenderer = qApp->getEntities(); EntityTreePointer entityTree = entityTreeRenderer ? entityTreeRenderer->getTree() : nullptr; return entityTree.get(); } + +void Base3DOverlay::setVisible(bool visible) { + Parent::setVisible(visible); + notifyRenderVariableChange(); +} \ No newline at end of file diff --git a/interface/src/ui/overlays/Base3DOverlay.h b/interface/src/ui/overlays/Base3DOverlay.h index 436cfbf97b..83c5873260 100644 --- a/interface/src/ui/overlays/Base3DOverlay.h +++ b/interface/src/ui/overlays/Base3DOverlay.h @@ -18,11 +18,14 @@ class Base3DOverlay : public Overlay, public SpatiallyNestable { Q_OBJECT + using Parent = Overlay; public: Base3DOverlay(); Base3DOverlay(const Base3DOverlay* base3DOverlay); + void setVisible(bool visible) override; + virtual OverlayID getOverlayID() const override { return OverlayID(getID().toString()); } void setOverlayID(OverlayID overlayID) override { setID(overlayID); } @@ -56,7 +59,7 @@ public: void update(float deltatime) override; - void notifyRenderTransformChange() const; + void notifyRenderVariableChange() const; void setProperties(const QVariantMap& properties) override; QVariant getProperty(const QString& property) override; @@ -76,8 +79,10 @@ protected: virtual void parentDeleted() override; mutable Transform _renderTransform; + mutable bool _renderVisible; virtual Transform evalRenderTransform(); virtual void setRenderTransform(const Transform& transform); + void setRenderVisible(bool visible); const Transform& getRenderTransform() const { return _renderTransform; } float _lineWidth; @@ -87,7 +92,7 @@ protected: bool _drawInFront; bool _drawHUDLayer; bool _isGrabbable { false }; - mutable bool _renderTransformDirty{ true }; + mutable bool _renderVariableDirty { true }; QString _name; }; diff --git a/interface/src/ui/overlays/Circle3DOverlay.cpp b/interface/src/ui/overlays/Circle3DOverlay.cpp index 536b2c764f..69ce331c99 100644 --- a/interface/src/ui/overlays/Circle3DOverlay.cpp +++ b/interface/src/ui/overlays/Circle3DOverlay.cpp @@ -59,7 +59,7 @@ Circle3DOverlay::~Circle3DOverlay() { } } void Circle3DOverlay::render(RenderArgs* args) { - if (!_visible) { + if (!_renderVisible) { return; // do nothing if we're not visible } @@ -259,7 +259,7 @@ void Circle3DOverlay::render(RenderArgs* args) { } const render::ShapeKey Circle3DOverlay::getShapeKey() { - auto builder = render::ShapeKey::Builder().withoutCullFace().withUnlit(); + auto builder = render::ShapeKey::Builder().withoutCullFace(); if (isTransparent()) { builder.withTranslucent(); } diff --git a/interface/src/ui/overlays/Cube3DOverlay.cpp b/interface/src/ui/overlays/Cube3DOverlay.cpp index b6df1dbc31..caa9a1b397 100644 --- a/interface/src/ui/overlays/Cube3DOverlay.cpp +++ b/interface/src/ui/overlays/Cube3DOverlay.cpp @@ -44,7 +44,7 @@ Cube3DOverlay::~Cube3DOverlay() { } void Cube3DOverlay::render(RenderArgs* args) { - if (!_visible) { + if (!_renderVisible) { return; // do nothing if we're not visible } diff --git a/interface/src/ui/overlays/Grid3DOverlay.cpp b/interface/src/ui/overlays/Grid3DOverlay.cpp index ca275368cb..6fd132b531 100644 --- a/interface/src/ui/overlays/Grid3DOverlay.cpp +++ b/interface/src/ui/overlays/Grid3DOverlay.cpp @@ -53,7 +53,7 @@ AABox Grid3DOverlay::getBounds() const { } void Grid3DOverlay::render(RenderArgs* args) { - if (!_visible) { + if (!_renderVisible) { return; // do nothing if we're not visible } diff --git a/interface/src/ui/overlays/Image3DOverlay.cpp b/interface/src/ui/overlays/Image3DOverlay.cpp index 313d71fc40..a8ae293c83 100644 --- a/interface/src/ui/overlays/Image3DOverlay.cpp +++ b/interface/src/ui/overlays/Image3DOverlay.cpp @@ -60,7 +60,7 @@ void Image3DOverlay::update(float deltatime) { } void Image3DOverlay::render(RenderArgs* args) { - if (!_visible || !getParentVisible() || !_texture || !_texture->isLoaded()) { + if (!_renderVisible || !getParentVisible() || !_texture || !_texture->isLoaded()) { return; } diff --git a/interface/src/ui/overlays/Line3DOverlay.cpp b/interface/src/ui/overlays/Line3DOverlay.cpp index bdb35d4f49..5ef820b2e0 100644 --- a/interface/src/ui/overlays/Line3DOverlay.cpp +++ b/interface/src/ui/overlays/Line3DOverlay.cpp @@ -96,7 +96,7 @@ void Line3DOverlay::setEnd(const glm::vec3& end) { } else { _direction = glm::vec3(0.0f); } - notifyRenderTransformChange(); + notifyRenderVariableChange(); } void Line3DOverlay::setLocalEnd(const glm::vec3& localEnd) { @@ -123,7 +123,7 @@ AABox Line3DOverlay::getBounds() const { } void Line3DOverlay::render(RenderArgs* args) { - if (!_visible) { + if (!_renderVisible) { return; // do nothing if we're not visible } diff --git a/interface/src/ui/overlays/Planar3DOverlay.cpp b/interface/src/ui/overlays/Planar3DOverlay.cpp index ac3fe66ddc..a9f0bfe2f1 100644 --- a/interface/src/ui/overlays/Planar3DOverlay.cpp +++ b/interface/src/ui/overlays/Planar3DOverlay.cpp @@ -37,7 +37,7 @@ AABox Planar3DOverlay::getBounds() const { void Planar3DOverlay::setDimensions(const glm::vec2& value) { _dimensions = value; - notifyRenderTransformChange(); + notifyRenderVariableChange(); } void Planar3DOverlay::setProperties(const QVariantMap& properties) { diff --git a/interface/src/ui/overlays/Rectangle3DOverlay.cpp b/interface/src/ui/overlays/Rectangle3DOverlay.cpp index 47d47b76a5..f2e324206d 100644 --- a/interface/src/ui/overlays/Rectangle3DOverlay.cpp +++ b/interface/src/ui/overlays/Rectangle3DOverlay.cpp @@ -23,7 +23,6 @@ Rectangle3DOverlay::Rectangle3DOverlay() : for (size_t i = 0; i < _rectGeometryIds.size(); ++i) { _rectGeometryIds[i] = geometryCache->allocateID(); } - qDebug() << "Building rect3d overlay"; } Rectangle3DOverlay::Rectangle3DOverlay(const Rectangle3DOverlay* rectangle3DOverlay) : @@ -34,11 +33,9 @@ Rectangle3DOverlay::Rectangle3DOverlay(const Rectangle3DOverlay* rectangle3DOver for (size_t i = 0; i < _rectGeometryIds.size(); ++i) { _rectGeometryIds[i] = geometryCache->allocateID(); } - qDebug() << "Building rect3d overlay"; } Rectangle3DOverlay::~Rectangle3DOverlay() { - qDebug() << "Destryoing rect3d overlay"; auto geometryCache = DependencyManager::get<GeometryCache>(); if (geometryCache) { geometryCache->releaseID(_geometryCacheID); @@ -49,7 +46,7 @@ Rectangle3DOverlay::~Rectangle3DOverlay() { } void Rectangle3DOverlay::render(RenderArgs* args) { - if (!_visible) { + if (!_renderVisible) { return; // do nothing if we're not visible } @@ -58,18 +55,11 @@ void Rectangle3DOverlay::render(RenderArgs* args) { const float MAX_COLOR = 255.0f; glm::vec4 rectangleColor(color.red / MAX_COLOR, color.green / MAX_COLOR, color.blue / MAX_COLOR, alpha); - glm::vec3 position = getPosition(); - glm::vec2 dimensions = getDimensions(); - glm::vec2 halfDimensions = dimensions * 0.5f; - glm::quat rotation = getRotation(); - auto batch = args->_batch; - if (batch) { - // FIXME Start using the _renderTransform instead of calling for Transform and Dimensions from here, do the custom things needed in evalRenderTransform() - Transform transform; - transform.setTranslation(position); - transform.setRotation(rotation); + Transform transform = getRenderTransform(); + glm::vec2 halfDimensions = transform.getScale() * 0.5f; + transform.setScale(1.0f); batch->setModelTransform(transform); auto geometryCache = DependencyManager::get<GeometryCache>(); @@ -124,8 +114,3 @@ void Rectangle3DOverlay::setProperties(const QVariantMap& properties) { Rectangle3DOverlay* Rectangle3DOverlay::createClone() const { return new Rectangle3DOverlay(this); } - -Transform Rectangle3DOverlay::evalRenderTransform() { - return getTransform(); -} - diff --git a/interface/src/ui/overlays/Rectangle3DOverlay.h b/interface/src/ui/overlays/Rectangle3DOverlay.h index a26ed524fc..645553ed38 100644 --- a/interface/src/ui/overlays/Rectangle3DOverlay.h +++ b/interface/src/ui/overlays/Rectangle3DOverlay.h @@ -29,9 +29,6 @@ public: virtual Rectangle3DOverlay* createClone() const override; -protected: - Transform evalRenderTransform() override; - private: int _geometryCacheID; std::array<int, 4> _rectGeometryIds; diff --git a/interface/src/ui/overlays/Shape3DOverlay.cpp b/interface/src/ui/overlays/Shape3DOverlay.cpp index a3b51d40bf..bd8e6a9728 100644 --- a/interface/src/ui/overlays/Shape3DOverlay.cpp +++ b/interface/src/ui/overlays/Shape3DOverlay.cpp @@ -24,7 +24,7 @@ Shape3DOverlay::Shape3DOverlay(const Shape3DOverlay* Shape3DOverlay) : } void Shape3DOverlay::render(RenderArgs* args) { - if (!_visible) { + if (!_renderVisible) { return; // do nothing if we're not visible } diff --git a/interface/src/ui/overlays/Sphere3DOverlay.cpp b/interface/src/ui/overlays/Sphere3DOverlay.cpp index c9fc25b252..2013e5689a 100644 --- a/interface/src/ui/overlays/Sphere3DOverlay.cpp +++ b/interface/src/ui/overlays/Sphere3DOverlay.cpp @@ -27,7 +27,7 @@ Sphere3DOverlay::Sphere3DOverlay(const Sphere3DOverlay* Sphere3DOverlay) : } void Sphere3DOverlay::render(RenderArgs* args) { - if (!_visible) { + if (!_renderVisible) { return; // do nothing if we're not visible } diff --git a/interface/src/ui/overlays/Text3DOverlay.cpp b/interface/src/ui/overlays/Text3DOverlay.cpp index d8188cf6dc..1cbc73304b 100644 --- a/interface/src/ui/overlays/Text3DOverlay.cpp +++ b/interface/src/ui/overlays/Text3DOverlay.cpp @@ -92,7 +92,7 @@ void Text3DOverlay::update(float deltatime) { } void Text3DOverlay::render(RenderArgs* args) { - if (!_visible || !getParentVisible()) { + if (!_renderVisible || !getParentVisible()) { return; // do nothing if we're not visible } diff --git a/interface/src/ui/overlays/Volume3DOverlay.cpp b/interface/src/ui/overlays/Volume3DOverlay.cpp index 8aa8490937..e5a418cce5 100644 --- a/interface/src/ui/overlays/Volume3DOverlay.cpp +++ b/interface/src/ui/overlays/Volume3DOverlay.cpp @@ -28,7 +28,7 @@ AABox Volume3DOverlay::getBounds() const { void Volume3DOverlay::setDimensions(const glm::vec3& value) { _localBoundingBox.setBox(-value / 2.0f, value); - notifyRenderTransformChange(); + notifyRenderVariableChange(); } void Volume3DOverlay::setProperties(const QVariantMap& properties) { diff --git a/interface/src/ui/overlays/Web3DOverlay.cpp b/interface/src/ui/overlays/Web3DOverlay.cpp index 42d0ea0f12..d418a79fbf 100644 --- a/interface/src/ui/overlays/Web3DOverlay.cpp +++ b/interface/src/ui/overlays/Web3DOverlay.cpp @@ -268,7 +268,7 @@ Q_INVOKABLE int Web3DOverlay::deviceIdByTouchPoint(qreal x, qreal y) { } void Web3DOverlay::render(RenderArgs* args) { - if (!_visible || !getParentVisible()) { + if (!_renderVisible || !getParentVisible()) { return; } diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index 0f2bce5ca4..3dad4e3fb6 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<AnimClip>(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 { @@ -302,7 +303,9 @@ void Rig::setModelOffset(const glm::mat4& modelOffsetMat) { _rigToGeometryTransform = glm::inverse(_geometryToRigTransform); // rebuild cached default poses - buildAbsoluteRigPoses(_animSkeleton->getRelativeDefaultPoses(), _absoluteDefaultPoses); + if (_animSkeleton) { + buildAbsoluteRigPoses(_animSkeleton->getRelativeDefaultPoses(), _absoluteDefaultPoses); + } } } @@ -1638,6 +1641,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<QString, RoleAnimState> _roleAnimStates; float _leftHandOverlayAlpha { 0.0f }; float _rightHandOverlayAlpha { 0.0f }; diff --git a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp index 142e57c9e5..d2dc116e15 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp +++ b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp @@ -728,19 +728,19 @@ void Avatar::simulateAttachments(float deltaTime) { glm::quat jointRotation; if (attachment.isSoft) { // soft attachments do not have transform offsets - model->setTranslation(getPosition()); - model->setRotation(getOrientation() * Quaternions::Y_180); + model->setTransformNoUpdateRenderItems(Transform(getOrientation() * Quaternions::Y_180, glm::vec3(1.0), getPosition())); model->simulate(deltaTime); + model->updateRenderItems(); } else { if (_skeletonModel->getJointPositionInWorldFrame(jointIndex, jointPosition) && _skeletonModel->getJointRotationInWorldFrame(jointIndex, jointRotation)) { - model->setTranslation(jointPosition + jointRotation * attachment.translation * getModelScale()); - model->setRotation(jointRotation * attachment.rotation); + model->setTransformNoUpdateRenderItems(Transform(jointRotation * attachment.rotation, glm::vec3(1.0), jointPosition + jointRotation * attachment.translation * getModelScale())); float scale = getModelScale() * attachment.scale; model->setScaleToFit(true, model->getNaturalDimensions() * scale, true); // hack to force rescale model->setSnapModelToCenter(false); // hack to force resnap model->setSnapModelToCenter(true); model->simulate(deltaTime); + model->updateRenderItems(); } } } diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index 877bcd9353..bd313ac133 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -2223,11 +2223,13 @@ glm::vec3 variantToVec3(const QVariant& var) { return result; } -void AttachmentData::fromVariant(const QVariant& variant) { +bool AttachmentData::fromVariant(const QVariant& variant) { + bool isValid = false; auto map = variant.toMap(); if (map.contains("modelUrl")) { auto urlString = map["modelUrl"].toString(); modelURL = urlString; + isValid = true; } if (map.contains("jointName")) { jointName = map["jointName"].toString(); @@ -2244,6 +2246,7 @@ void AttachmentData::fromVariant(const QVariant& variant) { if (map.contains("soft")) { isSoft = map["soft"].toBool(); } + return isValid; } QVariantList AvatarData::getAttachmentsVariant() const { @@ -2259,8 +2262,7 @@ void AvatarData::setAttachmentsVariant(const QVariantList& variant) { newAttachments.reserve(variant.size()); for (const auto& attachmentVar : variant) { AttachmentData attachment; - attachment.fromVariant(attachmentVar); - if (!attachment.modelURL.isEmpty()) { + if (attachment.fromVariant(attachmentVar)) { newAttachments.append(attachment); } } diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index 89fe270af1..fcfeaf7741 100644 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -903,7 +903,7 @@ public: void fromJson(const QJsonObject& json); QVariant toVariant() const; - void fromVariant(const QVariant& variant); + bool fromVariant(const QVariant& variant); }; QDataStream& operator<<(QDataStream& out, const AttachmentData& attachment); diff --git a/libraries/entities-renderer/src/RenderableEntityItem.cpp b/libraries/entities-renderer/src/RenderableEntityItem.cpp index f9e88b430f..9e4d832037 100644 --- a/libraries/entities-renderer/src/RenderableEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableEntityItem.cpp @@ -295,6 +295,14 @@ void EntityRenderer::updateInScene(const ScenePointer& scene, Transaction& trans }); } +void EntityRenderer::clearSubRenderItemIDs() { + _subRenderItemIDs.clear(); +} + +void EntityRenderer::setSubRenderItemIDs(const render::ItemIDs& ids) { + _subRenderItemIDs = ids; +} + // // Internal methods // diff --git a/libraries/entities-renderer/src/RenderableEntityItem.h b/libraries/entities-renderer/src/RenderableEntityItem.h index d770e7c7aa..ed636ebf73 100644 --- a/libraries/entities-renderer/src/RenderableEntityItem.h +++ b/libraries/entities-renderer/src/RenderableEntityItem.h @@ -49,6 +49,9 @@ public: virtual bool addToScene(const ScenePointer& scene, Transaction& transaction) final; virtual void removeFromScene(const ScenePointer& scene, Transaction& transaction); + void clearSubRenderItemIDs(); + void setSubRenderItemIDs(const render::ItemIDs& ids); + protected: virtual bool needsRenderUpdateFromEntity() const final { return needsRenderUpdateFromEntity(_entity); } virtual void onAddToScene(const EntityItemPointer& entity); @@ -113,6 +116,7 @@ protected: SharedSoundPointer _collisionSound; QUuid _changeHandlerId; ItemID _renderItemID{ Item::INVALID_ITEM_ID }; + ItemIDs _subRenderItemIDs; quint64 _fadeStartTime{ usecTimestampNow() }; bool _isFading{ _entitiesShouldFadeFunction() }; bool _prevIsTransparent { false }; diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp index 03380ad321..6b30d2a7e8 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp @@ -953,7 +953,7 @@ ItemKey ModelEntityRenderer::getKey() { uint32_t ModelEntityRenderer::metaFetchMetaSubItems(ItemIDs& subItems) { if (_model) { - auto metaSubItems = _model->fetchRenderItemIDs(); + auto metaSubItems = _subRenderItemIDs; subItems.insert(subItems.end(), metaSubItems.begin(), metaSubItems.end()); return (uint32_t)metaSubItems.size(); } @@ -1202,6 +1202,10 @@ void ModelEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& sce if ((bool)model) { model->removeFromScene(scene, transaction); withWriteLock([&] { _model.reset(); }); + transaction.updateItem<PayloadProxyInterface>(getRenderItemID(), [](PayloadProxyInterface& data) { + auto entityRenderer = static_cast<EntityRenderer*>(&data); + entityRenderer->clearSubRenderItemIDs(); + }); } return; } @@ -1297,6 +1301,12 @@ void ModelEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& sce render::Item::Status::Getters statusGetters; makeStatusGetters(entity, statusGetters); model->addToScene(scene, transaction, statusGetters); + + auto newRenderItemIDs{ model->fetchRenderItemIDs() }; + transaction.updateItem<PayloadProxyInterface>(getRenderItemID(), [newRenderItemIDs](PayloadProxyInterface& data) { + auto entityRenderer = static_cast<EntityRenderer*>(&data); + entityRenderer->setSubRenderItemIDs(newRenderItemIDs); + }); } } 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<NodeList>(); + + 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<AddressManager>()->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<AddressManager>()->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<int>(LookupTrigger::UserInput)); - + requestParams.insert(LOOKUP_TRIGGER_KEY, static_cast<int>( + shouldMatchOrientation ? LookupTrigger::UserInput : LookupTrigger::VisitUserFromPAL + )); // this is a username - pull the captured name and lookup that user's location DependencyManager::get<AccountManager>()->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/CauterizedMeshPartPayload.cpp b/libraries/render-utils/src/CauterizedMeshPartPayload.cpp index 07628904f1..3bb2aa2ef9 100644 --- a/libraries/render-utils/src/CauterizedMeshPartPayload.cpp +++ b/libraries/render-utils/src/CauterizedMeshPartPayload.cpp @@ -20,11 +20,22 @@ using namespace render; CauterizedMeshPartPayload::CauterizedMeshPartPayload(ModelPointer model, int meshIndex, int partIndex, int shapeIndex, const Transform& transform, const Transform& offsetTransform) : ModelMeshPartPayload(model, meshIndex, partIndex, shapeIndex, transform, offsetTransform) {} -void CauterizedMeshPartPayload::updateTransformForCauterizedMesh( - const Transform& renderTransform, - const gpu::BufferPointer& buffer) { +void CauterizedMeshPartPayload::updateClusterBuffer(const std::vector<glm::mat4>& clusterMatrices, const std::vector<glm::mat4>& cauterizedClusterMatrices) { + ModelMeshPartPayload::updateClusterBuffer(clusterMatrices); + + if (cauterizedClusterMatrices.size() > 1) { + if (!_cauterizedClusterBuffer) { + _cauterizedClusterBuffer = std::make_shared<gpu::Buffer>(cauterizedClusterMatrices.size() * sizeof(glm::mat4), + (const gpu::Byte*) cauterizedClusterMatrices.data()); + } else { + _cauterizedClusterBuffer->setSubData(0, cauterizedClusterMatrices.size() * sizeof(glm::mat4), + (const gpu::Byte*) cauterizedClusterMatrices.data()); + } + } +} + +void CauterizedMeshPartPayload::updateTransformForCauterizedMesh(const Transform& renderTransform) { _cauterizedTransform = renderTransform; - _cauterizedClusterBuffer = buffer; } void CauterizedMeshPartPayload::bindTransform(gpu::Batch& batch, const render::ShapePipeline::LocationsPointer locations, RenderArgs::RenderMode renderMode) const { diff --git a/libraries/render-utils/src/CauterizedMeshPartPayload.h b/libraries/render-utils/src/CauterizedMeshPartPayload.h index 5e3135ea84..1c98f5abf3 100644 --- a/libraries/render-utils/src/CauterizedMeshPartPayload.h +++ b/libraries/render-utils/src/CauterizedMeshPartPayload.h @@ -15,7 +15,9 @@ class CauterizedMeshPartPayload : public ModelMeshPartPayload { public: CauterizedMeshPartPayload(ModelPointer model, int meshIndex, int partIndex, int shapeIndex, const Transform& transform, const Transform& offsetTransform); - void updateTransformForCauterizedMesh(const Transform& renderTransform, const gpu::BufferPointer& buffer); + void updateClusterBuffer(const std::vector<glm::mat4>& clusterMatrices, const std::vector<glm::mat4>& cauterizedClusterMatrices); + + void updateTransformForCauterizedMesh(const Transform& renderTransform); void bindTransform(gpu::Batch& batch, const render::ShapePipeline::LocationsPointer locations, RenderArgs::RenderMode renderMode) const override; diff --git a/libraries/render-utils/src/CauterizedModel.cpp b/libraries/render-utils/src/CauterizedModel.cpp index 47ada457a0..30121a232d 100644 --- a/libraries/render-utils/src/CauterizedModel.cpp +++ b/libraries/render-utils/src/CauterizedModel.cpp @@ -48,7 +48,7 @@ void CauterizedModel::createVisibleRenderItemSet() { const auto& meshes = _renderGeometry->getMeshes(); // all of our mesh vectors must match in size - if ((int)meshes.size() != _meshStates.size()) { + if (meshes.size() != _meshStates.size()) { qCDebug(renderutils) << "WARNING!!!! Mesh Sizes don't match! We will not segregate mesh groups yet."; return; } @@ -57,6 +57,7 @@ void CauterizedModel::createVisibleRenderItemSet() { Q_ASSERT(_modelMeshRenderItems.isEmpty()); _modelMeshRenderItems.clear(); + _modelMeshRenderItemShapes.clear(); Transform transform; transform.setTranslation(_translation); @@ -80,6 +81,7 @@ void CauterizedModel::createVisibleRenderItemSet() { for (int partIndex = 0; partIndex < numParts; partIndex++) { auto ptr = std::make_shared<CauterizedMeshPartPayload>(shared_from_this(), i, partIndex, shapeID, transform, offset); _modelMeshRenderItems << std::static_pointer_cast<ModelMeshPartPayload>(ptr); + _modelMeshRenderItemShapes.emplace_back(ShapeInfo{ (int)i }); shapeID++; } } @@ -102,7 +104,7 @@ void CauterizedModel::updateClusterMatrices() { _needsUpdateClusterMatrices = false; const FBXGeometry& geometry = getFBXGeometry(); - for (int i = 0; i < _meshStates.size(); i++) { + for (int i = 0; i < (int)_meshStates.size(); i++) { Model::MeshState& state = _meshStates[i]; const FBXMesh& mesh = geometry.meshes.at(i); for (int j = 0; j < mesh.clusters.size(); j++) { @@ -110,17 +112,6 @@ void CauterizedModel::updateClusterMatrices() { auto jointMatrix = _rig.getJointTransform(cluster.jointIndex); glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, state.clusterMatrices[j]); } - - // Once computed the cluster matrices, update the buffer(s) - if (mesh.clusters.size() > 1) { - if (!state.clusterBuffer) { - state.clusterBuffer = std::make_shared<gpu::Buffer>(state.clusterMatrices.size() * sizeof(glm::mat4), - (const gpu::Byte*) state.clusterMatrices.constData()); - } else { - state.clusterBuffer->setSubData(0, state.clusterMatrices.size() * sizeof(glm::mat4), - (const gpu::Byte*) state.clusterMatrices.constData()); - } - } } // as an optimization, don't build cautrizedClusterMatrices if the boneSet is empty. @@ -143,17 +134,6 @@ void CauterizedModel::updateClusterMatrices() { } glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, state.clusterMatrices[j]); } - - if (!_cauterizeBoneSet.empty() && (state.clusterMatrices.size() > 1)) { - if (!state.clusterBuffer) { - state.clusterBuffer = - std::make_shared<gpu::Buffer>(state.clusterMatrices.size() * sizeof(glm::mat4), - (const gpu::Byte*) state.clusterMatrices.constData()); - } else { - state.clusterBuffer->setSubData(0, state.clusterMatrices.size() * sizeof(glm::mat4), - (const gpu::Byte*) state.clusterMatrices.constData()); - } - } } } @@ -181,11 +161,11 @@ void CauterizedModel::updateRenderItems() { // queue up this work for later processing, at the end of update and just before rendering. // the application will ensure only the last lambda is actually invoked. void* key = (void*)this; - std::weak_ptr<Model> weakSelf = shared_from_this(); - AbstractViewStateInterface::instance()->pushPostUpdateLambda(key, [weakSelf, scale]() { + std::weak_ptr<CauterizedModel> weakSelf = std::dynamic_pointer_cast<CauterizedModel>(shared_from_this()); + AbstractViewStateInterface::instance()->pushPostUpdateLambda(key, [weakSelf]() { // do nothing, if the model has already been destroyed. auto self = weakSelf.lock(); - if (!self) { + if (!self || !self->isLoaded()) { return; } @@ -198,37 +178,28 @@ void CauterizedModel::updateRenderItems() { modelTransform.setTranslation(self->getTranslation()); modelTransform.setRotation(self->getRotation()); - Transform scaledModelTransform(modelTransform); - scaledModelTransform.setScale(scale); - - uint32_t deleteGeometryCounter = self->getGeometryCounter(); - render::Transaction transaction; - QList<render::ItemID> keys = self->getRenderItems().keys(); - foreach (auto itemID, keys) { - transaction.updateItem<CauterizedMeshPartPayload>(itemID, [modelTransform, deleteGeometryCounter](CauterizedMeshPartPayload& data) { - ModelPointer model = data._model.lock(); - if (model && model->isLoaded()) { - // Ensure the model geometry was not reset between frames - if (deleteGeometryCounter == model->getGeometryCounter()) { - // this stuff identical to what happens in regular Model - const Model::MeshState& state = model->getMeshState(data._meshIndex); - Transform renderTransform = modelTransform; - if (state.clusterMatrices.size() == 1) { - renderTransform = modelTransform.worldTransform(Transform(state.clusterMatrices[0])); - } - data.updateTransformForSkinnedMesh(renderTransform, modelTransform, state.clusterBuffer); + for (int i = 0; i < (int)self->_modelMeshRenderItemIDs.size(); i++) { - // this stuff for cauterized mesh - CauterizedModel* cModel = static_cast<CauterizedModel*>(model.get()); - const Model::MeshState& cState = cModel->getCauterizeMeshState(data._meshIndex); - renderTransform = modelTransform; - if (cState.clusterMatrices.size() == 1) { - renderTransform = modelTransform.worldTransform(Transform(cState.clusterMatrices[0])); - } - data.updateTransformForCauterizedMesh(renderTransform, cState.clusterBuffer); - } + auto itemID = self->_modelMeshRenderItemIDs[i]; + auto meshIndex = self->_modelMeshRenderItemShapes[i].meshIndex; + auto clusterMatrices(self->getMeshState(meshIndex).clusterMatrices); + auto clusterMatricesCauterized(self->getCauterizeMeshState(meshIndex).clusterMatrices); + + transaction.updateItem<CauterizedMeshPartPayload>(itemID, [modelTransform, clusterMatrices, clusterMatricesCauterized](CauterizedMeshPartPayload& data) { + data.updateClusterBuffer(clusterMatrices, clusterMatricesCauterized); + + Transform renderTransform = modelTransform; + if (clusterMatrices.size() == 1) { + renderTransform = modelTransform.worldTransform(Transform(clusterMatrices[0])); } + data.updateTransformForSkinnedMesh(renderTransform, modelTransform); + + renderTransform = modelTransform; + if (clusterMatricesCauterized.size() == 1) { + renderTransform = modelTransform.worldTransform(Transform(clusterMatricesCauterized[0])); + } + data.updateTransformForCauterizedMesh(renderTransform); }); } diff --git a/libraries/render-utils/src/DrawHaze.cpp b/libraries/render-utils/src/DrawHaze.cpp index 5faa1b206b..4431c1bbc3 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/MeshPartPayload.cpp b/libraries/render-utils/src/MeshPartPayload.cpp index 8b65c9f7ce..1ea3e1a705 100644 --- a/libraries/render-utils/src/MeshPartPayload.cpp +++ b/libraries/render-utils/src/MeshPartPayload.cpp @@ -337,7 +337,7 @@ ModelMeshPartPayload::ModelMeshPartPayload(ModelPointer model, int meshIndex, in if (state.clusterMatrices.size() == 1) { renderTransform = transform.worldTransform(Transform(state.clusterMatrices[0])); } - updateTransformForSkinnedMesh(renderTransform, transform, state.clusterBuffer); + updateTransformForSkinnedMesh(renderTransform, transform); initCache(); } @@ -367,12 +367,25 @@ void ModelMeshPartPayload::notifyLocationChanged() { } -void ModelMeshPartPayload::updateTransformForSkinnedMesh(const Transform& renderTransform, const Transform& boundTransform, - const gpu::BufferPointer& buffer) { + +void ModelMeshPartPayload::updateClusterBuffer(const std::vector<glm::mat4>& clusterMatrices) { + // Once computed the cluster matrices, update the buffer(s) + if (clusterMatrices.size() > 1) { + if (!_clusterBuffer) { + _clusterBuffer = std::make_shared<gpu::Buffer>(clusterMatrices.size() * sizeof(glm::mat4), + (const gpu::Byte*) clusterMatrices.data()); + } + else { + _clusterBuffer->setSubData(0, clusterMatrices.size() * sizeof(glm::mat4), + (const gpu::Byte*) clusterMatrices.data()); + } + } +} + +void ModelMeshPartPayload::updateTransformForSkinnedMesh(const Transform& renderTransform, const Transform& boundTransform) { _transform = renderTransform; _worldBound = _adjustedLocalBound; _worldBound.transform(boundTransform); - _clusterBuffer = buffer; } ItemKey ModelMeshPartPayload::getKey() const { @@ -416,7 +429,6 @@ int ModelMeshPartPayload::getLayer() const { } ShapeKey ModelMeshPartPayload::getShapeKey() const { - // guard against partially loaded meshes ModelPointer model = _model.lock(); if (!model || !model->isLoaded() || !model->getGeometry()) { @@ -582,11 +594,11 @@ void ModelMeshPartPayload::render(RenderArgs* args) { args->_details._trianglesRendered += _drawPart._numIndices / INDICES_PER_TRIANGLE; } -void ModelMeshPartPayload::computeAdjustedLocalBound(const QVector<glm::mat4>& clusterMatrices) { +void ModelMeshPartPayload::computeAdjustedLocalBound(const std::vector<glm::mat4>& clusterMatrices) { _adjustedLocalBound = _localBound; if (clusterMatrices.size() > 0) { _adjustedLocalBound.transform(clusterMatrices[0]); - for (int i = 1; i < clusterMatrices.size(); ++i) { + for (int i = 1; i < (int)clusterMatrices.size(); ++i) { AABox clusterBound = _localBound; clusterBound.transform(clusterMatrices[i]); _adjustedLocalBound += clusterBound; diff --git a/libraries/render-utils/src/MeshPartPayload.h b/libraries/render-utils/src/MeshPartPayload.h index 99c14510b5..971c6fe90b 100644 --- a/libraries/render-utils/src/MeshPartPayload.h +++ b/libraries/render-utils/src/MeshPartPayload.h @@ -87,9 +87,8 @@ public: typedef Payload::DataPointer Pointer; void notifyLocationChanged() override; - void updateTransformForSkinnedMesh(const Transform& renderTransform, - const Transform& boundTransform, - const gpu::BufferPointer& buffer); + void updateClusterBuffer(const std::vector<glm::mat4>& clusterMatrices); + void updateTransformForSkinnedMesh(const Transform& renderTransform, const Transform& boundTransform); // Render Item interface render::ItemKey getKey() const override; @@ -103,7 +102,7 @@ public: void initCache(); - void computeAdjustedLocalBound(const QVector<glm::mat4>& clusterMatrices); + void computeAdjustedLocalBound(const std::vector<glm::mat4>& clusterMatrices); gpu::BufferPointer _clusterBuffer; ModelWeakPointer _model; diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index 97f62a3ce0..428fcc7a54 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -226,7 +226,7 @@ void Model::updateRenderItems() { // do nothing, if the model has already been destroyed. auto self = weakSelf.lock(); - if (!self) { + if (!self || !self->isLoaded()) { return; } @@ -237,24 +237,20 @@ void Model::updateRenderItems() { Transform modelTransform = self->getTransform(); modelTransform.setScale(glm::vec3(1.0f)); - uint32_t deleteGeometryCounter = self->_deleteGeometryCounter; - render::Transaction transaction; - foreach (auto itemID, self->_modelMeshRenderItemsMap.keys()) { - transaction.updateItem<ModelMeshPartPayload>(itemID, [deleteGeometryCounter, modelTransform](ModelMeshPartPayload& data) { - ModelPointer model = data._model.lock(); - if (model && model->isLoaded()) { - // Ensure the model geometry was not reset between frames - if (deleteGeometryCounter == model->_deleteGeometryCounter) { + for (int i = 0; i < (int) self->_modelMeshRenderItemIDs.size(); i++) { - const Model::MeshState& state = model->getMeshState(data._meshIndex); - Transform renderTransform = modelTransform; - if (state.clusterMatrices.size() == 1) { - renderTransform = modelTransform.worldTransform(Transform(state.clusterMatrices[0])); - } - data.updateTransformForSkinnedMesh(renderTransform, modelTransform, state.clusterBuffer); - } + auto itemID = self->_modelMeshRenderItemIDs[i]; + auto meshIndex = self->_modelMeshRenderItemShapes[i].meshIndex; + auto clusterMatrices(self->getMeshState(meshIndex).clusterMatrices); + + transaction.updateItem<ModelMeshPartPayload>(itemID, [modelTransform, clusterMatrices](ModelMeshPartPayload& data) { + data.updateClusterBuffer(clusterMatrices); + Transform renderTransform = modelTransform; + if (clusterMatrices.size() == 1) { + renderTransform = modelTransform.worldTransform(Transform(clusterMatrices[0])); } + data.updateTransformForSkinnedMesh(renderTransform, modelTransform); }); } @@ -311,7 +307,7 @@ bool Model::updateGeometry() { foreach (const FBXMesh& mesh, fbxGeometry.meshes) { MeshState state; state.clusterMatrices.resize(mesh.clusters.size()); - _meshStates.append(state); + _meshStates.push_back(state); // Note: we add empty buffers for meshes that lack blendshapes so we can access the buffers by index // later in ModelMeshPayload, however the vast majority of meshes will not have them. @@ -686,6 +682,7 @@ void Model::removeFromScene(const render::ScenePointer& scene, render::Transacti _modelMeshRenderItemIDs.clear(); _modelMeshRenderItemsMap.clear(); _modelMeshRenderItems.clear(); + _modelMeshRenderItemShapes.clear(); foreach(auto item, _collisionRenderItemsMap.keys()) { transaction.removeItem(item); @@ -1127,7 +1124,7 @@ void Model::updateClusterMatrices() { } _needsUpdateClusterMatrices = false; const FBXGeometry& geometry = getFBXGeometry(); - for (int i = 0; i < _meshStates.size(); i++) { + for (int i = 0; i < (int) _meshStates.size(); i++) { MeshState& state = _meshStates[i]; const FBXMesh& mesh = geometry.meshes.at(i); for (int j = 0; j < mesh.clusters.size(); j++) { @@ -1135,17 +1132,6 @@ void Model::updateClusterMatrices() { auto jointMatrix = _rig.getJointTransform(cluster.jointIndex); glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, state.clusterMatrices[j]); } - - // Once computed the cluster matrices, update the buffer(s) - if (mesh.clusters.size() > 1) { - if (!state.clusterBuffer) { - state.clusterBuffer = std::make_shared<gpu::Buffer>(state.clusterMatrices.size() * sizeof(glm::mat4), - (const gpu::Byte*) state.clusterMatrices.constData()); - } else { - state.clusterBuffer->setSubData(0, state.clusterMatrices.size() * sizeof(glm::mat4), - (const gpu::Byte*) state.clusterMatrices.constData()); - } - } } // post the blender if we're not currently waiting for one to finish @@ -1255,7 +1241,7 @@ void Model::createVisibleRenderItemSet() { const auto& meshes = _renderGeometry->getMeshes(); // all of our mesh vectors must match in size - if ((int)meshes.size() != _meshStates.size()) { + if (meshes.size() != _meshStates.size()) { qCDebug(renderutils) << "WARNING!!!! Mesh Sizes don't match! We will not segregate mesh groups yet."; return; } @@ -1264,6 +1250,7 @@ void Model::createVisibleRenderItemSet() { Q_ASSERT(_modelMeshRenderItems.isEmpty()); _modelMeshRenderItems.clear(); + _modelMeshRenderItemShapes.clear(); Transform transform; transform.setTranslation(_translation); @@ -1286,6 +1273,7 @@ void Model::createVisibleRenderItemSet() { int numParts = (int)mesh->getNumParts(); for (int partIndex = 0; partIndex < numParts; partIndex++) { _modelMeshRenderItems << std::make_shared<ModelMeshPartPayload>(shared_from_this(), i, partIndex, shapeID, transform, offset); + _modelMeshRenderItemShapes.emplace_back(ShapeInfo{ (int)i }); shapeID++; } } @@ -1327,7 +1315,7 @@ void Model::createCollisionRenderItemSet() { } bool Model::isRenderable() const { - return !_meshStates.isEmpty() || (isLoaded() && _renderGeometry->getMeshes().empty()); + return !_meshStates.empty() || (isLoaded() && _renderGeometry->getMeshes().empty()); } class CollisionRenderGeometry : public Geometry { diff --git a/libraries/render-utils/src/Model.h b/libraries/render-utils/src/Model.h index 3abf7e2758..c537a928b3 100644 --- a/libraries/render-utils/src/Model.h +++ b/libraries/render-utils/src/Model.h @@ -246,8 +246,7 @@ public: class MeshState { public: - QVector<glm::mat4> clusterMatrices; - gpu::BufferPointer clusterBuffer; + std::vector<glm::mat4> clusterMatrices; }; const MeshState& getMeshState(int index) { return _meshStates.at(index); } @@ -317,7 +316,7 @@ protected: bool _snappedToRegistrationPoint; /// are we currently snapped to a registration point glm::vec3 _registrationPoint = glm::vec3(0.5f); /// the point in model space our center is snapped to - QVector<MeshState> _meshStates; + std::vector<MeshState> _meshStates; virtual void initJointStates(); @@ -388,8 +387,9 @@ protected: QVector<std::shared_ptr<ModelMeshPartPayload>> _modelMeshRenderItems; QMap<render::ItemID, render::PayloadPointer> _modelMeshRenderItemsMap; - render::ItemIDs _modelMeshRenderItemIDs; + using ShapeInfo = struct { int meshIndex; }; + std::vector<ShapeInfo> _modelMeshRenderItemShapes; bool _addedToScene { false }; // has been added to scene bool _needsFixupInScene { true }; // needs to be removed/re-added to scene diff --git a/libraries/render-utils/src/SoftAttachmentModel.cpp b/libraries/render-utils/src/SoftAttachmentModel.cpp index 63b18d49b8..63991f9422 100644 --- a/libraries/render-utils/src/SoftAttachmentModel.cpp +++ b/libraries/render-utils/src/SoftAttachmentModel.cpp @@ -43,7 +43,7 @@ void SoftAttachmentModel::updateClusterMatrices() { const FBXGeometry& geometry = getFBXGeometry(); - for (int i = 0; i < _meshStates.size(); i++) { + for (int i = 0; i < (int) _meshStates.size(); i++) { MeshState& state = _meshStates[i]; const FBXMesh& mesh = geometry.meshes.at(i); @@ -60,17 +60,6 @@ void SoftAttachmentModel::updateClusterMatrices() { } glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, state.clusterMatrices[j]); } - - // Once computed the cluster matrices, update the buffer(s) - if (mesh.clusters.size() > 1) { - if (!state.clusterBuffer) { - state.clusterBuffer = std::make_shared<gpu::Buffer>(state.clusterMatrices.size() * sizeof(glm::mat4), - (const gpu::Byte*) state.clusterMatrices.constData()); - } else { - state.clusterBuffer->setSubData(0, state.clusterMatrices.size() * sizeof(glm::mat4), - (const gpu::Byte*) state.clusterMatrices.constData()); - } - } } // post the blender if we're not currently waiting for one to finish 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 <QSettings> +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..15f1c2f6c1 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 + })); } } @@ -1533,7 +1542,7 @@ function handeMenuEvent(menuItem) { Window.openFileChanged.connect(onFileOpenChanged); Window.browseAsync("Select Model to Import", "", "*.json"); } else { - Window.promptTextChanged.connect(onFileOpenChanged); + Window.promptTextChanged.connect(onPromptTextChanged); Window.promptAsync("URL of SVO to import", ""); } } else if (menuItem === "Entity List...") { 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/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);