Merge branch 'master' of git://github.com/highfidelity/hifi into bloom

This commit is contained in:
Olivier Prat 2017-11-08 08:17:55 +01:00
commit 91d8435a99
48 changed files with 580 additions and 403 deletions

View file

@ -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) {

View file

@ -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;

View file

@ -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 ""

View file

@ -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({

View file

@ -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();
}
})

View file

@ -58,6 +58,7 @@ $(document).ready(function(){
reloadSettings(function(success) {
if (success) {
getDomainFromAPI();
setupWizardSteps();
updatePlaceNameDisplay();
updateUsernameDisplay();

View file

@ -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;
}

View file

@ -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"

View file

@ -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}));
}
}

View file

@ -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;

View file

@ -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;
}
}

View file

@ -90,7 +90,7 @@ void Ledger::buy(const QString& hfc_key, int cost, const QString& asset_id, cons
signedSend("transaction", transactionString, hfc_key, "buy", "buySuccess", "buyFailure", controlled_failure);
}
bool Ledger::receiveAt(const QString& hfc_key, const QString& old_key) {
bool Ledger::receiveAt(const QString& hfc_key, const QString& old_key, const QString& machine_fingerprint) {
auto accountManager = DependencyManager::get<AccountManager>();
if (!accountManager->isLoggedIn()) {
qCWarning(commerce) << "Cannot set receiveAt when not logged in.";
@ -99,7 +99,13 @@ bool Ledger::receiveAt(const QString& hfc_key, const QString& old_key) {
return false; // We know right away that we will fail, so tell the caller.
}
signedSend("public_key", hfc_key.toUtf8(), old_key, "receive_at", "receiveAtSuccess", "receiveAtFailure");
QJsonObject transaction;
transaction["hfc_key"] = hfc_key;
transaction["machine_fingerprint"] = machine_fingerprint;
QJsonDocument transactionDoc{ transaction };
auto transactionString = transactionDoc.toJson(QJsonDocument::Compact);
signedSend("transaction", transactionString, old_key, "receive_at", "receiveAtSuccess", "receiveAtFailure");
return true; // Note that there may still be an asynchronous signal of failure that callers might be interested in.
}

View file

@ -26,7 +26,7 @@ class Ledger : public QObject, public Dependency {
public:
void buy(const QString& hfc_key, int cost, const QString& asset_id, const QString& inventory_key, const bool controlled_failure = false);
bool receiveAt(const QString& hfc_key, const QString& old_key);
bool receiveAt(const QString& hfc_key, const QString& old_key, const QString& machine_fingerprint);
void balance(const QStringList& keys);
void inventory(const QStringList& keys);
void history(const QStringList& keys);

View file

@ -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("");
});
}

View file

@ -16,6 +16,7 @@
#include "ui/ImageProvider.h"
#include "scripting/HMDScriptingInterface.h"
#include <FingerprintUtils.h>
#include <PathUtils.h>
#include <OffscreenUi.h>
#include <AccountManager.h>
@ -541,7 +542,8 @@ bool Wallet::generateKeyPair() {
// 2. It is maximally private, and we can step back from that later if desired.
// 3. It maximally exercises all the machinery, so we are most likely to surface issues now.
auto ledger = DependencyManager::get<Ledger>();
return ledger->receiveAt(key, oldKey);
QString machineFingerprint = uuidStringWithoutCurlyBraces(FingerprintUtils::getMachineFingerprint());
return ledger->receiveAt(key, oldKey, machineFingerprint);
}
QStringList Wallet::listPublicKeys() {

View file

@ -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;
});
}

View file

@ -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;

View file

@ -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);
}
}

View file

@ -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();

View file

@ -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);

View file

@ -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 {
@ -1638,6 +1639,11 @@ void Rig::initAnimGraph(const QUrl& url) {
_userAnimState = { UserAnimState::None, "", 30.0f, false, 0.0f, 0.0f };
overrideAnimation(origState.url, origState.fps, origState.loop, origState.firstFrame, origState.lastFrame);
}
// restore the role animations we had before reset.
for (auto& roleAnimState : _roleAnimStates) {
auto roleState = roleAnimState.second;
overrideRoleAnimation(roleState.role, roleState.url, roleState.fps, roleState.loop, roleState.firstFrame, roleState.lastFrame);
}
_animLoading = false;
emit onLoadComplete();

View file

@ -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 };

View file

@ -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:
//

View file

@ -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; }

View file

@ -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
}
}

View file

@ -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());

View file

@ -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);

View file

@ -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);

View file

@ -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,

View file

@ -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);

View file

@ -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();

View file

@ -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));

View file

@ -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

View file

@ -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));

View file

@ -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));
}

View file

@ -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);

View file

@ -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);

View file

@ -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);

View file

@ -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;

View file

@ -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

View file

@ -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) {

View file

@ -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;

View file

@ -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
}));
}
}

View file

@ -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);

View file

@ -89,7 +89,7 @@
window.location = "https://clara.io/library?gameCheck=true&public=true";
});
$('#exploreHifiMarketplace').on('click', function () {
window.location = "http://www.highfidelity.com/marketplace";
window.location = metaverseServerURL + "/marketplace";
});
}
@ -612,9 +612,9 @@
var HIFI_ITEM_PAGE = 3;
var pageType = DIRECTORY;
if (location.href.indexOf("highfidelity.com/") !== -1) { pageType = HIFI; }
if (location.href.indexOf(metaverseServerURL + "/") !== -1) { pageType = HIFI; }
if (location.href.indexOf("clara.io/") !== -1) { pageType = CLARA; }
if (location.href.indexOf("highfidelity.com/marketplace/items/") !== -1) { pageType = HIFI_ITEM_PAGE; }
if (location.href.indexOf(metaverseServerURL + "/marketplace/items/") !== -1) { pageType = HIFI_ITEM_PAGE; }
injectCommonCode(pageType === DIRECTORY);
switch (pageType) {

View file

@ -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') {

View file

@ -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

View file

@ -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);