mirror of
https://github.com/overte-org/overte.git
synced 2025-04-19 12:23:24 +02:00
Merge branch 'master' into hazeZone
This commit is contained in:
commit
349e12c065
41 changed files with 2835 additions and 645 deletions
11
cmake/externals/draco/CMakeLists.txt
vendored
11
cmake/externals/draco/CMakeLists.txt
vendored
|
@ -13,7 +13,7 @@ ExternalProject_Add(
|
|||
${EXTERNAL_NAME}
|
||||
URL http://hifi-public.s3.amazonaws.com/dependencies/draco-1.1.0.zip
|
||||
URL_MD5 208f8b04c91d5f1c73d731a3ea37c5bb
|
||||
CONFIGURE_COMMAND CMAKE_ARGS ${ANDROID_CMAKE_ARGS} -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} -DCMAKE_INSTALL_PREFIX:PATH=<INSTALL_DIR> ${EXTRA_CMAKE_FLAGS}
|
||||
CONFIGURE_COMMAND CMAKE_ARGS ${ANDROID_CMAKE_ARGS} -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} -DCMAKE_INSTALL_PREFIX:PATH=<INSTALL_DIR>-$<CONFIG> ${EXTRA_CMAKE_FLAGS}
|
||||
LOG_DOWNLOAD 1
|
||||
LOG_CONFIGURE 1
|
||||
LOG_BUILD 1
|
||||
|
@ -23,10 +23,11 @@ ExternalProject_Add(
|
|||
set_target_properties(${EXTERNAL_NAME} PROPERTIES FOLDER "hidden/externals")
|
||||
|
||||
ExternalProject_Get_Property(${EXTERNAL_NAME} INSTALL_DIR)
|
||||
set(SUFFIXED_INSTALL_DIR "${INSTALL_DIR}-$<CONFIG>")
|
||||
|
||||
string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER)
|
||||
|
||||
set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIRS ${INSTALL_DIR}/include CACHE PATH "List of Draco include directories")
|
||||
set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIRS ${SUFFIXED_INSTALL_DIR}/include CACHE PATH "List of Draco include directories")
|
||||
|
||||
if (UNIX)
|
||||
set(LIB_PREFIX "lib")
|
||||
|
@ -35,6 +36,6 @@ elseif (WIN32)
|
|||
set(LIB_EXT "lib")
|
||||
endif ()
|
||||
|
||||
set(${EXTERNAL_NAME_UPPER}_LIBRARY ${INSTALL_DIR}/lib/${LIB_PREFIX}draco.${LIB_EXT} CACHE FILEPATH "Path to Draco release library")
|
||||
set(${EXTERNAL_NAME_UPPER}_ENCODER_LIBRARY ${INSTALL_DIR}/lib/${LIB_PREFIX}dracoenc.${LIB_EXT} CACHE FILEPATH "Path to Draco encoder release library")
|
||||
set(${EXTERNAL_NAME_UPPER}_DECODER_LIBRARY ${INSTALL_DIR}/lib/${LIB_PREFIX}dracodec.${LIB_EXT} CACHE FILEPATH "Path to Draco decoder release library")
|
||||
set(${EXTERNAL_NAME_UPPER}_LIBRARY ${SUFFIXED_INSTALL_DIR}/lib/${LIB_PREFIX}draco.${LIB_EXT} CACHE FILEPATH "Path to Draco release library")
|
||||
set(${EXTERNAL_NAME_UPPER}_ENCODER_LIBRARY ${SUFFIXED_INSTALL_DIR}/lib/${LIB_PREFIX}dracoenc.${LIB_EXT} CACHE FILEPATH "Path to Draco encoder release library")
|
||||
set(${EXTERNAL_NAME_UPPER}_DECODER_LIBRARY ${SUFFIXED_INSTALL_DIR}/lib/${LIB_PREFIX}dracodec.${LIB_EXT} CACHE FILEPATH "Path to Draco decoder release library")
|
||||
|
|
|
@ -1,6 +1,12 @@
|
|||
{
|
||||
"version": 1.9,
|
||||
"version": 2.0,
|
||||
"settings": [
|
||||
{
|
||||
"name": "label",
|
||||
"label": "Label",
|
||||
"settings": [
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "metaverse",
|
||||
"label": "Metaverse / Networking",
|
||||
|
@ -14,7 +20,8 @@
|
|||
{
|
||||
"name": "id",
|
||||
"label": "Domain ID",
|
||||
"help": "This is your High Fidelity domain ID. If you do not want your domain to be registered in the High Fidelity metaverse you can leave this blank."
|
||||
"help": "This is your High Fidelity domain ID. If you do not want your domain to be registered in the High Fidelity metaverse you can leave this blank.",
|
||||
"advanced": true
|
||||
},
|
||||
{
|
||||
"name": "automatic_networking",
|
||||
|
@ -82,11 +89,13 @@
|
|||
{
|
||||
"name": "description",
|
||||
"label": "Description",
|
||||
"advanced": true,
|
||||
"help": "A description of your domain (256 character limit)."
|
||||
},
|
||||
{
|
||||
"name": "maturity",
|
||||
"label": "Maturity",
|
||||
"advanced": true,
|
||||
"help": "A maturity rating, available as a guideline for content on your domain.",
|
||||
"default": "unrated",
|
||||
"type": "select",
|
||||
|
@ -116,6 +125,7 @@
|
|||
{
|
||||
"name": "hosts",
|
||||
"label": "Hosts",
|
||||
"advanced": true,
|
||||
"type": "table",
|
||||
"can_add_new_rows": true,
|
||||
"help": "Usernames of hosts who can reliably show your domain to new visitors.",
|
||||
|
@ -131,6 +141,7 @@
|
|||
{
|
||||
"name": "tags",
|
||||
"label": "Tags",
|
||||
"advanced": true,
|
||||
"type": "table",
|
||||
"can_add_new_rows": true,
|
||||
"help": "Common categories under which your domain falls.",
|
||||
|
@ -1625,6 +1636,29 @@
|
|||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "wizard",
|
||||
"label": "Setup Wizard",
|
||||
"restart": false,
|
||||
"hidden": true,
|
||||
"settings": [
|
||||
{
|
||||
"name": "cloud_domain",
|
||||
"type": "checkbox",
|
||||
"default": false
|
||||
},
|
||||
{
|
||||
"name": "steps_completed",
|
||||
"type": "int",
|
||||
"default": 0
|
||||
},
|
||||
{
|
||||
"name": "completed_once",
|
||||
"type": "checkbox",
|
||||
"default": false
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
body {
|
||||
position: relative;
|
||||
padding-bottom: 30px;
|
||||
margin-top: 70px;
|
||||
}
|
||||
|
||||
[hidden] {
|
||||
|
@ -27,14 +28,14 @@ body {
|
|||
.table .value-row td,
|
||||
.table .value-category td,
|
||||
.table .inputs td {
|
||||
vertical-align: middle;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.table .table-checkbox {
|
||||
/* Fix IE sizing checkboxes to fill table cell */
|
||||
width: auto;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
/* Fix IE sizing checkboxes to fill table cell */
|
||||
width: auto;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
.value-category:not(.inputs) {
|
||||
|
@ -80,8 +81,10 @@ span.port {
|
|||
}
|
||||
|
||||
#setup-sidebar.affix {
|
||||
position: fixed;
|
||||
top: 15px;
|
||||
/* This overrides a case where going to the bottom of the page,
|
||||
* then scrolling up, causes `position: relative` to be added to the style
|
||||
*/
|
||||
position: fixed !important;
|
||||
}
|
||||
|
||||
#setup-sidebar button {
|
||||
|
@ -145,55 +148,55 @@ table {
|
|||
}
|
||||
|
||||
caption {
|
||||
color: #333;
|
||||
font-weight: 700;
|
||||
padding-top: 0;
|
||||
color: #333;
|
||||
font-weight: 700;
|
||||
padding-top: 0;
|
||||
}
|
||||
|
||||
table > tbody > .headers > td {
|
||||
vertical-align: middle;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
table .headers + .headers td {
|
||||
font-size: 13px;
|
||||
color: #222;
|
||||
font-size: 13px;
|
||||
color: #222;
|
||||
}
|
||||
|
||||
#security table .headers td + td {
|
||||
text-align: center;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.tooltip.top .tooltip-arrow {
|
||||
border-top-color: #fff;
|
||||
border-width: 10px 10px 0;
|
||||
margin-bottom: -5px;
|
||||
border-top-color: #fff;
|
||||
border-width: 10px 10px 0;
|
||||
margin-bottom: -5px;
|
||||
}
|
||||
|
||||
.tooltip-inner {
|
||||
padding: 20px 20px 10px 20px;
|
||||
font-size: 14px;
|
||||
text-align: left;
|
||||
color: #333;
|
||||
background-color: #fff;
|
||||
box-shadow: 0 3px 8px 8px #e8e8e8;
|
||||
padding: 20px 20px 10px 20px;
|
||||
font-size: 14px;
|
||||
text-align: left;
|
||||
color: #333;
|
||||
background-color: #fff;
|
||||
box-shadow: 0 3px 8px 8px #e8e8e8;
|
||||
}
|
||||
|
||||
.tooltip.in {
|
||||
opacity: 1;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.tooltip-inner ul {
|
||||
padding-left: 0;
|
||||
margin-bottom: 15px;
|
||||
padding-left: 0;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.tooltip-inner li {
|
||||
list-style-type: none;
|
||||
margin-bottom: 5px;
|
||||
list-style-type: none;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
#security .tooltip-inner {
|
||||
max-width: 520px;
|
||||
max-width: 520px;
|
||||
}
|
||||
|
||||
#xs-advanced-container {
|
||||
|
@ -241,6 +244,20 @@ table .headers + .headers td {
|
|||
animation-delay: -0.16s;
|
||||
}
|
||||
|
||||
.col-centered {
|
||||
float: none;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.centered-hack-parent {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.centered-hack {
|
||||
text-align: left;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
@-webkit-keyframes bouncedelay {
|
||||
0%, 80%, 100% { -webkit-transform: scale(0.0) }
|
||||
40% { -webkit-transform: scale(1.0) }
|
||||
|
@ -255,3 +272,50 @@ table .headers + .headers td {
|
|||
-webkit-transform: scale(1.0);
|
||||
}
|
||||
}
|
||||
|
||||
/* From https://gist.github.com/alexandrevicenzi/680147013e902a4eaa5d */
|
||||
.glyphicon-refresh-animate {
|
||||
-animation: spin .7s infinite linear;
|
||||
-ms-animation: spin .7s infinite linear;
|
||||
-webkit-animation: spinw .7s infinite linear;
|
||||
-moz-animation: spinm .7s infinite linear;
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
from { transform: scale(1) rotate(0deg); }
|
||||
to { transform: scale(1) rotate(360deg); }
|
||||
}
|
||||
|
||||
@-webkit-keyframes spinw {
|
||||
from { -webkit-transform: rotate(0deg); }
|
||||
to { -webkit-transform: rotate(360deg); }
|
||||
}
|
||||
|
||||
@-moz-keyframes spinm {
|
||||
from { -moz-transform: rotate(0deg); }
|
||||
to { -moz-transform: rotate(360deg); }
|
||||
}
|
||||
|
||||
.warning-text {
|
||||
padding-top: 10px;
|
||||
color: #EB5757;
|
||||
}
|
||||
|
||||
.account-connected-header {
|
||||
color: #6FCF97;
|
||||
font-size: 30px;
|
||||
margin-right: 20px;
|
||||
}
|
||||
|
||||
#visit-domain-link,
|
||||
.blue-link {
|
||||
font-size: 14px;
|
||||
text-decoration-line: underline;
|
||||
font-weight: normal;
|
||||
color: #2F80ED;
|
||||
}
|
||||
|
||||
#manage-cloud-domains-link {
|
||||
text-align: center;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
|
BIN
domain-server/resources/web/favicon.ico
Normal file
BIN
domain-server/resources/web/favicon.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 5.3 KiB |
|
@ -13,7 +13,7 @@
|
|||
<script src='/js/sweetalert.min.js'></script>
|
||||
</head>
|
||||
<body>
|
||||
<nav class="navbar navbar-default" role="navigation">
|
||||
<nav class="navbar navbar-default navbar-fixed-top" role="navigation">
|
||||
<div class="container-fluid">
|
||||
<!-- Brand and toggle get grouped for better mobile display -->
|
||||
<div class="navbar-header">
|
||||
|
@ -23,7 +23,6 @@
|
|||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
</button>
|
||||
<a class="navbar-brand" href="/">domain-server</a>
|
||||
</div>
|
||||
|
||||
<!-- Collect the nav links, forms, and other content for toggling -->
|
||||
|
@ -40,6 +39,7 @@
|
|||
<li><a href="/settings/">Settings</a></li>
|
||||
</ul>
|
||||
<ul class="nav navbar-right navbar-nav">
|
||||
<li><a id="visit-domain-link" target="_blank" style="display: none;">Visit domain in VR</a></li>
|
||||
<li><a href="#" id="restart-server"><span class="glyphicon glyphicon-refresh"></span> Restart</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
|
25
domain-server/resources/web/images/checkmark.svg
Normal file
25
domain-server/resources/web/images/checkmark.svg
Normal file
|
@ -0,0 +1,25 @@
|
|||
<svg width="676" height="676" viewBox="0 0 676 676" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<title>CongratulationImage</title>
|
||||
<desc>Created using Figma</desc>
|
||||
<g id="Canvas" transform="matrix(4 0 0 4 -21208 -17980)">
|
||||
<g id="CongratulationImage">
|
||||
<g id="Ellipse">
|
||||
<use xlink:href="#path0_fill" transform="translate(5302 4495)" fill="#FFFFFF"/>
|
||||
<mask id="mask0_outline_ins">
|
||||
<use xlink:href="#path0_fill" fill="white" transform="translate(5302 4495)"/>
|
||||
</mask>
|
||||
<g mask="url(#mask0_outline_ins)">
|
||||
<use xlink:href="#path1_stroke_2x" transform="translate(5302 4495)" fill="#219653"/>
|
||||
</g>
|
||||
</g>
|
||||
<g id="Vector 2">
|
||||
<use xlink:href="#path2_stroke" transform="translate(5355 4559)" fill="#219653"/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<defs>
|
||||
<path id="path0_fill" d="M 169 84.5C 169 131.168 131.168 169 84.5 169C 37.8319 169 0 131.168 0 84.5C 0 37.8319 37.8319 0 84.5 0C 131.168 0 169 37.8319 169 84.5Z"/>
|
||||
<path id="path1_stroke_2x" d="M 154 84.5C 154 122.884 122.884 154 84.5 154L 84.5 184C 139.452 184 184 139.452 184 84.5L 154 84.5ZM 84.5 154C 46.1162 154 15 122.884 15 84.5L -15 84.5C -15 139.452 29.5477 184 84.5 184L 84.5 154ZM 15 84.5C 15 46.1162 46.1162 15 84.5 15L 84.5 -15C 29.5477 -15 -15 29.5477 -15 84.5L 15 84.5ZM 84.5 15C 122.884 15 154 46.1162 154 84.5L 184 84.5C 184 29.5477 139.452 -15 84.5 -15L 84.5 15Z"/>
|
||||
<path id="path2_stroke" d="M 5.18747 19.8031C 2.19593 16.9382 -2.5517 17.0408 -5.41666 20.0323C -8.28162 23.0238 -8.17901 27.7715 -5.18747 30.6364L 5.18747 19.8031ZM 20.6541 45L 15.4667 50.4167C 18.3816 53.2083 22.9831 53.1924 25.8787 50.3809L 20.6541 45ZM 72.2246 5.38085C 75.1964 2.49539 75.2663 -2.25283 72.3809 -5.2246C 69.4954 -8.19636 64.7472 -8.26632 61.7754 -5.38085L 72.2246 5.38085ZM -5.18747 30.6364L 15.4667 50.4167L 25.8416 39.5833L 5.18747 19.8031L -5.18747 30.6364ZM 25.8787 50.3809L 72.2246 5.38085L 61.7754 -5.38085L 15.4295 39.6191L 25.8787 50.3809Z"/>
|
||||
</defs>
|
||||
</svg>
|
After Width: | Height: | Size: 1.9 KiB |
|
@ -32,7 +32,6 @@ $(document).ready(function(){
|
|||
$('ul.nav a').filter(function() {
|
||||
return this.href == url;
|
||||
}).parent().addClass('active');
|
||||
|
||||
$('body').on('click', '#restart-server', function(e) {
|
||||
swal( {
|
||||
title: "Are you sure?",
|
||||
|
@ -46,4 +45,4 @@ $(document).ready(function(){
|
|||
});
|
||||
return false;
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
393
domain-server/resources/web/js/shared.js
Normal file
393
domain-server/resources/web/js/shared.js
Normal file
|
@ -0,0 +1,393 @@
|
|||
var Settings = {
|
||||
showAdvanced: false,
|
||||
ADVANCED_CLASS: 'advanced-setting',
|
||||
DEPRECATED_CLASS: 'deprecated-setting',
|
||||
TRIGGER_CHANGE_CLASS: 'trigger-change',
|
||||
DATA_ROW_CLASS: 'value-row',
|
||||
DATA_COL_CLASS: 'value-col',
|
||||
DATA_CATEGORY_CLASS: 'value-category',
|
||||
ADD_ROW_BUTTON_CLASS: 'add-row',
|
||||
ADD_ROW_SPAN_CLASSES: 'glyphicon glyphicon-plus add-row',
|
||||
DEL_ROW_BUTTON_CLASS: 'del-row',
|
||||
DEL_ROW_SPAN_CLASSES: 'glyphicon glyphicon-remove del-row',
|
||||
ADD_CATEGORY_BUTTON_CLASS: 'add-category',
|
||||
ADD_CATEGORY_SPAN_CLASSES: 'glyphicon glyphicon-plus add-category',
|
||||
TOGGLE_CATEGORY_COLUMN_CLASS: 'toggle-category',
|
||||
TOGGLE_CATEGORY_SPAN_CLASS: 'toggle-category-icon',
|
||||
TOGGLE_CATEGORY_SPAN_CLASSES: 'glyphicon toggle-category-icon',
|
||||
TOGGLE_CATEGORY_EXPANDED_CLASS: 'glyphicon-triangle-bottom',
|
||||
TOGGLE_CATEGORY_CONTRACTED_CLASS: 'glyphicon-triangle-right',
|
||||
DEL_CATEGORY_BUTTON_CLASS: 'del-category',
|
||||
DEL_CATEGORY_SPAN_CLASSES: 'glyphicon glyphicon-remove del-category',
|
||||
MOVE_UP_BUTTON_CLASS: 'move-up',
|
||||
MOVE_UP_SPAN_CLASSES: 'glyphicon glyphicon-chevron-up move-up',
|
||||
MOVE_DOWN_BUTTON_CLASS: 'move-down',
|
||||
MOVE_DOWN_SPAN_CLASSES: 'glyphicon glyphicon-chevron-down move-down',
|
||||
TABLE_BUTTONS_CLASS: 'buttons',
|
||||
ADD_DEL_BUTTONS_CLASS: 'add-del-buttons',
|
||||
ADD_DEL_BUTTONS_CLASSES: 'buttons add-del-buttons',
|
||||
REORDER_BUTTONS_CLASS: 'reorder-buttons',
|
||||
REORDER_BUTTONS_CLASSES: 'buttons reorder-buttons',
|
||||
NEW_ROW_CLASS: 'new-row',
|
||||
CONNECT_ACCOUNT_BTN_ID: 'connect-account-btn',
|
||||
DISCONNECT_ACCOUNT_BTN_ID: 'disconnect-account-btn',
|
||||
CREATE_DOMAIN_ID_BTN_ID: 'create-domain-btn',
|
||||
CHOOSE_DOMAIN_ID_BTN_ID: 'choose-domain-btn',
|
||||
GET_TEMPORARY_NAME_BTN_ID: 'get-temp-name-btn',
|
||||
DOMAIN_ID_SELECTOR: '[name="metaverse.id"]',
|
||||
ACCESS_TOKEN_SELECTOR: '[name="metaverse.access_token"]',
|
||||
PLACES_TABLE_ID: 'places-table',
|
||||
ADD_PLACE_BTN_ID: 'add-place-btn',
|
||||
FORM_ID: 'settings-form',
|
||||
INVALID_ROW_CLASS: 'invalid-input',
|
||||
DATA_ROW_INDEX: 'data-row-index'
|
||||
};
|
||||
|
||||
var URLs = {
|
||||
// STABLE METAVERSE_URL: https://metaverse.highfidelity.com
|
||||
// STAGING METAVERSE_URL: https://staging.highfidelity.com
|
||||
METAVERSE_URL: 'https://metaverse.highfidelity.com',
|
||||
PLACE_URL: 'https://hifi.place',
|
||||
};
|
||||
|
||||
var Strings = {
|
||||
LOADING_SETTINGS_ERROR: "There was a problem loading the domain settings.\nPlease refresh the page to try again.",
|
||||
|
||||
CHOOSE_DOMAIN_BUTTON: "Choose from my domains",
|
||||
CREATE_DOMAIN_BUTTON: "Create new domain ID",
|
||||
CREATE_DOMAIN_SUCCESS_JUST_CONNECTED: "We connnected your High Fidelity account and created a new domain ID for this machine.",
|
||||
CREATE_DOMAIN_SUCCESS: "We created a new domain ID for this machine.",
|
||||
|
||||
// When a place modification fails, they will be brought back to the previous
|
||||
// dialog with new path still set, allowing them to retry immediately, and without
|
||||
// having to type the new path in again.
|
||||
EDIT_PLACE_TITLE: "Modify Viewpoint or Path",
|
||||
EDIT_PLACE_ERROR: "Failed to update place path. Please try again.",
|
||||
EDIT_PLACE_CONFIRM_BUTTON: "Save",
|
||||
EDIT_PLACE_CONFIRM_BUTTON_PENDING: "Saving...",
|
||||
EDIT_PLACE_CANCEL_BUTTON: "Cancel",
|
||||
|
||||
REMOVE_PLACE_TITLE: "Are you sure you want to remove <strong>{{place}}</strong>?",
|
||||
REMOVE_PLACE_ERROR: "Failed to remove place. Please try again.",
|
||||
REMOVE_PLACE_DELETE_BUTTON: "Delete",
|
||||
REMOVE_PLACE_DELETE_BUTTON_PENDING: "Deleting...",
|
||||
REMOVE_PLACE_CANCEL_BUTTON: "Cancel",
|
||||
|
||||
ADD_PLACE_TITLE: "Choose a place",
|
||||
ADD_PLACE_MESSAGE: "Choose the High Fidelity place to point at this domain server.",
|
||||
ADD_PLACE_CONFIRM_BUTTON: "Choose place",
|
||||
ADD_PLACE_CONFIRM_BUTTON_PENDING: "Saving...",
|
||||
ADD_PLACE_CANCEL_BUTTON: "Cancel",
|
||||
ADD_PLACE_UNKNOWN_ERROR: "There was an error adding this place name.",
|
||||
|
||||
ADD_PLACE_NO_PLACES_MESSAGE: "<p>You do not have any places in your High Fidelity account."
|
||||
+ "<br/><br/>Go to your <a href='https://metaverse.highfidelity.com/user/places/new'>places page</a> to create a new one. Once your place is created re-open this dialog to select it.</p>",
|
||||
ADD_PLACE_NO_PLACES_BUTTON: "Create new place",
|
||||
ADD_PLACE_UNABLE_TO_LOAD_ERROR: "We were unable to load your place names. Please try again later.",
|
||||
ADD_PLACE_LOADING_DIALOG: "Loading your places...",
|
||||
|
||||
ADD_PLACE_NOT_CONNECTED_TITLE: "Access token required",
|
||||
ADD_PLACE_NOT_CONNECTED_MESSAGE: "You must have an access token to query your High Fidelity places.<br><br>Please follow the instructions on the settings page to add an access token.",
|
||||
};
|
||||
|
||||
var DOMAIN_ID_TYPE_NONE = 0;
|
||||
var DOMAIN_ID_TYPE_TEMP = 1;
|
||||
var DOMAIN_ID_TYPE_FULL = 2;
|
||||
var DOMAIN_ID_TYPE_UNKNOWN = 3;
|
||||
|
||||
function domainIDIsSet() {
|
||||
return Settings.data.values.metaverse.id.length > 0;
|
||||
}
|
||||
|
||||
function getCurrentDomainIDType() {
|
||||
if (!domainIDIsSet()) {
|
||||
return DOMAIN_ID_TYPE_NONE;
|
||||
}
|
||||
if (typeof DomainInfo === 'undefined') {
|
||||
return DOMAIN_ID_TYPE_UNKNOWN;
|
||||
}
|
||||
if (DomainInfo !== null) {
|
||||
if (DomainInfo.name !== undefined) {
|
||||
return DOMAIN_ID_TYPE_TEMP;
|
||||
}
|
||||
return DOMAIN_ID_TYPE_FULL;
|
||||
}
|
||||
return DOMAIN_ID_TYPE_UNKNOWN;
|
||||
}
|
||||
|
||||
function showLoadingDialog(msg) {
|
||||
var message = '<div class="text-center">';
|
||||
message += '<span class="glyphicon glyphicon-refresh glyphicon-refresh-animate"></span> ' + msg;
|
||||
message += '</div>';
|
||||
|
||||
return bootbox.dialog({
|
||||
message: message,
|
||||
closeButton: false
|
||||
});
|
||||
}
|
||||
|
||||
function sendUpdatePlaceRequest(id, path, domainID, clearDomainID, onSuccess, onError) {
|
||||
var data = {
|
||||
place_id: id,
|
||||
path: path
|
||||
};
|
||||
if (domainID) {
|
||||
data.domain_id = domainID;
|
||||
}
|
||||
if (clearDomainID) {
|
||||
data.domain_id = null;
|
||||
}
|
||||
$.ajax({
|
||||
url: '/api/places',
|
||||
type: 'PUT',
|
||||
data: data,
|
||||
success: onSuccess,
|
||||
error: onError
|
||||
});
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
});
|
||||
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();
|
||||
|
||||
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");
|
||||
});
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
} 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 {
|
||||
bootbox.alert(Strings.ADD_PLACE_UNABLE_TO_LOAD_ERROR);
|
||||
}
|
||||
},
|
||||
error: function() {
|
||||
bootbox.alert(Strings.ADD_PLACE_UNABLE_TO_LOAD_ERROR);
|
||||
},
|
||||
complete: function() {
|
||||
loadingDialog.modal('hide');
|
||||
}
|
||||
});
|
||||
|
||||
} else {
|
||||
bootbox.alert({
|
||||
title: Strings.ADD_PLACE_NOT_CONNECTED_TITLE,
|
||||
message: Strings.ADD_PLACE_NOT_CONNECTED_MESSAGE
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
function sendCreateDomainRequest(onSuccess, onError) {
|
||||
$.ajax({
|
||||
url: '/api/domains',
|
||||
dataType: 'json',
|
||||
type: 'POST',
|
||||
data: { label: "" },
|
||||
success: function(data) {
|
||||
onSuccess(data.domain.id);
|
||||
},
|
||||
error: onError
|
||||
});
|
||||
}
|
||||
|
||||
function waitForDomainServerRestart(callback) {
|
||||
function checkForDomainUp() {
|
||||
$.ajax('', {
|
||||
success: function() {
|
||||
callback();
|
||||
},
|
||||
error: function() {
|
||||
setTimeout(checkForDomainUp, 50);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
setTimeout(checkForDomainUp, 10);
|
||||
|
||||
}
|
||||
|
||||
function prepareAccessTokenPrompt(callback) {
|
||||
swal({
|
||||
title: "Connect Account",
|
||||
type: "input",
|
||||
text: "Paste your created access token here." +
|
||||
"</br></br>If you did not successfully create an access token click cancel below and attempt to connect your account again.</br></br>",
|
||||
showCancelButton: true,
|
||||
closeOnConfirm: false,
|
||||
html: true
|
||||
}, function(inputValue){
|
||||
if (inputValue === false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (inputValue === "") {
|
||||
swal.showInputError("Please paste your access token in the input field.")
|
||||
return false
|
||||
}
|
||||
|
||||
if (callback) {
|
||||
callback(inputValue);
|
||||
}
|
||||
|
||||
swal.close();
|
||||
});
|
||||
}
|
|
@ -9,16 +9,18 @@
|
|||
|
||||
<div class="row">
|
||||
<div class="col-md-3 col-sm-3" id="setup-sidebar-col">
|
||||
<div id="setup-sidebar" class="hidden-xs" data-spy="affix" data-offset-top="55" data-clampedwidth="#setup-sidebar-col">
|
||||
<div id="setup-sidebar" data-clampedwidth="#setup-sidebar-col">
|
||||
<script id="list-group-template" type="text/template">
|
||||
<% _.each(descriptions, function(group){ %>
|
||||
<% panelID = group.name ? group.name : group.html_id %>
|
||||
<li>
|
||||
<a href="#<%- panelID %>" class="list-group-item">
|
||||
<span class="badge"></span>
|
||||
<%- group.label %>
|
||||
</a>
|
||||
</li>
|
||||
<% if (!group.hidden) { %>
|
||||
<% panelID = group.name ? group.name : group.html_id %>
|
||||
<li>
|
||||
<a href="#<%- panelID %>" class="list-group-item">
|
||||
<span class="badge"></span>
|
||||
<%- group.label %>
|
||||
</a>
|
||||
</li>
|
||||
<% } %>
|
||||
<% }); %>
|
||||
</script>
|
||||
|
||||
|
@ -26,49 +28,63 @@
|
|||
</ul>
|
||||
|
||||
<button id="advanced-toggle-button" class="btn btn-info advanced-toggle">Show advanced</button>
|
||||
<button class="btn btn-success save-button">Save</button>
|
||||
<button class="btn btn-success save-button" disabled>Save</button>
|
||||
<div id="manage-cloud-domains-link" style="display: none;">
|
||||
<a href="https://highfidelity.com/user/cloud_domains" target="_blank" class="blue-link">Manage Cloud Hosted Domains</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-9 col-sm-9 col-xs-12">
|
||||
|
||||
<div id="xs-advanced-container" class="col-xs-12 hidden-sm hidden-md hidden-lg">
|
||||
<button id="advanced-toggle-button-xs" class="btn btn-info advanced-toggle">Show advanced</button>
|
||||
</div>
|
||||
|
||||
<div class="col-xs-12">
|
||||
|
||||
<div id="cloud-domains-alert" class="alert alert-info alert-dismissible" role="alert" style="display: none;">
|
||||
<button type="button" class="close" data-dismiss="alert" aria-label="Close"><span aria-hidden="true">×</span></button>
|
||||
<span class="alert-link">
|
||||
<a href="https://highfidelity.com/user/cloud_domains" target="_blank" class="blue-link">Visit Cloud Hosted Domains</a> to manage all your cloud domains
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<form id="settings-form" role="form">
|
||||
|
||||
<script id="panels-template" type="text/template">
|
||||
<% _.each(descriptions, function(group){ %>
|
||||
<% var settings = _.partition(group.settings, function(value, index) { return !value.deprecated })[0] %>
|
||||
<% split_settings = _.partition(settings, function(value, index) { return !value.advanced }) %>
|
||||
<% isAdvanced = _.isEmpty(split_settings[0]) %>
|
||||
<% if (isAdvanced) { %>
|
||||
<% $("a[href=#" + group.name + "]").addClass('advanced-setting').hide() %>
|
||||
<% } %>
|
||||
<% if (!group.hidden) { %>
|
||||
<% var settings = _.partition(group.settings, function(value, index) { return !value.deprecated })[0] %>
|
||||
<% split_settings = _.partition(settings, function(value, index) { return !value.advanced }) %>
|
||||
<% isAdvanced = _.isEmpty(split_settings[0]) && !_.isEmpty(split_settings[1]) %>
|
||||
<% if (isAdvanced) { %>
|
||||
<% $("a[href=#" + group.name + "]").addClass('advanced-setting').hide() %>
|
||||
<% } %>
|
||||
|
||||
<% isGrouped = !!group.name %>
|
||||
<% panelID = isGrouped ? group.name : group.html_id %>
|
||||
<% isGrouped = !!group.name %>
|
||||
<% panelID = isGrouped ? group.name : group.html_id %>
|
||||
|
||||
<div class="panel panel-default<%- (isAdvanced) ? ' advanced-setting' : '' %><%- (isGrouped) ? ' grouped' : '' %>"
|
||||
id="<%- panelID %>">
|
||||
<div class="panel-heading">
|
||||
<h3 class="panel-title"><%- group.label %></h3>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<% _.each(split_settings[0], function(setting) { %>
|
||||
<% keypath = isGrouped ? group.name + "." + setting.name : setting.name %>
|
||||
<%= getFormGroup(keypath, setting, values, false) %>
|
||||
<% }); %>
|
||||
<% if (!_.isEmpty(split_settings[1])) { %>
|
||||
<% $("#advanced-toggle-button").show() %>
|
||||
<% _.each(split_settings[1], function(setting) { %>
|
||||
<div class="panel panel-default<%- (isAdvanced) ? ' advanced-setting' : '' %><%- (isGrouped) ? ' grouped' : '' %>"
|
||||
id="<%- panelID %>">
|
||||
<div class="panel-heading">
|
||||
<h3 class="panel-title"><%- group.label %></h3>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<% _.each(split_settings[0], function(setting) { %>
|
||||
<% keypath = isGrouped ? group.name + "." + setting.name : setting.name %>
|
||||
<%= getFormGroup(keypath, setting, values, true) %>
|
||||
<%= getFormGroup(keypath, setting, values, false) %>
|
||||
<% }); %>
|
||||
<% }%>
|
||||
<% if (!_.isEmpty(split_settings[1])) { %>
|
||||
<% $("#advanced-toggle-button").show() %>
|
||||
<% _.each(split_settings[1], function(setting) { %>
|
||||
<% keypath = isGrouped ? group.name + "." + setting.name : setting.name %>
|
||||
<%= getFormGroup(keypath, setting, values, true) %>
|
||||
<% }); %>
|
||||
<% }%>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<% } %>
|
||||
<% }); %>
|
||||
</script>
|
||||
<div id="panels"></div>
|
||||
|
@ -85,8 +101,9 @@
|
|||
<script src='/js/underscore-min.js'></script>
|
||||
<script src='/js/underscore-keypath.min.js'></script>
|
||||
<script src='/js/bootbox.min.js'></script>
|
||||
<script src='js/bootstrap-switch.min.js'></script>
|
||||
<script src='js/settings.js'></script>
|
||||
<script src='/js/sha256.js'></script>
|
||||
<script src='js/form2js.min.js'></script>
|
||||
<script src='js/sha256.js'></script>
|
||||
<script src='js/bootstrap-switch.min.js'></script>
|
||||
<script src='/js/shared.js'></script>
|
||||
<script src='js/settings.js'></script>
|
||||
<!--#include virtual="page-end.html"-->
|
||||
|
|
File diff suppressed because it is too large
Load diff
89
domain-server/resources/web/wizard/css/style.css
Normal file
89
domain-server/resources/web/wizard/css/style.css
Normal file
|
@ -0,0 +1,89 @@
|
|||
label {
|
||||
font-style: normal;
|
||||
font-weight: normal;
|
||||
line-height: 24px;
|
||||
font-size: 16px;
|
||||
|
||||
color: #373A3C;
|
||||
}
|
||||
|
||||
.step-title {
|
||||
margin-bottom: 20px;
|
||||
line-height: 26px;
|
||||
font-size: 24px;
|
||||
|
||||
color: #373A3C;
|
||||
}
|
||||
|
||||
.step-description {
|
||||
line-height: 24px;
|
||||
font-size: 16px;
|
||||
|
||||
color: #818A91;
|
||||
}
|
||||
|
||||
.step-info {
|
||||
font-style: normal;
|
||||
font-weight: normal;
|
||||
line-height: 24px;
|
||||
font-size: 16px;
|
||||
|
||||
color: #373A3C;
|
||||
}
|
||||
|
||||
#admin-row {
|
||||
margin-top: 20px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
#connect-question {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
#connect-account-btn {
|
||||
margin-top: 30px;
|
||||
margin-bottom: 205px;
|
||||
}
|
||||
|
||||
#place-name-group {
|
||||
margin-top: 42px;
|
||||
margin-bottom: 140px;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
#place-name-link {
|
||||
line-height: 38px;
|
||||
font-size: 32px;
|
||||
|
||||
display: inline-block;
|
||||
|
||||
color: #373A3C;
|
||||
}
|
||||
|
||||
#place-name-edit {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
#change-place-name {
|
||||
line-height: 24px;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
#rez-options-row {
|
||||
margin-bottom: 60px;
|
||||
}
|
||||
|
||||
#verify-password-row {
|
||||
margin-bottom: 33px;
|
||||
}
|
||||
|
||||
#checkmark-image {
|
||||
margin-top: 66px;
|
||||
margin-bottom: 59px;
|
||||
width: 169px;
|
||||
height: 169px;
|
||||
}
|
||||
|
||||
#visit-domain-row {
|
||||
margin-bottom: 68px;
|
||||
}
|
29
domain-server/resources/web/wizard/header.html
Normal file
29
domain-server/resources/web/wizard/header.html
Normal file
|
@ -0,0 +1,29 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>domain-server</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
|
||||
<link href="/css/bootstrap.min.css" rel="stylesheet" media="screen">
|
||||
<link href="/css/style.css" rel="stylesheet" media="screen">
|
||||
<link href="/wizard/css/style.css" rel="stylesheet" media="screen">
|
||||
<link href="/css/sweetalert.css" rel="stylesheet" media="screen">
|
||||
<link href="/css/bootstrap-switch.min.css" rel="stylesheet" media="screen">
|
||||
|
||||
<script src='/js/sweetalert.min.js'></script>
|
||||
</head>
|
||||
<body>
|
||||
<nav class="navbar navbar-default navbar-fixed-top" role="navigation">
|
||||
<div class="container-fluid">
|
||||
<!-- Collect the nav links, forms, and other content for toggling -->
|
||||
<div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
|
||||
<a class="navbar-brand step-info" href="#">Setup Wizard (Domain Server Settings)</a>
|
||||
|
||||
<div class="navbar-form navbar-right">
|
||||
<button id="skip-wizard-button" type="button" class="btn btn-default" style="display:none;">Skip Wizard</button>
|
||||
</div>
|
||||
</div>
|
||||
</div><!-- /.container-fluid -->
|
||||
</nav>
|
||||
|
||||
<div class="container">
|
229
domain-server/resources/web/wizard/index.shtml
Normal file
229
domain-server/resources/web/wizard/index.shtml
Normal file
|
@ -0,0 +1,229 @@
|
|||
<!--#include virtual="wizard/header.html"-->
|
||||
<div class="wizard-step desktop-only col-md-6 col-centered" style="display: none;">
|
||||
<h4 class="step-title"></h4>
|
||||
<dl class="row">
|
||||
<dd class="col-md-12">
|
||||
<span class='step-description'>By connecting your High Fidelity Account you will be granting access to your account information.</span>
|
||||
</dd>
|
||||
</dl>
|
||||
<dl class="row">
|
||||
<dd class="col-md-12">
|
||||
<a id="connect-account-btn" role="button" class="btn btn-primary btn-md btn-block" target="_blank">Connect your High Fidelity account</a>
|
||||
</dd>
|
||||
</dl>
|
||||
|
||||
<dl class="row">
|
||||
<dd class="col-md-3">
|
||||
<button type="button" class="btn btn-md btn-block btn-default next-button">Skip</button>
|
||||
</dd>
|
||||
</dl>
|
||||
</div>
|
||||
|
||||
<div class="wizard-step col-md-8 col-centered" style="display: none;">
|
||||
<h4 class="step-title"></h4>
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<span class='step-description'>
|
||||
<a target='_blank' href='https://docs.highfidelity.com/create-and-explore/start-working-in-your-sandbox/place-names'>Place names</a> are similar to web addresses. Users who want to visit your domain can
|
||||
enter its Place Name in High Fidelity's Interface. You can choose a Place Name for your domain.</br>
|
||||
People can also use your <b>domain's IP address (shown below)</b> to visit your High Fidelity domain.
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="centered-hack-parent">
|
||||
<div id="place-name-group" class="centered-hack">
|
||||
<p id="place-name-link"></p>
|
||||
<div id="place-name-edit">
|
||||
<span class='glyphicon glyphicon-pencil'></span>
|
||||
<a href="#" id="change-place-name">Choose a custom Place Name instead</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<dl class="row">
|
||||
<dd class="col-md-3">
|
||||
<button type="button" class="btn btn-md btn-block btn-default back-button">Back</button>
|
||||
</dd>
|
||||
<dd class="col-md-3 col-md-offset-6">
|
||||
<button type="button" class="btn btn-md btn-block btn-primary next-button">Next</button>
|
||||
</dd>
|
||||
</dl>
|
||||
</div>
|
||||
|
||||
<div class="wizard-step col-md-9 col-centered" style="display: none;">
|
||||
<h4 class="step-title"></h4>
|
||||
<div class="row">
|
||||
<p id="permissions-description" class="col-md-12 step-info"><b>Localhost</b> has been granted administrator privileges to this domain. (Localhost is any</br>user on the same machine as the High Fidelity Server)</p>
|
||||
</div>
|
||||
<div id="admin-row" class="row">
|
||||
<p class="col-md-6">
|
||||
<span id="admin-description" class="step-info"><b>Add your High Fidelity username</b> and any other usernames to grant administrator privileges.</span>
|
||||
<span class='glyphicon glyphicon-info-sign' data-toggle="tooltip" title="Users who will have all the permissions for this domain."></span>
|
||||
</p>
|
||||
<div class="col-md-6">
|
||||
<input id="admin-usernames" type="text" class="form-control">
|
||||
<span class="help-block text-right">separate by commas (user1, user2,..)</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<span class="step-description">Grant basic permissions to other users. You can change these later.</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<p id="connect-question" class="step-info">
|
||||
Who can connect to your domain?
|
||||
<span class='glyphicon glyphicon-info-sign' data-toggle='tooltip' title='You can set this to allow a user to connect to this domain.'></span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<p class="col-md-2">
|
||||
<label>
|
||||
<input id="connect-none" name="connect-radio" type="radio" value="none" checked> None
|
||||
<span class='glyphicon glyphicon-info-sign' data-toggle='tooltip' title='Only the admins of this domain'></span>
|
||||
</label>
|
||||
</p>
|
||||
<p class="col-md-3">
|
||||
<label>
|
||||
<input id="connect-friends" name="connect-radio" type="radio" value="friends"> Friends
|
||||
<span class='glyphicon glyphicon-info-sign' data-toggle='tooltip' title='Users who are your Friends in High Fidelity'></span>
|
||||
</label>
|
||||
</p>
|
||||
<p class="col-md-5">
|
||||
<label>
|
||||
<input id="connect-logged-in" name="connect-radio" type="radio" value="logged-in"> Users logged in to High Fidelity
|
||||
<span class='glyphicon glyphicon-info-sign' data-toggle='tooltip' title='Users who are currently logged into High Fidelity'></span>
|
||||
</label>
|
||||
</p>
|
||||
<p class="col-md-2">
|
||||
<label>
|
||||
<input id="connect-everyone" name="connect-radio" type="radio" value="everyone"> Everyone
|
||||
<span class='glyphicon glyphicon-info-sign' data-toggle='tooltip' title="Users who aren't logged into High Fidelity"></span>
|
||||
</label>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<p class="step-info">
|
||||
Who can rez items in your domain?
|
||||
<span class='glyphicon glyphicon-info-sign' data-toggle='tooltip' title='You can set this to allow a user to create entities in this domain.'></span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div id="rez-options-row" class="row">
|
||||
<p class="col-md-2">
|
||||
<label>
|
||||
<input id="rez-none" name="rez-radio" type="radio" value="none" checked> None
|
||||
<span class='glyphicon glyphicon-info-sign' data-toggle='tooltip' title='Only the admins of this domain'></span>
|
||||
</label>
|
||||
</p>
|
||||
<p class="col-md-3">
|
||||
<label>
|
||||
<input id="rez-friends" name="rez-radio" type="radio" value="friends"> Friends
|
||||
<span class='glyphicon glyphicon-info-sign' data-toggle='tooltip' title='Users who are your Friends in High Fidelity'></span>
|
||||
</label>
|
||||
</p>
|
||||
<p class="col-md-5">
|
||||
<label>
|
||||
<input id="rez-logged-in" name="rez-radio" type="radio" value="logged-in"> Users logged in to High Fidelity
|
||||
<span class='glyphicon glyphicon-info-sign' data-toggle='tooltip' title='Users who are currently logged into High Fidelity'></span>
|
||||
</label>
|
||||
</p>
|
||||
<p class="col-md-2">
|
||||
<label>
|
||||
<input id="rez-everyone" name="rez-radio" type="radio" value="everyone"> Everyone
|
||||
<span class='glyphicon glyphicon-info-sign' data-toggle='tooltip' title="Users who aren't logged into High Fidelity"></span>
|
||||
</label>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-3 col-md-offset-9">
|
||||
<button id="save-permissions" type="button" class="btn btn-md btn-block btn-primary">Next</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="wizard-step cloud-only col-md-7 col-centered" style="display: none;">
|
||||
<h4 class="step-title"></h4>
|
||||
<dl class="row">
|
||||
<dd class="col-md-12">
|
||||
<span class='step-description'>
|
||||
Your server settings are currently accessible without a username and password.
|
||||
Adding credentials will ensure that only authorized users have access to modify the settings.
|
||||
</span>
|
||||
</dd>
|
||||
</dl>
|
||||
|
||||
<div class="row">
|
||||
<p class="col-md-12 step-info">Create a username and password to secure the access to your domain server settings.</p>
|
||||
</div>
|
||||
<dl class="row">
|
||||
<dt class="col-md-4 step-info">Username</dt>
|
||||
<dd class="col-md-8">
|
||||
<input id="http_username" type="text" class="form-control">
|
||||
<span class='help-block'>This does not have to be your High Fidelity username</span>
|
||||
</dd>
|
||||
</dl>
|
||||
<dl class="row">
|
||||
<dt class="col-md-4 step-info">Enter password</dt>
|
||||
<dd class="col-md-8">
|
||||
<input id="http_password" type="password" class="form-control">
|
||||
<span class='help-block'>This should not be the same as your High Fidelity password</span>
|
||||
</dd>
|
||||
</dl>
|
||||
<dl id="verify-password-row" class="row">
|
||||
<dt class="col-md-4 step-info">Re-enter password</dt>
|
||||
<dd class="col-md-8">
|
||||
<input id="verify_http_password" type="password" class="form-control">
|
||||
</dd>
|
||||
</dl>
|
||||
|
||||
<dl class="row">
|
||||
<dd class="col-md-3 col-md-offset-9">
|
||||
<button id="save-username-password" type="button" class="btn btn-md btn-block btn-primary">Finish</button>
|
||||
</dd>
|
||||
</dl>
|
||||
</div>
|
||||
|
||||
<div class="wizard-step cloud-only col-md-7 col-centered" style="display: none;">
|
||||
<div class="row">
|
||||
<div class="col-xs-4 col-centered">
|
||||
<img id="checkmark-image" src="../images/checkmark.svg">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<p class="step-info">Congratulations! You have successfully setup and configured your cloud hosted domain.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="visit-domain-row" class="row">
|
||||
<div class="col-md-12">
|
||||
<label><input id="go-to-domain" class="form-check-input" type="checkbox"> Visit domain in VR now</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<dl class="row">
|
||||
<dd class="col-md-5 col-md-offset-7">
|
||||
<button id="explore-settings" type="button" class="btn btn-md btn-block btn-primary">Explore all domain server settings</button>
|
||||
</dd>
|
||||
</dl>
|
||||
</div>
|
||||
|
||||
<!--#include virtual="footer.html"-->
|
||||
<script src='/js/underscore-min.js'></script>
|
||||
<script src='/js/bootbox.min.js'></script>
|
||||
<script src='/js/sha256.js'></script>
|
||||
<script src='/js/shared.js'></script>
|
||||
<script src='js/wizard.js'></script>
|
||||
<!--#include virtual="page-end.html"-->
|
471
domain-server/resources/web/wizard/js/wizard.js
Normal file
471
domain-server/resources/web/wizard/js/wizard.js
Normal file
|
@ -0,0 +1,471 @@
|
|||
var Metaverse = {
|
||||
accessToken: null
|
||||
}
|
||||
|
||||
$(document).ready(function(){
|
||||
Strings.ADD_PLACE_NOT_CONNECTED_MESSAGE = "You must have an access token to query your High Fidelity places.<br><br>" +
|
||||
"Please go back and connect your account.";
|
||||
|
||||
$('#connect-account-btn').attr('href', URLs.METAVERSE_URL + "/user/tokens/new?for_domain_server=true");
|
||||
|
||||
$('[data-toggle="tooltip"]').tooltip();
|
||||
|
||||
$('body').on('click', '.next-button', function() {
|
||||
goToNextStep();
|
||||
});
|
||||
|
||||
$('body').on('click', '.back-button', function() {
|
||||
goToPreviousStep();
|
||||
});
|
||||
|
||||
$('body').on('click', '#skip-wizard-button', function() {
|
||||
skipWizard();
|
||||
})
|
||||
|
||||
$('body').on('click', '#connect-account-btn', function() {
|
||||
$(this).blur();
|
||||
prepareAccessTokenPrompt(function(accessToken) {
|
||||
Metaverse.accessToken = accessToken;
|
||||
saveAccessToken();
|
||||
});
|
||||
});
|
||||
|
||||
$('body').on('click', '#save-permissions', function() {
|
||||
savePermissions();
|
||||
});
|
||||
|
||||
function triggerSaveUsernamePassword(event) {
|
||||
if (event.keyCode === 13) {
|
||||
$("#save-username-password").click();
|
||||
}
|
||||
}
|
||||
$("#http_username").keyup(triggerSaveUsernamePassword);
|
||||
$("#http_password").keyup(triggerSaveUsernamePassword);
|
||||
$("#verify_http_password").keyup(triggerSaveUsernamePassword);
|
||||
$('body').on('click', '#save-username-password', function() {
|
||||
saveUsernamePassword();
|
||||
});
|
||||
|
||||
$('body').on('click', '#change-place-name', function() {
|
||||
chooseFromHighFidelityPlaces(Settings.data.values.metaverse.access_token, "/0,-10,0", function(placeName) {
|
||||
updatePlaceNameLink(placeName);
|
||||
});
|
||||
});
|
||||
|
||||
$('body').on('click', '#explore-settings', function() {
|
||||
exploreSettings();
|
||||
});
|
||||
|
||||
reloadSettings(function(success) {
|
||||
if (success) {
|
||||
setupWizardSteps();
|
||||
updatePlaceNameDisplay();
|
||||
updateUsernameDisplay();
|
||||
} else {
|
||||
swal({
|
||||
title: '',
|
||||
type: 'error',
|
||||
text: "There was a problem loading the domain settings.\nPlease refresh the page to try again.",
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
function setupWizardSteps() {
|
||||
var stepsCompleted = Settings.data.values.wizard.steps_completed;
|
||||
var steps = null;
|
||||
|
||||
if (Settings.data.values.wizard.cloud_domain) {
|
||||
$('.desktop-only').remove();
|
||||
$('.wizard-step').find('.back-button').hide();
|
||||
|
||||
steps = $('.wizard-step');
|
||||
$(steps).each(function(i) {
|
||||
$(this).children(".step-title").text("Step " + (i + 1) + " of " + (steps.length - 1));
|
||||
});
|
||||
|
||||
$('#permissions-description').html('You <span id="username-display"></span>have been assigned administrator privileges to this domain.');
|
||||
$('#admin-description').html('Add more High Fidelity usernames to grant administrator privileges.');
|
||||
} else {
|
||||
$('.cloud-only').remove();
|
||||
$('#save-permissions').text("Finish");
|
||||
|
||||
steps = $('.wizard-step');
|
||||
$(steps).each(function(i) {
|
||||
$(this).children(".step-title").text("Step " + (i + 1) + " of " + steps.length);
|
||||
});
|
||||
|
||||
if (stepsCompleted == 0) {
|
||||
$('#skip-wizard-button').show();
|
||||
}
|
||||
}
|
||||
|
||||
var currentStep = steps[stepsCompleted];
|
||||
$(currentStep).show();
|
||||
}
|
||||
|
||||
function updatePlaceNameLink(address) {
|
||||
if (address) {
|
||||
$('#place-name-link').html('Your domain is reachable at: <a target="_blank" href="' + URLs.PLACE_URL + '/' + address + '">' + address + '</a>');
|
||||
}
|
||||
}
|
||||
|
||||
function updatePlaceNameDisplay() {
|
||||
if (Settings.data.values.metaverse.id) {
|
||||
$.getJSON(URLs.METAVERSE_URL + '/api/v1/domains/' + Settings.data.values.metaverse.id, function(data) {
|
||||
|
||||
if (data.status === 'success') {
|
||||
if (data.domain.default_place_name) {
|
||||
// Place name
|
||||
updatePlaceNameLink(data.domain.default_place_name);
|
||||
} else if (data.domain.name) {
|
||||
// Temporary name
|
||||
updatePlaceNameLink(data.domain.name);
|
||||
} else if (data.domain.network_address) {
|
||||
if (data.domain.network_port !== 40102) {
|
||||
// IP:PORT
|
||||
updatePlaceNameLink(data.domain.network_address + ':' + data.domain.network_port);
|
||||
} else {
|
||||
// IP
|
||||
updatePlaceNameLink(data.domain.network_address);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
console.warn('Request Failed');
|
||||
}
|
||||
}).fail(function() {
|
||||
console.warn('Request Failed');
|
||||
});
|
||||
} else {
|
||||
console.warn('No metaverse domain ID!');
|
||||
}
|
||||
}
|
||||
|
||||
function updateUsernameDisplay() {
|
||||
var permissions = Settings.data.values.security.permissions;
|
||||
if (permissions.length > 0) {
|
||||
$('#username-display').html('<b>(' + permissions[0].permissions_id + ')</b> ');
|
||||
}
|
||||
}
|
||||
|
||||
function reloadSettings(callback) {
|
||||
$.getJSON('/settings.json', function(data){
|
||||
Settings.data = data;
|
||||
|
||||
if (callback) {
|
||||
// call the callback now that settings are loaded
|
||||
callback(true);
|
||||
}
|
||||
}).fail(function() {
|
||||
if (callback) {
|
||||
// call the failure object since settings load failed
|
||||
callback(false)
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function postSettings(jsonSettings, callback) {
|
||||
console.log("----- SAVING ------");
|
||||
console.log(JSON.stringify(jsonSettings));
|
||||
|
||||
// POST the form JSON to the domain-server settings.json endpoint so the settings are saved
|
||||
$.ajax('/settings.json', {
|
||||
data: JSON.stringify(jsonSettings),
|
||||
contentType: 'application/json',
|
||||
type: 'POST'
|
||||
}).done(function(data){
|
||||
if (data.status == "success") {
|
||||
if (callback) {
|
||||
callback();
|
||||
}
|
||||
reloadSettings();
|
||||
} else {
|
||||
swal("Error", Strings.LOADING_SETTINGS_ERROR)
|
||||
reloadSettings();
|
||||
}
|
||||
}).fail(function(){
|
||||
swal("Error", Strings.LOADING_SETTINGS_ERROR)
|
||||
reloadSettings();
|
||||
});
|
||||
}
|
||||
|
||||
function goToNextStep() {
|
||||
$('#skip-wizard-button').hide();
|
||||
|
||||
var currentStep = $('body').find('.wizard-step:visible');
|
||||
var nextStep = currentStep.next('.wizard-step');
|
||||
|
||||
var formJSON = {
|
||||
"wizard": {}
|
||||
}
|
||||
|
||||
if (nextStep.length > 0) {
|
||||
currentStep.hide();
|
||||
nextStep.show();
|
||||
|
||||
var currentStepNumber = parseInt(Settings.data.values.wizard.steps_completed) + 1;
|
||||
|
||||
postSettings({
|
||||
"wizard": {
|
||||
"steps_completed": currentStepNumber.toString()
|
||||
}
|
||||
});
|
||||
} else {
|
||||
postSettings({
|
||||
"wizard": {
|
||||
"steps_completed": "0",
|
||||
"completed_once": true
|
||||
}
|
||||
}, redirectToSettings);
|
||||
}
|
||||
}
|
||||
|
||||
function goToPreviousStep() {
|
||||
var currentStep = $('body').find('.wizard-step:visible');
|
||||
var previousStep = currentStep.prev('.wizard-step');
|
||||
|
||||
var formJSON = {
|
||||
"wizard": {}
|
||||
}
|
||||
|
||||
if (previousStep.length > 0) {
|
||||
currentStep.hide();
|
||||
previousStep.show();
|
||||
|
||||
var currentStepNumber = parseInt(Settings.data.values.wizard.steps_completed) - 1;
|
||||
|
||||
postSettings({
|
||||
"wizard": {
|
||||
"steps_completed": currentStepNumber.toString()
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function skipWizard() {
|
||||
postSettings({
|
||||
"wizard": {
|
||||
"steps_completed": "0",
|
||||
"completed_once": true
|
||||
}
|
||||
}, redirectToSettings);
|
||||
}
|
||||
|
||||
function redirectToSettings() {
|
||||
var redirectURL = "/settings" + location.search;
|
||||
if (Settings.data.values.wizard.cloud_domain) {
|
||||
if (location.search.length > 0) {
|
||||
redirectURL += "&";
|
||||
} else {
|
||||
redirectURL += "?";
|
||||
}
|
||||
redirectURL += "cloud-wizard-exit";
|
||||
}
|
||||
location.href = redirectURL;
|
||||
}
|
||||
|
||||
function saveAccessToken() {
|
||||
var formJSON = {
|
||||
"metaverse": {}
|
||||
}
|
||||
if (Metaverse.accessToken) {
|
||||
formJSON.metaverse.access_token = Metaverse.accessToken;
|
||||
}
|
||||
|
||||
// remove focus from the button
|
||||
$(this).blur();
|
||||
|
||||
// POST the form JSON to the domain-server settings.json endpoint so the settings are saved
|
||||
postSettings(formJSON, goToNextStep);
|
||||
}
|
||||
|
||||
function getSettingDescriptionForKey(groupKey, settingKey) {
|
||||
for (var i in Settings.data.descriptions) {
|
||||
var group = Settings.data.descriptions[i];
|
||||
if (group.name === groupKey) {
|
||||
for (var j in group.settings) {
|
||||
var setting = group.settings[j];
|
||||
if (setting.name === settingKey) {
|
||||
return setting;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function savePermissions() {
|
||||
var localhostPermissions = (Settings.data.values.wizard.cloud_domain !== true)
|
||||
var anonymousCanConnect = false;
|
||||
var friendsCanConnect = false;
|
||||
var loggedInCanConnect = false;
|
||||
var anonymousCanRez = false;
|
||||
var friendsCanRez = false;
|
||||
var loggedInCanRez = false;
|
||||
|
||||
var connectValue = $('input[name=connect-radio]:checked').val();
|
||||
var rezValue = $('input[name=rez-radio]:checked').val();
|
||||
|
||||
switch (connectValue) {
|
||||
case "friends":
|
||||
friendsCanConnect = true;
|
||||
break;
|
||||
case "logged-in":
|
||||
friendsCanConnect = true;
|
||||
loggedInCanConnect = true;
|
||||
break;
|
||||
case "everyone":
|
||||
anonymousCanConnect = true;
|
||||
friendsCanConnect = true;
|
||||
loggedInCanConnect = true;
|
||||
break;
|
||||
}
|
||||
|
||||
switch (rezValue) {
|
||||
case "friends":
|
||||
friendsCanRez = true;
|
||||
break;
|
||||
case "logged-in":
|
||||
friendsCanRez = true;
|
||||
loggedInCanRez = true;
|
||||
break;
|
||||
case "everyone":
|
||||
anonymousCanRez = true;
|
||||
friendsCanRez = true;
|
||||
loggedInCanRez = true;
|
||||
break;
|
||||
}
|
||||
|
||||
var admins = $('#admin-usernames').val().split(',');
|
||||
|
||||
var existingAdmins = Settings.data.values.security.permissions.map(function(value) {
|
||||
return value.permissions_id;
|
||||
});
|
||||
admins = admins.concat(existingAdmins);
|
||||
|
||||
// Filter out unique values
|
||||
admins = _.uniq(admins.map(function(username) {
|
||||
return username.trim();
|
||||
})).filter(function(username) {
|
||||
return username !== "";
|
||||
});
|
||||
|
||||
var formJSON = {
|
||||
"security": {
|
||||
"standard_permissions": [
|
||||
{
|
||||
"id_can_connect": anonymousCanConnect,
|
||||
"id_can_rez": anonymousCanRez,
|
||||
"id_can_rez_certified": anonymousCanRez,
|
||||
"id_can_rez_tmp": anonymousCanRez,
|
||||
"id_can_rez_tmp_certified": anonymousCanRez,
|
||||
"permissions_id": "anonymous"
|
||||
},
|
||||
{
|
||||
"id_can_connect": friendsCanConnect,
|
||||
"id_can_rez": friendsCanRez,
|
||||
"id_can_rez_certified": friendsCanRez,
|
||||
"id_can_rez_tmp": friendsCanRez,
|
||||
"id_can_rez_tmp_certified": friendsCanRez,
|
||||
"permissions_id": "friends"
|
||||
},
|
||||
{
|
||||
"id_can_connect": loggedInCanConnect,
|
||||
"id_can_rez": loggedInCanRez,
|
||||
"id_can_rez_certified": loggedInCanRez,
|
||||
"id_can_rez_tmp": loggedInCanRez,
|
||||
"id_can_rez_tmp_certified": loggedInCanRez,
|
||||
"permissions_id": "logged-in"
|
||||
},
|
||||
{
|
||||
"id_can_adjust_locks": localhostPermissions,
|
||||
"id_can_connect": localhostPermissions,
|
||||
"id_can_connect_past_max_capacity": localhostPermissions,
|
||||
"id_can_kick": localhostPermissions,
|
||||
"id_can_replace_content": localhostPermissions,
|
||||
"id_can_rez": localhostPermissions,
|
||||
"id_can_rez_certified": localhostPermissions,
|
||||
"id_can_rez_tmp": localhostPermissions,
|
||||
"id_can_rez_tmp_certified": localhostPermissions,
|
||||
"id_can_write_to_asset_server": localhostPermissions,
|
||||
"permissions_id": "localhost"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
if (admins.length > 0) {
|
||||
formJSON.security.permissions = [];
|
||||
|
||||
var permissionsDesc = getSettingDescriptionForKey("security", "permissions");
|
||||
for (var i in admins) {
|
||||
var admin = admins[i];
|
||||
var perms = {};
|
||||
for (var i in permissionsDesc.columns) {
|
||||
var name = permissionsDesc.columns[i].name;
|
||||
if (name === "permissions_id") {
|
||||
perms[name] = admin;
|
||||
} else {
|
||||
perms[name] = true;
|
||||
}
|
||||
}
|
||||
formJSON.security.permissions.push(perms);
|
||||
}
|
||||
}
|
||||
|
||||
// remove focus from the button
|
||||
$(this).blur();
|
||||
|
||||
postSettings(formJSON, goToNextStep);
|
||||
}
|
||||
|
||||
function saveUsernamePassword() {
|
||||
var username = $("#http_username").val();
|
||||
var password = $("#http_password").val();
|
||||
var verify_password = $("#verify_http_password").val();
|
||||
|
||||
if (username.length == 0) {
|
||||
bootbox.alert({ "message": "You must set a username!", "title": "Username Error" });
|
||||
return;
|
||||
}
|
||||
|
||||
if (password.length == 0) {
|
||||
bootbox.alert({ "message": "You must set a password!", "title": "Password Error" });
|
||||
return;
|
||||
}
|
||||
|
||||
if (password != verify_password) {
|
||||
bootbox.alert({ "message": "Passwords must match!", "title": "Password Error" });
|
||||
return;
|
||||
}
|
||||
|
||||
var currentStepNumber = parseInt(Settings.data.values.wizard.steps_completed) + 1;
|
||||
|
||||
var formJSON = {
|
||||
"security": {
|
||||
"http_username": username,
|
||||
"http_password": sha256_digest(password)
|
||||
},
|
||||
"wizard": {
|
||||
"steps_completed": currentStepNumber.toString()
|
||||
}
|
||||
}
|
||||
|
||||
// remove focus from the button
|
||||
$(this).blur();
|
||||
|
||||
// POST the form JSON to the domain-server settings.json endpoint so the settings are saved
|
||||
postSettings(formJSON, function() {
|
||||
location.reload();
|
||||
});
|
||||
}
|
||||
|
||||
function exploreSettings() {
|
||||
if ($('#go-to-domain').is(":checked")) {
|
||||
var link = $('#place-name-link a:first');
|
||||
if (link.length > 0) {
|
||||
window.open(link.attr("href"));
|
||||
}
|
||||
}
|
||||
|
||||
goToNextStep();
|
||||
}
|
|
@ -814,9 +814,15 @@ void DomainGatekeeper::processICEPeerInformationPacket(QSharedPointer<ReceivedMe
|
|||
|
||||
void DomainGatekeeper::processICEPingPacket(QSharedPointer<ReceivedMessage> message) {
|
||||
auto limitedNodeList = DependencyManager::get<LimitedNodeList>();
|
||||
auto pingReplyPacket = limitedNodeList->constructICEPingReplyPacket(*message, limitedNodeList->getSessionUUID());
|
||||
|
||||
limitedNodeList->sendPacket(std::move(pingReplyPacket), message->getSenderSockAddr());
|
||||
// before we respond to this ICE ping packet, make sure we have a peer in the list that matches
|
||||
QUuid icePeerID = QUuid::fromRfc4122({ message->getRawMessage(), NUM_BYTES_RFC4122_UUID });
|
||||
|
||||
if (_icePeers.contains(icePeerID)) {
|
||||
auto pingReplyPacket = limitedNodeList->constructICEPingReplyPacket(*message, limitedNodeList->getSessionUUID());
|
||||
|
||||
limitedNodeList->sendPacket(std::move(pingReplyPacket), message->getSenderSockAddr());
|
||||
}
|
||||
}
|
||||
|
||||
void DomainGatekeeper::processICEPingReplyPacket(QSharedPointer<ReceivedMessage> message) {
|
||||
|
|
|
@ -46,6 +46,8 @@
|
|||
#include "DomainServerNodeData.h"
|
||||
#include "NodeConnectionData.h"
|
||||
|
||||
const QString ACCESS_TOKEN_KEY_PATH = "metaverse.access_token";
|
||||
|
||||
int const DomainServer::EXIT_CODE_REBOOT = 234923;
|
||||
|
||||
#if USE_STABLE_GLOBAL_SERVICES
|
||||
|
@ -54,6 +56,82 @@ const QString ICE_SERVER_DEFAULT_HOSTNAME = "ice.highfidelity.com";
|
|||
const QString ICE_SERVER_DEFAULT_HOSTNAME = "dev-ice.highfidelity.com";
|
||||
#endif
|
||||
|
||||
bool DomainServer::forwardMetaverseAPIRequest(HTTPConnection* connection,
|
||||
const QString& metaversePath,
|
||||
const QString& requestSubobjectKey,
|
||||
std::initializer_list<QString> requiredData,
|
||||
std::initializer_list<QString> optionalData,
|
||||
bool requireAccessToken) {
|
||||
|
||||
auto accessTokenVariant = valueForKeyPath(_settingsManager.getSettingsMap(), ACCESS_TOKEN_KEY_PATH);
|
||||
if (accessTokenVariant == nullptr && requireAccessToken) {
|
||||
connection->respond(HTTPConnection::StatusCode400, "User access token has not been set");
|
||||
return true;
|
||||
}
|
||||
|
||||
QJsonObject subobject;
|
||||
|
||||
auto params = connection->parseUrlEncodedForm();
|
||||
|
||||
for (auto& key : requiredData) {
|
||||
auto it = params.find(key);
|
||||
if (it == params.end()) {
|
||||
auto error = "Bad request, expected param '" + key + "'";
|
||||
connection->respond(HTTPConnection::StatusCode400, error.toLatin1());
|
||||
return true;
|
||||
}
|
||||
subobject.insert(key, it.value());
|
||||
}
|
||||
|
||||
for (auto& key : optionalData) {
|
||||
auto it = params.find(key);
|
||||
if (it != params.end()) {
|
||||
subobject.insert(key, it.value());
|
||||
}
|
||||
}
|
||||
|
||||
QJsonObject root;
|
||||
root.insert(requestSubobjectKey, subobject);
|
||||
QJsonDocument doc { root };
|
||||
|
||||
QUrl url { NetworkingConstants::METAVERSE_SERVER_URL.toString() + metaversePath };
|
||||
|
||||
QNetworkRequest req(url);
|
||||
req.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT);
|
||||
req.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
|
||||
|
||||
if (accessTokenVariant != nullptr) {
|
||||
auto accessTokenHeader = QString("Bearer ") + accessTokenVariant->toString();
|
||||
req.setRawHeader("Authorization", accessTokenHeader.toLatin1());
|
||||
}
|
||||
|
||||
QNetworkReply* reply;
|
||||
auto method = connection->requestOperation();
|
||||
if (method == QNetworkAccessManager::GetOperation) {
|
||||
reply = NetworkAccessManager::getInstance().get(req);
|
||||
} else if (method == QNetworkAccessManager::PostOperation) {
|
||||
reply = NetworkAccessManager::getInstance().post(req, doc.toJson());
|
||||
} else if (method == QNetworkAccessManager::PutOperation) {
|
||||
reply = NetworkAccessManager::getInstance().put(req, doc.toJson());
|
||||
} else {
|
||||
connection->respond(HTTPConnection::StatusCode400, "Error forwarding request, unsupported method");
|
||||
return true;
|
||||
}
|
||||
|
||||
connect(reply, &QNetworkReply::finished, this, [reply, connection]() {
|
||||
if (reply->error() != QNetworkReply::NoError) {
|
||||
auto data = reply->readAll();
|
||||
qDebug() << "Got error response from metaverse server (" << reply->url() << "): " << data << reply->errorString();
|
||||
connection->respond(HTTPConnection::StatusCode400, data);
|
||||
return;
|
||||
}
|
||||
|
||||
connection->respond(HTTPConnection::StatusCode200, reply->readAll());
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
DomainServer::DomainServer(int argc, char* argv[]) :
|
||||
QCoreApplication(argc, argv),
|
||||
_gatekeeper(this),
|
||||
|
@ -614,8 +692,6 @@ void DomainServer::setupNodeListAndAssignments() {
|
|||
nodeList->setPacketFilterOperator(&DomainServer::isPacketVerified);
|
||||
}
|
||||
|
||||
const QString ACCESS_TOKEN_KEY_PATH = "metaverse.access_token";
|
||||
|
||||
bool DomainServer::resetAccountManagerAccessToken() {
|
||||
if (!_oauthProviderURL.isEmpty()) {
|
||||
// check for an access-token in our settings, can optionally be overidden by env value
|
||||
|
@ -1731,6 +1807,8 @@ QString DomainServer::pathForRedirect(QString path) const {
|
|||
return "http://" + _hostname + ":" + QString::number(_httpManager.serverPort()) + path;
|
||||
}
|
||||
|
||||
|
||||
|
||||
const QString URI_OAUTH = "/oauth";
|
||||
bool DomainServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url, bool skipSubHandler) {
|
||||
const QString JSON_MIME_TYPE = "application/json";
|
||||
|
@ -1740,15 +1818,23 @@ bool DomainServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url
|
|||
const QString URI_SETTINGS = "/settings";
|
||||
const QString URI_ENTITY_FILE_UPLOAD = "/content/upload";
|
||||
const QString URI_RESTART = "/restart";
|
||||
const QString URI_API_PLACES = "/api/places";
|
||||
const QString URI_API_DOMAINS = "/api/domains";
|
||||
const QString URI_API_DOMAINS_ID = "/api/domains/";
|
||||
|
||||
const QString UUID_REGEX_STRING = "[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}";
|
||||
|
||||
auto nodeList = DependencyManager::get<LimitedNodeList>();
|
||||
|
||||
// allow sub-handlers to handle requests that do not require authentication
|
||||
if (_settingsManager.handlePublicHTTPRequest(connection, url)) {
|
||||
auto getSetting = [this](QString keyPath, QVariant& value) -> bool {
|
||||
QVariantMap& settingsMap = _settingsManager.getSettingsMap();
|
||||
QVariant* var = valueForKeyPath(settingsMap, keyPath);
|
||||
if (var == nullptr) {
|
||||
return false;
|
||||
}
|
||||
value = *var;
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
// check if this is a request for a scripted assignment (with a temp unique UUID)
|
||||
const QString ASSIGNMENT_REGEX_STRING = QString("\\%1\\/(%2)\\/?$").arg(URI_ASSIGNMENT).arg(UUID_REGEX_STRING);
|
||||
|
@ -1810,6 +1896,31 @@ bool DomainServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url
|
|||
return true;
|
||||
}
|
||||
|
||||
// Check if we should redirect/prevent access to the wizard
|
||||
if (connection->requestOperation() == QNetworkAccessManager::GetOperation) {
|
||||
const QString URI_WIZARD = "/wizard/";
|
||||
const QString WIZARD_COMPLETED_ONCE_KEY_PATH = "wizard.completed_once";
|
||||
const QVariant* wizardCompletedOnce = valueForKeyPath(_settingsManager.getSettingsMap(), WIZARD_COMPLETED_ONCE_KEY_PATH);
|
||||
const bool completedOnce = wizardCompletedOnce && wizardCompletedOnce->toBool();
|
||||
|
||||
if (url.path() != URI_WIZARD && url.path().endsWith('/') && !completedOnce) {
|
||||
// First visit, redirect to the wizard
|
||||
QUrl redirectedURL = url;
|
||||
redirectedURL.setPath(URI_WIZARD);
|
||||
|
||||
Headers redirectHeaders;
|
||||
redirectHeaders.insert("Location", redirectedURL.toEncoded());
|
||||
|
||||
connection->respond(HTTPConnection::StatusCode302,
|
||||
QByteArray(), HTTPConnection::DefaultContentType, redirectHeaders);
|
||||
return true;
|
||||
} else if (url.path() == URI_WIZARD && completedOnce) {
|
||||
// Wizard already completed, return 404
|
||||
connection->respond(HTTPConnection::StatusCode404, "Resource not found.");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (connection->requestOperation() == QNetworkAccessManager::GetOperation) {
|
||||
if (url.path() == "/assignments.json") {
|
||||
// user is asking for json list of assignments
|
||||
|
@ -1899,6 +2010,13 @@ bool DomainServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url
|
|||
connection->respond(HTTPConnection::StatusCode200);
|
||||
restart();
|
||||
return true;
|
||||
} else if (url.path() == URI_API_DOMAINS) {
|
||||
return forwardMetaverseAPIRequest(connection, "/api/v1/domains", "");
|
||||
} else if (url.path().startsWith(URI_API_DOMAINS_ID)) {
|
||||
auto id = url.path().mid(URI_API_DOMAINS_ID.length());
|
||||
return forwardMetaverseAPIRequest(connection, "/api/v1/domains/" + id, "", {}, {}, false);
|
||||
} else if (url.path() == URI_API_PLACES) {
|
||||
return forwardMetaverseAPIRequest(connection, "/api/v1/user/places", "");
|
||||
} else {
|
||||
// check if this is for json stats for a node
|
||||
const QString NODE_JSON_REGEX_STRING = QString("\\%1\\/(%2).json\\/?$").arg(URI_NODES).arg(UUID_REGEX_STRING);
|
||||
|
@ -1978,8 +2096,6 @@ bool DomainServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url
|
|||
// this is an entity file upload, ask the HTTPConnection to parse the data
|
||||
QList<FormData> formData = connection->parseFormData();
|
||||
|
||||
Headers redirectHeaders;
|
||||
|
||||
if (formData.size() > 0 && formData[0].second.size() > 0) {
|
||||
// invoke our method to hand the new octree file off to the octree server
|
||||
QMetaObject::invokeMethod(this, "handleOctreeFileReplacement",
|
||||
|
@ -1993,7 +2109,98 @@ bool DomainServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url
|
|||
}
|
||||
|
||||
return true;
|
||||
|
||||
} else if (url.path() == "/domain_settings") {
|
||||
auto accessTokenVariant = valueForKeyPath(_settingsManager.getSettingsMap(), ACCESS_TOKEN_KEY_PATH);
|
||||
if (!accessTokenVariant) {
|
||||
connection->respond(HTTPConnection::StatusCode400);
|
||||
return true;
|
||||
}
|
||||
|
||||
} else if (url.path() == URI_API_DOMAINS) {
|
||||
|
||||
return forwardMetaverseAPIRequest(connection, "/api/v1/domains", "domain", { "label" });
|
||||
}
|
||||
} else if (connection->requestOperation() == QNetworkAccessManager::PutOperation) {
|
||||
if (url.path() == URI_API_DOMAINS) {
|
||||
QVariant domainSetting;
|
||||
if (!getSetting(METAVERSE_DOMAIN_ID_KEY_PATH, domainSetting)) {
|
||||
connection->respond(HTTPConnection::StatusCode400, "Domain id has not been set");
|
||||
return true;
|
||||
}
|
||||
auto domainID = domainSetting.toString();
|
||||
return forwardMetaverseAPIRequest(connection, "/api/v1/domains/" + domainID, "domain",
|
||||
{ }, { "network_address", "network_port", "label" });
|
||||
} else if (url.path() == URI_API_PLACES) {
|
||||
auto accessTokenVariant = valueForKeyPath(_settingsManager.getSettingsMap(), ACCESS_TOKEN_KEY_PATH);
|
||||
if (!accessTokenVariant->isValid()) {
|
||||
connection->respond(HTTPConnection::StatusCode400, "User access token has not been set");
|
||||
return true;
|
||||
}
|
||||
|
||||
auto params = connection->parseUrlEncodedForm();
|
||||
|
||||
auto it = params.find("place_id");
|
||||
if (it == params.end()) {
|
||||
connection->respond(HTTPConnection::StatusCode400);
|
||||
return true;
|
||||
}
|
||||
QString place_id = it.value();
|
||||
|
||||
it = params.find("path");
|
||||
if (it == params.end()) {
|
||||
connection->respond(HTTPConnection::StatusCode400);
|
||||
return true;
|
||||
}
|
||||
QString path = it.value();
|
||||
|
||||
it = params.find("domain_id");
|
||||
QString domainID;
|
||||
if (it == params.end()) {
|
||||
QVariant domainSetting;
|
||||
if (!getSetting(METAVERSE_DOMAIN_ID_KEY_PATH, domainSetting)) {
|
||||
connection->respond(HTTPConnection::StatusCode400);
|
||||
return true;
|
||||
}
|
||||
domainID = domainSetting.toString();
|
||||
} else {
|
||||
domainID = it.value();
|
||||
}
|
||||
|
||||
QJsonObject root {
|
||||
{
|
||||
"place",
|
||||
QJsonObject({
|
||||
{ "pointee_query", domainID },
|
||||
{ "path", path }
|
||||
})
|
||||
}
|
||||
};
|
||||
QJsonDocument doc(root);
|
||||
|
||||
|
||||
QUrl url { NetworkingConstants::METAVERSE_SERVER_URL.toString() + "/api/v1/places/" + place_id };
|
||||
|
||||
url.setQuery("access_token=" + accessTokenVariant->toString());
|
||||
|
||||
QNetworkRequest req(url);
|
||||
req.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT);
|
||||
req.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
|
||||
QNetworkReply* reply = NetworkAccessManager::getInstance().put(req, doc.toJson());
|
||||
|
||||
connect(reply, &QNetworkReply::finished, this, [reply, connection]() {
|
||||
if (reply->error() != QNetworkReply::NoError) {
|
||||
qDebug() << "Got error response from metaverse server: " << reply->readAll();
|
||||
connection->respond(HTTPConnection::StatusCode500,
|
||||
"Error communicating with Metaverse");
|
||||
return;
|
||||
}
|
||||
|
||||
connection->respond(HTTPConnection::StatusCode200, reply->readAll());
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
} else if (connection->requestOperation() == QNetworkAccessManager::DeleteOperation) {
|
||||
const QString ALL_NODE_DELETE_REGEX_STRING = QString("\\%1\\/?$").arg(URI_NODES);
|
||||
const QString NODE_DELETE_REGEX_STRING = QString("\\%1\\/(%2)\\/$").arg(URI_NODES).arg(UUID_REGEX_STRING);
|
||||
|
@ -2037,7 +2244,6 @@ static const QString HIFI_SESSION_COOKIE_KEY = "DS_WEB_SESSION_UUID";
|
|||
static const QString STATE_QUERY_KEY = "state";
|
||||
|
||||
bool DomainServer::handleHTTPSRequest(HTTPSConnection* connection, const QUrl &url, bool skipSubHandler) {
|
||||
qDebug() << "HTTPS request received at" << url.toString();
|
||||
if (url.path() == URI_OAUTH) {
|
||||
|
||||
QUrlQuery codeURLQuery(url);
|
||||
|
@ -2298,6 +2504,8 @@ QNetworkReply* DomainServer::profileRequestGivenTokenReply(QNetworkReply* tokenR
|
|||
profileURL.setPath("/api/v1/user/profile");
|
||||
profileURL.setQuery(QString("%1=%2").arg(OAUTH_JSON_ACCESS_TOKEN_KEY, accessToken));
|
||||
|
||||
qDebug() << "Sending profile request to: " << profileURL;
|
||||
|
||||
QNetworkRequest profileRequest(profileURL);
|
||||
profileRequest.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true);
|
||||
profileRequest.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT);
|
||||
|
|
|
@ -187,6 +187,13 @@ private:
|
|||
|
||||
HTTPSConnection* connectionFromReplyWithState(QNetworkReply* reply);
|
||||
|
||||
bool forwardMetaverseAPIRequest(HTTPConnection* connection,
|
||||
const QString& metaversePath,
|
||||
const QString& requestSubobject,
|
||||
std::initializer_list<QString> requiredData = { },
|
||||
std::initializer_list<QString> optionalData = { },
|
||||
bool requireAccessToken = true);
|
||||
|
||||
SubnetList _acSubnetWhitelist;
|
||||
|
||||
std::vector<QString> _replicatedUsernames;
|
||||
|
|
|
@ -310,6 +310,16 @@ void DomainServerSettingsManager::setupConfigMap(const QStringList& argumentList
|
|||
_standardAgentPermissions[NodePermissions::standardNameLocalhost]->set(NodePermissions::Permission::canRezTemporaryCertifiedEntities);
|
||||
packPermissions();
|
||||
}
|
||||
if (oldVersion < 2.0) {
|
||||
const QString WIZARD_COMPLETED_ONCE = "wizard.completed_once";
|
||||
|
||||
QVariant* wizardCompletedOnce = _configMap.valueForKeyPath(WIZARD_COMPLETED_ONCE, true);
|
||||
|
||||
*wizardCompletedOnce = QVariant(true);
|
||||
|
||||
// write the new settings to the json file
|
||||
persistToFile();
|
||||
}
|
||||
}
|
||||
|
||||
unpackPermissions();
|
||||
|
@ -960,29 +970,6 @@ QVariant DomainServerSettingsManager::valueOrDefaultValueForKeyPath(const QStrin
|
|||
return QVariant();
|
||||
}
|
||||
|
||||
bool DomainServerSettingsManager::handlePublicHTTPRequest(HTTPConnection* connection, const QUrl &url) {
|
||||
if (connection->requestOperation() == QNetworkAccessManager::GetOperation && url.path() == SETTINGS_PATH_JSON) {
|
||||
// this is a GET operation for our settings
|
||||
|
||||
// check if there is a query parameter for settings affecting a particular type of assignment
|
||||
const QString SETTINGS_TYPE_QUERY_KEY = "type";
|
||||
QUrlQuery settingsQuery(url);
|
||||
QString typeValue = settingsQuery.queryItemValue(SETTINGS_TYPE_QUERY_KEY);
|
||||
|
||||
if (!typeValue.isEmpty()) {
|
||||
QJsonObject responseObject = responseObjectForType(typeValue);
|
||||
|
||||
connection->respond(HTTPConnection::StatusCode200, QJsonDocument(responseObject).toJson(), "application/json");
|
||||
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool DomainServerSettingsManager::handleAuthenticatedHTTPRequest(HTTPConnection *connection, const QUrl &url) {
|
||||
if (connection->requestOperation() == QNetworkAccessManager::PostOperation && url.path() == SETTINGS_PATH_JSON) {
|
||||
// this is a POST operation to change one or more settings
|
||||
|
@ -1214,6 +1201,7 @@ bool DomainServerSettingsManager::recurseJSONObjectAndOverwriteSettings(const QJ
|
|||
static const QString SECURITY_ROOT_KEY = "security";
|
||||
static const QString AC_SUBNET_WHITELIST_KEY = "ac_subnet_whitelist";
|
||||
static const QString BROADCASTING_KEY = "broadcasting";
|
||||
static const QString WIZARD_KEY = "wizard";
|
||||
static const QString DESCRIPTION_ROOT_KEY = "descriptors";
|
||||
|
||||
auto& settingsVariant = _configMap.getConfig();
|
||||
|
@ -1266,7 +1254,8 @@ bool DomainServerSettingsManager::recurseJSONObjectAndOverwriteSettings(const QJ
|
|||
|
||||
if (!matchingDescriptionObject.isEmpty()) {
|
||||
updateSetting(rootKey, rootValue, *thisMap, matchingDescriptionObject);
|
||||
if (rootKey != SECURITY_ROOT_KEY && rootKey != BROADCASTING_KEY && rootKey != SETTINGS_PATHS_KEY ) {
|
||||
if (rootKey != SECURITY_ROOT_KEY && rootKey != BROADCASTING_KEY &&
|
||||
rootKey != SETTINGS_PATHS_KEY && rootKey != WIZARD_KEY) {
|
||||
needRestart = true;
|
||||
}
|
||||
} else {
|
||||
|
@ -1282,8 +1271,9 @@ bool DomainServerSettingsManager::recurseJSONObjectAndOverwriteSettings(const QJ
|
|||
if (!matchingDescriptionObject.isEmpty()) {
|
||||
const QJsonValue& settingValue = rootValue.toObject()[settingKey];
|
||||
updateSetting(settingKey, settingValue, *thisMap, matchingDescriptionObject);
|
||||
if ((rootKey != SECURITY_ROOT_KEY && rootKey != BROADCASTING_KEY && rootKey != DESCRIPTION_ROOT_KEY)
|
||||
|| settingKey == AC_SUBNET_WHITELIST_KEY) {
|
||||
if ((rootKey != SECURITY_ROOT_KEY && rootKey != BROADCASTING_KEY &&
|
||||
rootKey != DESCRIPTION_ROOT_KEY && rootKey != WIZARD_KEY) ||
|
||||
settingKey == AC_SUBNET_WHITELIST_KEY) {
|
||||
needRestart = true;
|
||||
}
|
||||
} else {
|
||||
|
|
|
@ -43,7 +43,6 @@ class DomainServerSettingsManager : public QObject {
|
|||
Q_OBJECT
|
||||
public:
|
||||
DomainServerSettingsManager();
|
||||
bool handlePublicHTTPRequest(HTTPConnection* connection, const QUrl& url);
|
||||
bool handleAuthenticatedHTTPRequest(HTTPConnection* connection, const QUrl& url);
|
||||
|
||||
void setupConfigMap(const QStringList& argumentList);
|
||||
|
|
|
@ -14,13 +14,25 @@ Item {
|
|||
readonly property var originalAttachments: MyAvatar.getAttachmentsVariant();
|
||||
property var attachments: [];
|
||||
|
||||
Component.onCompleted: {
|
||||
for (var i = 0; i < originalAttachments.length; ++i) {
|
||||
var attachment = originalAttachments[i];
|
||||
function reload(){
|
||||
content.attachments = [];
|
||||
var currentAttachments = MyAvatar.getAttachmentsVariant();
|
||||
listView.model.clear();
|
||||
for (var i = 0; i < currentAttachments.length; ++i) {
|
||||
var attachment = currentAttachments[i];
|
||||
content.attachments.push(attachment);
|
||||
listView.model.append({});
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: MyAvatar
|
||||
onAttachmentsChanged: reload()
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
reload()
|
||||
}
|
||||
|
||||
Column {
|
||||
width: pane.width
|
||||
|
|
|
@ -219,7 +219,7 @@ Item {
|
|||
|
||||
function setShown(value) {
|
||||
if (value === true) {
|
||||
HMD.openTablet()
|
||||
HMD.openTablet(HMD.tabletContextualMode) // pass in current contextual mode flag to maintain flag (otherwise uses default false argument)
|
||||
} else {
|
||||
HMD.closeTablet()
|
||||
}
|
||||
|
|
|
@ -31,7 +31,7 @@ Item {
|
|||
|
||||
BaseWebView {
|
||||
id: webview
|
||||
url: (Account.metaverseServerURL + "/marketplace?category=avatars)"
|
||||
url: (Account.metaverseServerURL + "/marketplace?category=avatars")
|
||||
focus: true
|
||||
|
||||
anchors {
|
||||
|
|
|
@ -1452,6 +1452,7 @@ void MyAvatar::setAttachmentData(const QVector<AttachmentData>& attachmentData)
|
|||
return;
|
||||
}
|
||||
Avatar::setAttachmentData(attachmentData);
|
||||
emit attachmentsChanged();
|
||||
}
|
||||
|
||||
glm::vec3 MyAvatar::getSkeletonPosition() const {
|
||||
|
|
|
@ -619,6 +619,7 @@ signals:
|
|||
void skeletonChanged();
|
||||
void dominantHandChanged(const QString& hand);
|
||||
void sensorToWorldScaleChanged(float sensorToWorldScale);
|
||||
void attachmentsChanged();
|
||||
|
||||
private:
|
||||
|
||||
|
|
|
@ -100,10 +100,21 @@ void HMDScriptingInterface::deactivateHMDHandMouse() {
|
|||
|
||||
void HMDScriptingInterface::closeTablet() {
|
||||
_showTablet = false;
|
||||
_tabletContextualMode = false;
|
||||
}
|
||||
|
||||
void HMDScriptingInterface::openTablet() {
|
||||
void HMDScriptingInterface::openTablet(bool contextualMode) {
|
||||
_showTablet = true;
|
||||
_tabletContextualMode = contextualMode;
|
||||
}
|
||||
|
||||
void HMDScriptingInterface::toggleShouldShowTablet() {
|
||||
setShouldShowTablet(!getShouldShowTablet());
|
||||
}
|
||||
|
||||
void HMDScriptingInterface::setShouldShowTablet(bool value) {
|
||||
_showTablet = value;
|
||||
_tabletContextualMode = false;
|
||||
}
|
||||
|
||||
QScriptValue HMDScriptingInterface::getHUDLookAtPosition2D(QScriptContext* context, QScriptEngine* engine) {
|
||||
|
|
|
@ -30,6 +30,7 @@ class HMDScriptingInterface : public AbstractHMDScriptingInterface, public Depen
|
|||
Q_PROPERTY(glm::quat orientation READ getOrientation)
|
||||
Q_PROPERTY(bool mounted READ isMounted NOTIFY mountedChanged)
|
||||
Q_PROPERTY(bool showTablet READ getShouldShowTablet)
|
||||
Q_PROPERTY(bool tabletContextualMode READ getTabletContextualMode)
|
||||
Q_PROPERTY(QUuid tabletID READ getCurrentTabletFrameID WRITE setCurrentTabletFrameID)
|
||||
Q_PROPERTY(QUuid homeButtonID READ getCurrentHomeButtonID WRITE setCurrentHomeButtonID)
|
||||
Q_PROPERTY(QUuid homeButtonHighlightID READ getCurrentHomeButtonHightlightID WRITE setCurrentHomeButtonHightlightID)
|
||||
|
@ -75,7 +76,7 @@ public:
|
|||
|
||||
Q_INVOKABLE void closeTablet();
|
||||
|
||||
Q_INVOKABLE void openTablet();
|
||||
Q_INVOKABLE void openTablet(bool contextualMode = false);
|
||||
|
||||
signals:
|
||||
bool shouldShowHandControllersChanged();
|
||||
|
@ -88,9 +89,10 @@ public:
|
|||
|
||||
bool isMounted() const;
|
||||
|
||||
void toggleShouldShowTablet() { _showTablet = !_showTablet; }
|
||||
void setShouldShowTablet(bool value) { _showTablet = value; }
|
||||
void toggleShouldShowTablet();
|
||||
void setShouldShowTablet(bool value);
|
||||
bool getShouldShowTablet() const { return _showTablet; }
|
||||
bool getTabletContextualMode() const { return _tabletContextualMode; }
|
||||
|
||||
void setCurrentTabletFrameID(QUuid tabletID) { _tabletUIID = tabletID; }
|
||||
QUuid getCurrentTabletFrameID() const { return _tabletUIID; }
|
||||
|
@ -106,6 +108,7 @@ public:
|
|||
|
||||
private:
|
||||
bool _showTablet { false };
|
||||
bool _tabletContextualMode { false };
|
||||
QUuid _tabletUIID; // this is the entityID of the tablet frame
|
||||
QUuid _tabletScreenID; // this is the overlayID which is part of (a child of) the tablet-ui.
|
||||
QUuid _homeButtonID;
|
||||
|
|
|
@ -222,6 +222,7 @@ void Web3DOverlay::setupQmlSurface() {
|
|||
_webSurface->getSurfaceContext()->setContextProperty("SoundCache", DependencyManager::get<SoundCache>().data());
|
||||
_webSurface->getSurfaceContext()->setContextProperty("MenuInterface", MenuScriptingInterface::getInstance());
|
||||
_webSurface->getSurfaceContext()->setContextProperty("Settings", SettingsScriptingInterface::getInstance());
|
||||
_webSurface->getSurfaceContext()->setContextProperty("Render", AbstractViewStateInterface::instance()->getRenderEngine()->getConfiguration().get());
|
||||
_webSurface->getSurfaceContext()->setContextProperty("Controller", DependencyManager::get<controller::ScriptingInterface>().data());
|
||||
_webSurface->getSurfaceContext()->setContextProperty("Web3DOverlay", this);
|
||||
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
#include "HTTPConnection.h"
|
||||
#include "EmbeddedWebserverLogging.h"
|
||||
#include "HTTPManager.h"
|
||||
#include <QUrlQuery>
|
||||
|
||||
const char* HTTPConnection::StatusCode200 = "200 OK";
|
||||
const char* HTTPConnection::StatusCode301 = "301 Moved Permanently";
|
||||
|
@ -52,10 +53,31 @@ HTTPConnection::~HTTPConnection() {
|
|||
}
|
||||
}
|
||||
|
||||
QHash<QString, QString> HTTPConnection::parseUrlEncodedForm() {
|
||||
// make sure we have the correct MIME type
|
||||
QList<QByteArray> elements = _requestHeaders.value("Content-Type").split(';');
|
||||
|
||||
QString contentType = elements.at(0).trimmed();
|
||||
if (contentType != "application/x-www-form-urlencoded") {
|
||||
return QHash<QString, QString>();
|
||||
}
|
||||
|
||||
QUrlQuery form { _requestContent };
|
||||
QHash<QString, QString> pairs;
|
||||
for (auto pair : form.queryItems()) {
|
||||
pairs[QUrl::fromPercentEncoding(pair.first.toLatin1())] = QUrl::fromPercentEncoding(pair.second.toLatin1());
|
||||
}
|
||||
|
||||
return pairs;
|
||||
}
|
||||
|
||||
QList<FormData> HTTPConnection::parseFormData() const {
|
||||
// make sure we have the correct MIME type
|
||||
QList<QByteArray> elements = _requestHeaders.value("Content-Type").split(';');
|
||||
if (elements.at(0).trimmed() != "multipart/form-data") {
|
||||
|
||||
QString contentType = elements.at(0).trimmed();
|
||||
|
||||
if (contentType != "multipart/form-data") {
|
||||
return QList<FormData>();
|
||||
}
|
||||
|
||||
|
|
|
@ -79,6 +79,10 @@ public:
|
|||
/// Parses the request content as form data, returning a list of header/content pairs.
|
||||
QList<FormData> parseFormData () const;
|
||||
|
||||
/// Parses the request content as a url encoded form, returning a hash of key/value pairs.
|
||||
/// Duplicate keys are not supported.
|
||||
QHash<QString, QString> parseUrlEncodedForm();
|
||||
|
||||
/// Sends a response and closes the connection.
|
||||
void respond (const char* code, const QByteArray& content = QByteArray(),
|
||||
const char* contentType = DefaultContentType,
|
||||
|
|
|
@ -100,7 +100,11 @@ EntityRendererPointer EntityTreeRenderer::renderableForEntityId(const EntityItem
|
|||
|
||||
render::ItemID EntityTreeRenderer::renderableIdForEntityId(const EntityItemID& id) const {
|
||||
auto renderable = renderableForEntityId(id);
|
||||
return renderable ? renderable->getRenderItemID() : render::Item::INVALID_ITEM_ID;
|
||||
if (renderable) {
|
||||
return renderable->getRenderItemID();
|
||||
} else {
|
||||
return render::Item::INVALID_ITEM_ID;
|
||||
}
|
||||
}
|
||||
|
||||
int EntityTreeRenderer::_entitiesScriptEngineCount = 0;
|
||||
|
|
|
@ -557,10 +557,10 @@ void NodeList::pingPunchForDomainServer() {
|
|||
flagTimeForConnectionStep(LimitedNodeList::ConnectionStep::SendPingsToDS);
|
||||
|
||||
// send the ping packet to the local and public sockets for this node
|
||||
auto localPingPacket = constructICEPingPacket(PingType::Local, _sessionUUID);
|
||||
auto localPingPacket = constructICEPingPacket(PingType::Local, _domainHandler.getICEClientID());
|
||||
sendPacket(std::move(localPingPacket), _domainHandler.getICEPeer().getLocalSocket());
|
||||
|
||||
auto publicPingPacket = constructICEPingPacket(PingType::Public, _sessionUUID);
|
||||
auto publicPingPacket = constructICEPingPacket(PingType::Public, _domainHandler.getICEClientID());
|
||||
sendPacket(std::move(publicPingPacket), _domainHandler.getICEPeer().getPublicSocket());
|
||||
|
||||
_domainHandler.getICEPeer().incrementConnectionAttempts();
|
||||
|
|
|
@ -72,6 +72,8 @@ PacketVersion versionForPacketType(PacketType packetType) {
|
|||
case PacketType::MicrophoneAudioWithEcho:
|
||||
case PacketType::AudioStreamStats:
|
||||
return static_cast<PacketVersion>(AudioVersion::HighDynamicRangeVolume);
|
||||
case PacketType::ICEPing:
|
||||
return static_cast<PacketVersion>(IcePingVersion::SendICEPeerID);
|
||||
default:
|
||||
return 17;
|
||||
}
|
||||
|
|
|
@ -278,4 +278,8 @@ enum class MessageDataVersion : PacketVersion {
|
|||
TextOrBinaryData = 18
|
||||
};
|
||||
|
||||
enum class IcePingVersion : PacketVersion {
|
||||
SendICEPeerID = 18
|
||||
};
|
||||
|
||||
#endif // hifi_PacketHeaders_h
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
//
|
||||
// ConfigSlider.qml
|
||||
// examples/utilities/tools/render
|
||||
//
|
||||
// Created by Zach Pomerantz on 2/8/2016
|
||||
// Copyright 2016 High Fidelity, Inc.
|
||||
|
@ -8,12 +7,21 @@
|
|||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or https://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
import QtQuick 2.5
|
||||
import QtQuick.Controls 1.4
|
||||
|
||||
import QtQuick 2.7
|
||||
import QtQuick.Controls 1.4 as Original
|
||||
import QtQuick.Controls.Styles 1.4
|
||||
|
||||
import "qrc:///qml/styles-uit"
|
||||
import "qrc:///qml/controls-uit" as HifiControls
|
||||
|
||||
|
||||
Item {
|
||||
HifiConstants { id: hifi }
|
||||
id: root
|
||||
width: 400
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
height: 24
|
||||
property bool integral: false
|
||||
property var config
|
||||
|
@ -28,22 +36,21 @@ Item {
|
|||
bindingControl.when = true;
|
||||
}
|
||||
|
||||
Label {
|
||||
HifiControls.Label {
|
||||
id: labelControl
|
||||
text: root.label
|
||||
enabled: true
|
||||
anchors.left: root.left
|
||||
anchors.leftMargin: 8
|
||||
anchors.top: root.top
|
||||
anchors.topMargin: 7
|
||||
anchors.right: root.horizontalCenter
|
||||
anchors.verticalCenter: root.verticalCenter
|
||||
}
|
||||
|
||||
Label {
|
||||
HifiControls.Label {
|
||||
id: labelValue
|
||||
text: sliderControl.value.toFixed(root.integral ? 0 : 2)
|
||||
anchors.left: root.left
|
||||
anchors.leftMargin: 200
|
||||
anchors.top: root.top
|
||||
anchors.topMargin: 15
|
||||
anchors.right: root.right
|
||||
anchors.bottom: root.bottom
|
||||
anchors.bottomMargin: 0
|
||||
}
|
||||
|
||||
Binding {
|
||||
|
@ -54,14 +61,13 @@ Item {
|
|||
when: false
|
||||
}
|
||||
|
||||
Slider {
|
||||
HifiControls.Slider {
|
||||
id: sliderControl
|
||||
stepSize: root.integral ? 1.0 : 0.0
|
||||
width: root.width-130
|
||||
height: 20
|
||||
anchors.left: root.horizontalCenter
|
||||
anchors.right: root.right
|
||||
anchors.rightMargin: 8
|
||||
anchors.rightMargin: 0
|
||||
anchors.top: root.top
|
||||
anchors.topMargin: 3
|
||||
anchors.topMargin: 0
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,49 +0,0 @@
|
|||
//
|
||||
// debugDeferredLighting.js
|
||||
//
|
||||
// Created by Sam Gateau on 6/6/2016
|
||||
// Copyright 2016 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or https://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
// Set up the qml ui
|
||||
var qml = Script.resolvePath('deferredLighting.qml');
|
||||
var window = new OverlayWindow({
|
||||
title: 'Lighting',
|
||||
source: qml,
|
||||
width: 400, height:400,
|
||||
});
|
||||
window.setPosition(Window.innerWidth - 420, 50);
|
||||
window.closed.connect(function() { Script.stop(); });
|
||||
|
||||
|
||||
var DDB = Render.RenderDeferredTask.DebugDeferredBuffer;
|
||||
DDB.enabled = true;
|
||||
DDB.mode = 0;
|
||||
|
||||
// Debug buffer sizing
|
||||
var resizing = false;
|
||||
Controller.mousePressEvent.connect(function (e) {
|
||||
if (shouldStartResizing(e.x)) {
|
||||
resizing = true;
|
||||
}
|
||||
});
|
||||
Controller.mouseReleaseEvent.connect(function() { resizing = false; });
|
||||
Controller.mouseMoveEvent.connect(function (e) { resizing && setDebugBufferSize(e.x); });
|
||||
Script.scriptEnding.connect(function () { DDB.enabled = false; });
|
||||
|
||||
function shouldStartResizing(eventX) {
|
||||
var x = Math.abs(eventX - Window.innerWidth * (1.0 + DDB.size.x) / 2.0);
|
||||
var mode = DDB.mode;
|
||||
return mode !== 0 && x < 20;
|
||||
}
|
||||
|
||||
function setDebugBufferSize(x) {
|
||||
x = (2.0 * (x / Window.innerWidth) - 1.0); // scale
|
||||
x = Math.min(Math.max(-1, x), 1); // clamp
|
||||
DDB.size = { x: x, y: -1, z: 1, w: 1 };
|
||||
}
|
||||
|
||||
|
|
@ -7,215 +7,268 @@
|
|||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or https://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
import QtQuick 2.5
|
||||
import QtQuick 2.7
|
||||
import QtQuick.Controls 1.4
|
||||
import "configSlider"
|
||||
import QtQuick.Layouts 1.3
|
||||
|
||||
Column {
|
||||
spacing: 8
|
||||
property var mainViewTask: Render.getConfig("RenderMainView")
|
||||
import "qrc:///qml/styles-uit"
|
||||
import "qrc:///qml/controls-uit" as HifiControls
|
||||
import "configSlider"
|
||||
|
||||
Rectangle {
|
||||
HifiConstants { id: hifi;}
|
||||
id: render;
|
||||
anchors.margins: hifi.dimensions.contentMargin.x
|
||||
|
||||
Row {
|
||||
spacing: 8
|
||||
Column {
|
||||
spacing: 10
|
||||
Repeater {
|
||||
model: [
|
||||
"Unlit:LightingModel:enableUnlit",
|
||||
"Emissive:LightingModel:enableEmissive",
|
||||
"Lightmap:LightingModel:enableLightmap",
|
||||
"Background:LightingModel:enableBackground",
|
||||
"ssao:AmbientOcclusion:enabled",
|
||||
"Textures:LightingModel:enableMaterialTexturing"
|
||||
]
|
||||
CheckBox {
|
||||
text: modelData.split(":")[0]
|
||||
checked: mainViewTask.getConfig(modelData.split(":")[1])[modelData.split(":")[2]]
|
||||
onCheckedChanged: { mainViewTask.getConfig(modelData.split(":")[1])[modelData.split(":")[2]] = checked }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Column {
|
||||
spacing: 10
|
||||
Repeater {
|
||||
model: [
|
||||
"Obscurance:LightingModel:enableObscurance",
|
||||
"Scattering:LightingModel:enableScattering",
|
||||
"Diffuse:LightingModel:enableDiffuse",
|
||||
"Specular:LightingModel:enableSpecular",
|
||||
"Albedo:LightingModel:enableAlbedo",
|
||||
"Wireframe:LightingModel:enableWireframe"
|
||||
]
|
||||
CheckBox {
|
||||
text: modelData.split(":")[0]
|
||||
checked: mainViewTask.getConfig(modelData.split(":")[1])[modelData.split(":")[2]]
|
||||
onCheckedChanged: { mainViewTask.getConfig(modelData.split(":")[1])[modelData.split(":")[2]] = checked }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Column {
|
||||
spacing: 10
|
||||
Repeater {
|
||||
model: [
|
||||
"Ambient:LightingModel:enableAmbientLight",
|
||||
"Directional:LightingModel:enableDirectionalLight",
|
||||
"Point:LightingModel:enablePointLight",
|
||||
"Spot:LightingModel:enableSpotLight",
|
||||
"Light Contour:LightingModel:showLightContour",
|
||||
"Zone Stack:DrawZoneStack:enabled",
|
||||
"Shadow:RenderShadowTask:enabled"
|
||||
]
|
||||
CheckBox {
|
||||
text: modelData.split(":")[0]
|
||||
checked: mainViewTask.getConfig(modelData.split(":")[1])[modelData.split(":")[2]]
|
||||
onCheckedChanged: { mainViewTask.getConfig(modelData.split(":")[1])[modelData.split(":")[2]] = checked }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
color: hifi.colors.baseGray;
|
||||
property var mainViewTask: Render.getConfig("RenderMainView")
|
||||
|
||||
Column {
|
||||
spacing: 10
|
||||
Repeater {
|
||||
model: [ "Tone Mapping Exposure:ToneMapping:exposure:5.0:-5.0"
|
||||
]
|
||||
ConfigSlider {
|
||||
label: qsTr(modelData.split(":")[0])
|
||||
integral: false
|
||||
config: mainViewTask.getConfig(modelData.split(":")[1])
|
||||
property: modelData.split(":")[2]
|
||||
max: modelData.split(":")[3]
|
||||
min: modelData.split(":")[4]
|
||||
spacing: 5
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.margins: hifi.dimensions.contentMargin.x
|
||||
//padding: hifi.dimensions.contentMargin.x
|
||||
HifiControls.Label {
|
||||
text: "Shading"
|
||||
}
|
||||
Row {
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
|
||||
spacing: 20
|
||||
Column {
|
||||
spacing: 10
|
||||
// padding: 10
|
||||
Repeater {
|
||||
model: [
|
||||
"Unlit:LightingModel:enableUnlit",
|
||||
"Emissive:LightingModel:enableEmissive",
|
||||
"Lightmap:LightingModel:enableLightmap",
|
||||
"Background:LightingModel:enableBackground",
|
||||
"ssao:AmbientOcclusion:enabled",
|
||||
"Textures:LightingModel:enableMaterialTexturing"
|
||||
]
|
||||
HifiControls.CheckBox {
|
||||
boxSize: 20
|
||||
text: modelData.split(":")[0]
|
||||
checked: render.mainViewTask.getConfig(modelData.split(":")[1])[modelData.split(":")[2]]
|
||||
onCheckedChanged: { render.mainViewTask.getConfig(modelData.split(":")[1])[modelData.split(":")[2]] = checked }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Column {
|
||||
spacing: 10
|
||||
Repeater {
|
||||
model: [
|
||||
"Obscurance:LightingModel:enableObscurance",
|
||||
"Scattering:LightingModel:enableScattering",
|
||||
"Diffuse:LightingModel:enableDiffuse",
|
||||
"Specular:LightingModel:enableSpecular",
|
||||
"Albedo:LightingModel:enableAlbedo",
|
||||
"Wireframe:LightingModel:enableWireframe"
|
||||
]
|
||||
HifiControls.CheckBox {
|
||||
boxSize: 20
|
||||
text: modelData.split(":")[0]
|
||||
checked: render.mainViewTask.getConfig(modelData.split(":")[1])[modelData.split(":")[2]]
|
||||
onCheckedChanged: { render.mainViewTask.getConfig(modelData.split(":")[1])[modelData.split(":")[2]] = checked }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Column {
|
||||
spacing: 10
|
||||
Repeater {
|
||||
model: [
|
||||
"Ambient:LightingModel:enableAmbientLight",
|
||||
"Directional:LightingModel:enableDirectionalLight",
|
||||
"Point:LightingModel:enablePointLight",
|
||||
"Spot:LightingModel:enableSpotLight",
|
||||
"Light Contour:LightingModel:showLightContour",
|
||||
"Zone Stack:DrawZoneStack:enabled",
|
||||
"Shadow:RenderShadowTask:enabled"
|
||||
]
|
||||
HifiControls.CheckBox {
|
||||
boxSize: 20
|
||||
text: modelData.split(":")[0]
|
||||
checked: render.mainViewTask.getConfig(modelData.split(":")[1])[modelData.split(":")[2]]
|
||||
onCheckedChanged: { render.mainViewTask.getConfig(modelData.split(":")[1])[modelData.split(":")[2]] = checked }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Separator {}
|
||||
Column {
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
spacing: 10
|
||||
Repeater {
|
||||
model: [ "Tone Mapping Exposure:ToneMapping:exposure:5.0:-5.0"
|
||||
]
|
||||
ConfigSlider {
|
||||
label: qsTr(modelData.split(":")[0])
|
||||
integral: false
|
||||
config: render.mainViewTask.getConfig(modelData.split(":")[1])
|
||||
property: modelData.split(":")[2]
|
||||
max: modelData.split(":")[3]
|
||||
min: modelData.split(":")[4]
|
||||
|
||||
Row {
|
||||
Label {
|
||||
text: "Tone Mapping Curve"
|
||||
anchors.left: root.left
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
}
|
||||
}
|
||||
|
||||
ComboBox {
|
||||
anchors.right: root.right
|
||||
currentIndex: 1
|
||||
Item {
|
||||
height: childrenRect.height
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
|
||||
HifiControls.Label {
|
||||
text: "Tone Mapping Curve"
|
||||
anchors.left: parent.left
|
||||
}
|
||||
|
||||
HifiControls.ComboBox {
|
||||
anchors.right: parent.right
|
||||
currentIndex: 1
|
||||
model: ListModel {
|
||||
id: cbItems
|
||||
ListElement { text: "RGB"; color: "Yellow" }
|
||||
ListElement { text: "SRGB"; color: "Green" }
|
||||
ListElement { text: "Reinhard"; color: "Yellow" }
|
||||
ListElement { text: "Filmic"; color: "White" }
|
||||
}
|
||||
width: 200
|
||||
onCurrentIndexChanged: { render.mainViewTask.getConfig("ToneMapping")["curve"] = currentIndex }
|
||||
}
|
||||
}
|
||||
}
|
||||
Separator {}
|
||||
|
||||
Item {
|
||||
height: childrenRect.height
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
|
||||
id: framebuffer
|
||||
|
||||
HifiControls.Label {
|
||||
text: "Debug Framebuffer"
|
||||
anchors.left: parent.left
|
||||
}
|
||||
|
||||
property var config: render.mainViewTask.getConfig("DebugDeferredBuffer")
|
||||
|
||||
function setDebugMode(mode) {
|
||||
framebuffer.config.enabled = (mode != 0);
|
||||
framebuffer.config.mode = mode;
|
||||
}
|
||||
|
||||
HifiControls.ComboBox {
|
||||
anchors.right: parent.right
|
||||
currentIndex: 0
|
||||
model: ListModel {
|
||||
id: cbItems
|
||||
ListElement { text: "RGB"; color: "Yellow" }
|
||||
ListElement { text: "SRGB"; color: "Green" }
|
||||
ListElement { text: "Reinhard"; color: "Yellow" }
|
||||
ListElement { text: "Filmic"; color: "White" }
|
||||
id: cbItemsFramebuffer
|
||||
ListElement { text: "Off"; color: "Yellow" }
|
||||
ListElement { text: "Depth"; color: "Green" }
|
||||
ListElement { text: "Albedo"; color: "Yellow" }
|
||||
ListElement { text: "Normal"; color: "White" }
|
||||
ListElement { text: "Roughness"; color: "White" }
|
||||
ListElement { text: "Metallic"; color: "White" }
|
||||
ListElement { text: "Emissive"; color: "White" }
|
||||
ListElement { text: "Unlit"; color: "White" }
|
||||
ListElement { text: "Occlusion"; color: "White" }
|
||||
ListElement { text: "Lightmap"; color: "White" }
|
||||
ListElement { text: "Scattering"; color: "White" }
|
||||
ListElement { text: "Lighting"; color: "White" }
|
||||
ListElement { text: "Shadow"; color: "White" }
|
||||
ListElement { text: "Linear Depth"; color: "White" }
|
||||
ListElement { text: "Half Linear Depth"; color: "White" }
|
||||
ListElement { text: "Half Normal"; color: "White" }
|
||||
ListElement { text: "Mid Curvature"; color: "White" }
|
||||
ListElement { text: "Mid Normal"; color: "White" }
|
||||
ListElement { text: "Low Curvature"; color: "White" }
|
||||
ListElement { text: "Low Normal"; color: "White" }
|
||||
ListElement { text: "Curvature Occlusion"; color: "White" }
|
||||
ListElement { text: "Debug Scattering"; color: "White" }
|
||||
ListElement { text: "Ambient Occlusion"; color: "White" }
|
||||
ListElement { text: "Ambient Occlusion Blurred"; color: "White" }
|
||||
ListElement { text: "Custom"; color: "White" }
|
||||
}
|
||||
width: 200
|
||||
onCurrentIndexChanged: { mainViewTask.getConfig("ToneMapping")["curve"] = currentIndex }
|
||||
onCurrentIndexChanged: { framebuffer.setDebugMode(currentIndex) }
|
||||
}
|
||||
}
|
||||
|
||||
Separator {}
|
||||
Row {
|
||||
spacing: 10
|
||||
Column {
|
||||
spacing: 10
|
||||
|
||||
HifiControls.CheckBox {
|
||||
boxSize: 20
|
||||
text: "Opaques"
|
||||
checked: render.mainViewTask.getConfig("DrawOpaqueBounds")["enabled"]
|
||||
onCheckedChanged: { render.mainViewTask.getConfig("DrawOpaqueBounds")["enabled"] = checked }
|
||||
}
|
||||
HifiControls.CheckBox {
|
||||
boxSize: 20
|
||||
text: "Transparents"
|
||||
checked: render.mainViewTask.getConfig("DrawTransparentBounds")["enabled"]
|
||||
onCheckedChanged: { render.mainViewTask.getConfig("DrawTransparentBounds")["enabled"] = checked }
|
||||
}
|
||||
HifiControls.CheckBox {
|
||||
boxSize: 20
|
||||
text: "Opaques in Front"
|
||||
checked: render.mainViewTask.getConfig("DrawOverlayInFrontOpaqueBounds")["enabled"]
|
||||
onCheckedChanged: { render.mainViewTask.getConfig("DrawOverlayInFrontOpaqueBounds")["enabled"] = checked }
|
||||
}
|
||||
HifiControls.CheckBox {
|
||||
boxSize: 20
|
||||
text: "Transparents in Front"
|
||||
checked: render.mainViewTask.getConfig("DrawOverlayInFrontTransparentBounds")["enabled"]
|
||||
onCheckedChanged: { render.mainViewTask.getConfig("DrawOverlayInFrontTransparentBounds")["enabled"] = checked }
|
||||
}
|
||||
HifiControls.CheckBox {
|
||||
boxSize: 20
|
||||
text: "Opaques in HUD"
|
||||
checked: render.mainViewTask.getConfig("DrawOverlayHUDOpaqueBounds")["enabled"]
|
||||
onCheckedChanged: { render.mainViewTask.getConfig("DrawOverlayHUDOpaqueBounds")["enabled"] = checked }
|
||||
}
|
||||
HifiControls.CheckBox {
|
||||
boxSize: 20
|
||||
text: "Transparents in HUD"
|
||||
checked: render.mainViewTask.getConfig("DrawOverlayHUDTransparentBounds")["enabled"]
|
||||
onCheckedChanged: { render.mainViewTask.getConfig("DrawOverlayHUDTransparentBounds")["enabled"] = checked }
|
||||
}
|
||||
|
||||
}
|
||||
Column {
|
||||
spacing: 10
|
||||
HifiControls.CheckBox {
|
||||
boxSize: 20
|
||||
text: "Metas"
|
||||
checked: render.mainViewTask.getConfig("DrawMetaBounds")["enabled"]
|
||||
onCheckedChanged: { render.mainViewTask.getConfig("DrawMetaBounds")["enabled"] = checked }
|
||||
}
|
||||
HifiControls.CheckBox {
|
||||
boxSize: 20
|
||||
text: "Lights"
|
||||
checked: render.mainViewTask.getConfig("DrawLightBounds")["enabled"]
|
||||
onCheckedChanged: { render.mainViewTask.getConfig("DrawLightBounds")["enabled"] = checked; }
|
||||
}
|
||||
HifiControls.CheckBox {
|
||||
boxSize: 20
|
||||
text: "Zones"
|
||||
checked: render.mainViewTask.getConfig("DrawZones")["enabled"]
|
||||
onCheckedChanged: { render.mainViewTask.getConfig("ZoneRenderer")["enabled"] = checked; render.mainViewTask.getConfig("DrawZones")["enabled"] = checked; }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Row {
|
||||
id: framebuffer
|
||||
spacing: 10
|
||||
|
||||
Label {
|
||||
text: "Debug Framebuffer"
|
||||
anchors.left: root.left
|
||||
}
|
||||
|
||||
property var config: mainViewTask.getConfig("DebugDeferredBuffer")
|
||||
|
||||
function setDebugMode(mode) {
|
||||
framebuffer.config.enabled = (mode != 0);
|
||||
framebuffer.config.mode = mode;
|
||||
}
|
||||
|
||||
ComboBox {
|
||||
anchors.right: root.right
|
||||
currentIndex: 0
|
||||
model: ListModel {
|
||||
id: cbItemsFramebuffer
|
||||
ListElement { text: "Off"; color: "Yellow" }
|
||||
ListElement { text: "Depth"; color: "Green" }
|
||||
ListElement { text: "Albedo"; color: "Yellow" }
|
||||
ListElement { text: "Normal"; color: "White" }
|
||||
ListElement { text: "Roughness"; color: "White" }
|
||||
ListElement { text: "Metallic"; color: "White" }
|
||||
ListElement { text: "Emissive"; color: "White" }
|
||||
ListElement { text: "Unlit"; color: "White" }
|
||||
ListElement { text: "Occlusion"; color: "White" }
|
||||
ListElement { text: "Lightmap"; color: "White" }
|
||||
ListElement { text: "Scattering"; color: "White" }
|
||||
ListElement { text: "Lighting"; color: "White" }
|
||||
ListElement { text: "Shadow"; color: "White" }
|
||||
ListElement { text: "Linear Depth"; color: "White" }
|
||||
ListElement { text: "Half Linear Depth"; color: "White" }
|
||||
ListElement { text: "Half Normal"; color: "White" }
|
||||
ListElement { text: "Mid Curvature"; color: "White" }
|
||||
ListElement { text: "Mid Normal"; color: "White" }
|
||||
ListElement { text: "Low Curvature"; color: "White" }
|
||||
ListElement { text: "Low Normal"; color: "White" }
|
||||
ListElement { text: "Curvature Occlusion"; color: "White" }
|
||||
ListElement { text: "Debug Scattering"; color: "White" }
|
||||
ListElement { text: "Ambient Occlusion"; color: "White" }
|
||||
ListElement { text: "Ambient Occlusion Blurred"; color: "White" }
|
||||
ListElement { text: "Custom"; color: "White" }
|
||||
}
|
||||
width: 200
|
||||
onCurrentIndexChanged: { framebuffer.setDebugMode(currentIndex) }
|
||||
}
|
||||
}
|
||||
|
||||
Row {
|
||||
Column {
|
||||
|
||||
CheckBox {
|
||||
text: "Opaques"
|
||||
checked: mainViewTask.getConfig("DrawOpaqueBounds")["enabled"]
|
||||
onCheckedChanged: { mainViewTask.getConfig("DrawOpaqueBounds")["enabled"] = checked }
|
||||
}
|
||||
CheckBox {
|
||||
text: "Transparents"
|
||||
checked: mainViewTask.getConfig("DrawTransparentBounds")["enabled"]
|
||||
onCheckedChanged: { mainViewTask.getConfig("DrawTransparentBounds")["enabled"] = checked }
|
||||
}
|
||||
CheckBox {
|
||||
text: "Opaques in Front"
|
||||
checked: mainViewTask.getConfig("DrawOverlayInFrontOpaqueBounds")["enabled"]
|
||||
onCheckedChanged: { mainViewTask.getConfig("DrawOverlayInFrontOpaqueBounds")["enabled"] = checked }
|
||||
}
|
||||
CheckBox {
|
||||
text: "Transparents in Front"
|
||||
checked: mainViewTask.getConfig("DrawOverlayInFrontTransparentBounds")["enabled"]
|
||||
onCheckedChanged: { mainViewTask.getConfig("DrawOverlayInFrontTransparentBounds")["enabled"] = checked }
|
||||
}
|
||||
CheckBox {
|
||||
text: "Opaques in HUD"
|
||||
checked: mainViewTask.getConfig("DrawOverlayHUDOpaqueBounds")["enabled"]
|
||||
onCheckedChanged: { mainViewTask.getConfig("DrawOverlayHUDOpaqueBounds")["enabled"] = checked }
|
||||
}
|
||||
CheckBox {
|
||||
text: "Transparents in HUD"
|
||||
checked: mainViewTask.getConfig("DrawOverlayHUDTransparentBounds")["enabled"]
|
||||
onCheckedChanged: { mainViewTask.getConfig("DrawOverlayHUDTransparentBounds")["enabled"] = checked }
|
||||
}
|
||||
|
||||
}
|
||||
Column {
|
||||
CheckBox {
|
||||
text: "Metas"
|
||||
checked: mainViewTask.getConfig("DrawMetaBounds")["enabled"]
|
||||
onCheckedChanged: { mainViewTask.getConfig("DrawMetaBounds")["enabled"] = checked }
|
||||
}
|
||||
CheckBox {
|
||||
text: "Lights"
|
||||
checked: mainViewTask.getConfig("DrawLightBounds")["enabled"]
|
||||
onCheckedChanged: { mainViewTask.getConfig("DrawLightBounds")["enabled"] = checked; }
|
||||
}
|
||||
CheckBox {
|
||||
text: "Zones"
|
||||
checked: mainViewTask.getConfig("DrawZones")["enabled"]
|
||||
onCheckedChanged: { mainViewTask.getConfig("ZoneRenderer")["enabled"] = checked; mainViewTask.getConfig("DrawZones")["enabled"] = checked; }
|
||||
}
|
||||
}
|
||||
}
|
||||
//}
|
||||
}
|
||||
|
||||
|
|
102
scripts/developer/utilities/render/luci.js
Normal file
102
scripts/developer/utilities/render/luci.js
Normal file
|
@ -0,0 +1,102 @@
|
|||
"use strict";
|
||||
|
||||
//
|
||||
// Luci.js
|
||||
// tablet-engine app
|
||||
//
|
||||
// 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() {
|
||||
var TABLET_BUTTON_NAME = "LUCI";
|
||||
var QMLAPP_URL = Script.resolvePath("./deferredLighting.qml");
|
||||
var ICON_URL = Script.resolvePath("../../../system/assets/images/luci-i.svg");
|
||||
var ACTIVE_ICON_URL = Script.resolvePath("../../../system/assets/images/luci-a.svg");
|
||||
|
||||
|
||||
var onLuciScreen = false;
|
||||
|
||||
function onClicked() {
|
||||
if (onLuciScreen) {
|
||||
tablet.gotoHomeScreen();
|
||||
} else {
|
||||
tablet.loadQMLSource(QMLAPP_URL);
|
||||
}
|
||||
}
|
||||
|
||||
var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system");
|
||||
var button = tablet.addButton({
|
||||
text: TABLET_BUTTON_NAME,
|
||||
icon: ICON_URL,
|
||||
activeIcon: ACTIVE_ICON_URL,
|
||||
sortOrder: 1
|
||||
});
|
||||
|
||||
var hasEventBridge = false;
|
||||
|
||||
function wireEventBridge(on) {
|
||||
if (!tablet) {
|
||||
print("Warning in wireEventBridge(): 'tablet' undefined!");
|
||||
return;
|
||||
}
|
||||
if (on) {
|
||||
if (!hasEventBridge) {
|
||||
tablet.fromQml.connect(fromQml);
|
||||
hasEventBridge = true;
|
||||
}
|
||||
} else {
|
||||
if (hasEventBridge) {
|
||||
tablet.fromQml.disconnect(fromQml);
|
||||
hasEventBridge = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function onScreenChanged(type, url) {
|
||||
if (url === QMLAPP_URL) {
|
||||
onLuciScreen = true;
|
||||
} else {
|
||||
onLuciScreen = false;
|
||||
}
|
||||
|
||||
button.editProperties({isActive: onLuciScreen});
|
||||
wireEventBridge(onLuciScreen);
|
||||
}
|
||||
|
||||
function fromQml(message) {
|
||||
}
|
||||
|
||||
button.clicked.connect(onClicked);
|
||||
tablet.screenChanged.connect(onScreenChanged);
|
||||
|
||||
var moveDebugCursor = false;
|
||||
Controller.mousePressEvent.connect(function (e) {
|
||||
if (e.isMiddleButton) {
|
||||
moveDebugCursor = true;
|
||||
setDebugCursor(e.x, e.y);
|
||||
}
|
||||
});
|
||||
Controller.mouseReleaseEvent.connect(function() { moveDebugCursor = false; });
|
||||
Controller.mouseMoveEvent.connect(function (e) { if (moveDebugCursor) setDebugCursor(e.x, e.y); });
|
||||
|
||||
|
||||
Script.scriptEnding.connect(function () {
|
||||
if (onLuciScreen) {
|
||||
tablet.gotoHomeScreen();
|
||||
}
|
||||
button.clicked.disconnect(onClicked);
|
||||
tablet.screenChanged.disconnect(onScreenChanged);
|
||||
tablet.removeButton(button);
|
||||
});
|
||||
|
||||
function setDebugCursor(x, y) {
|
||||
nx = (x / Window.innerWidth);
|
||||
ny = 1.0 - ((y) / (Window.innerHeight - 32));
|
||||
|
||||
Render.getConfig("RenderMainView").getConfig("Antialiasing").debugCursorTexcoord = { x: nx, y: ny };
|
||||
}
|
||||
|
||||
}());
|
10
scripts/system/assets/images/luci-a.svg
Normal file
10
scripts/system/assets/images/luci-a.svg
Normal file
|
@ -0,0 +1,10 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 21.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 50 50" style="enable-background:new 0 0 50 50;" xml:space="preserve">
|
||||
<path d="M18.5,36.5c-6.8-2.2-6.8-11.8-0.1-14.1l12.1-4.1c-2.6-2.4-6.1-3.8-9.9-3.8c-8.2,0-14.8,6.6-14.8,14.8
|
||||
c0,8.2,6.6,14.8,14.8,14.8c3.7,0,7.1-1.4,9.7-3.7L18.5,36.5z"/>
|
||||
<path d="M31.9,17.9c-0.5,0.2-0.9,0.3-1.3,0.5c2.9,2.7,4.8,6.6,4.8,10.9c0,4.4-1.9,8.3-4.9,11c0.5,0.2,1,0.5,1.6,0.7l5.9,2.4
|
||||
c3.1,1,6.2-1.3,6.2-4.5V13.2L31.9,17.9z"/>
|
||||
<circle cx="40" cy="6.6" r="3.7"/>
|
||||
</svg>
|
After Width: | Height: | Size: 721 B |
13
scripts/system/assets/images/luci-i.svg
Normal file
13
scripts/system/assets/images/luci-i.svg
Normal file
|
@ -0,0 +1,13 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 21.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 50 50" style="enable-background:new 0 0 50 50;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:#FFFFFF;}
|
||||
</style>
|
||||
<path class="st0" d="M18.5,36.5c-6.8-2.2-6.8-11.8-0.1-14.1l12.1-4.1c-2.6-2.4-6.1-3.8-9.9-3.8c-8.2,0-14.8,6.6-14.8,14.8
|
||||
c0,8.2,6.6,14.8,14.8,14.8c3.7,0,7.1-1.4,9.7-3.7L18.5,36.5z"/>
|
||||
<path class="st0" d="M31.9,17.9c-0.5,0.2-0.9,0.3-1.3,0.5c2.9,2.7,4.8,6.6,4.8,10.9c0,4.4-1.9,8.3-4.9,11c0.5,0.2,1,0.5,1.6,0.7
|
||||
l5.9,2.4c3.1,1,6.2-1.3,6.2-4.5V13.2L31.9,17.9z"/>
|
||||
<circle class="st0" cx="40" cy="6.6" r="3.7"/>
|
||||
</svg>
|
After Width: | Height: | Size: 811 B |
|
@ -124,7 +124,9 @@
|
|||
print("TABLET in showTabletUI, already rezzed");
|
||||
}
|
||||
var tabletProperties = {};
|
||||
UIWebTablet.calculateTabletAttachmentProperties(activeHand, true, tabletProperties);
|
||||
if (!HMD.tabletContextualMode) { // contextual mode forces tablet in place -> don't update attachment
|
||||
UIWebTablet.calculateTabletAttachmentProperties(activeHand, true, tabletProperties);
|
||||
}
|
||||
tabletProperties.visible = true;
|
||||
Overlays.editOverlay(HMD.tabletID, tabletProperties);
|
||||
Overlays.editOverlay(HMD.homeButtonID, { visible: true });
|
||||
|
|
Loading…
Reference in a new issue