mirror of
https://github.com/JulianGro/overte.git
synced 2025-04-29 20:42:56 +02:00
Merge branch 'master' of https://github.com/highfidelity/hifi into accept-forced-physics-values
This commit is contained in:
commit
6f4441028d
26 changed files with 661 additions and 173 deletions
|
@ -125,6 +125,10 @@ tr.new-row {
|
||||||
background-color: #dff0d8;
|
background-color: #dff0d8;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tr.invalid-input {
|
||||||
|
background-color: #f2dede;
|
||||||
|
}
|
||||||
|
|
||||||
.graphable-stat {
|
.graphable-stat {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
color: #5286BC;
|
color: #5286BC;
|
||||||
|
|
|
@ -38,7 +38,8 @@ var Settings = {
|
||||||
DOMAIN_ID_SELECTOR: '[name="metaverse.id"]',
|
DOMAIN_ID_SELECTOR: '[name="metaverse.id"]',
|
||||||
ACCESS_TOKEN_SELECTOR: '[name="metaverse.access_token"]',
|
ACCESS_TOKEN_SELECTOR: '[name="metaverse.access_token"]',
|
||||||
PLACES_TABLE_ID: 'places-table',
|
PLACES_TABLE_ID: 'places-table',
|
||||||
FORM_ID: 'settings-form'
|
FORM_ID: 'settings-form',
|
||||||
|
INVALID_ROW_CLASS: 'invalid-input'
|
||||||
};
|
};
|
||||||
|
|
||||||
var viewHelpers = {
|
var viewHelpers = {
|
||||||
|
@ -215,8 +216,8 @@ $(document).ready(function(){
|
||||||
sibling = sibling.next();
|
sibling = sibling.next();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sibling.hasClass(Settings.ADD_DEL_BUTTONS_CLASS)) {
|
// for tables with categories we add the entry and setup the new row on enter
|
||||||
sibling.find('.' + Settings.ADD_ROW_BUTTON_CLASS).click();
|
if (sibling.find("." + Settings.ADD_CATEGORY_BUTTON_CLASS).length) {
|
||||||
sibling.find("." + Settings.ADD_CATEGORY_BUTTON_CLASS).click();
|
sibling.find("." + Settings.ADD_CATEGORY_BUTTON_CLASS).click();
|
||||||
|
|
||||||
// set focus to the first input in the new row
|
// set focus to the first input in the new row
|
||||||
|
@ -891,10 +892,102 @@ function reloadSettings(callback) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function validateInputs() {
|
||||||
|
// check if any new values are bad
|
||||||
|
var tables = $('table');
|
||||||
|
|
||||||
|
var inputsValid = true;
|
||||||
|
|
||||||
|
var tables = $('table');
|
||||||
|
|
||||||
|
// clear any current invalid rows
|
||||||
|
$('tr.' + Settings.INVALID_ROW_CLASS).removeClass(Settings.INVALID_ROW_CLASS);
|
||||||
|
|
||||||
|
function markParentRowInvalid(rowChild) {
|
||||||
|
$(rowChild).closest('tr').addClass(Settings.INVALID_ROW_CLASS);
|
||||||
|
}
|
||||||
|
|
||||||
|
_.each(tables, function(table) {
|
||||||
|
var inputs = $(table).find('tr.' + Settings.NEW_ROW_CLASS + ':not([data-category]) input[data-changed="true"]');
|
||||||
|
|
||||||
|
var empty = false;
|
||||||
|
|
||||||
|
_.each(inputs, function(input){
|
||||||
|
var inputVal = $(input).val();
|
||||||
|
|
||||||
|
if (inputVal.length === 0) {
|
||||||
|
empty = true
|
||||||
|
|
||||||
|
markParentRowInvalid(input);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (empty) {
|
||||||
|
showErrorMessage("Error", "Empty field(s)");
|
||||||
|
inputsValid = false;
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// validate keys specificially for spaces and equality to an existing key
|
||||||
|
var newKeys = $(table).find('tr.' + Settings.NEW_ROW_CLASS + ' td.key');
|
||||||
|
|
||||||
|
var keyWithSpaces = false;
|
||||||
|
var duplicateKey = false;
|
||||||
|
|
||||||
|
_.each(newKeys, function(keyCell) {
|
||||||
|
var keyVal = $(keyCell).children('input').val();
|
||||||
|
|
||||||
|
if (keyVal.indexOf(' ') !== -1) {
|
||||||
|
keyWithSpaces = true;
|
||||||
|
markParentRowInvalid(keyCell);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// make sure we don't have duplicate keys in the table
|
||||||
|
var otherKeys = $(table).find('td.key').not(keyCell);
|
||||||
|
_.each(otherKeys, function(otherKeyCell) {
|
||||||
|
var keyInput = $(otherKeyCell).children('input');
|
||||||
|
|
||||||
|
if (keyInput.length) {
|
||||||
|
if ($(keyInput).val() == keyVal) {
|
||||||
|
duplicateKey = true;
|
||||||
|
}
|
||||||
|
} else if ($(otherKeyCell).html() == keyVal) {
|
||||||
|
duplicateKey = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (duplicateKey) {
|
||||||
|
markParentRowInvalid(keyCell);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
if (keyWithSpaces) {
|
||||||
|
showErrorMessage("Error", "Key contains spaces");
|
||||||
|
inputsValid = false;
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (duplicateKey) {
|
||||||
|
showErrorMessage("Error", "Two keys cannot be identical");
|
||||||
|
inputsValid = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return inputsValid;
|
||||||
|
}
|
||||||
|
|
||||||
var SETTINGS_ERROR_MESSAGE = "There was a problem saving domain settings. Please try again!";
|
var SETTINGS_ERROR_MESSAGE = "There was a problem saving domain settings. Please try again!";
|
||||||
|
|
||||||
function saveSettings() {
|
function saveSettings() {
|
||||||
|
|
||||||
|
if (validateInputs()) {
|
||||||
|
// POST the form JSON to the domain-server settings.json endpoint so the settings are saved
|
||||||
|
|
||||||
// disable any inputs not changed
|
// disable any inputs not changed
|
||||||
$("input:not([data-changed])").each(function(){
|
$("input:not([data-changed])").each(function(){
|
||||||
$(this).prop('disabled', true);
|
$(this).prop('disabled', true);
|
||||||
|
@ -904,10 +997,20 @@ function saveSettings() {
|
||||||
var formJSON = form2js('settings-form', ".", false, cleanupFormValues, true);
|
var formJSON = form2js('settings-form', ".", false, cleanupFormValues, true);
|
||||||
|
|
||||||
// check if we've set the basic http password - if so convert it to base64
|
// check if we've set the basic http password - if so convert it to base64
|
||||||
|
if (formJSON["security"]) {
|
||||||
|
var password = formJSON["security"]["http_password"];
|
||||||
|
if (password && password.length > 0) {
|
||||||
|
formJSON["security"]["http_password"] = sha256_digest(password);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// verify that the password and confirmation match before saving
|
||||||
var canPost = true;
|
var canPost = true;
|
||||||
|
|
||||||
if (formJSON["security"]) {
|
if (formJSON["security"]) {
|
||||||
var password = formJSON["security"]["http_password"];
|
var password = formJSON["security"]["http_password"];
|
||||||
var verify_password = formJSON["security"]["verify_http_password"];
|
var verify_password = formJSON["security"]["verify_http_password"];
|
||||||
|
|
||||||
if (password && password.length > 0) {
|
if (password && password.length > 0) {
|
||||||
if (password != verify_password) {
|
if (password != verify_password) {
|
||||||
bootbox.alert({"message": "Passwords must match!", "title":"Password Error"});
|
bootbox.alert({"message": "Passwords must match!", "title":"Password Error"});
|
||||||
|
@ -930,11 +1033,12 @@ function saveSettings() {
|
||||||
// remove focus from the button
|
// remove focus from the button
|
||||||
$(this).blur();
|
$(this).blur();
|
||||||
|
|
||||||
// POST the form JSON to the domain-server settings.json endpoint so the settings are saved
|
|
||||||
if (canPost) {
|
if (canPost) {
|
||||||
|
// POST the form JSON to the domain-server settings.json endpoint so the settings are saved
|
||||||
postSettings(formJSON);
|
postSettings(formJSON);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
$('body').on('click', '.save-button', function(e){
|
$('body').on('click', '.save-button', function(e){
|
||||||
saveSettings();
|
saveSettings();
|
||||||
|
@ -1110,8 +1214,9 @@ function makeTable(setting, keypath, setting_value) {
|
||||||
if (setting.can_add_new_categories) {
|
if (setting.can_add_new_categories) {
|
||||||
html += makeTableCategoryInput(setting, numVisibleColumns);
|
html += makeTableCategoryInput(setting, numVisibleColumns);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (setting.can_add_new_rows || setting.can_add_new_categories) {
|
if (setting.can_add_new_rows || setting.can_add_new_categories) {
|
||||||
html += makeTableInputs(setting, {}, "");
|
html += makeTableHiddenInputs(setting, {}, "");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
html += "</table>"
|
html += "</table>"
|
||||||
|
@ -1137,7 +1242,7 @@ function makeTableCategoryHeader(categoryKey, categoryValue, numVisibleColumns,
|
||||||
return html;
|
return html;
|
||||||
}
|
}
|
||||||
|
|
||||||
function makeTableInputs(setting, initialValues, categoryValue) {
|
function makeTableHiddenInputs(setting, initialValues, categoryValue) {
|
||||||
var html = "<tr class='inputs'" + (setting.can_add_new_categories && !categoryValue ? " hidden" : "") + " " +
|
var html = "<tr class='inputs'" + (setting.can_add_new_categories && !categoryValue ? " hidden" : "") + " " +
|
||||||
(categoryValue ? ("data-category='" + categoryValue + "'") : "") + " " +
|
(categoryValue ? ("data-category='" + categoryValue + "'") : "") + " " +
|
||||||
(setting.categorize_by_key ? ("data-keep-field='" + setting.categorize_by_key + "'") : "") + ">";
|
(setting.categorize_by_key ? ("data-keep-field='" + setting.categorize_by_key + "'") : "") + ">";
|
||||||
|
@ -1148,7 +1253,7 @@ function makeTableInputs(setting, initialValues, categoryValue) {
|
||||||
|
|
||||||
if (setting.key) {
|
if (setting.key) {
|
||||||
html += "<td class='key' name='" + setting.key.name + "'>\
|
html += "<td class='key' name='" + setting.key.name + "'>\
|
||||||
<input type='text' class='form-control' placeholder='" + (_.has(setting.key, 'placeholder') ? setting.key.placeholder : "") + "' value=''>\
|
<input type='text' style='display: none;' class='form-control' placeholder='" + (_.has(setting.key, 'placeholder') ? setting.key.placeholder : "") + "' value=''>\
|
||||||
</td>"
|
</td>"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1157,14 +1262,14 @@ function makeTableInputs(setting, initialValues, categoryValue) {
|
||||||
if (col.type === "checkbox") {
|
if (col.type === "checkbox") {
|
||||||
html +=
|
html +=
|
||||||
"<td class='" + Settings.DATA_COL_CLASS + "'name='" + col.name + "'>" +
|
"<td class='" + Settings.DATA_COL_CLASS + "'name='" + col.name + "'>" +
|
||||||
"<input type='checkbox' class='form-control table-checkbox' " +
|
"<input type='checkbox' style='display: none;' class='form-control table-checkbox' " +
|
||||||
"name='" + col.name + "'" + (defaultValue ? " checked" : "") + "/>" +
|
"name='" + col.name + "'" + (defaultValue ? " checked" : "") + "/>" +
|
||||||
"</td>";
|
"</td>";
|
||||||
} else {
|
} else {
|
||||||
html +=
|
html +=
|
||||||
"<td " + (col.hidden ? "style='display: none;'" : "") + " class='" + Settings.DATA_COL_CLASS + "' " +
|
"<td " + (col.hidden ? "style='display: none;'" : "") + " class='" + Settings.DATA_COL_CLASS + "' " +
|
||||||
"name='" + col.name + "'>" +
|
"name='" + col.name + "'>" +
|
||||||
"<input type='text' class='form-control' placeholder='" + (col.placeholder ? col.placeholder : "") + "' " +
|
"<input type='text' style='display: none;' class='form-control' placeholder='" + (col.placeholder ? col.placeholder : "") + "' " +
|
||||||
"value='" + (defaultValue || "") + "' data-default='" + (defaultValue || "") + "'" +
|
"value='" + (defaultValue || "") + "' data-default='" + (defaultValue || "") + "'" +
|
||||||
(col.readonly ? " readonly" : "") + ">" +
|
(col.readonly ? " readonly" : "") + ">" +
|
||||||
"</td>";
|
"</td>";
|
||||||
|
@ -1244,49 +1349,17 @@ function addTableRow(row) {
|
||||||
|
|
||||||
var columns = row.parent().children('.' + Settings.DATA_ROW_CLASS);
|
var columns = row.parent().children('.' + Settings.DATA_ROW_CLASS);
|
||||||
|
|
||||||
|
var input_clone = row.clone();
|
||||||
|
|
||||||
if (!isArray) {
|
if (!isArray) {
|
||||||
// Check key spaces
|
// show the key input
|
||||||
var key = row.children(".key").children("input").val()
|
var keyInput = row.children(".key").children("input");
|
||||||
if (key.indexOf(' ') !== -1) {
|
|
||||||
showErrorMessage("Error", "Key contains spaces")
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
// Check keys with the same name
|
|
||||||
var equals = false;
|
|
||||||
_.each(columns.children(".key"), function(element) {
|
|
||||||
if ($(element).text() === key) {
|
|
||||||
equals = true
|
|
||||||
return
|
|
||||||
}
|
|
||||||
})
|
|
||||||
if (equals) {
|
|
||||||
showErrorMessage("Error", "Two keys cannot be identical")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check empty fields
|
|
||||||
var empty = false;
|
|
||||||
_.each(row.children('.' + Settings.DATA_COL_CLASS + ' input'), function(element) {
|
|
||||||
if ($(element).val().length === 0) {
|
|
||||||
empty = true
|
|
||||||
return
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
if (empty) {
|
|
||||||
showErrorMessage("Error", "Empty field(s)")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
var input_clone = row.clone()
|
|
||||||
|
|
||||||
// Change input row to data row
|
// Change input row to data row
|
||||||
var table = row.parents("table")
|
var table = row.parents("table");
|
||||||
var setting_name = table.attr("name")
|
var setting_name = table.attr("name");
|
||||||
var full_name = setting_name + "." + key
|
row.addClass(Settings.DATA_ROW_CLASS + " " + Settings.NEW_ROW_CLASS);
|
||||||
row.addClass(Settings.DATA_ROW_CLASS + " " + Settings.NEW_ROW_CLASS)
|
|
||||||
row.removeClass("inputs")
|
|
||||||
|
|
||||||
_.each(row.children(), function(element) {
|
_.each(row.children(), function(element) {
|
||||||
if ($(element).hasClass("numbered")) {
|
if ($(element).hasClass("numbered")) {
|
||||||
|
@ -1308,56 +1381,43 @@ function addTableRow(row) {
|
||||||
anchor.addClass(Settings.DEL_ROW_SPAN_CLASSES)
|
anchor.addClass(Settings.DEL_ROW_SPAN_CLASSES)
|
||||||
} else if ($(element).hasClass("key")) {
|
} else if ($(element).hasClass("key")) {
|
||||||
var input = $(element).children("input")
|
var input = $(element).children("input")
|
||||||
$(element).html(input.val())
|
input.show();
|
||||||
input.remove()
|
|
||||||
} else if ($(element).hasClass(Settings.DATA_COL_CLASS)) {
|
} else if ($(element).hasClass(Settings.DATA_COL_CLASS)) {
|
||||||
// Hide inputs
|
// show inputs
|
||||||
var input = $(element).find("input")
|
var input = $(element).find("input");
|
||||||
var isCheckbox = false;
|
input.show();
|
||||||
var isTime = false;
|
|
||||||
if (input.hasClass("table-checkbox")) {
|
|
||||||
input = $(input).parent();
|
|
||||||
isCheckbox = true;
|
|
||||||
} else if (input.hasClass("table-time")) {
|
|
||||||
input = $(input).parent();
|
|
||||||
isTime = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
var val = input.val();
|
var isCheckbox = input.hasClass("table-checkbox");
|
||||||
if (isCheckbox) {
|
|
||||||
// don't hide the checkbox
|
|
||||||
val = $(input).find("input").is(':checked');
|
|
||||||
} else if (isTime) {
|
|
||||||
// don't hide the time
|
|
||||||
} else {
|
|
||||||
input.attr("type", "hidden")
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isArray) {
|
if (isArray) {
|
||||||
var row_index = row.siblings('.' + Settings.DATA_ROW_CLASS).length
|
var row_index = row.siblings('.' + Settings.DATA_ROW_CLASS).length
|
||||||
var key = $(element).attr('name')
|
var key = $(element).attr('name');
|
||||||
|
|
||||||
// are there multiple columns or just one?
|
// are there multiple columns or just one?
|
||||||
// with multiple we have an array of Objects, with one we have an array of whatever the value type is
|
// with multiple we have an array of Objects, with one we have an array of whatever the value type is
|
||||||
var num_columns = row.children('.' + Settings.DATA_COL_CLASS).length
|
var num_columns = row.children('.' + Settings.DATA_COL_CLASS).length
|
||||||
|
|
||||||
if (isCheckbox) {
|
if (isCheckbox) {
|
||||||
$(input).find("input").attr("name", setting_name + "[" + row_index + "]" + (num_columns > 1 ? "." + key : ""))
|
input.attr("name", setting_name + "[" + row_index + "]" + (num_columns > 1 ? "." + key : ""))
|
||||||
} else {
|
} else {
|
||||||
input.attr("name", setting_name + "[" + row_index + "]" + (num_columns > 1 ? "." + key : ""))
|
input.attr("name", setting_name + "[" + row_index + "]" + (num_columns > 1 ? "." + key : ""))
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
input.attr("name", full_name + "." + $(element).attr("name"))
|
// because the name of the setting in question requires the key
|
||||||
|
// setup a hook to change the HTML name of the element whenever the key changes
|
||||||
|
var colName = $(element).attr("name");
|
||||||
|
keyInput.on('change', function(){
|
||||||
|
input.attr("name", setting_name + "." + $(this).val() + "." + colName);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isCheckbox) {
|
if (isCheckbox) {
|
||||||
$(input).find("input").attr("data-changed", "true");
|
$(input).find("input").attr("data-changed", "true");
|
||||||
} else {
|
} else {
|
||||||
input.attr("data-changed", "true");
|
input.attr("data-changed", "true");
|
||||||
$(element).append(val);
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
console.log("Unknown table element")
|
console.log("Unknown table element");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -1387,7 +1447,12 @@ function deleteTableRow($row) {
|
||||||
$row.empty();
|
$row.empty();
|
||||||
|
|
||||||
if (!isArray) {
|
if (!isArray) {
|
||||||
|
if ($row.attr('name')) {
|
||||||
$row.html("<input type='hidden' class='form-control' name='" + $row.attr('name') + "' data-changed='true' value=''>");
|
$row.html("<input type='hidden' class='form-control' name='" + $row.attr('name') + "' data-changed='true' value=''>");
|
||||||
|
} else {
|
||||||
|
// for rows that didn't have a key, simply remove the row
|
||||||
|
$row.remove();
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
if ($table.find('.' + Settings.DATA_ROW_CLASS + "[data-category='" + categoryName + "']").length <= 1) {
|
if ($table.find('.' + Settings.DATA_ROW_CLASS + "[data-category='" + categoryName + "']").length <= 1) {
|
||||||
// This is the last row of the category, so delete the header
|
// This is the last row of the category, so delete the header
|
||||||
|
|
|
@ -33,6 +33,7 @@ Item {
|
||||||
propagateComposedEvents: true
|
propagateComposedEvents: true
|
||||||
acceptedButtons: "AllButtons"
|
acceptedButtons: "AllButtons"
|
||||||
onClicked: {
|
onClicked: {
|
||||||
|
menu.visible = false;
|
||||||
menu.done();
|
menu.done();
|
||||||
mouse.accepted = false;
|
mouse.accepted = false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,6 +32,7 @@ Item {
|
||||||
MouseArea {
|
MouseArea {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
onClicked: {
|
onClicked: {
|
||||||
|
menu.visible = false;
|
||||||
root.triggered();
|
root.triggered();
|
||||||
menu.done();
|
menu.done();
|
||||||
}
|
}
|
||||||
|
|
|
@ -75,6 +75,14 @@ Item {
|
||||||
source: buttonOutline
|
source: buttonOutline
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function urlHelper(src) {
|
||||||
|
if (src.match(/\bhttp/)) {
|
||||||
|
return src;
|
||||||
|
} else {
|
||||||
|
return "../../../" + src;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Image {
|
Image {
|
||||||
id: icon
|
id: icon
|
||||||
width: 50
|
width: 50
|
||||||
|
@ -84,7 +92,7 @@ Item {
|
||||||
anchors.bottomMargin: 5
|
anchors.bottomMargin: 5
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
fillMode: Image.Stretch
|
fillMode: Image.Stretch
|
||||||
source: "../../../" + tabletButton.icon
|
source: tabletButton.urlHelper(tabletButton.icon)
|
||||||
}
|
}
|
||||||
|
|
||||||
ColorOverlay {
|
ColorOverlay {
|
||||||
|
@ -185,7 +193,7 @@ Item {
|
||||||
|
|
||||||
PropertyChanges {
|
PropertyChanges {
|
||||||
target: icon
|
target: icon
|
||||||
source: "../../../" + tabletButton.activeIcon
|
source: tabletButton.urlHelper(tabletButton.activeIcon)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
State {
|
State {
|
||||||
|
|
|
@ -18,7 +18,7 @@
|
||||||
QString const ModelOverlay::TYPE = "model";
|
QString const ModelOverlay::TYPE = "model";
|
||||||
|
|
||||||
ModelOverlay::ModelOverlay()
|
ModelOverlay::ModelOverlay()
|
||||||
: _model(std::make_shared<Model>(std::make_shared<Rig>())),
|
: _model(std::make_shared<Model>(std::make_shared<Rig>(), nullptr, this)),
|
||||||
_modelTextures(QVariantMap())
|
_modelTextures(QVariantMap())
|
||||||
{
|
{
|
||||||
_model->init();
|
_model->init();
|
||||||
|
@ -27,7 +27,7 @@ ModelOverlay::ModelOverlay()
|
||||||
|
|
||||||
ModelOverlay::ModelOverlay(const ModelOverlay* modelOverlay) :
|
ModelOverlay::ModelOverlay(const ModelOverlay* modelOverlay) :
|
||||||
Volume3DOverlay(modelOverlay),
|
Volume3DOverlay(modelOverlay),
|
||||||
_model(std::make_shared<Model>(std::make_shared<Rig>())),
|
_model(std::make_shared<Model>(std::make_shared<Rig>(), nullptr, this)),
|
||||||
_modelTextures(QVariantMap()),
|
_modelTextures(QVariantMap()),
|
||||||
_url(modelOverlay->_url),
|
_url(modelOverlay->_url),
|
||||||
_updateModel(false)
|
_updateModel(false)
|
||||||
|
|
|
@ -540,7 +540,7 @@ void EntityTreeRenderer::processEraseMessage(ReceivedMessage& message, const Sha
|
||||||
std::static_pointer_cast<EntityTree>(_tree)->processEraseMessage(message, sourceNode);
|
std::static_pointer_cast<EntityTree>(_tree)->processEraseMessage(message, sourceNode);
|
||||||
}
|
}
|
||||||
|
|
||||||
ModelPointer EntityTreeRenderer::allocateModel(const QString& url, float loadingPriority) {
|
ModelPointer EntityTreeRenderer::allocateModel(const QString& url, float loadingPriority, SpatiallyNestable* spatiallyNestableOverride) {
|
||||||
ModelPointer model = nullptr;
|
ModelPointer model = nullptr;
|
||||||
|
|
||||||
// Only create and delete models on the thread that owns the EntityTreeRenderer
|
// Only create and delete models on the thread that owns the EntityTreeRenderer
|
||||||
|
@ -552,7 +552,7 @@ ModelPointer EntityTreeRenderer::allocateModel(const QString& url, float loading
|
||||||
return model;
|
return model;
|
||||||
}
|
}
|
||||||
|
|
||||||
model = std::make_shared<Model>(std::make_shared<Rig>());
|
model = std::make_shared<Model>(std::make_shared<Rig>(), nullptr, spatiallyNestableOverride);
|
||||||
model->setLoadingPriority(loadingPriority);
|
model->setLoadingPriority(loadingPriority);
|
||||||
model->init();
|
model->init();
|
||||||
model->setURL(QUrl(url));
|
model->setURL(QUrl(url));
|
||||||
|
|
|
@ -77,7 +77,7 @@ public:
|
||||||
void reloadEntityScripts();
|
void reloadEntityScripts();
|
||||||
|
|
||||||
/// if a renderable entity item needs a model, we will allocate it for them
|
/// if a renderable entity item needs a model, we will allocate it for them
|
||||||
Q_INVOKABLE ModelPointer allocateModel(const QString& url, float loadingPriority = 0.0f);
|
Q_INVOKABLE ModelPointer allocateModel(const QString& url, float loadingPriority = 0.0f, SpatiallyNestable* spatiallyNestableOverride = nullptr);
|
||||||
|
|
||||||
/// if a renderable entity item needs to update the URL of a model, we will handle that for the entity
|
/// if a renderable entity item needs to update the URL of a model, we will handle that for the entity
|
||||||
Q_INVOKABLE ModelPointer updateModel(ModelPointer original, const QString& newUrl);
|
Q_INVOKABLE ModelPointer updateModel(ModelPointer original, const QString& newUrl);
|
||||||
|
|
|
@ -504,8 +504,7 @@ ModelPointer RenderableModelEntityItem::getModel(QSharedPointer<EntityTreeRender
|
||||||
if (!getModelURL().isEmpty()) {
|
if (!getModelURL().isEmpty()) {
|
||||||
// If we don't have a model, allocate one *immediately*
|
// If we don't have a model, allocate one *immediately*
|
||||||
if (!_model) {
|
if (!_model) {
|
||||||
_model = _myRenderer->allocateModel(getModelURL(), renderer->getEntityLoadingPriority(*this));
|
_model = _myRenderer->allocateModel(getModelURL(), renderer->getEntityLoadingPriority(*this), this);
|
||||||
_model->setSpatiallyNestableOverride(shared_from_this());
|
|
||||||
_needsInitialSimulation = true;
|
_needsInitialSimulation = true;
|
||||||
// If we need to change URLs, update it *after rendering* (to avoid access violations)
|
// If we need to change URLs, update it *after rendering* (to avoid access violations)
|
||||||
} else if (QUrl(getModelURL()) != _model->getURL()) {
|
} else if (QUrl(getModelURL()) != _model->getURL()) {
|
||||||
|
|
|
@ -693,6 +693,13 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef
|
||||||
somethingChanged = true;
|
somethingChanged = true;
|
||||||
_simulationOwner.clearCurrentOwner();
|
_simulationOwner.clearCurrentOwner();
|
||||||
}
|
}
|
||||||
|
} else if (newSimOwner.matchesValidID(myNodeID) && !_hasBidOnSimulation) {
|
||||||
|
// entity-server tells us that we have simulation ownership while we never requested this for this EntityItem,
|
||||||
|
// this could happen when the user reloads the cache and entity tree.
|
||||||
|
_dirtyFlags |= Simulation::DIRTY_SIMULATOR_ID;
|
||||||
|
somethingChanged = true;
|
||||||
|
_simulationOwner.clearCurrentOwner();
|
||||||
|
weOwnSimulation = false;
|
||||||
} else if (_simulationOwner.set(newSimOwner)) {
|
} else if (_simulationOwner.set(newSimOwner)) {
|
||||||
_dirtyFlags |= Simulation::DIRTY_SIMULATOR_ID;
|
_dirtyFlags |= Simulation::DIRTY_SIMULATOR_ID;
|
||||||
somethingChanged = true;
|
somethingChanged = true;
|
||||||
|
@ -1273,7 +1280,7 @@ void EntityItem::grabSimulationOwnership() {
|
||||||
auto nodeList = DependencyManager::get<NodeList>();
|
auto nodeList = DependencyManager::get<NodeList>();
|
||||||
if (_simulationOwner.matchesValidID(nodeList->getSessionUUID())) {
|
if (_simulationOwner.matchesValidID(nodeList->getSessionUUID())) {
|
||||||
// we already own it
|
// we already own it
|
||||||
_simulationOwner.promotePriority(SCRIPT_POKE_SIMULATION_PRIORITY);
|
_simulationOwner.promotePriority(SCRIPT_GRAB_SIMULATION_PRIORITY);
|
||||||
} else {
|
} else {
|
||||||
// we don't own it yet
|
// we don't own it yet
|
||||||
_simulationOwner.setPendingPriority(SCRIPT_GRAB_SIMULATION_PRIORITY, usecTimestampNow());
|
_simulationOwner.setPendingPriority(SCRIPT_GRAB_SIMULATION_PRIORITY, usecTimestampNow());
|
||||||
|
@ -1884,6 +1891,10 @@ void EntityItem::setPendingOwnershipPriority(quint8 priority, const quint64& tim
|
||||||
_simulationOwner.setPendingPriority(priority, timestamp);
|
_simulationOwner.setPendingPriority(priority, timestamp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void EntityItem::rememberHasSimulationOwnershipBid() const {
|
||||||
|
_hasBidOnSimulation = true;
|
||||||
|
}
|
||||||
|
|
||||||
QString EntityItem::actionsToDebugString() {
|
QString EntityItem::actionsToDebugString() {
|
||||||
QString result;
|
QString result;
|
||||||
QVector<QByteArray> serializedActions;
|
QVector<QByteArray> serializedActions;
|
||||||
|
|
|
@ -321,6 +321,7 @@ public:
|
||||||
void updateSimulationOwner(const SimulationOwner& owner);
|
void updateSimulationOwner(const SimulationOwner& owner);
|
||||||
void clearSimulationOwnership();
|
void clearSimulationOwnership();
|
||||||
void setPendingOwnershipPriority(quint8 priority, const quint64& timestamp);
|
void setPendingOwnershipPriority(quint8 priority, const quint64& timestamp);
|
||||||
|
void rememberHasSimulationOwnershipBid() const;
|
||||||
|
|
||||||
const QString& getMarketplaceID() const { return _marketplaceID; }
|
const QString& getMarketplaceID() const { return _marketplaceID; }
|
||||||
void setMarketplaceID(const QString& value) { _marketplaceID = value; }
|
void setMarketplaceID(const QString& value) { _marketplaceID = value; }
|
||||||
|
@ -497,16 +498,16 @@ protected:
|
||||||
mutable AABox _cachedAABox;
|
mutable AABox _cachedAABox;
|
||||||
mutable AACube _maxAACube;
|
mutable AACube _maxAACube;
|
||||||
mutable AACube _minAACube;
|
mutable AACube _minAACube;
|
||||||
mutable bool _recalcAABox = true;
|
mutable bool _recalcAABox { true };
|
||||||
mutable bool _recalcMinAACube = true;
|
mutable bool _recalcMinAACube { true };
|
||||||
mutable bool _recalcMaxAACube = true;
|
mutable bool _recalcMaxAACube { true };
|
||||||
|
|
||||||
float _localRenderAlpha;
|
float _localRenderAlpha;
|
||||||
float _density = ENTITY_ITEM_DEFAULT_DENSITY; // kg/m^3
|
float _density { ENTITY_ITEM_DEFAULT_DENSITY }; // kg/m^3
|
||||||
// NOTE: _volumeMultiplier is used to allow some mass properties code exist in the EntityItem base class
|
// NOTE: _volumeMultiplier is used to allow some mass properties code exist in the EntityItem base class
|
||||||
// rather than in all of the derived classes. If we ever collapse these classes to one we could do it a
|
// rather than in all of the derived classes. If we ever collapse these classes to one we could do it a
|
||||||
// different way.
|
// different way.
|
||||||
float _volumeMultiplier = 1.0f;
|
float _volumeMultiplier { 1.0f };
|
||||||
glm::vec3 _gravity;
|
glm::vec3 _gravity;
|
||||||
glm::vec3 _acceleration;
|
glm::vec3 _acceleration;
|
||||||
float _damping;
|
float _damping;
|
||||||
|
@ -562,8 +563,8 @@ protected:
|
||||||
uint32_t _dirtyFlags; // things that have changed from EXTERNAL changes (via script or packet) but NOT from simulation
|
uint32_t _dirtyFlags; // things that have changed from EXTERNAL changes (via script or packet) but NOT from simulation
|
||||||
|
|
||||||
// these backpointers are only ever set/cleared by friends:
|
// these backpointers are only ever set/cleared by friends:
|
||||||
EntityTreeElementPointer _element = nullptr; // set by EntityTreeElement
|
EntityTreeElementPointer _element { nullptr }; // set by EntityTreeElement
|
||||||
void* _physicsInfo = nullptr; // set by EntitySimulation
|
void* _physicsInfo { nullptr }; // set by EntitySimulation
|
||||||
bool _simulated; // set by EntitySimulation
|
bool _simulated; // set by EntitySimulation
|
||||||
|
|
||||||
bool addActionInternal(EntitySimulationPointer simulation, EntityActionPointer action);
|
bool addActionInternal(EntitySimulationPointer simulation, EntityActionPointer action);
|
||||||
|
@ -580,12 +581,15 @@ protected:
|
||||||
// are used to keep track of and work around this situation.
|
// are used to keep track of and work around this situation.
|
||||||
void checkWaitingToRemove(EntitySimulationPointer simulation = nullptr);
|
void checkWaitingToRemove(EntitySimulationPointer simulation = nullptr);
|
||||||
mutable QSet<QUuid> _actionsToRemove;
|
mutable QSet<QUuid> _actionsToRemove;
|
||||||
mutable bool _actionDataDirty = false;
|
mutable bool _actionDataDirty { false };
|
||||||
mutable bool _actionDataNeedsTransmit = false;
|
mutable bool _actionDataNeedsTransmit { false };
|
||||||
// _previouslyDeletedActions is used to avoid an action being re-added due to server round-trip lag
|
// _previouslyDeletedActions is used to avoid an action being re-added due to server round-trip lag
|
||||||
static quint64 _rememberDeletedActionTime;
|
static quint64 _rememberDeletedActionTime;
|
||||||
mutable QHash<QUuid, quint64> _previouslyDeletedActions;
|
mutable QHash<QUuid, quint64> _previouslyDeletedActions;
|
||||||
|
|
||||||
|
// per entity keep state if it ever bid on simulation, so that we can ignore false simulation ownership
|
||||||
|
mutable bool _hasBidOnSimulation { false };
|
||||||
|
|
||||||
QUuid _sourceUUID; /// the server node UUID we came from
|
QUuid _sourceUUID; /// the server node UUID we came from
|
||||||
|
|
||||||
bool _clientOnly { false };
|
bool _clientOnly { false };
|
||||||
|
|
|
@ -231,6 +231,7 @@ QUuid EntityScriptingInterface::addEntity(const EntityItemProperties& properties
|
||||||
// and make note of it now, so we can act on it right away.
|
// and make note of it now, so we can act on it right away.
|
||||||
propertiesWithSimID.setSimulationOwner(myNodeID, SCRIPT_POKE_SIMULATION_PRIORITY);
|
propertiesWithSimID.setSimulationOwner(myNodeID, SCRIPT_POKE_SIMULATION_PRIORITY);
|
||||||
entity->setSimulationOwner(myNodeID, SCRIPT_POKE_SIMULATION_PRIORITY);
|
entity->setSimulationOwner(myNodeID, SCRIPT_POKE_SIMULATION_PRIORITY);
|
||||||
|
entity->rememberHasSimulationOwnershipBid();
|
||||||
}
|
}
|
||||||
|
|
||||||
entity->setLastBroadcast(usecTimestampNow());
|
entity->setLastBroadcast(usecTimestampNow());
|
||||||
|
@ -444,6 +445,7 @@ QUuid EntityScriptingInterface::editEntity(QUuid id, const EntityItemProperties&
|
||||||
// we make a bid for simulation ownership
|
// we make a bid for simulation ownership
|
||||||
properties.setSimulationOwner(myNodeID, SCRIPT_POKE_SIMULATION_PRIORITY);
|
properties.setSimulationOwner(myNodeID, SCRIPT_POKE_SIMULATION_PRIORITY);
|
||||||
entity->pokeSimulationOwnership();
|
entity->pokeSimulationOwnership();
|
||||||
|
entity->rememberHasSimulationOwnershipBid();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (properties.parentRelatedPropertyChanged() && entity->computePuffedQueryAACube()) {
|
if (properties.parentRelatedPropertyChanged() && entity->computePuffedQueryAACube()) {
|
||||||
|
|
|
@ -64,7 +64,7 @@ EntityTree::~EntityTree() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void EntityTree::setEntityScriptSourceWhitelist(const QString& entityScriptSourceWhitelist) {
|
void EntityTree::setEntityScriptSourceWhitelist(const QString& entityScriptSourceWhitelist) {
|
||||||
_entityScriptSourceWhitelist = entityScriptSourceWhitelist.split(',');
|
_entityScriptSourceWhitelist = entityScriptSourceWhitelist.split(',', QString::SkipEmptyParts);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -1111,7 +1111,15 @@ int EntityTree::processEditPacketData(ReceivedMessage& message, const unsigned c
|
||||||
endUpdate = usecTimestampNow();
|
endUpdate = usecTimestampNow();
|
||||||
_totalUpdates++;
|
_totalUpdates++;
|
||||||
} else if (message.getType() == PacketType::EntityAdd) {
|
} else if (message.getType() == PacketType::EntityAdd) {
|
||||||
if (senderNode->getCanRez() || senderNode->getCanRezTmp()) {
|
bool failedAdd = !allowed;
|
||||||
|
if (!allowed) {
|
||||||
|
qCDebug(entities) << "Filtered entity add. ID:" << entityItemID;
|
||||||
|
} else if (!senderNode->getCanRez() && !senderNode->getCanRezTmp()) {
|
||||||
|
failedAdd = true;
|
||||||
|
qCDebug(entities) << "User without 'rez rights' [" << senderNode->getUUID()
|
||||||
|
<< "] attempted to add an entity ID:" << entityItemID;
|
||||||
|
|
||||||
|
} else {
|
||||||
// this is a new entity... assign a new entityID
|
// this is a new entity... assign a new entityID
|
||||||
properties.setCreated(properties.getLastEdited());
|
properties.setCreated(properties.getLastEdited());
|
||||||
properties.setLastEditedBy(senderNode->getUUID());
|
properties.setLastEditedBy(senderNode->getUUID());
|
||||||
|
@ -1136,10 +1144,14 @@ int EntityTree::processEditPacketData(ReceivedMessage& message, const unsigned c
|
||||||
}
|
}
|
||||||
endLogging = usecTimestampNow();
|
endLogging = usecTimestampNow();
|
||||||
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
qCDebug(entities) << "User without 'rez rights' [" << senderNode->getUUID()
|
failedAdd = true;
|
||||||
<< "] attempted to add an entity.";
|
qCDebug(entities) << "Add entity failed ID:" << entityItemID;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (failedAdd) { // Let client know it failed, so that they don't have an entity that no one else sees.
|
||||||
|
QWriteLocker locker(&_recentlyDeletedEntitiesLock);
|
||||||
|
_recentlyDeletedEntityItemIDs.insert(usecTimestampNow(), entityItemID);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
static QString repeatedMessage =
|
static QString repeatedMessage =
|
||||||
|
|
|
@ -582,6 +582,8 @@ void EntityMotionState::sendUpdate(OctreeEditPacketSender* packetSender, uint32_
|
||||||
_nextOwnershipBid = now + USECS_BETWEEN_OWNERSHIP_BIDS;
|
_nextOwnershipBid = now + USECS_BETWEEN_OWNERSHIP_BIDS;
|
||||||
// copy _outgoingPriority into pendingPriority...
|
// copy _outgoingPriority into pendingPriority...
|
||||||
_entity->setPendingOwnershipPriority(_outgoingPriority, now);
|
_entity->setPendingOwnershipPriority(_outgoingPriority, now);
|
||||||
|
// don't forget to remember that we have made a bid
|
||||||
|
_entity->rememberHasSimulationOwnershipBid();
|
||||||
// ...then reset _outgoingPriority in preparation for the next frame
|
// ...then reset _outgoingPriority in preparation for the next frame
|
||||||
_outgoingPriority = 0;
|
_outgoingPriority = 0;
|
||||||
} else if (_outgoingPriority != _entity->getSimulationPriority()) {
|
} else if (_outgoingPriority != _entity->getSimulationPriority()) {
|
||||||
|
|
|
@ -374,12 +374,7 @@ void ModelMeshPartPayload::updateTransformForSkinnedMesh(const Transform& transf
|
||||||
_transform = transform;
|
_transform = transform;
|
||||||
|
|
||||||
if (clusterMatrices.size() > 0) {
|
if (clusterMatrices.size() > 0) {
|
||||||
_worldBound = AABox();
|
_worldBound = _adjustedLocalBound;
|
||||||
for (auto& clusterMatrix : clusterMatrices) {
|
|
||||||
AABox clusterBound = _localBound;
|
|
||||||
clusterBound.transform(clusterMatrix);
|
|
||||||
_worldBound += clusterBound;
|
|
||||||
}
|
|
||||||
_worldBound.transform(_transform);
|
_worldBound.transform(_transform);
|
||||||
if (clusterMatrices.size() == 1) {
|
if (clusterMatrices.size() == 1) {
|
||||||
_transform = _transform.worldTransform(Transform(clusterMatrices[0]));
|
_transform = _transform.worldTransform(Transform(clusterMatrices[0]));
|
||||||
|
@ -612,3 +607,15 @@ void ModelMeshPartPayload::render(RenderArgs* args) const {
|
||||||
const int INDICES_PER_TRIANGLE = 3;
|
const int INDICES_PER_TRIANGLE = 3;
|
||||||
args->_details._trianglesRendered += _drawPart._numIndices / INDICES_PER_TRIANGLE;
|
args->_details._trianglesRendered += _drawPart._numIndices / INDICES_PER_TRIANGLE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ModelMeshPartPayload::computeAdjustedLocalBound(const QVector<glm::mat4>& clusterMatrices) {
|
||||||
|
_adjustedLocalBound = _localBound;
|
||||||
|
if (clusterMatrices.size() > 0) {
|
||||||
|
_adjustedLocalBound.transform(clusterMatrices[0]);
|
||||||
|
for (int i = 1; i < clusterMatrices.size(); ++i) {
|
||||||
|
AABox clusterBound = _localBound;
|
||||||
|
clusterBound.transform(clusterMatrices[i]);
|
||||||
|
_adjustedLocalBound += clusterBound;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
//
|
//
|
||||||
// ModelMeshPartPayload.h
|
// MeshPartPayload.h
|
||||||
// interface/src/renderer
|
// interface/src/renderer
|
||||||
//
|
//
|
||||||
// Created by Sam Gateau on 10/3/15.
|
// Created by Sam Gateau on 10/3/15.
|
||||||
|
@ -61,6 +61,7 @@ public:
|
||||||
bool _hasColorAttrib { false };
|
bool _hasColorAttrib { false };
|
||||||
|
|
||||||
model::Box _localBound;
|
model::Box _localBound;
|
||||||
|
model::Box _adjustedLocalBound;
|
||||||
mutable model::Box _worldBound;
|
mutable model::Box _worldBound;
|
||||||
std::shared_ptr<const model::Mesh> _drawMesh;
|
std::shared_ptr<const model::Mesh> _drawMesh;
|
||||||
|
|
||||||
|
@ -105,6 +106,8 @@ public:
|
||||||
|
|
||||||
void initCache();
|
void initCache();
|
||||||
|
|
||||||
|
void computeAdjustedLocalBound(const QVector<glm::mat4>& clusterMatrices);
|
||||||
|
|
||||||
Model* _model;
|
Model* _model;
|
||||||
|
|
||||||
int _meshIndex;
|
int _meshIndex;
|
||||||
|
|
|
@ -78,11 +78,12 @@ void initCollisionMaterials() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Model::Model(RigPointer rig, QObject* parent) :
|
Model::Model(RigPointer rig, QObject* parent, SpatiallyNestable* spatiallyNestableOverride) :
|
||||||
QObject(parent),
|
QObject(parent),
|
||||||
_renderGeometry(),
|
_renderGeometry(),
|
||||||
_collisionGeometry(),
|
_collisionGeometry(),
|
||||||
_renderWatcher(_renderGeometry),
|
_renderWatcher(_renderGeometry),
|
||||||
|
_spatiallyNestableOverride(spatiallyNestableOverride),
|
||||||
_translation(0.0f),
|
_translation(0.0f),
|
||||||
_rotation(),
|
_rotation(),
|
||||||
_scale(1.0f, 1.0f, 1.0f),
|
_scale(1.0f, 1.0f, 1.0f),
|
||||||
|
@ -133,16 +134,10 @@ void Model::setRotation(const glm::quat& rotation) {
|
||||||
updateRenderItems();
|
updateRenderItems();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Model::setSpatiallyNestableOverride(SpatiallyNestablePointer override) {
|
|
||||||
_spatiallyNestableOverride = override;
|
|
||||||
updateRenderItems();
|
|
||||||
}
|
|
||||||
|
|
||||||
Transform Model::getTransform() const {
|
Transform Model::getTransform() const {
|
||||||
SpatiallyNestablePointer spatiallyNestableOverride = _spatiallyNestableOverride.lock();
|
if (_spatiallyNestableOverride) {
|
||||||
if (spatiallyNestableOverride) {
|
|
||||||
bool success;
|
bool success;
|
||||||
Transform transform = spatiallyNestableOverride->getTransform(success);
|
Transform transform = _spatiallyNestableOverride->getTransform(success);
|
||||||
if (success) {
|
if (success) {
|
||||||
transform.setScale(getScale());
|
transform.setScale(getScale());
|
||||||
return transform;
|
return transform;
|
||||||
|
@ -1149,6 +1144,8 @@ void Model::simulate(float deltaTime, bool fullUpdate) {
|
||||||
// update the world space transforms for all joints
|
// update the world space transforms for all joints
|
||||||
glm::mat4 parentTransform = glm::scale(_scale) * glm::translate(_offset);
|
glm::mat4 parentTransform = glm::scale(_scale) * glm::translate(_offset);
|
||||||
updateRig(deltaTime, parentTransform);
|
updateRig(deltaTime, parentTransform);
|
||||||
|
|
||||||
|
computeMeshPartLocalBounds();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1158,6 +1155,14 @@ void Model::updateRig(float deltaTime, glm::mat4 parentTransform) {
|
||||||
_rig->updateAnimations(deltaTime, parentTransform);
|
_rig->updateAnimations(deltaTime, parentTransform);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Model::computeMeshPartLocalBounds() {
|
||||||
|
for (auto& part : _modelMeshRenderItemsSet) {
|
||||||
|
assert(part->_meshIndex < _modelMeshRenderItemsSet.size());
|
||||||
|
const Model::MeshState& state = _meshStates.at(part->_meshIndex);
|
||||||
|
part->computeAdjustedLocalBound(state.clusterMatrices);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// virtual
|
// virtual
|
||||||
void Model::updateClusterMatrices() {
|
void Model::updateClusterMatrices() {
|
||||||
PerformanceTimer perfTimer("Model::updateClusterMatrices");
|
PerformanceTimer perfTimer("Model::updateClusterMatrices");
|
||||||
|
@ -1334,6 +1339,7 @@ void Model::createVisibleRenderItemSet() {
|
||||||
shapeID++;
|
shapeID++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
computeMeshPartLocalBounds();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Model::createCollisionRenderItemSet() {
|
void Model::createCollisionRenderItemSet() {
|
||||||
|
|
|
@ -67,7 +67,7 @@ public:
|
||||||
|
|
||||||
static void setAbstractViewStateInterface(AbstractViewStateInterface* viewState) { _viewState = viewState; }
|
static void setAbstractViewStateInterface(AbstractViewStateInterface* viewState) { _viewState = viewState; }
|
||||||
|
|
||||||
Model(RigPointer rig, QObject* parent = nullptr);
|
Model(RigPointer rig, QObject* parent = nullptr, SpatiallyNestable* spatiallyNestableOverride = nullptr);
|
||||||
virtual ~Model();
|
virtual ~Model();
|
||||||
|
|
||||||
inline ModelPointer getThisPointer() const {
|
inline ModelPointer getThisPointer() const {
|
||||||
|
@ -205,7 +205,6 @@ public:
|
||||||
|
|
||||||
void setTranslation(const glm::vec3& translation);
|
void setTranslation(const glm::vec3& translation);
|
||||||
void setRotation(const glm::quat& rotation);
|
void setRotation(const glm::quat& rotation);
|
||||||
void setSpatiallyNestableOverride(SpatiallyNestablePointer ptr);
|
|
||||||
|
|
||||||
const glm::vec3& getTranslation() const { return _translation; }
|
const glm::vec3& getTranslation() const { return _translation; }
|
||||||
const glm::quat& getRotation() const { return _rotation; }
|
const glm::quat& getRotation() const { return _rotation; }
|
||||||
|
@ -244,7 +243,6 @@ public:
|
||||||
public:
|
public:
|
||||||
QVector<glm::mat4> clusterMatrices;
|
QVector<glm::mat4> clusterMatrices;
|
||||||
gpu::BufferPointer clusterBuffer;
|
gpu::BufferPointer clusterBuffer;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const MeshState& getMeshState(int index) { return _meshStates.at(index); }
|
const MeshState& getMeshState(int index) { return _meshStates.at(index); }
|
||||||
|
@ -293,12 +291,11 @@ protected:
|
||||||
|
|
||||||
GeometryResourceWatcher _renderWatcher;
|
GeometryResourceWatcher _renderWatcher;
|
||||||
|
|
||||||
|
SpatiallyNestable* _spatiallyNestableOverride;
|
||||||
|
|
||||||
glm::vec3 _translation;
|
glm::vec3 _translation;
|
||||||
glm::quat _rotation;
|
glm::quat _rotation;
|
||||||
glm::vec3 _scale;
|
glm::vec3 _scale;
|
||||||
|
|
||||||
SpatiallyNestableWeakPointer _spatiallyNestableOverride;
|
|
||||||
|
|
||||||
glm::vec3 _offset;
|
glm::vec3 _offset;
|
||||||
|
|
||||||
static float FAKE_DIMENSION_PLACEHOLDER;
|
static float FAKE_DIMENSION_PLACEHOLDER;
|
||||||
|
@ -319,6 +316,7 @@ protected:
|
||||||
void scaleToFit();
|
void scaleToFit();
|
||||||
void snapToRegistrationPoint();
|
void snapToRegistrationPoint();
|
||||||
|
|
||||||
|
void computeMeshPartLocalBounds();
|
||||||
virtual void updateRig(float deltaTime, glm::mat4 parentTransform);
|
virtual void updateRig(float deltaTime, glm::mat4 parentTransform);
|
||||||
|
|
||||||
/// Restores the indexed joint to its default position.
|
/// Restores the indexed joint to its default position.
|
||||||
|
|
|
@ -170,14 +170,14 @@ public:
|
||||||
/**jsdoc
|
/**jsdoc
|
||||||
* Returns the current value of this button's properties
|
* Returns the current value of this button's properties
|
||||||
* @function TabletButtonProxy#getProperties
|
* @function TabletButtonProxy#getProperties
|
||||||
* @returns {object}
|
* @returns {ButtonProperties}
|
||||||
*/
|
*/
|
||||||
Q_INVOKABLE QVariantMap getProperties() const;
|
Q_INVOKABLE QVariantMap getProperties() const;
|
||||||
|
|
||||||
/**jsdoc
|
/**jsdoc
|
||||||
* Replace the values of some of this button's properties
|
* Replace the values of some of this button's properties
|
||||||
* @function TabletButtonProxy#editProperties
|
* @function TabletButtonProxy#editProperties
|
||||||
* @param properties {object} set of properties to change
|
* @param {ButtonProperties} properties - set of properties to change
|
||||||
*/
|
*/
|
||||||
Q_INVOKABLE void editProperties(QVariantMap properties);
|
Q_INVOKABLE void editProperties(QVariantMap properties);
|
||||||
|
|
||||||
|
@ -199,4 +199,13 @@ protected:
|
||||||
QVariantMap _properties;
|
QVariantMap _properties;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**jsdoc
|
||||||
|
* @typedef TabletButtonProxy.ButtonProperties
|
||||||
|
* @property {string} text - button caption
|
||||||
|
* @property {string} icon - url to button icon. (50 x 50)
|
||||||
|
* @property {string} activeText - button caption when button is active
|
||||||
|
* @property {string} activeIcon - url to button icon used when button is active. (50 x 50)
|
||||||
|
* @property {string} isActive - true when button is active.
|
||||||
|
*/
|
||||||
|
|
||||||
#endif // hifi_TabletScriptingInterface_h
|
#endif // hifi_TabletScriptingInterface_h
|
||||||
|
|
|
@ -143,10 +143,17 @@ static inline bool cpuSupportsAVX2() {
|
||||||
bool result = false;
|
bool result = false;
|
||||||
if (cpuSupportsAVX()) {
|
if (cpuSupportsAVX()) {
|
||||||
|
|
||||||
if (__get_cpuid(0x7, &eax, &ebx, &ecx, &edx) && ((ebx & MASK_AVX2) == MASK_AVX2)) {
|
// Work around a bug where __get_cpuid(0x7) returns wrong values on older GCC
|
||||||
|
// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=77756
|
||||||
|
if (__get_cpuid(0x0, &eax, &ebx, &ecx, &edx) && (eax >= 0x7)) {
|
||||||
|
|
||||||
|
__cpuid_count(0x7, 0x0, eax, ebx, ecx, edx);
|
||||||
|
|
||||||
|
if ((ebx & MASK_AVX2) == MASK_AVX2) {
|
||||||
result = true;
|
result = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -853,7 +853,7 @@ function MyController(hand) {
|
||||||
};
|
};
|
||||||
|
|
||||||
this.setState = function(newState, reason) {
|
this.setState = function(newState, reason) {
|
||||||
if (isInEditMode() && (newState !== STATE_OFF &&
|
if ((isInEditMode() && this.grabbedEntity !== HMD.tabletID )&& (newState !== STATE_OFF &&
|
||||||
newState !== STATE_SEARCHING &&
|
newState !== STATE_SEARCHING &&
|
||||||
newState !== STATE_OVERLAY_STYLUS_TOUCHING)) {
|
newState !== STATE_OVERLAY_STYLUS_TOUCHING)) {
|
||||||
return;
|
return;
|
||||||
|
@ -1703,7 +1703,7 @@ function MyController(hand) {
|
||||||
};
|
};
|
||||||
|
|
||||||
this.isTablet = function (entityID) {
|
this.isTablet = function (entityID) {
|
||||||
if (entityID === HMD.tabletID) { // XXX what's a better way to know this?
|
if (entityID === HMD.tabletID) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
|
241
scripts/system/html/users.html
Normal file
241
scripts/system/html/users.html
Normal file
|
@ -0,0 +1,241 @@
|
||||||
|
<!--
|
||||||
|
// users.html
|
||||||
|
//
|
||||||
|
// Created by Faye Li on 18 Jan 2017
|
||||||
|
// Copyright 2017 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
-->
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>Users Online</title>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<link href="https://fonts.googleapis.com/css?family=Raleway:300,400,600,700"" rel="stylesheet">
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
width: 100%;
|
||||||
|
font-family: 'Raleway', sans-serif;
|
||||||
|
color: white;
|
||||||
|
background: linear-gradient(#2b2b2b, #0f212e);
|
||||||
|
}
|
||||||
|
|
||||||
|
.top-bar {
|
||||||
|
width: 100%;
|
||||||
|
height: 90px;
|
||||||
|
background: linear-gradient(#2b2b2b, #1e1e1e);
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.top-bar .container {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
margin-left: 30px;
|
||||||
|
margin-right: 30px;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
#refresh-button {
|
||||||
|
width: 24px;
|
||||||
|
height: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.main {
|
||||||
|
padding: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tabs {
|
||||||
|
list-style: none;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tabs li {
|
||||||
|
display: inline-block;
|
||||||
|
padding: 10px 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tabs li.current {
|
||||||
|
background: rgba(255,255,255,0.15);
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-content {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-content.current {
|
||||||
|
display: inherit;
|
||||||
|
background: rgba(255,255,255,0.15);
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-content ul {
|
||||||
|
list-style: none;
|
||||||
|
padding: 15px 0px 15px 15px;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-content ul li {
|
||||||
|
padding: 2px 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type=button] {
|
||||||
|
font-family: 'Raleway';
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 13px;
|
||||||
|
text-transform: uppercase;
|
||||||
|
vertical-align: top;
|
||||||
|
height: 28px;
|
||||||
|
min-width: 120px;
|
||||||
|
padding: 0px 18px;
|
||||||
|
margin-right: 6px;
|
||||||
|
border-radius: 5px;
|
||||||
|
border: none;
|
||||||
|
color: #fff;
|
||||||
|
background-color: #000;
|
||||||
|
background: linear-gradient(#343434 20%, #000 100%);
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type=button].blue {
|
||||||
|
color: #fff;
|
||||||
|
background-color: #1080b8;
|
||||||
|
background: linear-gradient(#00b4ef 20%, #1080b8 100%);
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type=button].blue:hover {
|
||||||
|
background: linear-gradient(#00b4ef, #00b4ef);
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type=button].blue:active {
|
||||||
|
background: linear-gradient(#1080b8, #1080b8);
|
||||||
|
}
|
||||||
|
|
||||||
|
#friends-button {
|
||||||
|
margin: 0px 0px 15px 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="top-bar">
|
||||||
|
<div class="container">
|
||||||
|
<div>Users Online</div>
|
||||||
|
<img id="refresh-button" onclick="pollUsers()" src="https://hifi-content.s3.amazonaws.com/faye/tablet-dev/refresh-icon.svg"></img>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="main">
|
||||||
|
<ul class="tabs">
|
||||||
|
<li tab-id="tab-1" class="current">Everyone (0)</li>
|
||||||
|
<li tab-id="tab-2">Friends (0)</li>
|
||||||
|
</ul>
|
||||||
|
<div id="tab-1" class="tab-content current">
|
||||||
|
<ul></ul>
|
||||||
|
</div>
|
||||||
|
<div id="tab-2" class="tab-content">
|
||||||
|
<ul></ul>
|
||||||
|
<input type="button" class="blue" id="friends-button" value="Add/Remove Friends">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
|
||||||
|
<script>
|
||||||
|
var METAVERSE_API_URL = "https://metaverse.highfidelity.com/api/v1/users?status=online";
|
||||||
|
var FRIENDS_FILTER = "&filter=friends";
|
||||||
|
var myUsername = null;
|
||||||
|
|
||||||
|
function displayUsers(data, element) {
|
||||||
|
element.empty();
|
||||||
|
for (var i = 0; i < data.users.length; i++) {
|
||||||
|
// Don't display users who aren't in a domain
|
||||||
|
if (typeof data.users[i].location.root.name === "undefined") {
|
||||||
|
console.log(data.users[i].username + "is online but not in a domain");
|
||||||
|
$("#dev-div").append("<p>" + data.users[i].username + "is online but not in a domain</p>");
|
||||||
|
} else {
|
||||||
|
$("#dev-div").append("<li>" + data.users[i].username + " @ " + data.users[i].location.root.name + "</li>");
|
||||||
|
// Don't display yourself
|
||||||
|
if (data.users[i].username !== myUsername) {
|
||||||
|
console.log(data.users[i].username + " @ " + data.users[i].location.root.name);
|
||||||
|
element.append("<li>" + data.users[i].username + " @ " + data.users[i].location.root.name + "</li>");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function processData(data, type) {
|
||||||
|
var num = data.users.length;
|
||||||
|
if (type === "everyone") {
|
||||||
|
$(".tabs li:nth-child(1)").text("Everyone (" + num + ")");
|
||||||
|
displayUsers(data, $("#tab-1 ul"));
|
||||||
|
} else if (type === "friends") {
|
||||||
|
$(".tabs li:nth-child(2)").text("Friends (" + num + ")");
|
||||||
|
displayUsers(data, $("#tab-2 ul"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function pollUsers() {
|
||||||
|
$("#dev-div").append("<p>polling users..</p>");
|
||||||
|
$.ajax({
|
||||||
|
url: METAVERSE_API_URL,
|
||||||
|
success: function(response) {
|
||||||
|
console.log(response);
|
||||||
|
$("#dev-div").append("<p>polling everyone sucess</p>");
|
||||||
|
processData(response.data, "everyone");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
$.ajax({
|
||||||
|
url: METAVERSE_API_URL + FRIENDS_FILTER,
|
||||||
|
success: function(response) {
|
||||||
|
console.log(response);
|
||||||
|
$("#dev-div").append("<p>polling friends sucess</p>");
|
||||||
|
processData(response.data, "friends");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function onScriptEventReceived(event) {
|
||||||
|
$("#dev-div").append("<p>Received a script event, its type is " + typeof event + "</p>");
|
||||||
|
if (typeof event === "string") {
|
||||||
|
// Parse the string into an object
|
||||||
|
event = JSON.parse(event);
|
||||||
|
}
|
||||||
|
if (event.type === "sendUsername") {
|
||||||
|
myUsername = event.data.username;
|
||||||
|
$("#dev-div").append("<p>myUsername is " + myUsername + "</p>");
|
||||||
|
consoloe.log("myUsername is " + myUsername);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$(document).ready(function() {
|
||||||
|
$("#dev-div").append("<p>ready</p>");
|
||||||
|
// Auto-load user lists when page loads
|
||||||
|
pollUsers();
|
||||||
|
|
||||||
|
// Click listener for tabs
|
||||||
|
$(".tabs li").click(function() {
|
||||||
|
var tabID = $(this).attr("tab-id");
|
||||||
|
$(".tab-content").removeClass("current");
|
||||||
|
$("#" + tabID).addClass("current");
|
||||||
|
$(".tabs li").removeClass("current");
|
||||||
|
$(this).addClass("current");
|
||||||
|
});
|
||||||
|
|
||||||
|
// Listen for events from hifi
|
||||||
|
EventBridge.scriptEventReceived.connect(onScriptEventReceived);
|
||||||
|
|
||||||
|
// Send a ready event to hifi
|
||||||
|
var eventObject = {"type": "ready"};
|
||||||
|
EventBridge.emitWebEvent(JSON.stringify(eventObject));
|
||||||
|
|
||||||
|
// Click listener mangage friends button
|
||||||
|
$("#friends-button").click(function() {
|
||||||
|
// Send a manage friends event to hifi
|
||||||
|
eventObject = {"type": "manage-friends"};
|
||||||
|
EventBridge.emitWebEvent(JSON.stringify(eventObject));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -24,6 +24,7 @@ var CAMERA_MATRIX = -7;
|
||||||
var ROT_Y_180 = {x: 0, y: 1, z: 0, w: 0};
|
var ROT_Y_180 = {x: 0, y: 1, z: 0, w: 0};
|
||||||
var TABLET_TEXTURE_RESOLUTION = { x: 480, y: 706 };
|
var TABLET_TEXTURE_RESOLUTION = { x: 480, y: 706 };
|
||||||
var INCHES_TO_METERS = 1 / 39.3701;
|
var INCHES_TO_METERS = 1 / 39.3701;
|
||||||
|
var NO_HANDS = -1;
|
||||||
|
|
||||||
var TABLET_URL = Script.resourcesPath() + "meshes/tablet-with-home-button.fbx";
|
var TABLET_URL = Script.resourcesPath() + "meshes/tablet-with-home-button.fbx";
|
||||||
|
|
||||||
|
@ -35,18 +36,21 @@ var TABLET_MODEL_PATH = "http://hifi-content.s3.amazonaws.com/alan/dev/tablet-wi
|
||||||
// * position - position in front of the user
|
// * position - position in front of the user
|
||||||
// * rotation - rotation of entity so it faces the user.
|
// * rotation - rotation of entity so it faces the user.
|
||||||
function calcSpawnInfo(hand, height) {
|
function calcSpawnInfo(hand, height) {
|
||||||
var noHands = -1;
|
|
||||||
var finalPosition;
|
var finalPosition;
|
||||||
if (HMD.active && hand !== noHands) {
|
|
||||||
|
var headPos = (HMD.active && Camera.mode === "first person") ? HMD.position : Camera.position;
|
||||||
|
var headRot = (HMD.active && Camera.mode === "first person") ? HMD.orientation : Camera.orientation;
|
||||||
|
|
||||||
|
if (HMD.active && hand !== NO_HANDS) {
|
||||||
var handController = getControllerWorldLocation(hand, true);
|
var handController = getControllerWorldLocation(hand, true);
|
||||||
var controllerPosition = handController.position;
|
var controllerPosition = handController.position;
|
||||||
|
|
||||||
// compute the angle of the chord with length (height / 2)
|
// compute the angle of the chord with length (height / 2)
|
||||||
var theta = Math.asin(height / (2 * Vec3.distance(HMD.position, controllerPosition)));
|
var theta = Math.asin(height / (2 * Vec3.distance(headPos, controllerPosition)));
|
||||||
|
|
||||||
// then we can use this angle to rotate the vector between the HMD position and the center of the tablet.
|
// then we can use this angle to rotate the vector between the HMD position and the center of the tablet.
|
||||||
// this vector, u, will become our new look at direction.
|
// this vector, u, will become our new look at direction.
|
||||||
var d = Vec3.normalize(Vec3.subtract(HMD.position, controllerPosition));
|
var d = Vec3.normalize(Vec3.subtract(headPos, controllerPosition));
|
||||||
var w = Vec3.normalize(Vec3.cross(Y_AXIS, d));
|
var w = Vec3.normalize(Vec3.cross(Y_AXIS, d));
|
||||||
var q = Quat.angleAxis(theta * (180 / Math.PI), w);
|
var q = Quat.angleAxis(theta * (180 / Math.PI), w);
|
||||||
var u = Vec3.multiplyQbyV(q, d);
|
var u = Vec3.multiplyQbyV(q, d);
|
||||||
|
@ -64,8 +68,8 @@ function calcSpawnInfo(hand, height) {
|
||||||
rotation: lookAtRot
|
rotation: lookAtRot
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
var front = Quat.getFront(Camera.orientation);
|
var front = Quat.getFront(headRot);
|
||||||
finalPosition = Vec3.sum(Camera.position, Vec3.multiply(0.6, front));
|
finalPosition = Vec3.sum(headPos, Vec3.multiply(0.6, front));
|
||||||
var orientation = Quat.lookAt({x: 0, y: 0, z: 0}, front, {x: 0, y: 1, z: 0});
|
var orientation = Quat.lookAt({x: 0, y: 0, z: 0}, front, {x: 0, y: 1, z: 0});
|
||||||
return {
|
return {
|
||||||
position: finalPosition,
|
position: finalPosition,
|
||||||
|
@ -198,6 +202,11 @@ WebTablet = function (url, width, dpi, hand, clientOnly) {
|
||||||
_this.geometryChanged(geometry);
|
_this.geometryChanged(geometry);
|
||||||
};
|
};
|
||||||
Window.geometryChanged.connect(this.myGeometryChanged);
|
Window.geometryChanged.connect(this.myGeometryChanged);
|
||||||
|
|
||||||
|
this.myCameraModeChanged = function(newMode) {
|
||||||
|
_this.cameraModeChanged(newMode);
|
||||||
|
};
|
||||||
|
Camera.modeUpdated.connect(this.myCameraModeChanged);
|
||||||
};
|
};
|
||||||
|
|
||||||
WebTablet.prototype.setHomeButtonTexture = function() {
|
WebTablet.prototype.setHomeButtonTexture = function() {
|
||||||
|
@ -228,11 +237,11 @@ WebTablet.prototype.destroy = function () {
|
||||||
Controller.mouseReleaseEvent.disconnect(this.myMouseReleaseEvent);
|
Controller.mouseReleaseEvent.disconnect(this.myMouseReleaseEvent);
|
||||||
|
|
||||||
Window.geometryChanged.disconnect(this.myGeometryChanged);
|
Window.geometryChanged.disconnect(this.myGeometryChanged);
|
||||||
|
Camera.modeUpdated.disconnect(this.myCameraModeChanged);
|
||||||
};
|
};
|
||||||
|
|
||||||
WebTablet.prototype.geometryChanged = function (geometry) {
|
WebTablet.prototype.geometryChanged = function (geometry) {
|
||||||
if (!HMD.active) {
|
if (!HMD.active) {
|
||||||
var NO_HANDS = -1;
|
|
||||||
var tabletProperties = {};
|
var tabletProperties = {};
|
||||||
// compute position, rotation & parentJointIndex of the tablet
|
// compute position, rotation & parentJointIndex of the tablet
|
||||||
this.calculateTabletAttachmentProperties(NO_HANDS, tabletProperties);
|
this.calculateTabletAttachmentProperties(NO_HANDS, tabletProperties);
|
||||||
|
@ -288,7 +297,6 @@ WebTablet.prototype.onHmdChanged = function () {
|
||||||
Controller.mouseReleaseEvent.connect(this.myMouseReleaseEvent);
|
Controller.mouseReleaseEvent.connect(this.myMouseReleaseEvent);
|
||||||
}
|
}
|
||||||
|
|
||||||
var NO_HANDS = -1;
|
|
||||||
var tabletProperties = {};
|
var tabletProperties = {};
|
||||||
// compute position, rotation & parentJointIndex of the tablet
|
// compute position, rotation & parentJointIndex of the tablet
|
||||||
this.calculateTabletAttachmentProperties(NO_HANDS, tabletProperties);
|
this.calculateTabletAttachmentProperties(NO_HANDS, tabletProperties);
|
||||||
|
@ -370,6 +378,18 @@ WebTablet.prototype.mousePressEvent = function (event) {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
WebTablet.prototype.cameraModeChanged = function (newMode) {
|
||||||
|
// reposition the tablet.
|
||||||
|
// This allows HMD.position to reflect the new camera mode.
|
||||||
|
if (HMD.active) {
|
||||||
|
var self = this;
|
||||||
|
var tabletProperties = {};
|
||||||
|
// compute position, rotation & parentJointIndex of the tablet
|
||||||
|
self.calculateTabletAttachmentProperties(NO_HANDS, tabletProperties);
|
||||||
|
Entities.editEntity(self.tabletEntityID, tabletProperties);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
function rayIntersectPlane(planePosition, planeNormal, rayStart, rayDirection) {
|
function rayIntersectPlane(planePosition, planeNormal, rayStart, rayDirection) {
|
||||||
var rayDirectionDotPlaneNormal = Vec3.dot(rayDirection, planeNormal);
|
var rayDirectionDotPlaneNormal = Vec3.dot(rayDirection, planeNormal);
|
||||||
if (rayDirectionDotPlaneNormal > 0.00001 || rayDirectionDotPlaneNormal < -0.00001) {
|
if (rayDirectionDotPlaneNormal > 0.00001 || rayDirectionDotPlaneNormal < -0.00001) {
|
||||||
|
|
|
@ -1038,7 +1038,7 @@ SelectionDisplay = (function() {
|
||||||
if (entityIntersection.intersects &&
|
if (entityIntersection.intersects &&
|
||||||
(!overlayIntersection.intersects || (entityIntersection.distance < overlayIntersection.distance))) {
|
(!overlayIntersection.intersects || (entityIntersection.distance < overlayIntersection.distance))) {
|
||||||
|
|
||||||
if (HMD.tabletID == entityIntersection.entityID) {
|
if (HMD.tabletID === entityIntersection.entityID) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
67
scripts/system/tablet-users.js
Normal file
67
scripts/system/tablet-users.js
Normal file
|
@ -0,0 +1,67 @@
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
//
|
||||||
|
// users.js
|
||||||
|
//
|
||||||
|
// Created by Faye Li on 18 Jan 2017.
|
||||||
|
// Copyright 2017 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
|
||||||
|
(function() { // BEGIN LOCAL_SCOPE
|
||||||
|
var USERS_URL = "https://hifi-content.s3.amazonaws.com/faye/tablet-dev/users.html";
|
||||||
|
var FRIENDS_WINDOW_URL = "https://metaverse.highfidelity.com/user/friends";
|
||||||
|
var FRIENDS_WINDOW_WIDTH = 290;
|
||||||
|
var FRIENDS_WINDOW_HEIGHT = 500;
|
||||||
|
var FRIENDS_WINDOW_TITLE = "Add/Remove Friends";
|
||||||
|
var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system");
|
||||||
|
var button = tablet.addButton({
|
||||||
|
icon: "icons/tablet-icons/people-i.svg",
|
||||||
|
text: "Users"
|
||||||
|
});
|
||||||
|
|
||||||
|
function onClicked() {
|
||||||
|
tablet.gotoWebScreen(USERS_URL);
|
||||||
|
}
|
||||||
|
|
||||||
|
function onWebEventReceived(event) {
|
||||||
|
print("Script received a web event, its type is " + typeof event);
|
||||||
|
if (typeof event === "string") {
|
||||||
|
event = JSON.parse(event);
|
||||||
|
}
|
||||||
|
if (event.type === "ready") {
|
||||||
|
// send username to html
|
||||||
|
var myUsername = GlobalServices.username;
|
||||||
|
var object = {
|
||||||
|
"type": "sendUsername",
|
||||||
|
"data": {"username": myUsername}
|
||||||
|
};
|
||||||
|
print("sending username: " + myUsername);
|
||||||
|
tablet.emitScriptEvent(JSON.stringify(object));
|
||||||
|
}
|
||||||
|
if (event.type === "manage-friends") {
|
||||||
|
// open a web overlay to metaverse friends page
|
||||||
|
var friendsWindow = new OverlayWebWindow({
|
||||||
|
title: FRIENDS_WINDOW_TITLE,
|
||||||
|
width: FRIENDS_WINDOW_WIDTH,
|
||||||
|
height: FRIENDS_WINDOW_HEIGHT,
|
||||||
|
visible: false
|
||||||
|
});
|
||||||
|
friendsWindow.setURL(FRIENDS_WINDOW_URL);
|
||||||
|
friendsWindow.setVisible(true);
|
||||||
|
friendsWindow.raise();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
button.clicked.connect(onClicked);
|
||||||
|
tablet.webEventReceived.connect(onWebEventReceived);
|
||||||
|
|
||||||
|
function cleanup() {
|
||||||
|
button.clicked.disconnect(onClicked);
|
||||||
|
tablet.removeButton(button);
|
||||||
|
}
|
||||||
|
|
||||||
|
Script.scriptEnding.connect(cleanup);
|
||||||
|
}()); // END LOCAL_SCOPE
|
21
unpublishedScripts/marketplace/teaLight/teaLight.js
Normal file
21
unpublishedScripts/marketplace/teaLight/teaLight.js
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
(function() {
|
||||||
|
var MINIMUM_LIGHT_INTENSITY = 100.0;
|
||||||
|
var MAXIMUM_LIGHT_INTENSITY = 125.0;
|
||||||
|
|
||||||
|
// Return a random number between `low` (inclusive) and `high` (exclusive)
|
||||||
|
function randFloat(low, high) {
|
||||||
|
return low + Math.random() * (high - low);
|
||||||
|
}
|
||||||
|
|
||||||
|
var self = this;
|
||||||
|
this.preload = function(entityID) {
|
||||||
|
self.intervalID = Script.setInterval(function() {
|
||||||
|
Entities.editEntity(entityID, {
|
||||||
|
intensity: randFloat(MINIMUM_LIGHT_INTENSITY, MAXIMUM_LIGHT_INTENSITY)
|
||||||
|
});
|
||||||
|
}, 100);
|
||||||
|
};
|
||||||
|
this.unload = function() {
|
||||||
|
Script.clearInterval(self.intervalID);
|
||||||
|
}
|
||||||
|
});
|
Loading…
Reference in a new issue