mirror of
https://github.com/overte-org/overte.git
synced 2025-04-08 07:12:40 +02:00
Merge branch 'master' of git://github.com/highfidelity/hifi into shadow
This commit is contained in:
commit
8b6a2eeb24
79 changed files with 805 additions and 591 deletions
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
|
|
4
cmake/externals/wasapi/CMakeLists.txt
vendored
4
cmake/externals/wasapi/CMakeLists.txt
vendored
|
@ -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 ""
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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({
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
})
|
||||
|
|
|
@ -58,6 +58,7 @@ $(document).ready(function(){
|
|||
|
||||
reloadSettings(function(success) {
|
||||
if (success) {
|
||||
getDomainFromAPI();
|
||||
setupWizardSteps();
|
||||
updatePlaceNameDisplay();
|
||||
updateUsernameDisplay();
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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}));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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"});
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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("");
|
||||
});
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
}
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -44,7 +44,7 @@ Cube3DOverlay::~Cube3DOverlay() {
|
|||
}
|
||||
|
||||
void Cube3DOverlay::render(RenderArgs* args) {
|
||||
if (!_visible) {
|
||||
if (!_renderVisible) {
|
||||
return; // do nothing if we're not visible
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
@ -29,9 +29,6 @@ public:
|
|||
|
||||
virtual Rectangle3DOverlay* createClone() const override;
|
||||
|
||||
protected:
|
||||
Transform evalRenderTransform() override;
|
||||
|
||||
private:
|
||||
int _geometryCacheID;
|
||||
std::array<int, 4> _rectGeometryIds;
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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 };
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
//
|
||||
|
|
|
@ -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 };
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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:
|
||||
//
|
||||
|
|
|
@ -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; }
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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...") {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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') {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in a new issue