mirror of
https://github.com/overte-org/overte.git
synced 2025-04-22 11:13:27 +02:00
Merge branch 'master' of git://github.com/highfidelity/hifi into outline
This commit is contained in:
commit
f2444f9a30
121 changed files with 4084 additions and 1617 deletions
assignment-client/src
cmake/externals/draco
domain-server
interface
resources/qml
AddressBarDialog.qml
controls-uit
dialogs/preferences
hifi
src
libraries
avatars-renderer/src/avatars-renderer
embedded-webserver/src
entities-renderer/src
entities
fbx/src
model/src/model
networking/src
AccountManager.cppAccountManager.hAddressManager.cppAddressManager.hNetworkingConstants.hNodeList.cpp
udt
octree/src
render-utils/src
DeferredGlobalLight.slhDrawHaze.cppDrawHaze.hGeometryCache.cppHaze.slfHaze.slhHazeStage.cppHazeStage.h
script-engine/src
ui/src/ui/types
scripts/developer
|
@ -16,6 +16,10 @@
|
|||
#include <ResourceCache.h>
|
||||
#include <ScriptCache.h>
|
||||
#include <EntityEditFilters.h>
|
||||
#include <NetworkingConstants.h>
|
||||
#include <QJsonArray>
|
||||
#include <QJsonDocument>
|
||||
#include <AddressManager.h>
|
||||
|
||||
#include "AssignmentParentFinder.h"
|
||||
#include "EntityNodeData.h"
|
||||
|
@ -29,15 +33,19 @@ const char* LOCAL_MODELS_PERSIST_FILE = "resources/models.svo";
|
|||
|
||||
EntityServer::EntityServer(ReceivedMessage& message) :
|
||||
OctreeServer(message),
|
||||
_entitySimulation(NULL)
|
||||
_entitySimulation(NULL),
|
||||
_dynamicDomainVerificationTimer(this)
|
||||
{
|
||||
DependencyManager::set<ResourceManager>();
|
||||
DependencyManager::set<ResourceCacheSharedItems>();
|
||||
DependencyManager::set<ScriptCache>();
|
||||
|
||||
auto& packetReceiver = DependencyManager::get<NodeList>()->getPacketReceiver();
|
||||
packetReceiver.registerListenerForTypes({ PacketType::EntityAdd, PacketType::EntityEdit, PacketType::EntityErase, PacketType::EntityPhysics },
|
||||
packetReceiver.registerListenerForTypes({ PacketType::EntityAdd, PacketType::EntityEdit, PacketType::EntityErase, PacketType::EntityPhysics, PacketType::ChallengeOwnership },
|
||||
this, "handleEntityPacket");
|
||||
|
||||
connect(&_dynamicDomainVerificationTimer, &QTimer::timeout, this, &EntityServer::startDynamicDomainVerification);
|
||||
_dynamicDomainVerificationTimer.setSingleShot(true);
|
||||
}
|
||||
|
||||
EntityServer::~EntityServer() {
|
||||
|
@ -93,6 +101,9 @@ void EntityServer::beforeRun() {
|
|||
connect(_pruneDeletedEntitiesTimer, SIGNAL(timeout()), this, SLOT(pruneDeletedEntities()));
|
||||
const int PRUNE_DELETED_MODELS_INTERVAL_MSECS = 1 * 1000; // once every second
|
||||
_pruneDeletedEntitiesTimer->start(PRUNE_DELETED_MODELS_INTERVAL_MSECS);
|
||||
|
||||
DomainHandler& domainHandler = DependencyManager::get<NodeList>()->getDomainHandler();
|
||||
connect(&domainHandler, &DomainHandler::settingsReceiveFail, this, &EntityServer::domainSettingsRequestFailed);
|
||||
}
|
||||
|
||||
void EntityServer::entityCreated(const EntityItem& newEntity, const SharedNodePointer& senderNode) {
|
||||
|
@ -296,6 +307,18 @@ void EntityServer::readAdditionalConfiguration(const QJsonObject& settingsSectio
|
|||
tree->setEntityMaxTmpLifetime(EntityTree::DEFAULT_MAX_TMP_ENTITY_LIFETIME);
|
||||
}
|
||||
|
||||
int minTime;
|
||||
if (readOptionInt("dynamicDomainVerificationTimeMin", settingsSectionObject, minTime)) {
|
||||
_MINIMUM_DYNAMIC_DOMAIN_VERIFICATION_TIMER_MS = minTime * 1000;
|
||||
}
|
||||
|
||||
int maxTime;
|
||||
if (readOptionInt("dynamicDomainVerificationTimeMax", settingsSectionObject, maxTime)) {
|
||||
_MAXIMUM_DYNAMIC_DOMAIN_VERIFICATION_TIMER_MS = maxTime * 1000;
|
||||
}
|
||||
|
||||
startDynamicDomainVerification();
|
||||
|
||||
tree->setWantEditLogging(wantEditLogging);
|
||||
tree->setWantTerseEditLogging(wantTerseEditLogging);
|
||||
|
||||
|
@ -410,3 +433,79 @@ QString EntityServer::serverSubclassStats() {
|
|||
|
||||
return statsString;
|
||||
}
|
||||
|
||||
void EntityServer::domainSettingsRequestFailed() {
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
qCDebug(entities) << "The EntityServer couldn't get the Domain Settings. Starting dynamic domain verification with default values...";
|
||||
|
||||
_MINIMUM_DYNAMIC_DOMAIN_VERIFICATION_TIMER_MS = DEFAULT_MINIMUM_DYNAMIC_DOMAIN_VERIFICATION_TIMER_MS;
|
||||
_MAXIMUM_DYNAMIC_DOMAIN_VERIFICATION_TIMER_MS = DEFAULT_MAXIMUM_DYNAMIC_DOMAIN_VERIFICATION_TIMER_MS;
|
||||
startDynamicDomainVerification();
|
||||
}
|
||||
|
||||
void EntityServer::startDynamicDomainVerification() {
|
||||
qCDebug(entities) << "Starting Dynamic Domain Verification...";
|
||||
|
||||
QString thisDomainID = DependencyManager::get<AddressManager>()->getDomainId().remove(QRegExp("\\{|\\}"));
|
||||
|
||||
EntityTreePointer tree = std::static_pointer_cast<EntityTree>(_tree);
|
||||
QHash<QString, EntityItemID> localMap(tree->getEntityCertificateIDMap());
|
||||
|
||||
QHashIterator<QString, EntityItemID> i(localMap);
|
||||
qCDebug(entities) << localMap.size() << "entities in _entityCertificateIDMap";
|
||||
while (i.hasNext()) {
|
||||
i.next();
|
||||
|
||||
EntityItemPointer entity = tree->findEntityByEntityItemID(i.value());
|
||||
|
||||
if (entity) {
|
||||
if (!entity->verifyStaticCertificateProperties()) {
|
||||
qCDebug(entities) << "During Dynamic Domain Verification, a certified entity with ID" << i.value() << "failed"
|
||||
<< "static certificate verification.";
|
||||
// Delete the entity if it doesn't pass static certificate verification
|
||||
tree->deleteEntity(i.value(), true);
|
||||
} else {
|
||||
|
||||
QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();
|
||||
QNetworkRequest networkRequest;
|
||||
networkRequest.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true);
|
||||
networkRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
|
||||
QUrl requestURL = NetworkingConstants::METAVERSE_SERVER_URL;
|
||||
requestURL.setPath("/api/v1/commerce/proof_of_purchase_status/location");
|
||||
QJsonObject request;
|
||||
request["certificate_id"] = i.key();
|
||||
networkRequest.setUrl(requestURL);
|
||||
|
||||
QNetworkReply* networkReply = NULL;
|
||||
networkReply = networkAccessManager.put(networkRequest, QJsonDocument(request).toJson());
|
||||
|
||||
connect(networkReply, &QNetworkReply::finished, [=]() {
|
||||
QJsonObject jsonObject = QJsonDocument::fromJson(networkReply->readAll()).object();
|
||||
jsonObject = jsonObject["data"].toObject();
|
||||
|
||||
if (networkReply->error() == QNetworkReply::NoError) {
|
||||
if (jsonObject["domain_id"].toString() != thisDomainID) {
|
||||
qCDebug(entities) << "Entity's cert's domain ID" << jsonObject["domain_id"].toString()
|
||||
<< "doesn't match the current Domain ID" << thisDomainID << "; deleting entity" << i.value();
|
||||
tree->deleteEntity(i.value(), true);
|
||||
} else {
|
||||
qCDebug(entities) << "Entity passed dynamic domain verification:" << i.value();
|
||||
}
|
||||
} else {
|
||||
qCDebug(entities) << "Call to" << networkReply->url() << "failed with error" << networkReply->error() << "; deleting entity" << i.value()
|
||||
<< "More info:" << jsonObject;
|
||||
tree->deleteEntity(i.value(), true);
|
||||
}
|
||||
|
||||
networkReply->deleteLater();
|
||||
});
|
||||
}
|
||||
} else {
|
||||
qCWarning(entities) << "During DDV, an entity with ID" << i.value() << "was NOT found in the Entity Tree!";
|
||||
}
|
||||
}
|
||||
|
||||
int nextInterval = qrand() % ((_MAXIMUM_DYNAMIC_DOMAIN_VERIFICATION_TIMER_MS + 1) - _MINIMUM_DYNAMIC_DOMAIN_VERIFICATION_TIMER_MS) + _MINIMUM_DYNAMIC_DOMAIN_VERIFICATION_TIMER_MS;
|
||||
qCDebug(entities) << "Restarting Dynamic Domain Verification timer for" << nextInterval / 1000 << "seconds";
|
||||
_dynamicDomainVerificationTimer.start(nextInterval);
|
||||
}
|
||||
|
|
|
@ -73,6 +73,7 @@ protected:
|
|||
|
||||
private slots:
|
||||
void handleEntityPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode);
|
||||
void domainSettingsRequestFailed();
|
||||
|
||||
private:
|
||||
SimpleEntitySimulationPointer _entitySimulation;
|
||||
|
@ -80,6 +81,13 @@ private:
|
|||
|
||||
QReadWriteLock _viewerSendingStatsLock;
|
||||
QMap<QUuid, QMap<QUuid, ViewerSendingStats>> _viewerSendingStats;
|
||||
|
||||
static const int DEFAULT_MINIMUM_DYNAMIC_DOMAIN_VERIFICATION_TIMER_MS = 45 * 60 * 1000; // 45m
|
||||
static const int DEFAULT_MAXIMUM_DYNAMIC_DOMAIN_VERIFICATION_TIMER_MS = 60 * 60 * 1000; // 1h
|
||||
int _MINIMUM_DYNAMIC_DOMAIN_VERIFICATION_TIMER_MS = DEFAULT_MINIMUM_DYNAMIC_DOMAIN_VERIFICATION_TIMER_MS; // 45m
|
||||
int _MAXIMUM_DYNAMIC_DOMAIN_VERIFICATION_TIMER_MS = DEFAULT_MAXIMUM_DYNAMIC_DOMAIN_VERIFICATION_TIMER_MS; // 1h
|
||||
QTimer _dynamicDomainVerificationTimer;
|
||||
void startDynamicDomainVerification();
|
||||
};
|
||||
|
||||
#endif // hifi_EntityServer_h
|
||||
|
|
|
@ -92,7 +92,11 @@ void OctreeInboundPacketProcessor::processPacket(QSharedPointer<ReceivedMessage>
|
|||
// Ask our tree subclass if it can handle the incoming packet...
|
||||
PacketType packetType = message->getType();
|
||||
|
||||
if (_myServer->getOctree()->handlesEditPacketType(packetType)) {
|
||||
if (packetType == PacketType::ChallengeOwnership) {
|
||||
_myServer->getOctree()->withWriteLock([&] {
|
||||
_myServer->getOctree()->processChallengeOwnershipPacket(*message, sendingNode);
|
||||
});
|
||||
} else if (_myServer->getOctree()->handlesEditPacketType(packetType)) {
|
||||
PerformanceWarning warn(debugProcessPacket, "processPacket KNOWN TYPE", debugProcessPacket);
|
||||
_receivedPacketCount++;
|
||||
|
||||
|
|
|
@ -22,7 +22,6 @@
|
|||
#include <HTTPConnection.h>
|
||||
#include <LogHandler.h>
|
||||
#include <shared/NetworkUtils.h>
|
||||
#include <NetworkingConstants.h>
|
||||
#include <NumericalConstants.h>
|
||||
#include <UUID.h>
|
||||
|
||||
|
|
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.8,
|
||||
"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.",
|
||||
|
@ -207,7 +218,7 @@
|
|||
"name": "standard_permissions",
|
||||
"type": "table",
|
||||
"label": "Domain-Wide User Permissions",
|
||||
"help": "Indicate which types of users can have which <a data-toggle='tooltip' data-html=true title='<p><strong>Domain-Wide User Permissions</strong></p><ul><li><strong>Connect</strong><br />Sets whether a user can connect to the domain.</li><li><strong>Lock / Unlock</strong><br />Sets whether a user change the “locked” property of an entity (either from on to off or off to on).</li><li><strong>Rez</strong><br />Sets whether a user can create new entities.</li><li><strong>Rez Temporary</strong><br />Sets whether a user can create new entities with a finite lifetime.</li><li><strong>Write Assets</strong><br />Sets whether a user can make changes to the domain’s asset-server assets.</li><li><strong>Ignore Max Capacity</strong><br />Sets whether a user can connect even if the domain has reached or exceeded its maximum allowed agents.</li><li><strong>Replace Content</strong><br>Sets whether a user can replace entire content sets by wiping existing domain content.</li></ul><p>Note that permissions assigned to a specific user will supersede any parameter-level permissions that might otherwise apply to that user. Additionally, if more than one parameter is applicable to a given user, the permissions given to that user will be the sum of all applicable parameters. For example, let’s say only localhost users can connect and only logged in users can lock and unlock entities. If a user is both logged in and on localhost then they will be able to both connect and lock/unlock entities.</p>'>domain-wide permissions</a>.",
|
||||
"help": "Indicate which types of users can have which <a data-toggle='tooltip' data-html=true title='<p><strong>Domain-Wide User Permissions</strong></p><ul><li><strong>Connect</strong><br />Sets whether a user can connect to the domain.</li><li><strong>Lock / Unlock</strong><br />Sets whether a user change the “locked” property of an entity (either from on to off or off to on).</li><li><strong>Rez</strong><br />Sets whether a user can create new entities.</li><li><strong>Rez Temporary</strong><br />Sets whether a user can create new entities with a finite lifetime.</li><li><strong>Rez Certified</strong><br />Sets whether a user can create new certified entities.</li><li><strong>Rez Temporary Certified</strong><br />Sets whether a user can create new certified entities with a finite lifetime.</li><li><strong>Write Assets</strong><br />Sets whether a user can make changes to the domain’s asset-server assets.</li><li><strong>Ignore Max Capacity</strong><br />Sets whether a user can connect even if the domain has reached or exceeded its maximum allowed agents.</li><li><strong>Replace Content</strong><br>Sets whether a user can replace entire content sets by wiping existing domain content.</li></ul><p>Note that permissions assigned to a specific user will supersede any parameter-level permissions that might otherwise apply to that user. Additionally, if more than one parameter is applicable to a given user, the permissions given to that user will be the sum of all applicable parameters. For example, let’s say only localhost users can connect and only logged in users can lock and unlock entities. If a user is both logged in and on localhost then they will be able to both connect and lock/unlock entities.</p>'>domain-wide permissions</a>.",
|
||||
"caption": "Standard Permissions",
|
||||
"can_add_new_rows": false,
|
||||
"groups": [
|
||||
|
@ -216,8 +227,8 @@
|
|||
"span": 1
|
||||
},
|
||||
{
|
||||
"label": "Permissions <a data-toggle='tooltip' data-html='true' title='<p><strong>Domain-Wide User Permissions</strong></p><ul><li><strong>Connect</strong><br />Sets whether a user can connect to the domain.</li><li><strong>Lock / Unlock</strong><br />Sets whether a user change the “locked” property of an entity (either from on to off or off to on).</li><li><strong>Rez</strong><br />Sets whether a user can create new entities.</li><li><strong>Rez Temporary</strong><br />Sets whether a user can create new entities with a finite lifetime.</li><li><strong>Write Assets</strong><br />Sets whether a user can make changes to the domain’s asset-server assets.</li><li><strong>Ignore Max Capacity</strong><br />Sets whether a user can connect even if the domain has reached or exceeded its maximum allowed agents.</li><li><strong>Replace Content</strong><br>Sets whether a user can replace entire content sets by wiping existing domain content.</li></ul><p>Note that permissions assigned to a specific user will supersede any parameter-level permissions that might otherwise apply to that user. Additionally, if more than one parameter is applicable to a given user, the permissions given to that user will be the sum of all applicable parameters. For example, let’s say only localhost users can connect and only logged in users can lock and unlock entities. If a user is both logged in and on localhost then they will be able to both connect and lock/unlock entities.</p>'>?</a>",
|
||||
"span": 8
|
||||
"label": "Permissions <a data-toggle='tooltip' data-html='true' title='<p><strong>Domain-Wide User Permissions</strong></p><ul><li><strong>Connect</strong><br />Sets whether a user can connect to the domain.</li><li><strong>Lock / Unlock</strong><br />Sets whether a user change the “locked” property of an entity (either from on to off or off to on).</li><li><strong>Rez</strong><br />Sets whether a user can create new entities.</li><li><strong>Rez Temporary</strong><br />Sets whether a user can create new entities with a finite lifetime.</li><li><strong>Rez Certified</strong><br />Sets whether a user can create new certified entities.</li><li><strong>Rez Temporary Certified</strong><br />Sets whether a user can create new certified entities with a finite lifetime.</li><li><strong>Write Assets</strong><br />Sets whether a user can make changes to the domain’s asset-server assets.</li><li><strong>Ignore Max Capacity</strong><br />Sets whether a user can connect even if the domain has reached or exceeded its maximum allowed agents.</li><li><strong>Replace Content</strong><br>Sets whether a user can replace entire content sets by wiping existing domain content.</li></ul><p>Note that permissions assigned to a specific user will supersede any parameter-level permissions that might otherwise apply to that user. Additionally, if more than one parameter is applicable to a given user, the permissions given to that user will be the sum of all applicable parameters. For example, let’s say only localhost users can connect and only logged in users can lock and unlock entities. If a user is both logged in and on localhost then they will be able to both connect and lock/unlock entities.</p>'>?</a>",
|
||||
"span": 10
|
||||
}
|
||||
],
|
||||
"columns": [
|
||||
|
@ -253,6 +264,20 @@
|
|||
"editable": true,
|
||||
"default": false
|
||||
},
|
||||
{
|
||||
"name": "id_can_rez_certified",
|
||||
"label": "Rez Certified",
|
||||
"type": "checkbox",
|
||||
"editable": true,
|
||||
"default": false
|
||||
},
|
||||
{
|
||||
"name": "id_can_rez_tmp_certified",
|
||||
"label": "Rez Temporary Certified",
|
||||
"type": "checkbox",
|
||||
"editable": true,
|
||||
"default": false
|
||||
},
|
||||
{
|
||||
"name": "id_can_write_to_asset_server",
|
||||
"label": "Write Assets",
|
||||
|
@ -283,7 +308,7 @@
|
|||
}
|
||||
],
|
||||
"non-deletable-row-key": "permissions_id",
|
||||
"non-deletable-row-values": ["localhost", "anonymous", "logged-in"]
|
||||
"non-deletable-row-values": [ "localhost", "anonymous", "logged-in" ]
|
||||
},
|
||||
{
|
||||
"name": "group_permissions",
|
||||
|
@ -300,8 +325,8 @@
|
|||
"span": 1
|
||||
},
|
||||
{
|
||||
"label": "Permissions <a data-toggle='tooltip' data-html='true' title='<p><strong>Domain-Wide User Permissions</strong></p><ul><li><strong>Connect</strong><br />Sets whether users in specific groups can connect to the domain.</li><li><strong>Lock / Unlock</strong><br />Sets whether users in specific groups can change the “locked” property of an entity (either from on to off or off to on).</li><li><strong>Rez</strong><br />Sets whether users in specific groups can create new entities.</li><li><strong>Rez Temporary</strong><br />Sets whether users in specific groups can create new entities with a finite lifetime.</li><li><strong>Write Assets</strong><br />Sets whether users in specific groups can make changes to the domain’s asset-server assets.</li><li><strong>Ignore Max Capacity</strong><br />Sets whether user in specific groups can connect even if the domain has reached or exceeded its maximum allowed agents.</li><li><strong>Replace Content</strong><br>Sets whether a user can replace entire content sets by wiping existing domain content.</li></ul><p>Permissions granted to a specific user will be a union of the permissions granted to the groups they are in, as well as permissions from the previous section. Group permissions are only granted if the user doesn’t have their own row in the per-account section, below.</p>'>?</a>",
|
||||
"span": 8
|
||||
"label": "Permissions <a data-toggle='tooltip' data-html='true' title='<p><strong>Domain-Wide User Permissions</strong></p><ul><li><strong>Connect</strong><br />Sets whether users in specific groups can connect to the domain.</li><li><strong>Lock / Unlock</strong><br />Sets whether users in specific groups can change the “locked” property of an entity (either from on to off or off to on).</li><li><strong>Rez</strong><br />Sets whether users in specific groups can create new entities.</li><li><strong>Rez Temporary</strong><br />Sets whether users in specific groups can create new entities with a finite lifetime.</li><li><strong>Rez Temporary</strong><br />Sets whether users in specific groups can create new entities with a finite lifetime.</li><li><strong>Rez Certified</strong><br />Sets whether a users in specific groups can create new certified entities.</li><li><strong>Rez Temporary Certified</strong><br />Sets whether a user can create new certified entities with a finite lifetime.</li><li><strong>Write Assets</strong><br />Sets whether users in specific groups can make changes to the domain’s asset-server assets.</li><li><strong>Ignore Max Capacity</strong><br />Sets whether user in specific groups can connect even if the domain has reached or exceeded its maximum allowed agents.</li><li><strong>Replace Content</strong><br>Sets whether a user can replace entire content sets by wiping existing domain content.</li></ul><p>Permissions granted to a specific user will be a union of the permissions granted to the groups they are in, as well as permissions from the previous section. Group permissions are only granted if the user doesn’t have their own row in the per-account section, below.</p>'>?</a>",
|
||||
"span": 10
|
||||
}
|
||||
],
|
||||
"columns": [
|
||||
|
@ -362,6 +387,20 @@
|
|||
"editable": true,
|
||||
"default": false
|
||||
},
|
||||
{
|
||||
"name": "id_can_rez_certified",
|
||||
"label": "Rez Certified",
|
||||
"type": "checkbox",
|
||||
"editable": true,
|
||||
"default": false
|
||||
},
|
||||
{
|
||||
"name": "id_can_rez_tmp_certified",
|
||||
"label": "Rez Temporary Certified",
|
||||
"type": "checkbox",
|
||||
"editable": true,
|
||||
"default": false
|
||||
},
|
||||
{
|
||||
"name": "id_can_write_to_asset_server",
|
||||
"label": "Write Assets",
|
||||
|
@ -383,7 +422,7 @@
|
|||
"editable": true,
|
||||
"default": false
|
||||
},
|
||||
{
|
||||
{
|
||||
"name": "id_can_replace_content",
|
||||
"label": "Replace Content",
|
||||
"type": "checkbox",
|
||||
|
@ -407,8 +446,8 @@
|
|||
"span": 1
|
||||
},
|
||||
{
|
||||
"label": "Permissions <a data-toggle='tooltip' data-html='true' title='<p><strong>Domain-Wide User Permissions</strong></p><ul><li><strong>Connect</strong><br />Sets whether users in specific groups can connect to the domain.</li><li><strong>Lock / Unlock</strong><br />Sets whether users in specific groups can change the “locked” property of an entity (either from on to off or off to on).</li><li><strong>Rez</strong><br />Sets whether users in specific groups can create new entities.</li><li><strong>Rez Temporary</strong><br />Sets whether users in specific groups can create new entities with a finite lifetime.</li><li><strong>Write Assets</strong><br />Sets whether users in specific groups can make changes to the domain’s asset-server assets.</li><li><strong>Ignore Max Capacity</strong><br />Sets whether user in specific groups can connect even if the domain has reached or exceeded its maximum allowed agents.</li><li><strong>Replace Content</strong><br>Sets whether users in specific groups can replace entire content sets by wiping existing domain content.</li></ul><p>Permissions granted to a specific user will be a union of the permissions granted to the groups they are in. Group permissions are only granted if the user doesn’t have their own row in the per-account section, below.</p>'>?</a>",
|
||||
"span": 8
|
||||
"label": "Permissions <a data-toggle='tooltip' data-html='true' title='<p><strong>Domain-Wide User Permissions</strong></p><ul><li><strong>Connect</strong><br />Sets whether users in specific groups can connect to the domain.</li><li><strong>Lock / Unlock</strong><br />Sets whether users in specific groups can change the “locked” property of an entity (either from on to off or off to on).</li><li><strong>Rez</strong><br />Sets whether users in specific groups can create new entities.</li><li><strong>Rez Temporary</strong><br />Sets whether users in specific groups can create new entities with a finite lifetime.</li><li><strong>Rez Certified</strong><br />Sets whether a users in specific groups can create new certified entities.</li><li><strong>Rez Temporary Certified</strong><br />Sets whether a user can create new certified entities with a finite lifetime.</li><li><strong>Write Assets</strong><br />Sets whether users in specific groups can make changes to the domain’s asset-server assets.</li><li><strong>Ignore Max Capacity</strong><br />Sets whether user in specific groups can connect even if the domain has reached or exceeded its maximum allowed agents.</li><li><strong>Replace Content</strong><br>Sets whether users in specific groups can replace entire content sets by wiping existing domain content.</li></ul><p>Permissions granted to a specific user will be a union of the permissions granted to the groups they are in. Group permissions are only granted if the user doesn’t have their own row in the per-account section, below.</p>'>?</a>",
|
||||
"span": 10
|
||||
}
|
||||
],
|
||||
"columns": [
|
||||
|
@ -466,6 +505,20 @@
|
|||
"editable": true,
|
||||
"default": false
|
||||
},
|
||||
{
|
||||
"name": "id_can_rez_certified",
|
||||
"label": "Rez Certified",
|
||||
"type": "checkbox",
|
||||
"editable": true,
|
||||
"default": false
|
||||
},
|
||||
{
|
||||
"name": "id_can_rez_tmp_certified",
|
||||
"label": "Rez Temporary Certified",
|
||||
"type": "checkbox",
|
||||
"editable": true,
|
||||
"default": false
|
||||
},
|
||||
{
|
||||
"name": "id_can_write_to_asset_server",
|
||||
"label": "Write Assets",
|
||||
|
@ -487,7 +540,7 @@
|
|||
"editable": true,
|
||||
"default": false
|
||||
},
|
||||
{
|
||||
{
|
||||
"name": "id_can_replace_content",
|
||||
"label": "Replace Content",
|
||||
"type": "checkbox",
|
||||
|
@ -507,8 +560,8 @@
|
|||
"span": 1
|
||||
},
|
||||
{
|
||||
"label": "Permissions <a data-toggle='tooltip' data-html='true' title='<p><strong>Domain-Wide User Permissions</strong></p><ul><li><strong>Connect</strong><br />Sets whether a user can connect to the domain.</li><li><strong>Lock / Unlock</strong><br />Sets whether a user change the “locked” property of an entity (either from on to off or off to on).</li><li><strong>Rez</strong><br />Sets whether a user can create new entities.</li><li><strong>Rez Temporary</strong><br />Sets whether a user can create new entities with a finite lifetime.</li><li><strong>Write Assets</strong><br />Sets whether a user can make changes to the domain’s asset-server assets.</li><li><strong>Ignore Max Capacity</strong><br />Sets whether a user can connect even if the domain has reached or exceeded its maximum allowed agents.</li><li><strong>Replace Content</strong><br>Sets whether a user can replace entire content sets by wiping existing domain content.</li></ul><p>Note that permissions assigned to a specific user will supersede any parameter-level or group permissions that might otherwise apply to that user.</p>'>?</a>",
|
||||
"span": 8
|
||||
"label": "Permissions <a data-toggle='tooltip' data-html='true' title='<p><strong>Domain-Wide User Permissions</strong></p><ul><li><strong>Connect</strong><br />Sets whether a user can connect to the domain.</li><li><strong>Lock / Unlock</strong><br />Sets whether a user change the “locked” property of an entity (either from on to off or off to on).</li><li><strong>Rez</strong><br />Sets whether a user can create new entities.</li><li><strong>Rez Temporary</strong><br />Sets whether a user can create new entities with a finite lifetime.</li><li><strong>Rez Temporary</strong><br />Sets whether a user can create new entities with a finite lifetime.</li><li><strong>Rez Certified</strong><br />Sets whether a user can create new certified entities.</li><li><strong>Rez Temporary Certified</strong><br />Sets whether a user can create new certified entities with a finite lifetime.</li><li><strong>Write Assets</strong><br />Sets whether a user can make changes to the domain’s asset-server assets.</li><li><strong>Ignore Max Capacity</strong><br />Sets whether a user can connect even if the domain has reached or exceeded its maximum allowed agents.</li><li><strong>Replace Content</strong><br>Sets whether a user can replace entire content sets by wiping existing domain content.</li></ul><p>Note that permissions assigned to a specific user will supersede any parameter-level or group permissions that might otherwise apply to that user.</p>'>?</a>",
|
||||
"span": 10
|
||||
}
|
||||
],
|
||||
"columns": [
|
||||
|
@ -544,6 +597,20 @@
|
|||
"editable": true,
|
||||
"default": false
|
||||
},
|
||||
{
|
||||
"name": "id_can_rez_certified",
|
||||
"label": "Rez Certified",
|
||||
"type": "checkbox",
|
||||
"editable": true,
|
||||
"default": false
|
||||
},
|
||||
{
|
||||
"name": "id_can_rez_tmp_certified",
|
||||
"label": "Rez Temporary Certified",
|
||||
"type": "checkbox",
|
||||
"editable": true,
|
||||
"default": false
|
||||
},
|
||||
{
|
||||
"name": "id_can_write_to_asset_server",
|
||||
"label": "Write Assets",
|
||||
|
@ -565,7 +632,7 @@
|
|||
"editable": true,
|
||||
"default": false
|
||||
},
|
||||
{
|
||||
{
|
||||
"name": "id_can_replace_content",
|
||||
"label": "Replace Content",
|
||||
"type": "checkbox",
|
||||
|
@ -585,8 +652,8 @@
|
|||
"span": 1
|
||||
},
|
||||
{
|
||||
"label": "Permissions <a data-toggle='tooltip' data-html='true' title='<p><strong>Domain-Wide IP Permissions</strong></p><ul><li><strong>Connect</strong><br />Sets whether users from specific IPs can connect to the domain.</li><li><strong>Lock / Unlock</strong><br />Sets whether users from specific IPs can change the “locked” property of an entity (either from on to off or off to on).</li><li><strong>Rez</strong><br />Sets whether users from specific IPs can create new entities.</li><li><strong>Rez Temporary</strong><br />Sets whether users from specific IPs can create new entities with a finite lifetime.</li><li><strong>Write Assets</strong><br />Sets whether users from specific IPs can make changes to the domain’s asset-server assets.</li><li><strong>Ignore Max Capacity</strong><br />Sets whether users from specific IPs can connect even if the domain has reached or exceeded its maximum allowed agents.</li><li><strong>Replace Content</strong><br>Sets whether users from specific IPs can replace entire content sets by wiping existing domain content.</li></ul><p>Note that permissions assigned to a specific IP will supersede any parameter-level permissions that might otherwise apply to that user (from groups or standard permissions above). IP address permissions are overriden if the user has their own row in the users section.</p>'>?</a>",
|
||||
"span": 8
|
||||
"label": "Permissions <a data-toggle='tooltip' data-html='true' title='<p><strong>Domain-Wide IP Permissions</strong></p><ul><li><strong>Connect</strong><br />Sets whether users from specific IPs can connect to the domain.</li><li><strong>Lock / Unlock</strong><br />Sets whether users from specific IPs can change the “locked” property of an entity (either from on to off or off to on).</li><li><strong>Rez</strong><br />Sets whether users from specific IPs can create new entities.</li><li><strong>Rez Temporary</strong><br />Sets whether users from specific IPs can create new entities with a finite lifetime.</li><li><strong>Rez Temporary</strong><br />Sets whether a user can create new entities with a finite lifetime.</li><li><strong>Rez Certified</strong><br />Sets whether users from specific IPs can create new certified entities.</li><li><strong>Rez Temporary Certified</strong><br />Sets whether users from specific IPs can create new certified entities with a finite lifetime.</li><li><strong>Write Assets</strong><br />Sets whether users from specific IPs can make changes to the domain’s asset-server assets.</li><li><strong>Ignore Max Capacity</strong><br />Sets whether users from specific IPs can connect even if the domain has reached or exceeded its maximum allowed agents.</li><li><strong>Replace Content</strong><br>Sets whether users from specific IPs can replace entire content sets by wiping existing domain content.</li></ul><p>Note that permissions assigned to a specific IP will supersede any parameter-level permissions that might otherwise apply to that user (from groups or standard permissions above). IP address permissions are overriden if the user has their own row in the users section.</p>'>?</a>",
|
||||
"span": 10
|
||||
}
|
||||
],
|
||||
"columns": [
|
||||
|
@ -622,6 +689,20 @@
|
|||
"editable": true,
|
||||
"default": false
|
||||
},
|
||||
{
|
||||
"name": "id_can_rez_certified",
|
||||
"label": "Rez Certified",
|
||||
"type": "checkbox",
|
||||
"editable": true,
|
||||
"default": false
|
||||
},
|
||||
{
|
||||
"name": "id_can_rez_tmp_certified",
|
||||
"label": "Rez Temporary Certified",
|
||||
"type": "checkbox",
|
||||
"editable": true,
|
||||
"default": false
|
||||
},
|
||||
{
|
||||
"name": "id_can_write_to_asset_server",
|
||||
"label": "Write Assets",
|
||||
|
@ -643,7 +724,7 @@
|
|||
"editable": true,
|
||||
"default": false
|
||||
},
|
||||
{
|
||||
{
|
||||
"name": "id_can_replace_content",
|
||||
"label": "Replace Content",
|
||||
"type": "checkbox",
|
||||
|
@ -663,8 +744,8 @@
|
|||
"span": 1
|
||||
},
|
||||
{
|
||||
"label": "Permissions <a data-toggle='tooltip' data-html='true' title='<p><strong>Domain-Wide MAC Permissions</strong></p><ul><li><strong>Connect</strong><br />Sets whether users with specific MACs can connect to the domain.</li><li><strong>Lock / Unlock</strong><br />Sets whether users from specific MACs can change the “locked” property of an entity (either from on to off or off to on).</li><li><strong>Rez</strong><br />Sets whether users with specific MACs can create new entities.</li><li><strong>Rez Temporary</strong><br />Sets whether users with specific MACs can create new entities with a finite lifetime.</li><li><strong>Write Assets</strong><br />Sets whether users with specific MACs can make changes to the domain’s asset-server assets.</li><li><strong>Ignore Max Capacity</strong><br />Sets whether users with specific MACs can connect even if the domain has reached or exceeded its maximum allowed agents.</li><li><strong>Replace Content</strong><br>Sets whether users with specific MACs can replace entire content sets by wiping existing domain content.</li></ul><p>Note that permissions assigned to a specific MAC will supersede any parameter-level permissions that might otherwise apply to that user (from groups or standard permissions above). MAC address permissions are overriden if the user has their own row in the users section.</p>'>?</a>",
|
||||
"span": 8
|
||||
"label": "Permissions <a data-toggle='tooltip' data-html='true' title='<p><strong>Domain-Wide MAC Permissions</strong></p><ul><li><strong>Connect</strong><br />Sets whether users with specific MACs can connect to the domain.</li><li><strong>Lock / Unlock</strong><br />Sets whether users from specific MACs can change the “locked” property of an entity (either from on to off or off to on).</li><li><strong>Rez</strong><br />Sets whether users with specific MACs can create new entities.</li><li><strong>Rez Temporary</strong><br />Sets whether users with specific MACs can create new entities with a finite lifetime.</li><li><strong>Rez Certified</strong><br />Sets whether users with specific MACs can create new certified entities.</li><li><strong>Rez Temporary Certified</strong><br />Sets whether users with specific MACs can create new certified entities with a finite lifetime.</li><li><strong>Write Assets</strong><br />Sets whether users with specific MACs can make changes to the domain’s asset-server assets.</li><li><strong>Ignore Max Capacity</strong><br />Sets whether users with specific MACs can connect even if the domain has reached or exceeded its maximum allowed agents.</li><li><strong>Replace Content</strong><br>Sets whether users with specific MACs can replace entire content sets by wiping existing domain content.</li></ul><p>Note that permissions assigned to a specific MAC will supersede any parameter-level permissions that might otherwise apply to that user (from groups or standard permissions above). MAC address permissions are overriden if the user has their own row in the users section.</p>'>?</a>",
|
||||
"span": 10
|
||||
}
|
||||
],
|
||||
"columns": [
|
||||
|
@ -700,6 +781,20 @@
|
|||
"editable": true,
|
||||
"default": false
|
||||
},
|
||||
{
|
||||
"name": "id_can_rez_certified",
|
||||
"label": "Rez Certified",
|
||||
"type": "checkbox",
|
||||
"editable": true,
|
||||
"default": false
|
||||
},
|
||||
{
|
||||
"name": "id_can_rez_tmp_certified",
|
||||
"label": "Rez Temporary Certified",
|
||||
"type": "checkbox",
|
||||
"editable": true,
|
||||
"default": false
|
||||
},
|
||||
{
|
||||
"name": "id_can_write_to_asset_server",
|
||||
"label": "Write Assets",
|
||||
|
@ -721,7 +816,7 @@
|
|||
"editable": true,
|
||||
"default": false
|
||||
},
|
||||
{
|
||||
{
|
||||
"name": "id_can_replace_content",
|
||||
"label": "Replace Content",
|
||||
"type": "checkbox",
|
||||
|
@ -741,8 +836,8 @@
|
|||
"span": 1
|
||||
},
|
||||
{
|
||||
"label": "Permissions <a data-toggle='tooltip' data-html='true' title='<p><strong>Domain-Wide Machine Fingerprint Permissions</strong></p><ul><li><strong>Connect</strong><br />Sets whether users with specific Machine Fingerprints can connect to the domain.</li><li><strong>Lock / Unlock</strong><br />Sets whether users from specific Machine Fingerprints can change the “locked” property of an entity (either from on to off or off to on).</li><li><strong>Rez</strong><br />Sets whether users with specific Machine Fingerprints can create new entities.</li><li><strong>Rez Temporary</strong><br />Sets whether users with specific Machine Fingerprints can create new entities with a finite lifetime.</li><li><strong>Write Assets</strong><br />Sets whether users with specific Machine Fingerprints can make changes to the domain’s asset-server assets.</li><li><strong>Ignore Max Capacity</strong><br />Sets whether users with specific Machine Fingerprints can connect even if the domain has reached or exceeded its maximum allowed agents.</li><li><strong>Replace Content</strong><br>Sets whether users with specific Machine Fingerprints can replace entire content sets by wiping existing domain content.</li></ul><p>Note that permissions assigned to a specific Machine Fingerprint will supersede any parameter-level permissions that might otherwise apply to that user (from groups or standard permissions above). Machine Fingerprint address permissions are overriden if the user has their own row in the users section.</p>'>?</a>",
|
||||
"span": 8
|
||||
"label": "Permissions <a data-toggle='tooltip' data-html='true' title='<p><strong>Domain-Wide Machine Fingerprint Permissions</strong></p><ul><li><strong>Connect</strong><br />Sets whether users with specific Machine Fingerprints can connect to the domain.</li><li><strong>Lock / Unlock</strong><br />Sets whether users from specific Machine Fingerprints can change the “locked” property of an entity (either from on to off or off to on).</li><li><strong>Rez</strong><br />Sets whether users with specific Machine Fingerprints can create new entities.</li><li><strong>Rez Temporary</strong><br />Sets whether users with specific Machine Fingerprints can create new entities with a finite lifetime.</li><li><strong>Rez Certified</strong><br />Sets whether users with specific Machine Fingerprints can create new certified entities.</li><li><strong>Rez Temporary Certified</strong><br />Sets whether users with specific Machine Fingerprints can create new certified entities with a finite lifetime.</li><li><strong>Write Assets</strong><br />Sets whether users with specific Machine Fingerprints can make changes to the domain’s asset-server assets.</li><li><strong>Ignore Max Capacity</strong><br />Sets whether users with specific Machine Fingerprints can connect even if the domain has reached or exceeded its maximum allowed agents.</li><li><strong>Replace Content</strong><br>Sets whether users with specific Machine Fingerprints can replace entire content sets by wiping existing domain content.</li></ul><p>Note that permissions assigned to a specific Machine Fingerprint will supersede any parameter-level permissions that might otherwise apply to that user (from groups or standard permissions above). Machine Fingerprint address permissions are overriden if the user has their own row in the users section.</p>'>?</a>",
|
||||
"span": 10
|
||||
}
|
||||
],
|
||||
"columns": [
|
||||
|
@ -778,6 +873,20 @@
|
|||
"editable": true,
|
||||
"default": false
|
||||
},
|
||||
{
|
||||
"name": "id_can_rez_certified",
|
||||
"label": "Rez Certified",
|
||||
"type": "checkbox",
|
||||
"editable": true,
|
||||
"default": false
|
||||
},
|
||||
{
|
||||
"name": "id_can_rez_tmp_certified",
|
||||
"label": "Rez Temporary Certified",
|
||||
"type": "checkbox",
|
||||
"editable": true,
|
||||
"default": false
|
||||
},
|
||||
{
|
||||
"name": "id_can_write_to_asset_server",
|
||||
"label": "Write Assets",
|
||||
|
@ -799,7 +908,7 @@
|
|||
"editable": true,
|
||||
"default": false
|
||||
},
|
||||
{
|
||||
{
|
||||
"name": "id_can_replace_content",
|
||||
"label": "Replace Content",
|
||||
"type": "checkbox",
|
||||
|
@ -841,7 +950,7 @@
|
|||
{
|
||||
"name": "asset_server",
|
||||
"label": "Asset Server (ATP)",
|
||||
"assignment-types": [3],
|
||||
"assignment-types": [ 3 ],
|
||||
"settings": [
|
||||
{
|
||||
"name": "enabled",
|
||||
|
@ -872,7 +981,7 @@
|
|||
{
|
||||
"name": "entity_script_server",
|
||||
"label": "Entity Script Server (ESS)",
|
||||
"assignment-types": [5],
|
||||
"assignment-types": [ 5 ],
|
||||
"settings": [
|
||||
{
|
||||
"name": "entity_pps_per_script",
|
||||
|
@ -895,7 +1004,7 @@
|
|||
{
|
||||
"name": "avatars",
|
||||
"label": "Avatars",
|
||||
"assignment-types": [1, 2],
|
||||
"assignment-types": [ 1, 2 ],
|
||||
"settings": [
|
||||
{
|
||||
"name": "min_avatar_scale",
|
||||
|
@ -934,7 +1043,7 @@
|
|||
{
|
||||
"name": "audio_threading",
|
||||
"label": "Audio Threading",
|
||||
"assignment-types": [0],
|
||||
"assignment-types": [ 0 ],
|
||||
"settings": [
|
||||
{
|
||||
"name": "auto_threads",
|
||||
|
@ -957,7 +1066,7 @@
|
|||
{
|
||||
"name": "audio_env",
|
||||
"label": "Audio Environment",
|
||||
"assignment-types": [0],
|
||||
"assignment-types": [ 0 ],
|
||||
"settings": [
|
||||
{
|
||||
"name": "attenuation_per_doubling_in_distance",
|
||||
|
@ -1164,6 +1273,22 @@
|
|||
"default": "3600",
|
||||
"advanced": true
|
||||
},
|
||||
{
|
||||
"name": "dynamicDomainVerificationTimeMin",
|
||||
"label": "Dynamic Domain Verification Time (seconds) - Minimum",
|
||||
"help": "The lower limit on the amount of time that passes before Dynamic Domain Verification on entities occurs. Units are seconds.",
|
||||
"placeholder": "2700",
|
||||
"default": "2700",
|
||||
"advanced": true
|
||||
},
|
||||
{
|
||||
"name": "dynamicDomainVerificationTimeMax",
|
||||
"label": "Dynamic Domain Verification Time (seconds) - Maximum",
|
||||
"help": "The upper limit on the amount of time that passes before Dynamic Domain Verification on entities occurs. Units are seconds.",
|
||||
"placeholder": "3600",
|
||||
"default": "3600",
|
||||
"advanced": true
|
||||
},
|
||||
{
|
||||
"name": "entityScriptSourceWhitelist",
|
||||
"label": "Entity Scripts Allowed from:",
|
||||
|
@ -1511,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: 32px | Height: 32px | 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 (image error) 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);
|
||||
|
|
|
@ -87,7 +87,7 @@ Window {
|
|||
onReceivedHifiSchemeURL: resetAfterTeleport();
|
||||
|
||||
// Update location after using back and forward buttons.
|
||||
onMetaverseServerUrlChanged: updateLocationTextTimer.start();
|
||||
onHostChanged: updateLocationTextTimer.start();
|
||||
|
||||
ListModel { id: suggestions }
|
||||
|
||||
|
|
|
@ -55,8 +55,21 @@ Item {
|
|||
mouse.accepted = true;
|
||||
}
|
||||
|
||||
property var _HAPTIC_STRENGTH: 0.1;
|
||||
property var _HAPTIC_DURATION: 3.0;
|
||||
property var leftHand: 0;
|
||||
property var rightHand: 1;
|
||||
|
||||
onEntered: {
|
||||
keyItem.state = "mouseOver";
|
||||
|
||||
var globalPosition = keyItem.mapToGlobal(mouseArea1.mouseX, mouseArea1.mouseY);
|
||||
var deviceId = Web3DOverlay.deviceIdByTouchPoint(globalPosition.x, globalPosition.y);
|
||||
var hand = deviceId - 1; // based on touchEventUtils.js, deviceId is 'hand + 1', so 'hand' is 'deviceId' - 1
|
||||
|
||||
if (hand == leftHand || hand == rightHand) {
|
||||
Controller.triggerHapticPulse(_HAPTIC_STRENGTH, _HAPTIC_DURATION, hand);
|
||||
}
|
||||
}
|
||||
|
||||
onExited: {
|
||||
|
|
|
@ -33,7 +33,7 @@ Window {
|
|||
|
||||
BaseWebView {
|
||||
id: webview
|
||||
url: "https://metaverse.highfidelity.com/marketplace?category=avatars"
|
||||
url: Account.metaverseServerURL + "/marketplace?category=avatars"
|
||||
focus: true
|
||||
|
||||
anchors {
|
||||
|
|
|
@ -17,7 +17,7 @@ import "../styles-uit"
|
|||
import "../controls-uit" as HifiControls
|
||||
import "toolbars"
|
||||
|
||||
// references Users, UserActivityLogger, MyAvatar, Vec3, Quat, AddressManager from root context
|
||||
// references Users, UserActivityLogger, MyAvatar, Vec3, Quat, AddressManager, Account from root context
|
||||
|
||||
Item {
|
||||
id: thisNameCard
|
||||
|
@ -30,7 +30,6 @@ Item {
|
|||
|
||||
// Properties
|
||||
property string profileUrl: "";
|
||||
property string defaultBaseUrl: AddressManager.metaverseServerUrl;
|
||||
property string connectionStatus : ""
|
||||
property string uuid: ""
|
||||
property string displayName: ""
|
||||
|
@ -59,7 +58,7 @@ Item {
|
|||
clip: true
|
||||
Image {
|
||||
id: userImage
|
||||
source: profileUrl !== "" ? ((0 === profileUrl.indexOf("http")) ? profileUrl : (defaultBaseUrl + profileUrl)) : "";
|
||||
source: profileUrl !== "" ? ((0 === profileUrl.indexOf("http")) ? profileUrl : (Account.metaverseServerURL + profileUrl)) : "";
|
||||
mipmap: true;
|
||||
// Anchors
|
||||
anchors.fill: parent
|
||||
|
@ -95,7 +94,7 @@ Item {
|
|||
enabled: (selected && activeTab == "nearbyTab") || isMyCard;
|
||||
hoverEnabled: enabled
|
||||
onClicked: {
|
||||
userInfoViewer.url = defaultBaseUrl + "/users/" + userName;
|
||||
userInfoViewer.url = Account.metaverseServerURL + "/users/" + userName;
|
||||
userInfoViewer.visible = true;
|
||||
}
|
||||
onEntered: infoHoverImage.visible = true;
|
||||
|
@ -366,7 +365,7 @@ Item {
|
|||
enabled: selected
|
||||
hoverEnabled: true
|
||||
onClicked: {
|
||||
userInfoViewer.url = defaultBaseUrl + "/users/" + userName;
|
||||
userInfoViewer.url = Account.metaverseServerURL + "/users/" + userName;
|
||||
userInfoViewer.visible = true;
|
||||
}
|
||||
onEntered: {
|
||||
|
|
|
@ -23,9 +23,6 @@ import "../controls" as HifiControls
|
|||
|
||||
Rectangle {
|
||||
id: pal;
|
||||
// Size
|
||||
width: parent.width;
|
||||
height: parent.height;
|
||||
// Style
|
||||
color: "#E3E3E3";
|
||||
// Properties
|
||||
|
|
|
@ -585,7 +585,7 @@ Rectangle {
|
|||
// "Rez" button
|
||||
HifiControlsUit.Button {
|
||||
id: rezNowButton;
|
||||
enabled: root.canRezCertifiedItems;
|
||||
enabled: root.canRezCertifiedItems || root.isWearable;
|
||||
buttonGlyph: hifi.glyphs.lightning;
|
||||
color: hifi.buttons.red;
|
||||
colorScheme: hifi.colorSchemes.light;
|
||||
|
@ -634,7 +634,7 @@ Rectangle {
|
|||
}
|
||||
RalewaySemiBold {
|
||||
id: explainRezText;
|
||||
//visible: !root.isWearable;
|
||||
visible: !root.isWearable;
|
||||
text: '<font color="' + hifi.colors.redAccent + '"><a href="#">What does "Rez" mean?</a></font>'
|
||||
// Text size
|
||||
size: 16;
|
||||
|
|
|
@ -25,7 +25,7 @@ Item {
|
|||
HifiConstants { id: hifi; }
|
||||
|
||||
id: root;
|
||||
property string referrerURL: "https://metaverse.highfidelity.com/marketplace?";
|
||||
property string referrerURL: (Account.metaverseServerURL + "/marketplace?");
|
||||
readonly property int additionalDropdownHeight: usernameDropdown.height - myUsernameButton.anchors.bottomMargin;
|
||||
property alias usernameDropdownVisible: usernameDropdown.visible;
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -134,7 +134,7 @@ StackView {
|
|||
bottom: parent.bottom
|
||||
}
|
||||
|
||||
onMetaverseServerUrlChanged: updateLocationTextTimer.start();
|
||||
onHostChanged: updateLocationTextTimer.start();
|
||||
Rectangle {
|
||||
id: navBar
|
||||
width: parent.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: "https://metaverse.highfidelity.com/marketplace?category=avatars"
|
||||
url: (Account.metaverseServerURL + "/marketplace?category=avatars")
|
||||
focus: true
|
||||
|
||||
anchors {
|
||||
|
|
|
@ -1018,6 +1018,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
|
|||
|
||||
// connect to the packet sent signal of the _entityEditSender
|
||||
connect(&_entityEditSender, &EntityEditPacketSender::packetSent, this, &Application::packetSent);
|
||||
connect(&_entityEditSender, &EntityEditPacketSender::addingEntityWithCertificate, this, &Application::addingEntityWithCertificate);
|
||||
|
||||
const char** constArgv = const_cast<const char**>(argv);
|
||||
QString concurrentDownloadsStr = getCmdOption(argc, constArgv, "--concurrent-downloads");
|
||||
|
@ -5739,6 +5740,11 @@ int Application::processOctreeStats(ReceivedMessage& message, SharedNodePointer
|
|||
void Application::packetSent(quint64 length) {
|
||||
}
|
||||
|
||||
void Application::addingEntityWithCertificate(const QString& certificateID, const QString& placeName) {
|
||||
auto ledger = DependencyManager::get<Ledger>();
|
||||
ledger->updateLocation(certificateID, placeName);
|
||||
}
|
||||
|
||||
void Application::registerScriptEngineWithApplicationServices(ScriptEnginePointer scriptEngine) {
|
||||
|
||||
scriptEngine->setEmitScriptUpdatesFunction([this]() {
|
||||
|
|
|
@ -428,6 +428,7 @@ private slots:
|
|||
void nodeActivated(SharedNodePointer node);
|
||||
void nodeKilled(SharedNodePointer node);
|
||||
static void packetSent(quint64 length);
|
||||
static void addingEntityWithCertificate(const QString& certificateID, const QString& placeName);
|
||||
void updateDisplayMode();
|
||||
void domainConnectionRefused(const QString& reasonMessage, int reason, const QString& extraInfo);
|
||||
|
||||
|
|
|
@ -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:
|
||||
|
||||
|
|
|
@ -220,18 +220,26 @@ void Ledger::account() {
|
|||
}
|
||||
|
||||
// The api/failResponse is called just for the side effect of logging.
|
||||
void Ledger::updateLocationSuccess(QNetworkReply& reply) { apiResponse("reset", reply); }
|
||||
void Ledger::updateLocationFailure(QNetworkReply& reply) { failResponse("reset", reply); }
|
||||
void Ledger::updateLocationSuccess(QNetworkReply& reply) { apiResponse("updateLocation", reply); }
|
||||
void Ledger::updateLocationFailure(QNetworkReply& reply) { failResponse("updateLocation", reply); }
|
||||
void Ledger::updateLocation(const QString& asset_id, const QString location, const bool controlledFailure) {
|
||||
auto wallet = DependencyManager::get<Wallet>();
|
||||
QStringList keys = wallet->listPublicKeys();
|
||||
QString key = keys[0];
|
||||
QJsonObject transaction;
|
||||
transaction["asset_id"] = asset_id;
|
||||
transaction["location"] = location;
|
||||
QJsonDocument transactionDoc{ transaction };
|
||||
auto transactionString = transactionDoc.toJson(QJsonDocument::Compact);
|
||||
signedSend("transaction", transactionString, key, "location", "updateLocationSuccess", "updateLocationFailure", controlledFailure);
|
||||
auto walletScriptingInterface = DependencyManager::get<WalletScriptingInterface>();
|
||||
uint walletStatus = walletScriptingInterface->getWalletStatus();
|
||||
|
||||
if (walletStatus != (uint)wallet->WALLET_STATUS_READY) {
|
||||
emit walletScriptingInterface->walletNotSetup();
|
||||
qDebug(commerce) << "User attempted to update the location of a certificate, but their wallet wasn't ready. Status:" << walletStatus;
|
||||
} else {
|
||||
QStringList keys = wallet->listPublicKeys();
|
||||
QString key = keys[0];
|
||||
QJsonObject transaction;
|
||||
transaction["certificate_id"] = asset_id;
|
||||
transaction["place_name"] = location;
|
||||
QJsonDocument transactionDoc{ transaction };
|
||||
auto transactionString = transactionDoc.toJson(QJsonDocument::Compact);
|
||||
signedSend("transaction", transactionString, key, "location", "updateLocationSuccess", "updateLocationFailure", controlledFailure);
|
||||
}
|
||||
}
|
||||
|
||||
void Ledger::certificateInfoSuccess(QNetworkReply& reply) {
|
||||
|
|
|
@ -717,32 +717,52 @@ bool Wallet::changePassphrase(const QString& newPassphrase) {
|
|||
}
|
||||
|
||||
void Wallet::handleChallengeOwnershipPacket(QSharedPointer<ReceivedMessage> packet, SharedNodePointer sendingNode) {
|
||||
QString decryptedText;
|
||||
quint64 encryptedTextSize;
|
||||
quint64 publicKeySize;
|
||||
unsigned char decryptedText[64];
|
||||
int certIDByteArraySize;
|
||||
int encryptedTextByteArraySize;
|
||||
|
||||
if (verifyOwnerChallenge(packet->read(packet->readPrimitive(&encryptedTextSize)), packet->read(packet->readPrimitive(&publicKeySize)), decryptedText)) {
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
// setup the packet
|
||||
auto decryptedTextPacket = NLPacket::create(PacketType::ChallengeOwnership, NUM_BYTES_RFC4122_UUID + decryptedText.size(), true);
|
||||
packet->readPrimitive(&certIDByteArraySize);
|
||||
packet->readPrimitive(&encryptedTextByteArraySize);
|
||||
|
||||
// write the decrypted text to the packet
|
||||
decryptedTextPacket->write(decryptedText.toUtf8());
|
||||
QByteArray certID = packet->read(certIDByteArraySize);
|
||||
QByteArray encryptedText = packet->read(encryptedTextByteArraySize);
|
||||
|
||||
qCDebug(commerce) << "Sending ChallengeOwnership Packet containing decrypted text";
|
||||
RSA* rsa = readKeys(keyFilePath().toStdString().c_str());
|
||||
|
||||
nodeList->sendPacket(std::move(decryptedTextPacket), *sendingNode);
|
||||
if (rsa) {
|
||||
const int decryptionStatus = RSA_private_decrypt(encryptedTextByteArraySize,
|
||||
reinterpret_cast<const unsigned char*>(encryptedText.constData()),
|
||||
decryptedText,
|
||||
rsa,
|
||||
RSA_PKCS1_OAEP_PADDING);
|
||||
|
||||
RSA_free(rsa);
|
||||
|
||||
if (decryptionStatus != -1) {
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
|
||||
QByteArray decryptedTextByteArray = QByteArray(reinterpret_cast<const char*>(decryptedText), decryptionStatus);
|
||||
int decryptedTextByteArraySize = decryptedTextByteArray.size();
|
||||
int certIDSize = certID.size();
|
||||
// setup the packet
|
||||
auto decryptedTextPacket = NLPacket::create(PacketType::ChallengeOwnership, certIDSize + decryptedTextByteArraySize + 2 * sizeof(int), true);
|
||||
|
||||
decryptedTextPacket->writePrimitive(certIDSize);
|
||||
decryptedTextPacket->writePrimitive(decryptedTextByteArraySize);
|
||||
decryptedTextPacket->write(certID);
|
||||
decryptedTextPacket->write(decryptedTextByteArray);
|
||||
|
||||
qCDebug(commerce) << "Sending ChallengeOwnership Packet containing decrypted text" << decryptedTextByteArray << "for CertID" << certID;
|
||||
|
||||
nodeList->sendPacket(std::move(decryptedTextPacket), *sendingNode);
|
||||
} else {
|
||||
qCDebug(commerce) << "During entity ownership challenge, decrypting the encrypted text failed.";
|
||||
}
|
||||
} else {
|
||||
qCDebug(commerce) << "verifyOwnerChallenge() returned false";
|
||||
qCDebug(commerce) << "During entity ownership challenge, creating the RSA object failed.";
|
||||
}
|
||||
}
|
||||
|
||||
bool Wallet::verifyOwnerChallenge(const QByteArray& encryptedText, const QString& publicKey, QString& decryptedText) {
|
||||
// I have no idea how to do this yet, so here's some dummy code that may not even work.
|
||||
decryptedText = QString("hello");
|
||||
return true;
|
||||
}
|
||||
|
||||
void Wallet::account() {
|
||||
auto ledger = DependencyManager::get<Ledger>();
|
||||
ledger->account();
|
||||
|
|
|
@ -82,8 +82,6 @@ private:
|
|||
bool readSecurityImage(const QString& inputFilePath, unsigned char** outputBufferPtr, int* outputBufferLen);
|
||||
bool writeBackupInstructions();
|
||||
|
||||
bool verifyOwnerChallenge(const QByteArray& encryptedText, const QString& publicKey, QString& decryptedText);
|
||||
|
||||
void account();
|
||||
};
|
||||
|
||||
|
|
|
@ -25,6 +25,11 @@ class AccountScriptingInterface : public QObject {
|
|||
* @property username {String} username if user is logged in, otherwise it returns "Unknown user"
|
||||
*/
|
||||
|
||||
public:
|
||||
|
||||
Q_PROPERTY(QUrl metaverseServerURL READ getMetaverseServerURL)
|
||||
QUrl getMetaverseServerURL() { return DependencyManager::get<AccountManager>()->getMetaverseServerURL(); }
|
||||
|
||||
signals:
|
||||
|
||||
/**jsdoc
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -39,7 +39,7 @@ AddressBarDialog::AddressBarDialog(QQuickItem* parent) : OffscreenQmlDialog(pare
|
|||
});
|
||||
_backEnabled = !(DependencyManager::get<AddressManager>()->getBackStack().isEmpty());
|
||||
_forwardEnabled = !(DependencyManager::get<AddressManager>()->getForwardStack().isEmpty());
|
||||
connect(addressManager.data(), &AddressManager::hostChanged, this, &AddressBarDialog::metaverseServerUrlChanged);
|
||||
connect(addressManager.data(), &AddressManager::hostChanged, this, &AddressBarDialog::hostChanged);
|
||||
connect(DependencyManager::get<DialogsManager>().data(), &DialogsManager::setUseFeed, this, &AddressBarDialog::setUseFeed);
|
||||
connect(qApp, &Application::receivedHifiSchemeURL, this, &AddressBarDialog::receivedHifiSchemeURL);
|
||||
}
|
||||
|
|
|
@ -22,7 +22,7 @@ class AddressBarDialog : public OffscreenQmlDialog {
|
|||
Q_PROPERTY(bool backEnabled READ backEnabled NOTIFY backEnabledChanged)
|
||||
Q_PROPERTY(bool forwardEnabled READ forwardEnabled NOTIFY forwardEnabledChanged)
|
||||
Q_PROPERTY(bool useFeed READ useFeed WRITE setUseFeed NOTIFY useFeedChanged)
|
||||
Q_PROPERTY(QString metaverseServerUrl READ metaverseServerUrl NOTIFY metaverseServerUrlChanged)
|
||||
Q_PROPERTY(QString metaverseServerUrl READ metaverseServerUrl)
|
||||
|
||||
public:
|
||||
AddressBarDialog(QQuickItem* parent = nullptr);
|
||||
|
@ -37,7 +37,7 @@ signals:
|
|||
void forwardEnabledChanged();
|
||||
void useFeedChanged();
|
||||
void receivedHifiSchemeURL(const QString& url);
|
||||
void metaverseServerUrlChanged();
|
||||
void hostChanged();
|
||||
|
||||
protected:
|
||||
void displayAddressOfflineMessage();
|
||||
|
|
|
@ -16,7 +16,6 @@
|
|||
#include <QtCore/QJsonDocument>
|
||||
#include <QtNetwork/QNetworkReply>
|
||||
|
||||
#include <NetworkingConstants.h>
|
||||
#include <plugins/PluginManager.h>
|
||||
#include <plugins/SteamClientPlugin.h>
|
||||
#include <ui/TabletScriptingInterface.h>
|
||||
|
|
|
@ -11,7 +11,6 @@
|
|||
#include <AudioClient.h>
|
||||
#include <avatar/AvatarManager.h>
|
||||
#include <devices/DdeFaceTracker.h>
|
||||
#include <NetworkingConstants.h>
|
||||
#include <ScriptEngines.h>
|
||||
#include <OffscreenUi.h>
|
||||
#include <Preferences.h>
|
||||
|
|
|
@ -222,6 +222,9 @@ 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);
|
||||
|
||||
_webSurface->getSurfaceContext()->setContextProperty("pathToFonts", "../../");
|
||||
|
||||
|
@ -250,6 +253,20 @@ void Web3DOverlay::onResizeWebSurface() {
|
|||
_webSurface->resize(QSize(_resolution.x, _resolution.y));
|
||||
}
|
||||
|
||||
const int INVALID_DEVICE_ID = -1;
|
||||
|
||||
Q_INVOKABLE int Web3DOverlay::deviceIdByTouchPoint(qreal x, qreal y) {
|
||||
auto mapped = _webSurface->getRootItem()->mapFromGlobal(QPoint(x, y));
|
||||
|
||||
for (auto pair : _activeTouchPoints) {
|
||||
if (mapped.x() == (int)pair.second.pos().x() && mapped.y() == (int)pair.second.pos().y()) {
|
||||
return pair.first;
|
||||
}
|
||||
}
|
||||
|
||||
return INVALID_DEVICE_ID;
|
||||
}
|
||||
|
||||
void Web3DOverlay::render(RenderArgs* args) {
|
||||
if (!_visible || !getParentVisible()) {
|
||||
return;
|
||||
|
|
|
@ -67,6 +67,8 @@ public:
|
|||
void destroyWebSurface();
|
||||
void onResizeWebSurface();
|
||||
|
||||
Q_INVOKABLE int deviceIdByTouchPoint(qreal x, qreal y);
|
||||
|
||||
public slots:
|
||||
void emitScriptEvent(const QVariant& scriptMessage);
|
||||
|
||||
|
|
|
@ -286,6 +286,13 @@ void Avatar::updateAvatarEntities() {
|
|||
properties.setScript(noScript);
|
||||
}
|
||||
|
||||
auto specifiedHref = properties.getHref();
|
||||
if (!isMyAvatar() && !specifiedHref.isEmpty()) {
|
||||
qCDebug(avatars_renderer) << "removing entity href from avatar attached entity:" << entityID << "old href:" << specifiedHref;
|
||||
QString noHref;
|
||||
properties.setHref(noHref);
|
||||
}
|
||||
|
||||
// When grabbing avatar entities, they are parented to the joint moving them, then when un-grabbed
|
||||
// they go back to the default parent (null uuid). When un-gripped, others saw the entity disappear.
|
||||
// The thinking here is the local position was noticed as changing, but not the parentID (since it is now
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -342,23 +342,23 @@ void ZoneEntityRenderer::updateHazeFromEntity(const TypedEntityPointer& entity)
|
|||
haze->setHazeActive(hazeMode == COMPONENT_MODE_ENABLED);
|
||||
haze->setAltitudeBased(_hazeProperties.getHazeAltitudeEffect());
|
||||
|
||||
haze->setHazeRangeFactor(model::convertHazeRangeToHazeRangeFactor(_hazeProperties.getHazeRange()));
|
||||
haze->setHazeRangeFactor(model::Haze::convertHazeRangeToHazeRangeFactor(_hazeProperties.getHazeRange()));
|
||||
xColor hazeColor = _hazeProperties.getHazeColor();
|
||||
haze->setHazeColor(glm::vec3(hazeColor.red / 255.0, hazeColor.green / 255.0, hazeColor.blue / 255.0));
|
||||
xColor hazeGlareColor = _hazeProperties.getHazeGlareColor();
|
||||
haze->setDirectionalLightColor(glm::vec3(hazeGlareColor.red / 255.0, hazeGlareColor.green / 255.0, hazeGlareColor.blue / 255.0));
|
||||
haze->setHazeGlareColor(glm::vec3(hazeGlareColor.red / 255.0, hazeGlareColor.green / 255.0, hazeGlareColor.blue / 255.0));
|
||||
haze->setHazeEnableGlare(_hazeProperties.getHazeEnableGlare());
|
||||
haze->setDirectionalLightBlend(model::convertDirectionalLightAngleToPower(_hazeProperties.getHazeGlareAngle()));
|
||||
haze->setHazeGlareBlend(model::Haze::convertGlareAngleToPower(_hazeProperties.getHazeGlareAngle()));
|
||||
|
||||
float hazeAltitude = _hazeProperties.getHazeCeiling() - _hazeProperties.getHazeBaseRef();
|
||||
haze->setHazeAltitudeFactor(model::convertHazeAltitudeToHazeAltitudeFactor(hazeAltitude));
|
||||
haze->setHazeAltitudeFactor(model::Haze::convertHazeAltitudeToHazeAltitudeFactor(hazeAltitude));
|
||||
haze->setHazeBaseReference(_hazeProperties.getHazeBaseRef());
|
||||
|
||||
haze->setHazeBackgroundBlendValue(_hazeProperties.getHazeBackgroundBlend());
|
||||
haze->setHazeBackgroundBlend(_hazeProperties.getHazeBackgroundBlend());
|
||||
|
||||
haze->setHazeAttenuateKeyLight(_hazeProperties.getHazeAttenuateKeyLight());
|
||||
haze->setHazeKeyLightRangeFactor(model::convertHazeRangeToHazeRangeFactor(_hazeProperties.getHazeKeyLightRange()));
|
||||
haze->setHazeKeyLightAltitudeFactor(model::convertHazeAltitudeToHazeAltitudeFactor(_hazeProperties.getHazeKeyLightAltitude()));
|
||||
haze->setHazeKeyLightRangeFactor(model::Haze::convertHazeRangeToHazeRangeFactor(_hazeProperties.getHazeKeyLightRange()));
|
||||
haze->setHazeKeyLightAltitudeFactor(model::Haze::convertHazeAltitudeToHazeAltitudeFactor(_hazeProperties.getHazeKeyLightAltitude()));
|
||||
|
||||
haze->setZoneTransform(entity->getTransform().getMatrix());
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
set(TARGET_NAME entities)
|
||||
setup_hifi_library(Network Script)
|
||||
include_directories(SYSTEM "${OPENSSL_INCLUDE_DIR}")
|
||||
link_hifi_libraries(shared networking octree avatars)
|
||||
link_hifi_libraries(shared networking octree avatars model)
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
#include "EntitiesLogging.h"
|
||||
#include "EntityItem.h"
|
||||
#include "EntityItemProperties.h"
|
||||
#include <AddressManager.h>
|
||||
|
||||
EntityEditPacketSender::EntityEditPacketSender() {
|
||||
auto& packetReceiver = DependencyManager::get<NodeList>()->getPacketReceiver();
|
||||
|
@ -93,9 +94,9 @@ void EntityEditPacketSender::queueEditEntityMessage(PacketType type,
|
|||
QByteArray bufferOut(NLPacket::maxPayloadSize(type), 0);
|
||||
|
||||
bool success;
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
if (properties.parentIDChanged() && properties.getParentID() == AVATAR_SELF_ID) {
|
||||
EntityItemProperties propertiesCopy = properties;
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
const QUuid myNodeID = nodeList->getSessionUUID();
|
||||
propertiesCopy.setParentID(myNodeID);
|
||||
success = EntityItemProperties::encodeEntityEditPacket(type, entityItemID, propertiesCopy, bufferOut);
|
||||
|
@ -110,6 +111,9 @@ void EntityEditPacketSender::queueEditEntityMessage(PacketType type,
|
|||
qCDebug(entities) << " properties:" << properties;
|
||||
#endif
|
||||
queueOctreeEditMessage(type, bufferOut);
|
||||
if (type == PacketType::EntityAdd && !properties.getCertificateID().isEmpty()) {
|
||||
emit addingEntityWithCertificate(properties.getCertificateID(), DependencyManager::get<AddressManager>()->getPlaceName());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -43,6 +43,9 @@ public:
|
|||
virtual char getMyNodeType() const override { return NodeType::EntityServer; }
|
||||
virtual void adjustEditPacketForClockSkew(PacketType type, QByteArray& buffer, qint64 clockSkew) override;
|
||||
|
||||
signals:
|
||||
void addingEntityWithCertificate(const QString& certificateID, const QString& placeName);
|
||||
|
||||
public slots:
|
||||
void processEntityEditNackPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer sendingNode);
|
||||
|
||||
|
|
|
@ -14,9 +14,14 @@
|
|||
#include <QtCore/QObject>
|
||||
#include <QtEndian>
|
||||
#include <QJsonDocument>
|
||||
#include <openssl/rsa.h> // see comments for DEBUG_CERT
|
||||
#include <openssl/err.h>
|
||||
#include <openssl/rsa.h>
|
||||
#include <openssl/pem.h>
|
||||
#include <openssl/x509.h>
|
||||
#include <NetworkingConstants.h>
|
||||
#include <NetworkAccessManager.h>
|
||||
#include <QtNetwork/QNetworkReply>
|
||||
#include <QtNetwork/QNetworkRequest>
|
||||
|
||||
#include <glm/gtx/transform.hpp>
|
||||
|
||||
|
@ -41,6 +46,7 @@ int entityItemPointernMetaTypeId = qRegisterMetaType<EntityItemPointer>();
|
|||
|
||||
int EntityItem::_maxActionsDataSize = 800;
|
||||
quint64 EntityItem::_rememberDeletedActionTime = 20 * USECS_PER_SECOND;
|
||||
QString EntityItem::_marketplacePublicKey;
|
||||
|
||||
EntityItem::EntityItem(const EntityItemID& entityItemID) :
|
||||
SpatiallyNestable(NestableType::Entity, entityItemID)
|
||||
|
@ -1583,16 +1589,16 @@ QByteArray EntityItem::getStaticCertificateJSON() const {
|
|||
// It is important that this be reproducible in the same order each time. Since we also generate these on the server, we do it alphabetically
|
||||
// to help maintainence in two different code bases.
|
||||
if (!propertySet.getAnimation().getURL().isEmpty()) {
|
||||
json["animation.url"] = propertySet.getAnimation().getURL();
|
||||
json["animationURL"] = propertySet.getAnimation().getURL();
|
||||
}
|
||||
ADD_STRING_PROPERTY(collisionSoundURL, CollisionSoundURL);
|
||||
ADD_STRING_PROPERTY(compoundShapeURL, CompoundShapeURL);
|
||||
ADD_INT_PROPERTY(editionNumber, EditionNumber);
|
||||
ADD_INT_PROPERTY(entityInstanceNumber, EntityInstanceNumber);
|
||||
ADD_INT_PROPERTY(instanceNumber, EntityInstanceNumber);
|
||||
ADD_STRING_PROPERTY(itemArtist, ItemArtist);
|
||||
ADD_STRING_PROPERTY(itemCategories, ItemCategories);
|
||||
ADD_STRING_PROPERTY(itemDescription, ItemDescription);
|
||||
ADD_STRING_PROPERTY(itemLicense, ItemLicense);
|
||||
ADD_STRING_PROPERTY(itemLicenseUrl, ItemLicense);
|
||||
ADD_STRING_PROPERTY(itemName, ItemName);
|
||||
ADD_INT_PROPERTY(limitedRun, LimitedRun);
|
||||
ADD_STRING_PROPERTY(marketplaceID, MarketplaceID);
|
||||
|
@ -1607,39 +1613,6 @@ QByteArray EntityItem::getStaticCertificateHash() const {
|
|||
return QCryptographicHash::hash(getStaticCertificateJSON(), QCryptographicHash::Sha256);
|
||||
}
|
||||
|
||||
#ifdef DEBUG_CERT
|
||||
QString EntityItem::computeCertificateID() {
|
||||
// Until the marketplace generates it, compute and answer the certificateID here.
|
||||
// Does not set it, as that will have to be done from script engine in order to update server, etc.
|
||||
const auto hash = getStaticCertificateHash();
|
||||
const auto text = reinterpret_cast<const unsigned char*>(hash.constData());
|
||||
const unsigned int textLength = hash.length();
|
||||
|
||||
const char privateKey[] = "-----BEGIN RSA PRIVATE KEY-----\n\
|
||||
MIIBOQIBAAJBALCoBiDAZOClO26tC5pd7JikBL61WIgpAqbcNnrV/TcG6LPI7Zbi\n\
|
||||
MjdUixmTNvYMRZH3Wlqtl2IKG1W68y3stKECAwEAAQJABvOlwhYwIhL+gr12jm2R\n\
|
||||
yPPzZ9nVEQ6kFxLlZfIT09119fd6OU1X5d4sHWfMfSIEgjwQIDS3ZU1kY3XKo87X\n\
|
||||
zQIhAOPHlYa1OC7BLhaTouy68qIU2vCKLP8mt4S31/TT0UOnAiEAxor6gU6yupTQ\n\
|
||||
yuyV3yHvr5LkZKBGqhjmOTmDfgtX7ncCIChGbgX3nQuHVOLhD/nTxHssPNozVGl5\n\
|
||||
KxHof+LmYSYZAiB4U+yEh9SsXdq40W/3fpLMPuNq1PRezJ5jGidGMcvF+wIgUNec\n\
|
||||
3Kg2U+CVZr8/bDT/vXRrsKj1zfobYuvbfVH02QY=\n\
|
||||
-----END RSA PRIVATE KEY-----";
|
||||
BIO* bio = BIO_new_mem_buf((void*)privateKey, sizeof(privateKey));
|
||||
RSA* rsa = PEM_read_bio_RSAPrivateKey(bio, NULL, NULL, NULL);
|
||||
|
||||
QByteArray signature(RSA_size(rsa), 0);
|
||||
unsigned int signatureLength = 0;
|
||||
const int signOK = RSA_sign(NID_sha256, text, textLength, reinterpret_cast<unsigned char*>(signature.data()), &signatureLength, rsa);
|
||||
BIO_free(bio);
|
||||
RSA_free(rsa);
|
||||
if (!signOK) {
|
||||
qCWarning(entities) << "Unable to compute signature for" << getName() << getEntityItemID();
|
||||
return "";
|
||||
}
|
||||
return signature.toBase64();
|
||||
#endif
|
||||
}
|
||||
|
||||
bool EntityItem::verifyStaticCertificateProperties() {
|
||||
// True IIF a non-empty certificateID matches the static certificate json.
|
||||
// I.e., if we can verify that the certificateID was produced by High Fidelity signing the static certificate hash.
|
||||
|
@ -1647,27 +1620,69 @@ bool EntityItem::verifyStaticCertificateProperties() {
|
|||
if (getCertificateID().isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
const auto signatureBytes = QByteArray::fromBase64(getCertificateID().toLatin1());
|
||||
const auto signature = reinterpret_cast<const unsigned char*>(signatureBytes.constData());
|
||||
const unsigned int signatureLength = signatureBytes.length();
|
||||
|
||||
const auto hash = getStaticCertificateHash();
|
||||
const auto text = reinterpret_cast<const unsigned char*>(hash.constData());
|
||||
const unsigned int textLength = hash.length();
|
||||
const QByteArray marketplacePublicKeyByteArray = EntityItem::_marketplacePublicKey.toUtf8();
|
||||
const unsigned char* marketplacePublicKey = reinterpret_cast<const unsigned char*>(marketplacePublicKeyByteArray.constData());
|
||||
int marketplacePublicKeyLength = marketplacePublicKeyByteArray.length();
|
||||
|
||||
// After DEBUG_CERT ends, we will get/cache this once from the marketplace when needed, and it likely won't be RSA.
|
||||
const char publicKey[] = "-----BEGIN PUBLIC KEY-----\n\
|
||||
MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBALCoBiDAZOClO26tC5pd7JikBL61WIgp\n\
|
||||
AqbcNnrV/TcG6LPI7ZbiMjdUixmTNvYMRZH3Wlqtl2IKG1W68y3stKECAwEAAQ==\n\
|
||||
-----END PUBLIC KEY-----";
|
||||
BIO *bio = BIO_new_mem_buf((void*)publicKey, sizeof(publicKey));
|
||||
BIO *bio = BIO_new_mem_buf((void*)marketplacePublicKey, marketplacePublicKeyLength);
|
||||
EVP_PKEY* evp_key = PEM_read_bio_PUBKEY(bio, NULL, NULL, NULL);
|
||||
RSA* rsa = EVP_PKEY_get1_RSA(evp_key);
|
||||
bool answer = RSA_verify(NID_sha256, text, textLength, signature, signatureLength, rsa);
|
||||
BIO_free(bio);
|
||||
RSA_free(rsa);
|
||||
EVP_PKEY_free(evp_key);
|
||||
return answer;
|
||||
if (evp_key) {
|
||||
RSA* rsa = EVP_PKEY_get1_RSA(evp_key);
|
||||
if (rsa) {
|
||||
const QByteArray digestByteArray = getStaticCertificateHash();
|
||||
const unsigned char* digest = reinterpret_cast<const unsigned char*>(digestByteArray.constData());
|
||||
int digestLength = digestByteArray.length();
|
||||
|
||||
const QByteArray signatureByteArray = QByteArray::fromBase64(getCertificateID().toUtf8());
|
||||
const unsigned char* signature = reinterpret_cast<const unsigned char*>(signatureByteArray.constData());
|
||||
int signatureLength = signatureByteArray.length();
|
||||
|
||||
ERR_clear_error();
|
||||
bool answer = RSA_verify(NID_sha256,
|
||||
digest,
|
||||
digestLength,
|
||||
signature,
|
||||
signatureLength,
|
||||
rsa);
|
||||
long error = ERR_get_error();
|
||||
if (error != 0) {
|
||||
const char* error_str = ERR_error_string(error, NULL);
|
||||
qCWarning(entities) << "ERROR while verifying static certificate properties! RSA error:" << error_str
|
||||
<< "\nStatic Cert JSON:" << getStaticCertificateJSON()
|
||||
<< "\nKey:" << EntityItem::_marketplacePublicKey << "\nKey Length:" << marketplacePublicKeyLength
|
||||
<< "\nDigest:" << digest << "\nDigest Length:" << digestLength
|
||||
<< "\nSignature:" << signature << "\nSignature Length:" << signatureLength;
|
||||
}
|
||||
RSA_free(rsa);
|
||||
if (bio) {
|
||||
BIO_free(bio);
|
||||
}
|
||||
if (evp_key) {
|
||||
EVP_PKEY_free(evp_key);
|
||||
}
|
||||
return answer;
|
||||
} else {
|
||||
if (bio) {
|
||||
BIO_free(bio);
|
||||
}
|
||||
if (evp_key) {
|
||||
EVP_PKEY_free(evp_key);
|
||||
}
|
||||
long error = ERR_get_error();
|
||||
const char* error_str = ERR_error_string(error, NULL);
|
||||
qCWarning(entities) << "Failed to verify static certificate properties! RSA error:" << error_str;
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if (bio) {
|
||||
BIO_free(bio);
|
||||
}
|
||||
long error = ERR_get_error();
|
||||
const char* error_str = ERR_error_string(error, NULL);
|
||||
qCWarning(entities) << "Failed to verify static certificate properties! RSA error:" << error_str;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void EntityItem::adjustShapeInfoByRegistration(ShapeInfo& info) const {
|
||||
|
@ -1754,7 +1769,6 @@ void EntityItem::updateDimensions(const glm::vec3& value) {
|
|||
setDimensions(value);
|
||||
markDirtyFlags(Simulation::DIRTY_SHAPE | Simulation::DIRTY_MASS);
|
||||
_queryAACubeSet = false;
|
||||
dimensionsChanged();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2986,3 +3000,34 @@ void EntityItem::somethingChangedNotification() {
|
|||
}
|
||||
});
|
||||
}
|
||||
|
||||
void EntityItem::retrieveMarketplacePublicKey() {
|
||||
QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();
|
||||
QNetworkRequest networkRequest;
|
||||
networkRequest.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true);
|
||||
QUrl requestURL = NetworkingConstants::METAVERSE_SERVER_URL;
|
||||
requestURL.setPath("/api/v1/commerce/marketplace_key");
|
||||
QJsonObject request;
|
||||
networkRequest.setUrl(requestURL);
|
||||
|
||||
QNetworkReply* networkReply = NULL;
|
||||
networkReply = networkAccessManager.get(networkRequest);
|
||||
|
||||
connect(networkReply, &QNetworkReply::finished, [=]() {
|
||||
QJsonObject jsonObject = QJsonDocument::fromJson(networkReply->readAll()).object();
|
||||
jsonObject = jsonObject["data"].toObject();
|
||||
|
||||
if (networkReply->error() == QNetworkReply::NoError) {
|
||||
if (!jsonObject["public_key"].toString().isEmpty()) {
|
||||
EntityItem::_marketplacePublicKey = jsonObject["public_key"].toString();
|
||||
qCWarning(entities) << "Marketplace public key has been set to" << _marketplacePublicKey;
|
||||
} else {
|
||||
qCWarning(entities) << "Marketplace public key is empty!";
|
||||
}
|
||||
} else {
|
||||
qCWarning(entities) << "Call to" << networkRequest.url() << "failed! Error:" << networkReply->error();
|
||||
}
|
||||
|
||||
networkReply->deleteLater();
|
||||
});
|
||||
}
|
||||
|
|
|
@ -36,9 +36,6 @@
|
|||
#include "SimulationFlags.h"
|
||||
#include "EntityDynamicInterface.h"
|
||||
|
||||
// FIXME: The server-side marketplace will soon create the certificateID. At that point, all of the DEBUG_CERT stuff will go away.
|
||||
#define DEBUG_CERT 1
|
||||
|
||||
class EntitySimulation;
|
||||
class EntityTreeElement;
|
||||
class EntityTreeElementExtraEncodeData;
|
||||
|
@ -334,9 +331,6 @@ public:
|
|||
QByteArray getStaticCertificateJSON() const;
|
||||
QByteArray getStaticCertificateHash() const;
|
||||
bool verifyStaticCertificateProperties();
|
||||
#ifdef DEBUG_CERT
|
||||
QString computeCertificateID();
|
||||
#endif
|
||||
|
||||
// TODO: get rid of users of getRadius()...
|
||||
float getRadius() const;
|
||||
|
@ -483,6 +477,9 @@ public:
|
|||
ChangeHandlerId registerChangeHandler(const ChangeHandlerCallback& handler);
|
||||
void deregisterChangeHandler(const ChangeHandlerId& changeHandlerId);
|
||||
|
||||
static QString _marketplacePublicKey;
|
||||
static void retrieveMarketplacePublicKey();
|
||||
|
||||
protected:
|
||||
QHash<ChangeHandlerId, ChangeHandlerCallback> _changeHandlers;
|
||||
|
||||
|
|
|
@ -202,6 +202,24 @@ enum EntityPropertyList {
|
|||
PROP_ENTITY_INSTANCE_NUMBER,
|
||||
PROP_CERTIFICATE_ID,
|
||||
|
||||
PROP_HAZE_MODE,
|
||||
|
||||
PROP_HAZE_RANGE,
|
||||
PROP_HAZE_COLOR,
|
||||
PROP_HAZE_GLARE_COLOR,
|
||||
PROP_HAZE_ENABLE_GLARE,
|
||||
PROP_HAZE_GLARE_ANGLE,
|
||||
|
||||
PROP_HAZE_ALTITUDE_EFFECT,
|
||||
PROP_HAZE_CEILING,
|
||||
PROP_HAZE_BASE_REF,
|
||||
|
||||
PROP_HAZE_BACKGROUND_BLEND,
|
||||
|
||||
PROP_HAZE_ATTENUATE_KEYLIGHT,
|
||||
PROP_HAZE_KEYLIGHT_RANGE,
|
||||
PROP_HAZE_KEYLIGHT_ALTITUDE,
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// ATTENTION: add new properties to end of list just ABOVE this line
|
||||
PROP_AFTER_LAST_ITEM,
|
||||
|
@ -234,24 +252,6 @@ enum EntityPropertyList {
|
|||
PROP_STAGE_AUTOMATIC_HOURDAY = PROP_ANIMATION_FRAME_INDEX,
|
||||
PROP_BACKGROUND_MODE = PROP_MODEL_URL,
|
||||
|
||||
PROP_HAZE_MODE = PROP_COLOR,
|
||||
|
||||
PROP_HAZE_RANGE = PROP_INTENSITY,
|
||||
PROP_HAZE_COLOR = PROP_CUTOFF,
|
||||
PROP_HAZE_GLARE_COLOR = PROP_EXPONENT,
|
||||
PROP_HAZE_ENABLE_GLARE = PROP_IS_SPOTLIGHT,
|
||||
PROP_HAZE_GLARE_ANGLE = PROP_DIFFUSE_COLOR,
|
||||
|
||||
PROP_HAZE_ALTITUDE_EFFECT = PROP_AMBIENT_COLOR_UNUSED,
|
||||
PROP_HAZE_CEILING = PROP_SPECULAR_COLOR_UNUSED,
|
||||
PROP_HAZE_BASE_REF = PROP_LINEAR_ATTENUATION_UNUSED,
|
||||
|
||||
PROP_HAZE_BACKGROUND_BLEND = PROP_QUADRATIC_ATTENUATION_UNUSED,
|
||||
|
||||
PROP_HAZE_ATTENUATE_KEYLIGHT = PROP_ANIMATION_FRAME_INDEX,
|
||||
PROP_HAZE_KEYLIGHT_RANGE = PROP_MODEL_URL,
|
||||
PROP_HAZE_KEYLIGHT_ALTITUDE = PROP_ANIMATION_URL,
|
||||
|
||||
PROP_SKYBOX_COLOR = PROP_ANIMATION_URL,
|
||||
PROP_SKYBOX_URL = PROP_ANIMATION_FPS,
|
||||
PROP_KEYLIGHT_AMBIENT_URL = PROP_ANIMATION_PLAYING,
|
||||
|
|
|
@ -1833,18 +1833,3 @@ bool EntityScriptingInterface::verifyStaticCertificateProperties(const QUuid& en
|
|||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
#ifdef DEBUG_CERT
|
||||
QString EntityScriptingInterface::computeCertificateID(const QUuid& entityID) {
|
||||
QString result { "" };
|
||||
if (_entityTree) {
|
||||
_entityTree->withReadLock([&] {
|
||||
EntityItemPointer entity = _entityTree->findEntityByEntityItemID(EntityItemID(entityID));
|
||||
if (entity) {
|
||||
result = entity->computeCertificateID();
|
||||
}
|
||||
});
|
||||
}
|
||||
return result;
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -424,9 +424,6 @@ public slots:
|
|||
Q_INVOKABLE glm::mat4 getEntityLocalTransform(const QUuid& entityID);
|
||||
|
||||
Q_INVOKABLE bool verifyStaticCertificateProperties(const QUuid& entityID);
|
||||
#ifdef DEBUG_CERT
|
||||
Q_INVOKABLE QString computeCertificateID(const QUuid& entityID);
|
||||
#endif
|
||||
|
||||
signals:
|
||||
void collisionWithEntity(const EntityItemID& idA, const EntityItemID& idB, const Collision& collision);
|
||||
|
|
|
@ -13,6 +13,16 @@
|
|||
#include <QtCore/QDateTime>
|
||||
#include <QtCore/QQueue>
|
||||
|
||||
#include <openssl/err.h>
|
||||
#include <openssl/rsa.h>
|
||||
#include <openssl/pem.h>
|
||||
#include <openssl/x509.h>
|
||||
#include <NetworkingConstants.h>
|
||||
#include "AccountManager.h"
|
||||
#include <QJsonObject>
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonArray>
|
||||
|
||||
#include <QtScript/QScriptEngine>
|
||||
|
||||
#include <Extents.h>
|
||||
|
@ -62,6 +72,8 @@ EntityTree::EntityTree(bool shouldReaverage) :
|
|||
Octree(shouldReaverage)
|
||||
{
|
||||
resetClientEditStats();
|
||||
|
||||
EntityItem::retrieveMarketplacePublicKey();
|
||||
}
|
||||
|
||||
EntityTree::~EntityTree() {
|
||||
|
@ -228,6 +240,31 @@ bool EntityTree::handlesEditPacketType(PacketType packetType) const {
|
|||
/// Adds a new entity item to the tree
|
||||
void EntityTree::postAddEntity(EntityItemPointer entity) {
|
||||
assert(entity);
|
||||
|
||||
if (getIsServer()) {
|
||||
QString certID(entity->getCertificateID());
|
||||
EntityItemID entityItemID = entity->getEntityItemID();
|
||||
EntityItemID existingEntityItemID;
|
||||
|
||||
{
|
||||
QWriteLocker locker(&_entityCertificateIDMapLock);
|
||||
existingEntityItemID = _entityCertificateIDMap.value(certID);
|
||||
if (!certID.isEmpty()) {
|
||||
_entityCertificateIDMap.insert(certID, entityItemID);
|
||||
qCDebug(entities) << "Certificate ID" << certID << "belongs to" << entityItemID;
|
||||
}
|
||||
}
|
||||
|
||||
// Delete an already-existing entity from the tree if it has the same
|
||||
// CertificateID as the entity we're trying to add.
|
||||
if (!existingEntityItemID.isNull()) {
|
||||
qCDebug(entities) << "Certificate ID" << certID << "already exists on entity with ID"
|
||||
<< existingEntityItemID << ". Deleting existing entity.";
|
||||
deleteEntity(existingEntityItemID, true);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// check to see if we need to simulate this entity..
|
||||
if (_simulation) {
|
||||
_simulation->addEntity(entity);
|
||||
|
@ -643,8 +680,16 @@ void EntityTree::processRemovedEntities(const DeleteEntityOperator& theOperator)
|
|||
theEntity->die();
|
||||
|
||||
if (getIsServer()) {
|
||||
{
|
||||
QWriteLocker entityCertificateIDMapLocker(&_entityCertificateIDMapLock);
|
||||
QString certID = theEntity->getCertificateID();
|
||||
if (theEntity->getEntityItemID() == _entityCertificateIDMap.value(certID)) {
|
||||
_entityCertificateIDMap.remove(certID);
|
||||
}
|
||||
}
|
||||
|
||||
// set up the deleted entities ID
|
||||
QWriteLocker locker(&_recentlyDeletedEntitiesLock);
|
||||
QWriteLocker recentlyDeletedEntitiesLocker(&_recentlyDeletedEntitiesLock);
|
||||
_recentlyDeletedEntityItemIDs.insert(deletedAt, theEntity->getEntityItemID());
|
||||
} else {
|
||||
// on the client side, we also remember that we deleted this entity, we don't care about the time
|
||||
|
@ -1090,6 +1135,203 @@ bool EntityTree::isScriptInWhitelist(const QString& scriptProperty) {
|
|||
return false;
|
||||
}
|
||||
|
||||
void EntityTree::startChallengeOwnershipTimer(const EntityItemID& entityItemID) {
|
||||
QTimer* _challengeOwnershipTimeoutTimer = new QTimer(this);
|
||||
connect(this, &EntityTree::killChallengeOwnershipTimeoutTimer, this, [=](const QString& certID) {
|
||||
QReadLocker locker(&_entityCertificateIDMapLock);
|
||||
EntityItemID id = _entityCertificateIDMap.value(certID);
|
||||
if (entityItemID == id && _challengeOwnershipTimeoutTimer) {
|
||||
_challengeOwnershipTimeoutTimer->stop();
|
||||
_challengeOwnershipTimeoutTimer->deleteLater();
|
||||
}
|
||||
});
|
||||
connect(_challengeOwnershipTimeoutTimer, &QTimer::timeout, this, [=]() {
|
||||
qCDebug(entities) << "Ownership challenge timed out, deleting entity" << entityItemID;
|
||||
deleteEntity(entityItemID, true);
|
||||
if (_challengeOwnershipTimeoutTimer) {
|
||||
_challengeOwnershipTimeoutTimer->stop();
|
||||
_challengeOwnershipTimeoutTimer->deleteLater();
|
||||
}
|
||||
});
|
||||
_challengeOwnershipTimeoutTimer->setSingleShot(true);
|
||||
_challengeOwnershipTimeoutTimer->start(5000);
|
||||
}
|
||||
|
||||
void EntityTree::startPendingTransferStatusTimer(const QString& certID, const EntityItemID& entityItemID, const SharedNodePointer& senderNode) {
|
||||
qCDebug(entities) << "'transfer_status' is 'pending', checking again in 90 seconds..." << entityItemID;
|
||||
QTimer* transferStatusRetryTimer = new QTimer(this);
|
||||
connect(transferStatusRetryTimer, &QTimer::timeout, this, [=]() {
|
||||
validatePop(certID, entityItemID, senderNode, true);
|
||||
});
|
||||
transferStatusRetryTimer->setSingleShot(true);
|
||||
transferStatusRetryTimer->start(90000);
|
||||
}
|
||||
|
||||
QByteArray EntityTree::computeEncryptedNonce(const QString& certID, const QString ownerKey) {
|
||||
QString ownerKeyWithHeaders = ("-----BEGIN RSA PUBLIC KEY-----\n" + ownerKey + "\n-----END RSA PUBLIC KEY-----");
|
||||
BIO* bio = BIO_new_mem_buf((void*)ownerKeyWithHeaders.toUtf8().constData(), -1);
|
||||
BIO_set_flags(bio, BIO_FLAGS_BASE64_NO_NL); // NO NEWLINE
|
||||
RSA* rsa = PEM_read_bio_RSAPublicKey(bio, NULL, NULL, NULL);
|
||||
|
||||
if (rsa) {
|
||||
QUuid nonce = QUuid::createUuid();
|
||||
const unsigned int textLength = nonce.toString().length();
|
||||
QByteArray encryptedText(RSA_size(rsa), 0);
|
||||
const int encryptStatus = RSA_public_encrypt(textLength,
|
||||
reinterpret_cast<const unsigned char*>(qPrintable(nonce.toString())),
|
||||
reinterpret_cast<unsigned char*>(encryptedText.data()),
|
||||
rsa,
|
||||
RSA_PKCS1_OAEP_PADDING);
|
||||
if (bio) {
|
||||
BIO_free(bio);
|
||||
}
|
||||
RSA_free(rsa);
|
||||
if (encryptStatus == -1) {
|
||||
long error = ERR_get_error();
|
||||
const char* error_str = ERR_error_string(error, NULL);
|
||||
qCWarning(entities) << "Unable to compute encrypted nonce for" << certID << "\nRSA error:" << error_str;
|
||||
return "";
|
||||
}
|
||||
|
||||
QWriteLocker locker(&_certNonceMapLock);
|
||||
_certNonceMap.insert(certID, nonce);
|
||||
qCDebug(entities) << "Challenging ownership of Cert ID" << certID << "by encrypting and sending nonce" << nonce << "to owner.";
|
||||
|
||||
return encryptedText;
|
||||
} else {
|
||||
if (bio) {
|
||||
BIO_free(bio);
|
||||
}
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
bool EntityTree::verifyDecryptedNonce(const QString& certID, const QString& decryptedNonce) {
|
||||
|
||||
EntityItemID id;
|
||||
{
|
||||
QReadLocker certIdMapLocker(&_entityCertificateIDMapLock);
|
||||
id = _entityCertificateIDMap.value(certID);
|
||||
}
|
||||
|
||||
QString actualNonce;
|
||||
{
|
||||
QWriteLocker locker(&_certNonceMapLock);
|
||||
actualNonce = _certNonceMap.take(certID).toString();
|
||||
}
|
||||
|
||||
bool verificationSuccess = (actualNonce == decryptedNonce);
|
||||
if (!verificationSuccess) {
|
||||
if (!id.isNull()) {
|
||||
qCDebug(entities) << "Ownership challenge for Cert ID" << certID << "failed; deleting entity" << id
|
||||
<< "\nActual nonce:" << actualNonce << "\nDecrypted nonce:" << decryptedNonce;
|
||||
deleteEntity(id, true);
|
||||
}
|
||||
} else {
|
||||
qCDebug(entities) << "Ownership challenge for Cert ID" << certID << "succeeded; keeping entity" << id;
|
||||
}
|
||||
|
||||
return verificationSuccess;
|
||||
}
|
||||
|
||||
void EntityTree::validatePop(const QString& certID, const EntityItemID& entityItemID, const SharedNodePointer& senderNode, bool isRetryingValidation) {
|
||||
// Start owner verification.
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
// First, asynchronously hit "proof_of_purchase_status?transaction_type=transfer" endpoint.
|
||||
QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();
|
||||
QNetworkRequest networkRequest;
|
||||
networkRequest.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true);
|
||||
networkRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
|
||||
QUrl requestURL = NetworkingConstants::METAVERSE_SERVER_URL;
|
||||
requestURL.setPath("/api/v1/commerce/proof_of_purchase_status/transfer");
|
||||
QJsonObject request;
|
||||
request["certificate_id"] = certID;
|
||||
networkRequest.setUrl(requestURL);
|
||||
|
||||
QNetworkReply* networkReply = NULL;
|
||||
networkReply = networkAccessManager.put(networkRequest, QJsonDocument(request).toJson());
|
||||
|
||||
connect(networkReply, &QNetworkReply::finished, [=]() {
|
||||
QJsonObject jsonObject = QJsonDocument::fromJson(networkReply->readAll()).object();
|
||||
jsonObject = jsonObject["data"].toObject();
|
||||
|
||||
if (networkReply->error() == QNetworkReply::NoError) {
|
||||
if (!jsonObject["invalid_reason"].toString().isEmpty()) {
|
||||
qCDebug(entities) << "invalid_reason not empty, deleting entity" << entityItemID;
|
||||
deleteEntity(entityItemID, true);
|
||||
} else if (jsonObject["transfer_status"].toArray().first().toString() == "failed") {
|
||||
qCDebug(entities) << "'transfer_status' is 'failed', deleting entity" << entityItemID;
|
||||
deleteEntity(entityItemID, true);
|
||||
} else if (jsonObject["transfer_status"].toArray().first().toString() == "pending") {
|
||||
if (isRetryingValidation) {
|
||||
qCDebug(entities) << "'transfer_status' is 'pending' after retry, deleting entity" << entityItemID;
|
||||
deleteEntity(entityItemID, true);
|
||||
} else {
|
||||
if (thread() != QThread::currentThread()) {
|
||||
QMetaObject::invokeMethod(this, "startPendingTransferStatusTimer",
|
||||
Q_ARG(const QString&, certID),
|
||||
Q_ARG(const EntityItemID&, entityItemID),
|
||||
Q_ARG(const SharedNodePointer&, senderNode));
|
||||
return;
|
||||
} else {
|
||||
startPendingTransferStatusTimer(certID, entityItemID, senderNode);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Second, challenge ownership of the PoP cert
|
||||
// 1. Encrypt a nonce with the owner's public key
|
||||
QByteArray encryptedText = computeEncryptedNonce(certID, jsonObject["transfer_recipient_key"].toString());
|
||||
|
||||
if (encryptedText == "") {
|
||||
qCDebug(entities) << "CRITICAL ERROR: Couldn't compute encrypted nonce. Deleting entity...";
|
||||
deleteEntity(entityItemID, true);
|
||||
} else {
|
||||
// 2. Send the encrypted text to the rezzing avatar's node
|
||||
QByteArray certIDByteArray = certID.toUtf8();
|
||||
int certIDByteArraySize = certIDByteArray.size();
|
||||
auto challengeOwnershipPacket = NLPacket::create(PacketType::ChallengeOwnership,
|
||||
certIDByteArraySize + encryptedText.length() + 2 * sizeof(int),
|
||||
true);
|
||||
challengeOwnershipPacket->writePrimitive(certIDByteArraySize);
|
||||
challengeOwnershipPacket->writePrimitive(encryptedText.length());
|
||||
challengeOwnershipPacket->write(certIDByteArray);
|
||||
challengeOwnershipPacket->write(encryptedText);
|
||||
nodeList->sendPacket(std::move(challengeOwnershipPacket), *senderNode);
|
||||
|
||||
// 3. Kickoff a 10-second timeout timer that deletes the entity if we don't get an ownership response in time
|
||||
if (thread() != QThread::currentThread()) {
|
||||
QMetaObject::invokeMethod(this, "startChallengeOwnershipTimer", Q_ARG(const EntityItemID&, entityItemID));
|
||||
return;
|
||||
} else {
|
||||
startChallengeOwnershipTimer(entityItemID);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
qCDebug(entities) << "Call to" << networkReply->url() << "failed with error" << networkReply->error() << "; deleting entity" << entityItemID
|
||||
<< "More info:" << jsonObject;
|
||||
deleteEntity(entityItemID, true);
|
||||
}
|
||||
|
||||
networkReply->deleteLater();
|
||||
});
|
||||
}
|
||||
|
||||
void EntityTree::processChallengeOwnershipPacket(ReceivedMessage& message, const SharedNodePointer& sourceNode) {
|
||||
int certIDByteArraySize;
|
||||
int decryptedTextByteArraySize;
|
||||
|
||||
message.readPrimitive(&certIDByteArraySize);
|
||||
message.readPrimitive(&decryptedTextByteArraySize);
|
||||
|
||||
QString certID(message.read(certIDByteArraySize));
|
||||
QString decryptedText(message.read(decryptedTextByteArraySize));
|
||||
|
||||
emit killChallengeOwnershipTimeoutTimer(certID);
|
||||
|
||||
verifyDecryptedNonce(certID, decryptedText);
|
||||
}
|
||||
|
||||
int EntityTree::processEditPacketData(ReceivedMessage& message, const unsigned char* editData, int maxLength,
|
||||
const SharedNodePointer& senderNode) {
|
||||
|
||||
|
@ -1265,13 +1507,17 @@ int EntityTree::processEditPacketData(ReceivedMessage& message, const unsigned c
|
|||
_totalUpdates++;
|
||||
} else if (isAdd) {
|
||||
bool failedAdd = !allowed;
|
||||
bool isCertified = !properties.getCertificateID().isEmpty();
|
||||
if (!allowed) {
|
||||
qCDebug(entities) << "Filtered entity add. ID:" << entityItemID;
|
||||
} else if (!senderNode->getCanRez() && !senderNode->getCanRezTmp()) {
|
||||
} else if (!isCertified && !senderNode->getCanRez() && !senderNode->getCanRezTmp()) {
|
||||
failedAdd = true;
|
||||
qCDebug(entities) << "User without 'rez rights' [" << senderNode->getUUID()
|
||||
<< "] attempted to add an entity ID:" << entityItemID;
|
||||
|
||||
qCDebug(entities) << "User without 'uncertified rez rights' [" << senderNode->getUUID()
|
||||
<< "] attempted to add an uncertified entity with ID:" << entityItemID;
|
||||
} else if (isCertified && !senderNode->getCanRezCertified() && !senderNode->getCanRezTmpCertified()) {
|
||||
failedAdd = true;
|
||||
qCDebug(entities) << "User without 'certified rez rights' [" << senderNode->getUUID()
|
||||
<< "] attempted to add a certified entity with ID:" << entityItemID;
|
||||
} else {
|
||||
// this is a new entity... assign a new entityID
|
||||
properties.setCreated(properties.getLastEdited());
|
||||
|
@ -1280,6 +1526,19 @@ int EntityTree::processEditPacketData(ReceivedMessage& message, const unsigned c
|
|||
EntityItemPointer newEntity = addEntity(entityItemID, properties);
|
||||
endCreate = usecTimestampNow();
|
||||
_totalCreates++;
|
||||
|
||||
if (newEntity && isCertified && getIsServer()) {
|
||||
if (!newEntity->verifyStaticCertificateProperties()) {
|
||||
qCDebug(entities) << "User" << senderNode->getUUID()
|
||||
<< "attempted to add a certified entity with ID" << entityItemID << "which failed"
|
||||
<< "static certificate verification.";
|
||||
// Delete the entity we just added if it doesn't pass static certificate verification
|
||||
deleteEntity(entityItemID, true);
|
||||
} else {
|
||||
validatePop(properties.getCertificateID(), entityItemID, senderNode, false);
|
||||
}
|
||||
}
|
||||
|
||||
if (newEntity) {
|
||||
newEntity->markAsChangedOnServer();
|
||||
notifyNewlyCreatedEntity(*newEntity, senderNode);
|
||||
|
|
|
@ -93,6 +93,7 @@ public:
|
|||
void fixupTerseEditLogging(EntityItemProperties& properties, QList<QString>& changedProperties);
|
||||
virtual int processEditPacketData(ReceivedMessage& message, const unsigned char* editData, int maxLength,
|
||||
const SharedNodePointer& senderNode) override;
|
||||
virtual void processChallengeOwnershipPacket(ReceivedMessage& message, const SharedNodePointer& sourceNode) override;
|
||||
|
||||
virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
|
||||
QVector<EntityItemID> entityIdsToInclude, QVector<EntityItemID> entityIdsToDiscard,
|
||||
|
@ -181,6 +182,11 @@ public:
|
|||
return _recentlyDeletedEntityItemIDs;
|
||||
}
|
||||
|
||||
QHash<QString, EntityItemID> getEntityCertificateIDMap() const {
|
||||
QReadLocker locker(&_entityCertificateIDMapLock);
|
||||
return _entityCertificateIDMap;
|
||||
}
|
||||
|
||||
void forgetEntitiesDeletedBefore(quint64 sinceTime);
|
||||
|
||||
int processEraseMessage(ReceivedMessage& message, const SharedNodePointer& sourceNode);
|
||||
|
@ -276,6 +282,7 @@ signals:
|
|||
void entityServerScriptChanging(const EntityItemID& entityItemID, const bool reload);
|
||||
void newCollisionSoundURL(const QUrl& url, const EntityItemID& entityID);
|
||||
void clearingEntities();
|
||||
void killChallengeOwnershipTimeoutTimer(const QString& certID);
|
||||
|
||||
protected:
|
||||
|
||||
|
@ -316,6 +323,12 @@ protected:
|
|||
mutable QReadWriteLock _entityMapLock;
|
||||
QHash<EntityItemID, EntityItemPointer> _entityMap;
|
||||
|
||||
mutable QReadWriteLock _entityCertificateIDMapLock;
|
||||
QHash<QString, EntityItemID> _entityCertificateIDMap;
|
||||
|
||||
mutable QReadWriteLock _certNonceMapLock;
|
||||
QHash<QString, QUuid> _certNonceMap;
|
||||
|
||||
EntitySimulationPointer _simulation;
|
||||
|
||||
bool _wantEditLogging = false;
|
||||
|
@ -357,6 +370,14 @@ protected:
|
|||
|
||||
MovingEntitiesOperator _entityMover;
|
||||
QHash<EntityItemID, EntityItemPointer> _entitiesToAdd;
|
||||
|
||||
Q_INVOKABLE void startChallengeOwnershipTimer(const EntityItemID& entityItemID);
|
||||
Q_INVOKABLE void startPendingTransferStatusTimer(const QString& certID, const EntityItemID& entityItemID, const SharedNodePointer& senderNode);
|
||||
|
||||
private:
|
||||
QByteArray computeEncryptedNonce(const QString& certID, const QString ownerKey);
|
||||
bool verifyDecryptedNonce(const QString& certID, const QString& decryptedNonce);
|
||||
void validatePop(const QString& certID, const EntityItemID& entityItemID, const SharedNodePointer& senderNode, bool isRetryingValidation);
|
||||
};
|
||||
|
||||
#endif // hifi_EntityTree_h
|
||||
|
|
|
@ -15,19 +15,6 @@
|
|||
#include "EntityItemProperties.h"
|
||||
#include "EntityItemPropertiesMacros.h"
|
||||
|
||||
const float HazePropertyGroup::DEFAULT_HAZE_RANGE{ 1000.0f };
|
||||
const xColor HazePropertyGroup::DEFAULT_HAZE_COLOR{ 128, 154, 179 }; // Bluish
|
||||
const xColor HazePropertyGroup::DEFAULT_HAZE_GLARE_COLOR{ 255, 229, 179 }; // Yellowish
|
||||
const float HazePropertyGroup::DEFAULT_HAZE_GLARE_ANGLE{ 20.0 };
|
||||
|
||||
const float HazePropertyGroup::DEFAULT_HAZE_CEILING{ 200.0f };
|
||||
const float HazePropertyGroup::DEFAULT_HAZE_BASE_REF{ 0.0f };
|
||||
|
||||
const float HazePropertyGroup::DEFAULT_HAZE_BACKGROUND_BLEND{ 0.0f };
|
||||
|
||||
const float HazePropertyGroup::DEFAULT_HAZE_KEYLIGHT_RANGE{ 1000.0 };
|
||||
const float HazePropertyGroup::DEFAULT_HAZE_KEYLIGHT_ALTITUDE{ 200.0f };
|
||||
|
||||
void HazePropertyGroup::copyToScriptValue(const EntityPropertyFlags& desiredProperties, QScriptValue& properties, QScriptEngine* engine, bool skipDefaults, EntityItemProperties& defaultEntityProperties) const {
|
||||
COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_HAZE_RANGE, Haze, haze, HazeRange, hazeRange);
|
||||
COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_HAZE_COLOR, Haze, haze, HazeColor, hazeColor);
|
||||
|
@ -306,6 +293,7 @@ EntityPropertyFlags HazePropertyGroup::getEntityProperties(EncodeBitstreamParams
|
|||
requestedProperties += PROP_HAZE_ENABLE_GLARE;
|
||||
requestedProperties += PROP_HAZE_GLARE_ANGLE;
|
||||
|
||||
requestedProperties += PROP_HAZE_ALTITUDE_EFFECT;
|
||||
requestedProperties += PROP_HAZE_CEILING;
|
||||
requestedProperties += PROP_HAZE_BASE_REF;
|
||||
|
||||
|
|
|
@ -27,6 +27,19 @@ class OctreePacketData;
|
|||
class EntityTreeElementExtraEncodeData;
|
||||
class ReadBitstreamToTreeParams;
|
||||
|
||||
static const float INITIAL_HAZE_RANGE{ 1000.0f };
|
||||
static const xColor initialHazeGlareColorXcolor{ 255, 229, 179 };
|
||||
static const xColor initialHazeColorXcolor{ 128, 154, 179 };
|
||||
static const float INITIAL_HAZE_GLARE_ANGLE{ 20.0f };
|
||||
|
||||
static const float INITIAL_HAZE_BASE_REFERENCE{ 0.0f };
|
||||
static const float INITIAL_HAZE_HEIGHT{ 200.0f };
|
||||
|
||||
static const float INITIAL_HAZE_BACKGROUND_BLEND{ 0.0f };
|
||||
|
||||
static const float INITIAL_KEY_LIGHT_RANGE{ 1000.0f };
|
||||
static const float INITIAL_KEY_LIGHT_ALTITUDE{ 200.0f };
|
||||
|
||||
class HazePropertyGroup : public PropertyGroup {
|
||||
public:
|
||||
// EntityItemProperty related helpers
|
||||
|
@ -74,38 +87,25 @@ public:
|
|||
EntityPropertyFlags& propertyFlags, bool overwriteLocalData,
|
||||
bool& somethingChanged) override;
|
||||
|
||||
static const float DEFAULT_HAZE_RANGE;
|
||||
static const xColor DEFAULT_HAZE_COLOR;
|
||||
static const xColor DEFAULT_HAZE_GLARE_COLOR;
|
||||
static const float DEFAULT_HAZE_GLARE_ANGLE;
|
||||
|
||||
static const float DEFAULT_HAZE_CEILING;
|
||||
static const float DEFAULT_HAZE_BASE_REF;
|
||||
|
||||
static const float DEFAULT_HAZE_BACKGROUND_BLEND;
|
||||
|
||||
static const float DEFAULT_HAZE_KEYLIGHT_RANGE;
|
||||
static const float DEFAULT_HAZE_KEYLIGHT_ALTITUDE;
|
||||
|
||||
// Range only parameters
|
||||
DEFINE_PROPERTY(PROP_HAZE_RANGE, HazeRange, hazeRange, float, DEFAULT_HAZE_RANGE);
|
||||
DEFINE_PROPERTY_REF(PROP_HAZE_COLOR, HazeColor, hazeColor, xColor, DEFAULT_HAZE_COLOR);
|
||||
DEFINE_PROPERTY_REF(PROP_HAZE_GLARE_COLOR, HazeGlareColor, hazeGlareColor, xColor, DEFAULT_HAZE_GLARE_COLOR);
|
||||
DEFINE_PROPERTY(PROP_HAZE_RANGE, HazeRange, hazeRange, float, INITIAL_HAZE_RANGE);
|
||||
DEFINE_PROPERTY_REF(PROP_HAZE_COLOR, HazeColor, hazeColor, xColor, initialHazeColorXcolor);
|
||||
DEFINE_PROPERTY_REF(PROP_HAZE_GLARE_COLOR, HazeGlareColor, hazeGlareColor, xColor, initialHazeGlareColorXcolor);
|
||||
DEFINE_PROPERTY(PROP_HAZE_ENABLE_GLARE, HazeEnableGlare, hazeEnableGlare, bool, false);
|
||||
DEFINE_PROPERTY_REF(PROP_HAZE_GLARE_ANGLE, HazeGlareAngle, hazeGlareAngle, float, DEFAULT_HAZE_GLARE_ANGLE);
|
||||
DEFINE_PROPERTY_REF(PROP_HAZE_GLARE_ANGLE, HazeGlareAngle, hazeGlareAngle, float, INITIAL_HAZE_GLARE_ANGLE);
|
||||
|
||||
// Altitude parameters
|
||||
DEFINE_PROPERTY(PROP_HAZE_ALTITUDE_EFFECT, HazeAltitudeEffect, hazeAltitudeEffect, bool, false);
|
||||
DEFINE_PROPERTY_REF(PROP_HAZE_CEILING, HazeCeiling, hazeCeiling, float, DEFAULT_HAZE_CEILING);
|
||||
DEFINE_PROPERTY_REF(PROP_HAZE_BASE_REF, HazeBaseRef, hazeBaseRef, float, DEFAULT_HAZE_BASE_REF);
|
||||
DEFINE_PROPERTY_REF(PROP_HAZE_CEILING, HazeCeiling, hazeCeiling, float, INITIAL_HAZE_BASE_REFERENCE + INITIAL_HAZE_HEIGHT);
|
||||
DEFINE_PROPERTY_REF(PROP_HAZE_BASE_REF, HazeBaseRef, hazeBaseRef, float, INITIAL_HAZE_BASE_REFERENCE);
|
||||
|
||||
// Background (skybox) blend value
|
||||
DEFINE_PROPERTY_REF(PROP_HAZE_BACKGROUND_BLEND, HazeBackgroundBlend, hazeBackgroundBlend, float, DEFAULT_HAZE_BACKGROUND_BLEND);
|
||||
DEFINE_PROPERTY_REF(PROP_HAZE_BACKGROUND_BLEND, HazeBackgroundBlend, hazeBackgroundBlend, float, INITIAL_HAZE_BACKGROUND_BLEND);
|
||||
|
||||
// hazeDirectional light attenuation
|
||||
DEFINE_PROPERTY(PROP_HAZE_ATTENUATE_KEYLIGHT, HazeAttenuateKeyLight, hazeAttenuateKeyLight, bool, false);
|
||||
DEFINE_PROPERTY_REF(PROP_HAZE_KEYLIGHT_RANGE, HazeKeyLightRange, hazeKeyLightRange, float, DEFAULT_HAZE_KEYLIGHT_RANGE);
|
||||
DEFINE_PROPERTY_REF(PROP_HAZE_KEYLIGHT_ALTITUDE, HazeKeyLightAltitude, hazeKeyLightAltitude, float, DEFAULT_HAZE_KEYLIGHT_ALTITUDE);
|
||||
DEFINE_PROPERTY_REF(PROP_HAZE_KEYLIGHT_RANGE, HazeKeyLightRange, hazeKeyLightRange, float, INITIAL_KEY_LIGHT_RANGE);
|
||||
DEFINE_PROPERTY_REF(PROP_HAZE_KEYLIGHT_ALTITUDE, HazeKeyLightAltitude, hazeKeyLightAltitude, float, INITIAL_KEY_LIGHT_ALTITUDE);
|
||||
};
|
||||
|
||||
#endif // hifi_HazePropertyGroup_h
|
||||
|
|
|
@ -51,12 +51,12 @@ namespace entity {
|
|||
}
|
||||
}
|
||||
|
||||
// shapeCalculator is a hook for external code that knows how to configure a ShapeInfo
|
||||
// hullShapeCalculator is a hook for external code that knows how to configure a ShapeInfo
|
||||
// for given entity::Shape and dimensions
|
||||
ShapeEntityItem::ShapeInfoCalculator shapeCalculator = nullptr;
|
||||
ShapeEntityItem::ShapeInfoCalculator hullShapeCalculator = nullptr;
|
||||
|
||||
void ShapeEntityItem::setShapeInfoCalulator(ShapeEntityItem::ShapeInfoCalculator callback) {
|
||||
shapeCalculator = callback;
|
||||
hullShapeCalculator = callback;
|
||||
}
|
||||
|
||||
ShapeEntityItem::Pointer ShapeEntityItem::baseFactory(const EntityItemID& entityID, const EntityItemProperties& properties) {
|
||||
|
@ -104,6 +104,14 @@ void ShapeEntityItem::setShape(const entity::Shape& shape) {
|
|||
case entity::Shape::Sphere:
|
||||
_type = EntityTypes::Sphere;
|
||||
break;
|
||||
case entity::Shape::Circle:
|
||||
// Circle is implicitly flat so we enforce flat dimensions
|
||||
setDimensions(getDimensions());
|
||||
break;
|
||||
case entity::Shape::Quad:
|
||||
// Quad is implicitly flat so we enforce flat dimensions
|
||||
setDimensions(getDimensions());
|
||||
break;
|
||||
default:
|
||||
_type = EntityTypes::Shape;
|
||||
break;
|
||||
|
@ -196,6 +204,18 @@ void ShapeEntityItem::setColor(const QColor& value) {
|
|||
setAlpha(value.alpha());
|
||||
}
|
||||
|
||||
void ShapeEntityItem::setDimensions(const glm::vec3& value) {
|
||||
const float MAX_FLAT_DIMENSION = 0.0001f;
|
||||
if ((_shape == entity::Shape::Circle || _shape == entity::Shape::Quad) && value.y > MAX_FLAT_DIMENSION) {
|
||||
// enforce flatness in Y
|
||||
glm::vec3 newDimensions = value;
|
||||
newDimensions.y = MAX_FLAT_DIMENSION;
|
||||
EntityItem::setDimensions(newDimensions);
|
||||
} else {
|
||||
EntityItem::setDimensions(value);
|
||||
}
|
||||
}
|
||||
|
||||
bool ShapeEntityItem::supportsDetailedRayIntersection() const {
|
||||
return _shape == entity::Sphere;
|
||||
}
|
||||
|
@ -249,19 +269,13 @@ void ShapeEntityItem::computeShapeInfo(ShapeInfo& info) {
|
|||
const glm::vec3 entityDimensions = getDimensions();
|
||||
|
||||
switch (_shape){
|
||||
case entity::Shape::Quad: {
|
||||
// Not in GeometryCache::buildShapes, unsupported.
|
||||
_collisionShapeType = SHAPE_TYPE_ELLIPSOID;
|
||||
//TODO WL21389: Add a SHAPE_TYPE_QUAD ShapeType and treat
|
||||
// as a special box (later if desired support)
|
||||
}
|
||||
break;
|
||||
case entity::Shape::Quad:
|
||||
// Quads collide like flat Cubes
|
||||
case entity::Shape::Cube: {
|
||||
_collisionShapeType = SHAPE_TYPE_BOX;
|
||||
}
|
||||
break;
|
||||
case entity::Shape::Sphere: {
|
||||
|
||||
float diameter = entityDimensions.x;
|
||||
const float MIN_DIAMETER = 0.001f;
|
||||
const float MIN_RELATIVE_SPHERICAL_ERROR = 0.001f;
|
||||
|
@ -275,25 +289,28 @@ void ShapeEntityItem::computeShapeInfo(ShapeInfo& info) {
|
|||
}
|
||||
}
|
||||
break;
|
||||
case entity::Shape::Circle: {
|
||||
_collisionShapeType = SHAPE_TYPE_CIRCLE;
|
||||
}
|
||||
break;
|
||||
case entity::Shape::Circle:
|
||||
// Circles collide like flat Cylinders
|
||||
case entity::Shape::Cylinder: {
|
||||
_collisionShapeType = SHAPE_TYPE_CYLINDER_Y;
|
||||
// TODO WL21389: determine if rotation is axis-aligned
|
||||
//const Transform::Quat & rot = _transform.getRotation();
|
||||
|
||||
// TODO WL21389: some way to tell apart SHAPE_TYPE_CYLINDER_Y, _X, _Z based on rotation and
|
||||
// hull ( or dimensions, need circular cross section)
|
||||
// Should allow for minor variance along axes?
|
||||
|
||||
float diameter = entityDimensions.x;
|
||||
const float MIN_DIAMETER = 0.001f;
|
||||
const float MIN_RELATIVE_SPHERICAL_ERROR = 0.001f;
|
||||
if (diameter > MIN_DIAMETER
|
||||
&& fabsf(diameter - entityDimensions.z) / diameter < MIN_RELATIVE_SPHERICAL_ERROR) {
|
||||
_collisionShapeType = SHAPE_TYPE_SPHERE;
|
||||
} else if (hullShapeCalculator) {
|
||||
hullShapeCalculator(this, info);
|
||||
_collisionShapeType = SHAPE_TYPE_SIMPLE_HULL;
|
||||
} else {
|
||||
// woops, someone forgot to hook up the hullShapeCalculator()!
|
||||
// final fallback is ellipsoid
|
||||
_collisionShapeType = SHAPE_TYPE_ELLIPSOID;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case entity::Shape::Cone: {
|
||||
if (shapeCalculator) {
|
||||
shapeCalculator(this, info);
|
||||
// shapeCalculator only supports convex shapes (e.g. SHAPE_TYPE_HULL)
|
||||
if (hullShapeCalculator) {
|
||||
hullShapeCalculator(this, info);
|
||||
_collisionShapeType = SHAPE_TYPE_SIMPLE_HULL;
|
||||
} else {
|
||||
_collisionShapeType = SHAPE_TYPE_ELLIPSOID;
|
||||
|
@ -304,9 +321,8 @@ void ShapeEntityItem::computeShapeInfo(ShapeInfo& info) {
|
|||
case entity::Shape::Triangle:
|
||||
case entity::Shape::Hexagon:
|
||||
case entity::Shape::Octagon: {
|
||||
if (shapeCalculator) {
|
||||
shapeCalculator(this, info);
|
||||
// shapeCalculator only supports convex shapes (e.g. SHAPE_TYPE_HULL)
|
||||
if (hullShapeCalculator) {
|
||||
hullShapeCalculator(this, info);
|
||||
_collisionShapeType = SHAPE_TYPE_SIMPLE_HULL;
|
||||
} else {
|
||||
_collisionShapeType = SHAPE_TYPE_ELLIPSOID;
|
||||
|
@ -318,9 +334,8 @@ void ShapeEntityItem::computeShapeInfo(ShapeInfo& info) {
|
|||
case entity::Shape::Octahedron:
|
||||
case entity::Shape::Dodecahedron:
|
||||
case entity::Shape::Icosahedron: {
|
||||
if ( shapeCalculator ) {
|
||||
shapeCalculator(this, info);
|
||||
// shapeCalculator only supports convex shapes (e.g. SHAPE_TYPE_HULL)
|
||||
if ( hullShapeCalculator ) {
|
||||
hullShapeCalculator(this, info);
|
||||
_collisionShapeType = SHAPE_TYPE_SIMPLE_HULL;
|
||||
} else {
|
||||
_collisionShapeType = SHAPE_TYPE_ELLIPSOID;
|
||||
|
@ -330,7 +345,7 @@ void ShapeEntityItem::computeShapeInfo(ShapeInfo& info) {
|
|||
case entity::Shape::Torus: {
|
||||
// Not in GeometryCache::buildShapes, unsupported.
|
||||
_collisionShapeType = SHAPE_TYPE_ELLIPSOID;
|
||||
//TODO WL21389: SHAPE_TYPE_SIMPLE_HULL and pointCollection (later if desired support)
|
||||
//TODO handle this shape more correctly
|
||||
}
|
||||
break;
|
||||
default: {
|
||||
|
|
|
@ -80,6 +80,8 @@ public:
|
|||
const rgbColor& getColor() const { return _color; }
|
||||
void setColor(const rgbColor& value);
|
||||
|
||||
void setDimensions(const glm::vec3& value) override;
|
||||
|
||||
xColor getXColor() const;
|
||||
void setColor(const xColor& value);
|
||||
|
||||
|
|
|
@ -330,103 +330,3 @@ void ZoneEntityItem::setHazeMode(const uint32_t value) {
|
|||
uint32_t ZoneEntityItem::getHazeMode() const {
|
||||
return _hazeMode;
|
||||
}
|
||||
|
||||
void ZoneEntityItem::setHazeRange(const float hazeRange) {
|
||||
_hazeRange = hazeRange;
|
||||
_hazePropertiesChanged = true;
|
||||
}
|
||||
|
||||
float ZoneEntityItem::getHazeRange() const {
|
||||
return _hazeRange;
|
||||
}
|
||||
|
||||
void ZoneEntityItem::setHazeColor(const xColor hazeColor) {
|
||||
_hazeColor = hazeColor;
|
||||
_hazePropertiesChanged = true;
|
||||
}
|
||||
|
||||
xColor ZoneEntityItem::getHazeColor() const {
|
||||
return _hazeColor;
|
||||
}
|
||||
|
||||
void ZoneEntityItem::setHazeGlareColor(const xColor hazeGlareColor) {
|
||||
_hazeGlareColor = hazeGlareColor;
|
||||
_hazePropertiesChanged = true;
|
||||
}
|
||||
|
||||
xColor ZoneEntityItem::getHazeGlareColor()const {
|
||||
return _hazeGlareColor;
|
||||
}
|
||||
|
||||
void ZoneEntityItem::setHazeEnableGlare(const bool hazeEnableGlare) {
|
||||
_hazeEnableGlare = hazeEnableGlare;
|
||||
_hazePropertiesChanged = true;
|
||||
}
|
||||
|
||||
bool ZoneEntityItem::getHazeEnableGlare()const {
|
||||
return _hazeEnableGlare;
|
||||
}
|
||||
|
||||
void ZoneEntityItem::setHazeGlareAngle(const float hazeGlareAngle) {
|
||||
_hazeGlareAngle = hazeGlareAngle;
|
||||
_hazePropertiesChanged = true;
|
||||
}
|
||||
|
||||
float ZoneEntityItem::getHazeGlareAngle() const {
|
||||
return _hazeGlareAngle;
|
||||
}
|
||||
|
||||
void ZoneEntityItem::setHazeCeiling(const float hazeCeiling) {
|
||||
_hazeCeiling = hazeCeiling;
|
||||
_hazePropertiesChanged = true;
|
||||
}
|
||||
|
||||
float ZoneEntityItem::getHazeCeiling() const {
|
||||
return _hazeCeiling;
|
||||
}
|
||||
|
||||
void ZoneEntityItem::setHazeBaseRef(const float hazeBaseRef) {
|
||||
_hazeBaseRef = hazeBaseRef;
|
||||
_hazePropertiesChanged = true;
|
||||
}
|
||||
|
||||
float ZoneEntityItem::getHazeBaseRef() const {
|
||||
return _hazeBaseRef;
|
||||
}
|
||||
|
||||
void ZoneEntityItem::setHazeBackgroundBlend(const float hazeBackgroundBlend) {
|
||||
_hazeBackgroundBlend = hazeBackgroundBlend;
|
||||
_hazePropertiesChanged = true;
|
||||
}
|
||||
|
||||
float ZoneEntityItem::getHazeBackgroundBlend() const {
|
||||
return _hazeBackgroundBlend;
|
||||
}
|
||||
|
||||
void ZoneEntityItem::setHazeAttenuateKeyLight(const bool hazeAttenuateKeyLight) {
|
||||
_hazeAttenuateKeyLight = hazeAttenuateKeyLight;
|
||||
_hazePropertiesChanged = true;
|
||||
}
|
||||
|
||||
bool ZoneEntityItem::getHazeAttenuateKeyLight() const {
|
||||
return _hazeAttenuateKeyLight;
|
||||
}
|
||||
|
||||
void ZoneEntityItem::setHazeKeyLightRange(const float hazeKeyLightRange) {
|
||||
_hazeKeyLightRange = hazeKeyLightRange;
|
||||
_hazePropertiesChanged = true;
|
||||
}
|
||||
|
||||
float ZoneEntityItem::getHazeKeyLightRange() const {
|
||||
return _hazeKeyLightRange;
|
||||
}
|
||||
|
||||
void ZoneEntityItem::setHazeKeyLightAltitude(const float hazeKeyLightAltitude) {
|
||||
_hazeKeyLightAltitude = hazeKeyLightAltitude;
|
||||
_hazePropertiesChanged = true;
|
||||
}
|
||||
|
||||
float ZoneEntityItem::getHazeKeyLightAltitude() const {
|
||||
return _hazeKeyLightAltitude;
|
||||
}
|
||||
|
||||
|
|
|
@ -73,32 +73,6 @@ public:
|
|||
void setHazeMode(const uint32_t value);
|
||||
uint32_t getHazeMode() const;
|
||||
|
||||
void setHazeRange(const float hazeRange);
|
||||
float getHazeRange() const;
|
||||
void setHazeColor(const xColor hazeColor);
|
||||
xColor getHazeColor() const;
|
||||
void setHazeGlareColor(const xColor hazeGlareColor);
|
||||
xColor getHazeGlareColor() const;
|
||||
void setHazeEnableGlare(const bool hazeEnableGlare);
|
||||
bool getHazeEnableGlare() const;
|
||||
void setHazeGlareAngle(const float hazeGlareAngle);
|
||||
float getHazeGlareAngle() const;
|
||||
|
||||
void setHazeCeiling(const float hazeCeiling);
|
||||
float getHazeCeiling() const;
|
||||
void setHazeBaseRef(const float hazeBaseRef);
|
||||
float getHazeBaseRef() const;
|
||||
|
||||
void setHazeBackgroundBlend(const float hazeBackgroundBlend);
|
||||
float getHazeBackgroundBlend() const;
|
||||
|
||||
void setHazeAttenuateKeyLight(const bool hazeAttenuateKeyLight);
|
||||
bool getHazeAttenuateKeyLight() const;
|
||||
void setHazeKeyLightRange(const float hazeKeyLightRange);
|
||||
float getHazeKeyLightRange() const;
|
||||
void setHazeKeyLightAltitude(const float hazeKeyLightAltitude);
|
||||
float getHazeKeyLightAltitude() const;
|
||||
|
||||
SkyboxPropertyGroup getSkyboxProperties() const { return resultWithReadLock<SkyboxPropertyGroup>([&] { return _skyboxProperties; }); }
|
||||
|
||||
const HazePropertyGroup& getHazeProperties() const { return _hazeProperties; }
|
||||
|
@ -150,21 +124,6 @@ protected:
|
|||
|
||||
uint32_t _hazeMode{ DEFAULT_HAZE_MODE };
|
||||
|
||||
float _hazeRange{ HazePropertyGroup::DEFAULT_HAZE_RANGE };
|
||||
xColor _hazeColor{ HazePropertyGroup::DEFAULT_HAZE_COLOR };
|
||||
xColor _hazeGlareColor{ HazePropertyGroup::DEFAULT_HAZE_GLARE_COLOR };
|
||||
bool _hazeEnableGlare{ false };
|
||||
float _hazeGlareAngle{ HazePropertyGroup::DEFAULT_HAZE_GLARE_ANGLE };
|
||||
|
||||
float _hazeCeiling{ HazePropertyGroup::DEFAULT_HAZE_CEILING };
|
||||
float _hazeBaseRef{ HazePropertyGroup::DEFAULT_HAZE_BASE_REF };
|
||||
|
||||
float _hazeBackgroundBlend{ HazePropertyGroup::DEFAULT_HAZE_BACKGROUND_BLEND };
|
||||
|
||||
bool _hazeAttenuateKeyLight{ false };
|
||||
float _hazeKeyLightRange{ HazePropertyGroup::DEFAULT_HAZE_KEYLIGHT_RANGE };
|
||||
float _hazeKeyLightAltitude{ HazePropertyGroup::DEFAULT_HAZE_KEYLIGHT_ALTITUDE };
|
||||
|
||||
SkyboxPropertyGroup _skyboxProperties;
|
||||
HazePropertyGroup _hazeProperties;
|
||||
StagePropertyGroup _stageProperties;
|
||||
|
|
|
@ -15,7 +15,6 @@
|
|||
#include <QNetworkRequest>
|
||||
|
||||
#include <NetworkAccessManager.h>
|
||||
#include <NetworkingConstants.h>
|
||||
#include <SharedUtil.h>
|
||||
|
||||
#include "FSTReader.h"
|
||||
|
|
|
@ -9,12 +9,30 @@
|
|||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
#include <memory>
|
||||
#include <gpu/Resource.h>
|
||||
|
||||
#include "Haze.h"
|
||||
|
||||
using namespace model;
|
||||
|
||||
const float Haze::INITIAL_HAZE_RANGE{ 1000.0f };
|
||||
const float Haze::INITIAL_HAZE_HEIGHT{ 200.0f };
|
||||
|
||||
const float Haze::INITIAL_KEY_LIGHT_RANGE{ 1000.0f };
|
||||
const float Haze::INITIAL_KEY_LIGHT_ALTITUDE{ 200.0f };
|
||||
|
||||
const float Haze::INITIAL_HAZE_BACKGROUND_BLEND{ 0.0f };
|
||||
|
||||
const glm::vec3 Haze::INITIAL_HAZE_COLOR{ 0.5f, 0.6f, 0.7f }; // Bluish
|
||||
|
||||
const float Haze::INITIAL_HAZE_GLARE_ANGLE{ 20.0f };
|
||||
|
||||
const glm::vec3 Haze::INITIAL_HAZE_GLARE_COLOR{ 1.0f, 0.9f, 0.7f };
|
||||
|
||||
const float Haze::INITIAL_HAZE_BASE_REFERENCE{ 0.0f };
|
||||
|
||||
const float Haze::LOG_P_005{ logf(0.05f)};
|
||||
const float Haze::LOG_P_05{ logf(0.5f) };
|
||||
|
||||
Haze::Haze() {
|
||||
Parameters parameters;
|
||||
_hazeParametersBuffer = gpu::BufferView(std::make_shared<gpu::Buffer>(sizeof(Parameters), (const gpu::Byte*) ¶meters));
|
||||
|
@ -23,7 +41,7 @@ Haze::Haze() {
|
|||
enum HazeModes {
|
||||
HAZE_MODE_IS_ACTIVE = 1 << 0,
|
||||
HAZE_MODE_IS_ALTITUDE_BASED = 1 << 1,
|
||||
HAZE_MODE_IS_DIRECTIONAL_LIGHT_ATTENUATED = 1 << 2,
|
||||
HAZE_MODE_IS_KEYLIGHT_ATTENUATED = 1 << 2,
|
||||
HAZE_MODE_IS_MODULATE_COLOR = 1 << 3,
|
||||
HAZE_MODE_IS_ENABLE_LIGHT_BLEND = 1 << 4
|
||||
};
|
||||
|
@ -55,25 +73,25 @@ void Haze::setHazeEnableGlare(const bool isHazeEnableGlare) {
|
|||
}
|
||||
}
|
||||
|
||||
void Haze::setDirectionalLightBlend(const float hazeDirectionalLightBlend) {
|
||||
void Haze::setHazeGlareBlend(const float hazeGlareBlend) {
|
||||
auto& params = _hazeParametersBuffer.get<Parameters>();
|
||||
|
||||
if (params.directionalLightBlend != hazeDirectionalLightBlend) {
|
||||
_hazeParametersBuffer.edit<Parameters>().directionalLightBlend = hazeDirectionalLightBlend;
|
||||
if (params.hazeGlareBlend != hazeGlareBlend) {
|
||||
_hazeParametersBuffer.edit<Parameters>().hazeGlareBlend = hazeGlareBlend;
|
||||
}
|
||||
}
|
||||
|
||||
void Haze::setDirectionalLightColor(const glm::vec3 hazeDirectionalLightColor) {
|
||||
void Haze::setHazeGlareColor(const glm::vec3 hazeGlareColor) {
|
||||
auto& params = _hazeParametersBuffer.get<Parameters>();
|
||||
|
||||
if (params.directionalLightColor.r != hazeDirectionalLightColor.r) {
|
||||
_hazeParametersBuffer.edit<Parameters>().directionalLightColor.r = hazeDirectionalLightColor.r;
|
||||
if (params.hazeGlareColor.r != hazeGlareColor.r) {
|
||||
_hazeParametersBuffer.edit<Parameters>().hazeGlareColor.r = hazeGlareColor.r;
|
||||
}
|
||||
if (params.directionalLightColor.g != hazeDirectionalLightColor.g) {
|
||||
_hazeParametersBuffer.edit<Parameters>().directionalLightColor.g = hazeDirectionalLightColor.g;
|
||||
if (params.hazeGlareColor.g != hazeGlareColor.g) {
|
||||
_hazeParametersBuffer.edit<Parameters>().hazeGlareColor.g = hazeGlareColor.g;
|
||||
}
|
||||
if (params.directionalLightColor.b != hazeDirectionalLightColor.b) {
|
||||
_hazeParametersBuffer.edit<Parameters>().directionalLightColor.b = hazeDirectionalLightColor.b;
|
||||
if (params.hazeGlareColor.b != hazeGlareColor.b) {
|
||||
_hazeParametersBuffer.edit<Parameters>().hazeGlareColor.b = hazeGlareColor.b;
|
||||
}
|
||||
}
|
||||
void Haze::setHazeActive(const bool isHazeActive) {
|
||||
|
@ -99,10 +117,10 @@ void Haze::setAltitudeBased(const bool isAltitudeBased) {
|
|||
void Haze::setHazeAttenuateKeyLight(const bool isHazeAttenuateKeyLight) {
|
||||
auto& params = _hazeParametersBuffer.get<Parameters>();
|
||||
|
||||
if (((params.hazeMode & HAZE_MODE_IS_DIRECTIONAL_LIGHT_ATTENUATED) == HAZE_MODE_IS_DIRECTIONAL_LIGHT_ATTENUATED ) && !isHazeAttenuateKeyLight) {
|
||||
_hazeParametersBuffer.edit<Parameters>().hazeMode &= ~HAZE_MODE_IS_DIRECTIONAL_LIGHT_ATTENUATED;
|
||||
} else if (((params.hazeMode & HAZE_MODE_IS_DIRECTIONAL_LIGHT_ATTENUATED) != HAZE_MODE_IS_DIRECTIONAL_LIGHT_ATTENUATED) && isHazeAttenuateKeyLight) {
|
||||
_hazeParametersBuffer.edit<Parameters>().hazeMode |= HAZE_MODE_IS_DIRECTIONAL_LIGHT_ATTENUATED;
|
||||
if (((params.hazeMode & HAZE_MODE_IS_KEYLIGHT_ATTENUATED) == HAZE_MODE_IS_KEYLIGHT_ATTENUATED) && !isHazeAttenuateKeyLight) {
|
||||
_hazeParametersBuffer.edit<Parameters>().hazeMode &= ~HAZE_MODE_IS_KEYLIGHT_ATTENUATED;
|
||||
} else if (((params.hazeMode & HAZE_MODE_IS_KEYLIGHT_ATTENUATED) != HAZE_MODE_IS_KEYLIGHT_ATTENUATED) && isHazeAttenuateKeyLight) {
|
||||
_hazeParametersBuffer.edit<Parameters>().hazeMode |= HAZE_MODE_IS_KEYLIGHT_ATTENUATED;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -124,11 +142,11 @@ void Haze::setHazeRangeFactor(const float hazeRangeFactor) {
|
|||
}
|
||||
}
|
||||
|
||||
void Haze::setHazeAltitudeFactor(const float hazeAltitudeFactor) {
|
||||
void Haze::setHazeAltitudeFactor(const float hazeHeightFactor) {
|
||||
auto& params = _hazeParametersBuffer.get<Parameters>();
|
||||
|
||||
if (params.hazeAltitudeFactor != hazeAltitudeFactor) {
|
||||
_hazeParametersBuffer.edit<Parameters>().hazeAltitudeFactor = hazeAltitudeFactor;
|
||||
if (params.hazeHeightFactor != hazeHeightFactor) {
|
||||
_hazeParametersBuffer.edit<Parameters>().hazeHeightFactor = hazeHeightFactor;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -156,11 +174,11 @@ void Haze::setHazeBaseReference(const float hazeBaseReference) {
|
|||
}
|
||||
}
|
||||
|
||||
void Haze::setHazeBackgroundBlendValue(const float hazeBackgroundBlendValue) {
|
||||
void Haze::setHazeBackgroundBlend(const float hazeBackgroundBlend) {
|
||||
auto& params = _hazeParametersBuffer.get<Parameters>();
|
||||
|
||||
if (params.hazeBackgroundBlendValue != hazeBackgroundBlendValue) {
|
||||
_hazeParametersBuffer.edit<Parameters>().hazeBackgroundBlendValue = hazeBackgroundBlendValue;
|
||||
if (params.hazeBackgroundBlend != hazeBackgroundBlend) {
|
||||
_hazeParametersBuffer.edit<Parameters>().hazeBackgroundBlend = hazeBackgroundBlend;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -12,71 +12,70 @@
|
|||
#define hifi_model_Haze_h
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
#include <gpu/Resource.h>
|
||||
|
||||
#include "Transform.h"
|
||||
#include "NumericalConstants.h"
|
||||
|
||||
namespace model {
|
||||
const float LOG_P_005 = (float)log(0.05);
|
||||
const float LOG_P_05 = (float)log(0.5);
|
||||
|
||||
// Derivation (d is distance, b is haze coefficient, f is attenuation, solve for f = 0.05
|
||||
// f = exp(-d * b)
|
||||
// ln(f) = -d * b
|
||||
// b = -ln(f)/d
|
||||
inline glm::vec3 convertHazeRangeToHazeRangeFactor(const glm::vec3 hazeRange_m) {
|
||||
return glm::vec3(
|
||||
-LOG_P_005 / hazeRange_m.x,
|
||||
-LOG_P_005 / hazeRange_m.y,
|
||||
-LOG_P_005 / hazeRange_m.z);
|
||||
}
|
||||
|
||||
inline float convertHazeRangeToHazeRangeFactor(const float hazeRange_m) { return (-LOG_P_005 / hazeRange_m); }
|
||||
|
||||
inline float convertHazeAltitudeToHazeAltitudeFactor(const float hazeAltitude_m) {
|
||||
return -LOG_P_005 / hazeAltitude_m;
|
||||
}
|
||||
|
||||
// Derivation (s is the proportion of sun blend, a is the angle at which the blend is 50%, solve for m = 0.5
|
||||
// s = dot(lookAngle, sunAngle) = cos(a)
|
||||
// m = pow(s, p)
|
||||
// log(m) = p * log(s)
|
||||
// p = log(m) / log(s)
|
||||
inline float convertDirectionalLightAngleToPower(const float directionalLightAngle) {
|
||||
return LOG_P_05 / (float)log(cos(RADIANS_PER_DEGREE * directionalLightAngle));
|
||||
}
|
||||
|
||||
const glm::vec3 initialHazeColor{ 0.5f, 0.6f, 0.7f };
|
||||
const float initialDirectionalLightAngle_degs{ 30.0f };
|
||||
|
||||
const glm::vec3 initialDirectionalLightColor{ 1.0f, 0.9f, 0.7f };
|
||||
const float initialHazeBaseReference{ 0.0f };
|
||||
|
||||
// Haze range is defined here as the range the visibility is reduced by 95%
|
||||
// Haze altitude is defined here as the altitude (above 0) that the haze is reduced by 95%
|
||||
const float initialHazeRange_m{ 150.0f };
|
||||
const float initialHazeAltitude_m{ 150.0f };
|
||||
|
||||
const float initialHazeKeyLightRange_m{ 150.0f };
|
||||
const float initialHazeKeyLightAltitude_m{ 150.0f };
|
||||
|
||||
const float initialHazeBackgroundBlendValue{ 0.0f };
|
||||
|
||||
const glm::vec3 initialColorModulationFactor{
|
||||
convertHazeRangeToHazeRangeFactor(initialHazeRange_m),
|
||||
convertHazeRangeToHazeRangeFactor(initialHazeRange_m),
|
||||
convertHazeRangeToHazeRangeFactor(initialHazeRange_m)
|
||||
};
|
||||
|
||||
class Haze {
|
||||
public:
|
||||
using UniformBufferView = gpu::BufferView;
|
||||
// Initial values
|
||||
static const float INITIAL_HAZE_RANGE;
|
||||
static const float INITIAL_HAZE_HEIGHT;
|
||||
|
||||
static const float INITIAL_KEY_LIGHT_RANGE;
|
||||
static const float INITIAL_KEY_LIGHT_ALTITUDE;
|
||||
|
||||
static const float INITIAL_HAZE_BACKGROUND_BLEND;
|
||||
|
||||
static const glm::vec3 INITIAL_HAZE_COLOR;
|
||||
|
||||
static const float INITIAL_HAZE_GLARE_ANGLE;
|
||||
|
||||
static const glm::vec3 INITIAL_HAZE_GLARE_COLOR;
|
||||
|
||||
static const float INITIAL_HAZE_BASE_REFERENCE;
|
||||
|
||||
static const float LOG_P_005;
|
||||
static const float LOG_P_05;
|
||||
|
||||
// Derivation (d is distance, b is haze coefficient, f is attenuation, solve for f = 0.05
|
||||
// f = exp(-d * b)
|
||||
// ln(f) = -d * b
|
||||
// b = -ln(f)/d
|
||||
static inline glm::vec3 convertHazeRangeToHazeRangeFactor(const glm::vec3 hazeRange) {
|
||||
return glm::vec3(
|
||||
-LOG_P_005 / hazeRange.x,
|
||||
-LOG_P_005 / hazeRange.y,
|
||||
-LOG_P_005 / hazeRange.z);
|
||||
}
|
||||
|
||||
// limit range and altitude to no less than 1.0 metres
|
||||
static inline float convertHazeRangeToHazeRangeFactor(const float hazeRange) { return -LOG_P_005 / glm::max(hazeRange, 1.0f); }
|
||||
|
||||
static inline float convertHazeAltitudeToHazeAltitudeFactor(const float hazeHeight) { return -LOG_P_005 / glm::max(hazeHeight, 1.0f); }
|
||||
|
||||
// Derivation (s is the proportion of sun blend, a is the angle at which the blend is 50%, solve for m = 0.5
|
||||
// s = dot(lookAngle, sunAngle) = cos(a)
|
||||
// m = pow(s, p)
|
||||
// log(m) = p * log(s)
|
||||
// p = log(m) / log(s)
|
||||
// limit to 0.1 degrees
|
||||
static inline float convertGlareAngleToPower(const float hazeGlareAngle) {
|
||||
const float GLARE_ANGLE_LIMIT = 0.1f;
|
||||
return LOG_P_05 / logf(cosf(RADIANS_PER_DEGREE * glm::max(GLARE_ANGLE_LIMIT, hazeGlareAngle)));
|
||||
}
|
||||
|
||||
Haze();
|
||||
|
||||
void setHazeColor(const glm::vec3 hazeColor);
|
||||
void setDirectionalLightBlend(const float directionalLightBlend);
|
||||
void setHazeGlareBlend(const float hazeGlareBlend);
|
||||
|
||||
void setDirectionalLightColor(const glm::vec3 directionalLightColor);
|
||||
void setHazeGlareColor(const glm::vec3 hazeGlareColor);
|
||||
void setHazeBaseReference(const float hazeBaseReference);
|
||||
|
||||
void setHazeActive(const bool isHazeActive);
|
||||
|
@ -91,23 +90,24 @@ namespace model {
|
|||
void setHazeKeyLightRangeFactor(const float hazeKeyLightRange);
|
||||
void setHazeKeyLightAltitudeFactor(const float hazeKeyLightAltitude);
|
||||
|
||||
void setHazeBackgroundBlendValue(const float hazeBackgroundBlendValue);
|
||||
void setHazeBackgroundBlend(const float hazeBackgroundBlend);
|
||||
|
||||
void setZoneTransform(const glm::mat4& zoneTransform);
|
||||
|
||||
using UniformBufferView = gpu::BufferView;
|
||||
UniformBufferView getHazeParametersBuffer() const { return _hazeParametersBuffer; }
|
||||
|
||||
protected:
|
||||
class Parameters {
|
||||
public:
|
||||
// DO NOT CHANGE ORDER HERE WITHOUT UNDERSTANDING THE std140 LAYOUT
|
||||
glm::vec3 hazeColor{ initialHazeColor };
|
||||
float directionalLightBlend{ convertDirectionalLightAngleToPower(initialDirectionalLightAngle_degs) };
|
||||
glm::vec3 hazeColor{ INITIAL_HAZE_COLOR };
|
||||
float hazeGlareBlend{ convertGlareAngleToPower(INITIAL_HAZE_GLARE_ANGLE) };
|
||||
|
||||
glm::vec3 directionalLightColor{ initialDirectionalLightColor };
|
||||
float hazeBaseReference{ initialHazeBaseReference };
|
||||
glm::vec3 hazeGlareColor{ INITIAL_HAZE_GLARE_COLOR };
|
||||
float hazeBaseReference{ INITIAL_HAZE_BASE_REFERENCE };
|
||||
|
||||
glm::vec3 colorModulationFactor{ initialColorModulationFactor };
|
||||
glm::vec3 colorModulationFactor;
|
||||
int hazeMode{ 0 }; // bit 0 - set to activate haze attenuation of fragment color
|
||||
// bit 1 - set to add the effect of altitude to the haze attenuation
|
||||
// bit 2 - set to activate directional light attenuation mode
|
||||
|
@ -116,14 +116,14 @@ namespace model {
|
|||
glm::mat4 zoneTransform;
|
||||
|
||||
// Amount of background (skybox) to display, overriding the haze effect for the background
|
||||
float hazeBackgroundBlendValue{ initialHazeBackgroundBlendValue };
|
||||
float hazeBackgroundBlend{ INITIAL_HAZE_BACKGROUND_BLEND };
|
||||
|
||||
// The haze attenuation exponents used by both fragment and directional light attenuation
|
||||
float hazeRangeFactor{ convertHazeRangeToHazeRangeFactor(initialHazeRange_m) };
|
||||
float hazeAltitudeFactor{ convertHazeAltitudeToHazeAltitudeFactor(initialHazeAltitude_m) };
|
||||
float hazeRangeFactor{ convertHazeRangeToHazeRangeFactor(INITIAL_HAZE_RANGE) };
|
||||
float hazeHeightFactor{ convertHazeAltitudeToHazeAltitudeFactor(INITIAL_HAZE_HEIGHT) };
|
||||
|
||||
float hazeKeyLightRangeFactor{ convertHazeRangeToHazeRangeFactor(initialHazeKeyLightRange_m) };
|
||||
float hazeKeyLightAltitudeFactor{ convertHazeAltitudeToHazeAltitudeFactor(initialHazeKeyLightAltitude_m) };
|
||||
float hazeKeyLightRangeFactor{ convertHazeRangeToHazeRangeFactor(INITIAL_KEY_LIGHT_RANGE) };
|
||||
float hazeKeyLightAltitudeFactor{ convertHazeAltitudeToHazeAltitudeFactor(INITIAL_KEY_LIGHT_ALTITUDE) };
|
||||
|
||||
Parameters() {}
|
||||
};
|
||||
|
|
|
@ -256,10 +256,3 @@ void SunSkyStage::setSkybox(const SkyboxPointer& skybox) {
|
|||
_skybox = skybox;
|
||||
invalidate();
|
||||
}
|
||||
|
||||
void SunSkyStage::setHazeMode(uint32_t hazeMode) {
|
||||
if (hazeMode < COMPONENT_MODE_ITEM_COUNT) {
|
||||
_hazeMode = hazeMode;
|
||||
invalidate();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,7 +15,6 @@
|
|||
|
||||
#include "Light.h"
|
||||
#include "Skybox.h"
|
||||
#include "Haze.h"
|
||||
|
||||
namespace model {
|
||||
|
||||
|
@ -175,65 +174,8 @@ public:
|
|||
void setSkybox(const SkyboxPointer& skybox);
|
||||
const SkyboxPointer& getSkybox() const { valid(); return _skybox; }
|
||||
|
||||
// Haze
|
||||
enum HazeMode {
|
||||
HAZE_OFF,
|
||||
HAZE_ON,
|
||||
|
||||
NUM_HAZE_MODES
|
||||
};
|
||||
|
||||
void setHazeMode(uint32_t mode);
|
||||
uint32_t getHazeMode() const { return _hazeMode; }
|
||||
|
||||
void setHazeRange(float hazeRange) { _hazeRange = hazeRange; }
|
||||
float getHazeRange() const { return _hazeRange; }
|
||||
void setHazeColor(const xColor hazeColor) { _hazeColor = hazeColor; }
|
||||
xColor getHazeColor() { return _hazeColor; }
|
||||
void setHazeGlareColor(const xColor hazeGlareColor) { _hazeGlareColor = hazeGlareColor; }
|
||||
xColor getHazeGlareColor() const { return _hazeGlareColor; }
|
||||
void setHazeEnableGlare(bool hazeEnableGlare) { _hazeEnableGlare = hazeEnableGlare; }
|
||||
bool getHazeEnableGlare() const { return _hazeEnableGlare; }
|
||||
void setHazeGlareAngle(float hazeGlareAngle) { _hazeGlareAngle = hazeGlareAngle; }
|
||||
float getHazeGlareAngle() const { return _hazeGlareAngle; }
|
||||
|
||||
void setHazeAltitudeEffect(bool hazeAltitudeEffect) { _hazeAltitudeEffect = hazeAltitudeEffect; }
|
||||
bool getHazeAltitudeEffect() const { return _hazeAltitudeEffect; }
|
||||
void setHazeCeiling(float hazeCeiling) { _hazeCeiling = hazeCeiling; }
|
||||
float getHazeCeiling() const { return _hazeCeiling; }
|
||||
void setHazeBaseRef(float hazeBaseRef) { _hazeBaseRef = hazeBaseRef; }
|
||||
float getHazeBaseRef() const { return _hazeBaseRef; }
|
||||
|
||||
void setHazeBackgroundBlend(float hazeBackgroundBlend) { _hazeBackgroundBlend = hazeBackgroundBlend; }
|
||||
float getHazeBackgroundBlend() const { return _hazeBackgroundBlend; }
|
||||
|
||||
void setHazeAttenuateKeyLight(bool hazeAttenuateKeyLight) { _hazeAttenuateKeyLight = hazeAttenuateKeyLight; }
|
||||
bool getHazeAttenuateKeyLight() const { return _hazeAttenuateKeyLight; }
|
||||
void setHazeKeyLightRange(float hazeKeyLightRange) { _hazeKeyLightRange = hazeKeyLightRange; }
|
||||
float getHazeKeyLightRange() const { return _hazeKeyLightRange; }
|
||||
void setHazeKeyLightAltitude(float hazeKeyLightAltitude) { _hazeKeyLightAltitude = hazeKeyLightAltitude; }
|
||||
float getHazeKeyLightAltitude() const { return _hazeKeyLightAltitude; }
|
||||
|
||||
protected:
|
||||
BackgroundMode _backgroundMode = SKY_DEFAULT;
|
||||
|
||||
uint8_t _hazeMode = (uint8_t)HAZE_OFF;
|
||||
|
||||
float _hazeRange;
|
||||
xColor _hazeColor;
|
||||
xColor _hazeGlareColor;
|
||||
bool _hazeEnableGlare;
|
||||
float _hazeGlareAngle;
|
||||
|
||||
bool _hazeAltitudeEffect;
|
||||
float _hazeCeiling;
|
||||
float _hazeBaseRef;
|
||||
|
||||
float _hazeBackgroundBlend;
|
||||
|
||||
bool _hazeAttenuateKeyLight;
|
||||
float _hazeKeyLightRange;
|
||||
float _hazeKeyLightAltitude;
|
||||
|
||||
LightPointer _sunLight;
|
||||
mutable SkyboxPointer _skybox;
|
||||
|
|
|
@ -28,7 +28,6 @@
|
|||
|
||||
#include <SettingHandle.h>
|
||||
|
||||
#include "NetworkingConstants.h"
|
||||
#include "NetworkLogging.h"
|
||||
#include "NodeList.h"
|
||||
#include "udt/PacketHeaders.h"
|
||||
|
@ -240,7 +239,7 @@ void AccountManager::sendRequest(const QString& path,
|
|||
QUrl requestURL = _authURL;
|
||||
|
||||
if (requestURL.isEmpty()) { // Assignment client doesn't set _authURL.
|
||||
requestURL = NetworkingConstants::METAVERSE_SERVER_URL;
|
||||
requestURL = getMetaverseServerURL();
|
||||
}
|
||||
|
||||
if (path.startsWith("/")) {
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
#include <QtNetwork/QNetworkReply>
|
||||
#include <QUrlQuery>
|
||||
|
||||
#include "NetworkingConstants.h"
|
||||
#include "NetworkAccessManager.h"
|
||||
|
||||
#include "DataServerAccountInfo.h"
|
||||
|
@ -96,6 +97,8 @@ public:
|
|||
void setTemporaryDomain(const QUuid& domainID, const QString& key);
|
||||
const QString& getTemporaryDomainKey(const QUuid& domainID) { return _accountInfo.getTemporaryDomainKey(domainID); }
|
||||
|
||||
QUrl getMetaverseServerURL() { return NetworkingConstants::METAVERSE_SERVER_URL; }
|
||||
|
||||
public slots:
|
||||
void requestAccessToken(const QString& login, const QString& password);
|
||||
void requestAccessTokenWithSteam(QByteArray authSessionTicket);
|
||||
|
|
|
@ -24,7 +24,6 @@
|
|||
|
||||
#include "AddressManager.h"
|
||||
#include "NodeList.h"
|
||||
#include "NetworkingConstants.h"
|
||||
#include "NetworkLogging.h"
|
||||
#include "UserActivityLogger.h"
|
||||
#include "udt/PacketHeaders.h"
|
||||
|
@ -770,10 +769,6 @@ QString AddressManager::getDomainId() const {
|
|||
return DependencyManager::get<NodeList>()->getDomainHandler().getUUID().toString();
|
||||
}
|
||||
|
||||
const QUrl AddressManager::getMetaverseServerUrl() const {
|
||||
return NetworkingConstants::METAVERSE_SERVER_URL;
|
||||
}
|
||||
|
||||
void AddressManager::handleShareableNameAPIResponse(QNetworkReply& requestReply) {
|
||||
// make sure that this response is for the domain we're currently connected to
|
||||
auto domainID = DependencyManager::get<NodeList>()->getDomainHandler().getUUID();
|
||||
|
|
|
@ -41,7 +41,6 @@ class AddressManager : public QObject, public Dependency {
|
|||
Q_PROPERTY(QString pathname READ currentPath)
|
||||
Q_PROPERTY(QString placename READ getPlaceName)
|
||||
Q_PROPERTY(QString domainId READ getDomainId)
|
||||
Q_PROPERTY(QUrl metaverseServerUrl READ getMetaverseServerUrl NOTIFY metaverseServerUrlChanged)
|
||||
public:
|
||||
Q_INVOKABLE QString protocolVersion();
|
||||
using PositionGetter = std::function<glm::vec3()>;
|
||||
|
@ -71,7 +70,6 @@ public:
|
|||
const QUuid& getRootPlaceID() const { return _rootPlaceID; }
|
||||
const QString& getPlaceName() const { return _shareablePlaceName.isEmpty() ? _placeName : _shareablePlaceName; }
|
||||
QString getDomainId() const;
|
||||
const QUrl getMetaverseServerUrl() const;
|
||||
|
||||
const QString& getHost() const { return _host; }
|
||||
|
||||
|
@ -123,8 +121,6 @@ signals:
|
|||
void goBackPossible(bool isPossible);
|
||||
void goForwardPossible(bool isPossible);
|
||||
|
||||
void metaverseServerUrlChanged();
|
||||
|
||||
protected:
|
||||
AddressManager();
|
||||
private slots:
|
||||
|
|
|
@ -15,7 +15,12 @@
|
|||
#include <QtCore/QUrl>
|
||||
|
||||
namespace NetworkingConstants {
|
||||
const QUrl METAVERSE_SERVER_URL = QUrl("https://metaverse.highfidelity.com");
|
||||
// If you want to use STAGING instead of STABLE,
|
||||
// don't forget to ALSO change the Domain Server Metaverse Server URL, which is at the top of:
|
||||
// <hifi repo>\domain-server\resources\web\settings\js\settings.js
|
||||
const QUrl METAVERSE_SERVER_URL_STABLE("https://metaverse.highfidelity.com");
|
||||
const QUrl METAVERSE_SERVER_URL_STAGING("https://staging.highfidelity.com");
|
||||
const QUrl METAVERSE_SERVER_URL = METAVERSE_SERVER_URL_STABLE;
|
||||
}
|
||||
|
||||
#endif // hifi_NetworkingConstants_h
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -30,7 +30,7 @@ PacketVersion versionForPacketType(PacketType packetType) {
|
|||
case PacketType::EntityEdit:
|
||||
case PacketType::EntityData:
|
||||
case PacketType::EntityPhysics:
|
||||
return static_cast<PacketVersion>(EntityVersion::StrokeColorProperty);
|
||||
return static_cast<PacketVersion>(EntityVersion::HazeEffect);
|
||||
|
||||
case PacketType::EntityQuery:
|
||||
return static_cast<PacketVersion>(EntityQueryPacketVersion::JSONFilterWithFamilyTree);
|
||||
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -195,7 +195,9 @@ uint qHash(const PacketType& key, uint seed);
|
|||
QDebug operator<<(QDebug debug, const PacketType& type);
|
||||
|
||||
enum class EntityVersion : PacketVersion {
|
||||
StrokeColorProperty = 77
|
||||
StrokeColorProperty = 77,
|
||||
HasDynamicOwnershipTests,
|
||||
HazeEffect
|
||||
};
|
||||
|
||||
enum class EntityScriptCallMethodVersion : PacketVersion {
|
||||
|
@ -277,4 +279,8 @@ enum class MessageDataVersion : PacketVersion {
|
|||
TextOrBinaryData = 18
|
||||
};
|
||||
|
||||
enum class IcePingVersion : PacketVersion {
|
||||
SendICEPeerID = 18
|
||||
};
|
||||
|
||||
#endif // hifi_PacketHeaders_h
|
||||
|
|
|
@ -212,6 +212,7 @@ public:
|
|||
virtual bool handlesEditPacketType(PacketType packetType) const { return false; }
|
||||
virtual int processEditPacketData(ReceivedMessage& message, const unsigned char* editData, int maxLength,
|
||||
const SharedNodePointer& sourceNode) { return 0; }
|
||||
virtual void processChallengeOwnershipPacket(ReceivedMessage& message, const SharedNodePointer& sourceNode) { return; }
|
||||
|
||||
virtual bool recurseChildrenWithData() const { return true; }
|
||||
virtual bool rootElementHasData() const { return false; }
|
||||
|
|
|
@ -134,7 +134,7 @@ vec3 evalSkyboxGlobalColor(mat4 invViewMat, float shadowAttenuation, float obscu
|
|||
color += directionalSpecular;
|
||||
|
||||
// Attenuate the light if haze effect selected
|
||||
if ((hazeParams.hazeMode & HAZE_MODE_IS_DIRECTIONAL_LIGHT_ATTENUATED) == HAZE_MODE_IS_DIRECTIONAL_LIGHT_ATTENUATED) {
|
||||
if ((hazeParams.hazeMode & HAZE_MODE_IS_KEYLIGHT_ATTENUATED) == HAZE_MODE_IS_KEYLIGHT_ATTENUATED) {
|
||||
// Directional light attenuation is simulated by assuming the light source is at a fixed height above the
|
||||
// fragment. This height is where the haze density is reduced by 95% from the haze at the fragment's height
|
||||
//
|
||||
|
@ -147,8 +147,8 @@ vec3 evalSkyboxGlobalColor(mat4 invViewMat, float shadowAttenuation, float obscu
|
|||
|
||||
// Height at which haze density is reduced by 95% (default set to 2000.0 for safety ,this should never happen)
|
||||
float height_95p = 2000.0;
|
||||
if (hazeParams.hazeAltitudeFactorKeyLight > 0.0f) {
|
||||
height_95p = -log(0.05) / hazeParams.hazeAltitudeFactorKeyLight;
|
||||
if (hazeParams.hazeKeyLightAltitudeFactor > 0.0f) {
|
||||
height_95p = -log(0.05) / hazeParams.hazeKeyLightAltitudeFactor;
|
||||
}
|
||||
|
||||
// Note that the sine will always be positive
|
||||
|
@ -168,8 +168,8 @@ vec3 evalSkyboxGlobalColor(mat4 invViewMat, float shadowAttenuation, float obscu
|
|||
// Integration is from the fragment towards the light source
|
||||
// Note that the haze base reference affects only the haze density as function of altitude
|
||||
float hazeDensityDistribution =
|
||||
hazeParams.hazeRangeFactorKeyLight *
|
||||
exp(-hazeParams.hazeAltitudeFactorKeyLight * (worldFragPos.y - hazeParams.hazeBaseReference));
|
||||
hazeParams.hazeKeyLightRangeFactor *
|
||||
exp(-hazeParams.hazeKeyLightAltitudeFactor * (worldFragPos.y - hazeParams.hazeBaseReference));
|
||||
|
||||
float hazeIntegral = hazeDensityDistribution * distance;
|
||||
|
||||
|
|
|
@ -21,32 +21,16 @@
|
|||
|
||||
#include "Haze_frag.h"
|
||||
|
||||
void HazeConfig::setHazeColorR(const float value) {
|
||||
hazeColorR = value;
|
||||
void HazeConfig::setHazeColor(const glm::vec3 value) {
|
||||
hazeColor = value;
|
||||
}
|
||||
|
||||
void HazeConfig::setHazeColorG(const float value) {
|
||||
hazeColorG = value;
|
||||
void HazeConfig::setHazeGlareAngle(const float value) {
|
||||
hazeGlareAngle = value;
|
||||
}
|
||||
|
||||
void HazeConfig::setHazeColorB(const float value) {
|
||||
hazeColorB = value;
|
||||
}
|
||||
|
||||
void HazeConfig::setDirectionalLightAngle_degs(const float value) {
|
||||
hazeDirectionalLightAngle_degs = value;
|
||||
}
|
||||
|
||||
void HazeConfig::setDirectionalLightColorR(const float value) {
|
||||
hazeDirectionalLightColorR = value;
|
||||
}
|
||||
|
||||
void HazeConfig::setDirectionalLightColorG(const float value) {
|
||||
hazeDirectionalLightColorG = value;
|
||||
}
|
||||
|
||||
void HazeConfig::setDirectionalLightColorB(const float value) {
|
||||
hazeDirectionalLightColorB = value;
|
||||
void HazeConfig::setHazeGlareColor(const glm::vec3 value) {
|
||||
hazeGlareColor = value;
|
||||
}
|
||||
|
||||
void HazeConfig::setHazeBaseReference(const float value) {
|
||||
|
@ -73,24 +57,24 @@ void HazeConfig::setHazeEnableGlare(const bool active) {
|
|||
isHazeEnableGlare = active;
|
||||
}
|
||||
|
||||
void HazeConfig::setHazeRange_m(const float value) {
|
||||
hazeRange_m = value;
|
||||
void HazeConfig::setHazeRange(const float value) {
|
||||
hazeRange = value;
|
||||
}
|
||||
|
||||
void HazeConfig::setHazeAltitude_m(const float value) {
|
||||
hazeAltitude_m = value;
|
||||
void HazeConfig::setHazeAltitude(const float value) {
|
||||
hazeHeight = value;
|
||||
}
|
||||
|
||||
void HazeConfig::setHazeKeyLightRange_m(const float value) {
|
||||
hazeKeyLightRange_m = value;
|
||||
void HazeConfig::setHazeKeyLightRange(const float value) {
|
||||
hazeKeyLightRange = value;
|
||||
}
|
||||
|
||||
void HazeConfig::setHazeKeyLightAltitude_m(const float value) {
|
||||
hazeKeyLightAltitude_m = value;
|
||||
void HazeConfig::setHazeKeyLightAltitude(const float value) {
|
||||
hazeKeyLightAltitude = value;
|
||||
}
|
||||
|
||||
void HazeConfig::setHazeBackgroundBlendValue(const float value) {
|
||||
hazeBackgroundBlendValue = value;
|
||||
void HazeConfig::setHazeBackgroundBlend(const float value) {
|
||||
hazeBackgroundBlend = value;
|
||||
}
|
||||
|
||||
MakeHaze::MakeHaze() {
|
||||
|
@ -98,10 +82,10 @@ MakeHaze::MakeHaze() {
|
|||
}
|
||||
|
||||
void MakeHaze::configure(const Config& config) {
|
||||
_haze->setHazeColor(glm::vec3(config.hazeColorR, config.hazeColorG, config.hazeColorB));
|
||||
_haze->setDirectionalLightBlend(model::convertDirectionalLightAngleToPower(config.hazeDirectionalLightAngle_degs));
|
||||
_haze->setHazeColor(config.hazeColor);
|
||||
_haze->setHazeGlareBlend(model::Haze::convertGlareAngleToPower(config.hazeGlareAngle));
|
||||
|
||||
_haze->setDirectionalLightColor(glm::vec3(config.hazeDirectionalLightColorR, config.hazeDirectionalLightColorG, config.hazeDirectionalLightColorB));
|
||||
_haze->setHazeGlareColor(config.hazeGlareColor);
|
||||
_haze->setHazeBaseReference(config.hazeBaseReference);
|
||||
|
||||
_haze->setHazeActive(config.isHazeActive);
|
||||
|
@ -110,13 +94,13 @@ void MakeHaze::configure(const Config& config) {
|
|||
_haze->setModulateColorActive(config.isModulateColorActive);
|
||||
_haze->setHazeEnableGlare(config.isHazeEnableGlare);
|
||||
|
||||
_haze->setHazeRangeFactor(model::convertHazeRangeToHazeRangeFactor(config.hazeRange_m));
|
||||
_haze->setHazeAltitudeFactor(model::convertHazeAltitudeToHazeAltitudeFactor(config.hazeAltitude_m));
|
||||
_haze->setHazeRangeFactor(model::Haze::convertHazeRangeToHazeRangeFactor(config.hazeRange));
|
||||
_haze->setHazeAltitudeFactor(model::Haze::convertHazeAltitudeToHazeAltitudeFactor(config.hazeHeight));
|
||||
|
||||
_haze->setHazeKeyLightRangeFactor(model::convertHazeRangeToHazeRangeFactor(config.hazeKeyLightRange_m));
|
||||
_haze->setHazeKeyLightAltitudeFactor(model::convertHazeAltitudeToHazeAltitudeFactor(config.hazeKeyLightAltitude_m));
|
||||
_haze->setHazeKeyLightRangeFactor(model::Haze::convertHazeRangeToHazeRangeFactor(config.hazeKeyLightRange));
|
||||
_haze->setHazeKeyLightAltitudeFactor(model::Haze::convertHazeAltitudeToHazeAltitudeFactor(config.hazeKeyLightAltitude));
|
||||
|
||||
_haze->setHazeBackgroundBlendValue(config.hazeBackgroundBlendValue);
|
||||
_haze->setHazeBackgroundBlend(config.hazeBackgroundBlend);
|
||||
}
|
||||
|
||||
void MakeHaze::run(const render::RenderContextPointer& renderContext, model::HazePointer& haze) {
|
||||
|
|
|
@ -19,24 +19,19 @@
|
|||
#include <gpu/Pipeline.h>
|
||||
#include <render/Forward.h>
|
||||
#include <render/DrawTask.h>
|
||||
#include <model/Haze.h>
|
||||
|
||||
#include "SurfaceGeometryPass.h"
|
||||
|
||||
#include "model/Haze.h"
|
||||
|
||||
using LinearDepthFramebufferPointer = std::shared_ptr<LinearDepthFramebuffer>;
|
||||
|
||||
class MakeHazeConfig : public render::Job::Config {
|
||||
Q_OBJECT
|
||||
|
||||
Q_PROPERTY(float hazeColorR MEMBER hazeColorR WRITE setHazeColorR NOTIFY dirty);
|
||||
Q_PROPERTY(float hazeColorG MEMBER hazeColorG WRITE setHazeColorG NOTIFY dirty);
|
||||
Q_PROPERTY(float hazeColorB MEMBER hazeColorB WRITE setHazeColorB NOTIFY dirty);
|
||||
Q_PROPERTY(float hazeDirectionalLightAngle_degs MEMBER hazeDirectionalLightAngle_degs WRITE setDirectionalLightAngle_degs NOTIFY dirty);
|
||||
Q_PROPERTY(glm::vec3 hazeColor MEMBER hazeColor WRITE setHazeColor NOTIFY dirty);
|
||||
Q_PROPERTY(float hazeGlareAngle MEMBER hazeGlareAngle WRITE setHazeGlareAngle NOTIFY dirty);
|
||||
|
||||
Q_PROPERTY(float hazeDirectionalLightColorR MEMBER hazeDirectionalLightColorR WRITE setDirectionalLightColorR NOTIFY dirty);
|
||||
Q_PROPERTY(float hazeDirectionalLightColorG MEMBER hazeDirectionalLightColorG WRITE setDirectionalLightColorG NOTIFY dirty);
|
||||
Q_PROPERTY(float hazeDirectionalLightColorB MEMBER hazeDirectionalLightColorB WRITE setDirectionalLightColorB NOTIFY dirty);
|
||||
Q_PROPERTY(glm::vec3 hazeGlareColor MEMBER hazeGlareColor WRITE setHazeGlareColor NOTIFY dirty);
|
||||
Q_PROPERTY(float hazeBaseReference MEMBER hazeBaseReference WRITE setHazeBaseReference NOTIFY dirty);
|
||||
|
||||
Q_PROPERTY(bool isHazeActive MEMBER isHazeActive WRITE setHazeActive NOTIFY dirty);
|
||||
|
@ -45,26 +40,22 @@ class MakeHazeConfig : public render::Job::Config {
|
|||
Q_PROPERTY(bool isModulateColorActive MEMBER isModulateColorActive WRITE setModulateColorActive NOTIFY dirty);
|
||||
Q_PROPERTY(bool isHazeEnableGlare MEMBER isHazeEnableGlare WRITE setHazeEnableGlare NOTIFY dirty);
|
||||
|
||||
Q_PROPERTY(float hazeRange_m MEMBER hazeRange_m WRITE setHazeRange_m NOTIFY dirty);
|
||||
Q_PROPERTY(float hazeAltitude_m MEMBER hazeAltitude_m WRITE setHazeAltitude_m NOTIFY dirty);
|
||||
Q_PROPERTY(float hazeRange MEMBER hazeRange WRITE setHazeRange NOTIFY dirty);
|
||||
Q_PROPERTY(float hazeHeight MEMBER hazeHeight WRITE setHazeAltitude NOTIFY dirty);
|
||||
|
||||
Q_PROPERTY(float hazeKeyLightRange_m MEMBER hazeKeyLightRange_m WRITE setHazeKeyLightRange_m NOTIFY dirty);
|
||||
Q_PROPERTY(float hazeKeyLightAltitude_m MEMBER hazeKeyLightAltitude_m WRITE setHazeKeyLightAltitude_m NOTIFY dirty);
|
||||
Q_PROPERTY(float hazeKeyLightRange MEMBER hazeKeyLightRange WRITE setHazeKeyLightRange NOTIFY dirty);
|
||||
Q_PROPERTY(float hazeKeyLightAltitude MEMBER hazeKeyLightAltitude WRITE setHazeKeyLightAltitude NOTIFY dirty);
|
||||
|
||||
Q_PROPERTY(float hazeBackgroundBlendValue MEMBER hazeBackgroundBlendValue WRITE setHazeBackgroundBlendValue NOTIFY dirty);
|
||||
Q_PROPERTY(float hazeBackgroundBlend MEMBER hazeBackgroundBlend WRITE setHazeBackgroundBlend NOTIFY dirty);
|
||||
|
||||
public:
|
||||
MakeHazeConfig() : render::Job::Config() {}
|
||||
|
||||
float hazeColorR{ model::initialHazeColor.r };
|
||||
float hazeColorG{ model::initialHazeColor.g };
|
||||
float hazeColorB{ model::initialHazeColor.b };
|
||||
float hazeDirectionalLightAngle_degs{ model::initialDirectionalLightAngle_degs };
|
||||
glm::vec3 hazeColor{ model::Haze::INITIAL_HAZE_COLOR };
|
||||
float hazeGlareAngle{ model::Haze::INITIAL_HAZE_GLARE_ANGLE };
|
||||
|
||||
float hazeDirectionalLightColorR{ model::initialDirectionalLightColor.r };
|
||||
float hazeDirectionalLightColorG{ model::initialDirectionalLightColor.g };
|
||||
float hazeDirectionalLightColorB{ model::initialDirectionalLightColor.b };
|
||||
float hazeBaseReference{ model::initialHazeBaseReference };
|
||||
glm::vec3 hazeGlareColor{ model::Haze::INITIAL_HAZE_GLARE_COLOR };
|
||||
float hazeBaseReference{ model::Haze::INITIAL_HAZE_BASE_REFERENCE };
|
||||
|
||||
bool isHazeActive{ false };
|
||||
bool isAltitudeBased{ false };
|
||||
|
@ -72,23 +63,19 @@ public:
|
|||
bool isModulateColorActive{ false };
|
||||
bool isHazeEnableGlare{ false };
|
||||
|
||||
float hazeRange_m{ model::initialHazeRange_m };
|
||||
float hazeAltitude_m{ model::initialHazeAltitude_m };
|
||||
float hazeRange{ model::Haze::INITIAL_HAZE_RANGE };
|
||||
float hazeHeight{ model::Haze::INITIAL_HAZE_HEIGHT };
|
||||
|
||||
float hazeKeyLightRange_m{ model::initialHazeKeyLightRange_m };
|
||||
float hazeKeyLightAltitude_m{ model::initialHazeKeyLightAltitude_m };
|
||||
float hazeKeyLightRange{ model::Haze::INITIAL_KEY_LIGHT_RANGE };
|
||||
float hazeKeyLightAltitude{ model::Haze::INITIAL_KEY_LIGHT_ALTITUDE };
|
||||
|
||||
float hazeBackgroundBlendValue{ model::initialHazeBackgroundBlendValue };
|
||||
float hazeBackgroundBlend{ model::Haze::INITIAL_HAZE_BACKGROUND_BLEND };
|
||||
|
||||
public slots:
|
||||
void setHazeColorR(const float value) { hazeColorR = value; emit dirty(); }
|
||||
void setHazeColorG(const float value) { hazeColorG = value; emit dirty(); }
|
||||
void setHazeColorB(const float value) { hazeColorB = value; emit dirty(); }
|
||||
void setDirectionalLightAngle_degs(const float value) { hazeDirectionalLightAngle_degs = value; emit dirty(); }
|
||||
void setHazeColor(const glm::vec3 value) { hazeColor = value; emit dirty(); }
|
||||
void setHazeGlareAngle(const float value) { hazeGlareAngle = value; emit dirty(); }
|
||||
|
||||
void setDirectionalLightColorR(const float value) { hazeDirectionalLightColorR = value; emit dirty(); }
|
||||
void setDirectionalLightColorG(const float value) { hazeDirectionalLightColorG = value; emit dirty(); }
|
||||
void setDirectionalLightColorB(const float value) { hazeDirectionalLightColorB = value; emit dirty(); }
|
||||
void setHazeGlareColor(const glm::vec3 value) { hazeGlareColor = value; emit dirty(); }
|
||||
void setHazeBaseReference(const float value) { hazeBaseReference = value; ; emit dirty(); }
|
||||
|
||||
void setHazeActive(const bool active) { isHazeActive = active; emit dirty(); }
|
||||
|
@ -97,13 +84,13 @@ public slots:
|
|||
void setModulateColorActive(const bool active) { isModulateColorActive = active; emit dirty(); }
|
||||
void setHazeEnableGlare(const bool active) { isHazeEnableGlare = active; emit dirty(); }
|
||||
|
||||
void setHazeRange_m(const float value) { hazeRange_m = value; emit dirty(); }
|
||||
void setHazeAltitude_m(const float value) { hazeAltitude_m = value; emit dirty(); }
|
||||
void setHazeRange(const float value) { hazeRange = value; emit dirty(); }
|
||||
void setHazeAltitude(const float value) { hazeHeight = value; emit dirty(); }
|
||||
|
||||
void setHazeKeyLightRange_m(const float value) { hazeKeyLightRange_m = value; emit dirty(); }
|
||||
void setHazeKeyLightAltitude_m(const float value) { hazeKeyLightAltitude_m = value; emit dirty(); }
|
||||
void setHazeKeyLightRange(const float value) { hazeKeyLightRange = value; emit dirty(); }
|
||||
void setHazeKeyLightAltitude(const float value) { hazeKeyLightAltitude = value; emit dirty(); }
|
||||
|
||||
void setHazeBackgroundBlendValue(const float value) { hazeBackgroundBlendValue = value; ; emit dirty(); }
|
||||
void setHazeBackgroundBlend(const float value) { hazeBackgroundBlend = value; ; emit dirty(); }
|
||||
|
||||
signals:
|
||||
void dirty();
|
||||
|
@ -128,15 +115,11 @@ public:
|
|||
HazeConfig() : render::Job::Config(true) {}
|
||||
|
||||
// attributes
|
||||
float hazeColorR{ model::initialHazeColor.r };
|
||||
float hazeColorG{ model::initialHazeColor.g };
|
||||
float hazeColorB{ model::initialHazeColor.b };
|
||||
float hazeDirectionalLightAngle_degs{ model::initialDirectionalLightAngle_degs };
|
||||
glm::vec3 hazeColor{ model::Haze::INITIAL_HAZE_COLOR };
|
||||
float hazeGlareAngle{ model::Haze::INITIAL_HAZE_GLARE_ANGLE };
|
||||
|
||||
float hazeDirectionalLightColorR{ model::initialDirectionalLightColor.r };
|
||||
float hazeDirectionalLightColorG{ model::initialDirectionalLightColor.g };
|
||||
float hazeDirectionalLightColorB{ model::initialDirectionalLightColor.b };
|
||||
float hazeBaseReference{ model::initialHazeBaseReference };
|
||||
glm::vec3 hazeGlareColor{ model::Haze::INITIAL_HAZE_GLARE_COLOR };
|
||||
float hazeBaseReference{ model::Haze::INITIAL_HAZE_BASE_REFERENCE };
|
||||
|
||||
bool isHazeActive{ false }; // Setting this to true will set haze to on
|
||||
bool isAltitudeBased{ false };
|
||||
|
@ -144,23 +127,19 @@ public:
|
|||
bool isModulateColorActive{ false };
|
||||
bool isHazeEnableGlare{ false };
|
||||
|
||||
float hazeRange_m{ model::initialHazeRange_m };
|
||||
float hazeAltitude_m{ model::initialHazeAltitude_m };
|
||||
float hazeRange{ model::Haze::INITIAL_HAZE_RANGE };
|
||||
float hazeHeight{ model::Haze::INITIAL_HAZE_HEIGHT };
|
||||
|
||||
float hazeKeyLightRange_m{ model::initialHazeKeyLightRange_m };
|
||||
float hazeKeyLightAltitude_m{ model::initialHazeKeyLightAltitude_m };
|
||||
float hazeKeyLightRange{ model::Haze::INITIAL_KEY_LIGHT_RANGE };
|
||||
float hazeKeyLightAltitude{ model::Haze::INITIAL_KEY_LIGHT_ALTITUDE };
|
||||
|
||||
float hazeBackgroundBlendValue{ model::initialHazeBackgroundBlendValue };
|
||||
float hazeBackgroundBlend{ model::Haze::INITIAL_HAZE_BACKGROUND_BLEND };
|
||||
|
||||
// methods
|
||||
void setHazeColorR(const float value);
|
||||
void setHazeColorG(const float value);
|
||||
void setHazeColorB(const float value);
|
||||
void setDirectionalLightAngle_degs(const float value);
|
||||
void setHazeColor(const glm::vec3 value);
|
||||
void setHazeGlareAngle(const float value);
|
||||
|
||||
void setDirectionalLightColorR(const float value);
|
||||
void setDirectionalLightColorG(const float value);
|
||||
void setDirectionalLightColorB(const float value);
|
||||
void setHazeGlareColor(const glm::vec3 value);
|
||||
void setHazeBaseReference(const float value);
|
||||
|
||||
void setHazeActive(const bool active);
|
||||
|
@ -169,13 +148,13 @@ public:
|
|||
void setModulateColorActive(const bool active);
|
||||
void setHazeEnableGlare(const bool active);
|
||||
|
||||
void setHazeRange_m(const float value);
|
||||
void setHazeAltitude_m(const float value);
|
||||
void setHazeRange(const float value);
|
||||
void setHazeAltitude(const float value);
|
||||
|
||||
void setHazeKeyLightRange_m(const float value);
|
||||
void setHazeKeyLightAltitude_m(const float value);
|
||||
void setHazeKeyLightRange(const float value);
|
||||
void setHazeKeyLightAltitude(const float value);
|
||||
|
||||
void setHazeBackgroundBlendValue(const float value);
|
||||
void setHazeBackgroundBlend(const float value);
|
||||
};
|
||||
|
||||
class DrawHaze {
|
||||
|
|
|
@ -482,8 +482,10 @@ void GeometryCache::buildShapes() {
|
|||
using namespace geometry;
|
||||
auto vertexBuffer = std::make_shared<gpu::Buffer>();
|
||||
auto indexBuffer = std::make_shared<gpu::Buffer>();
|
||||
// Cube
|
||||
// Cube
|
||||
setupFlatShape(_shapes[Cube], geometry::cube(), _shapeVertices, _shapeIndices);
|
||||
//Quad renders as flat Cube
|
||||
setupFlatShape(_shapes[Quad], geometry::cube(), _shapeVertices, _shapeIndices);
|
||||
// Tetrahedron
|
||||
setupFlatShape(_shapes[Tetrahedron], geometry::tetrahedron(), _shapeVertices, _shapeIndices);
|
||||
// Icosahedron
|
||||
|
@ -524,12 +526,10 @@ void GeometryCache::buildShapes() {
|
|||
extrudePolygon<64>(_shapes[Cylinder], _shapeVertices, _shapeIndices);
|
||||
//Cone,
|
||||
extrudePolygon<64>(_shapes[Cone], _shapeVertices, _shapeIndices, true);
|
||||
//Circle
|
||||
drawCircle(_shapes[Circle], _shapeVertices, _shapeIndices);
|
||||
// Circle renders as flat Cylinder
|
||||
extrudePolygon<64>(_shapes[Circle], _shapeVertices, _shapeIndices);
|
||||
// Not implemented yet:
|
||||
//Quad,
|
||||
//Torus,
|
||||
|
||||
//Torus,
|
||||
}
|
||||
|
||||
const GeometryCache::ShapeData * GeometryCache::getShapeData(const Shape shape) const {
|
||||
|
|
|
@ -60,15 +60,15 @@ void main(void) {
|
|||
Light light = getLight();
|
||||
vec3 lightDirection = getLightDirection(light);
|
||||
|
||||
float directionalLightComponent = max(0.0, dot(eyeFragDir, -lightDirection));
|
||||
float power = min(1.0, pow(directionalLightComponent, hazeParams.directionalLightBlend));
|
||||
float glareComponent = max(0.0, dot(eyeFragDir, -lightDirection));
|
||||
float power = min(1.0, pow(glareComponent, hazeParams.hazeGlareBlend));
|
||||
|
||||
vec4 directionalLightColor = vec4(hazeParams.directionalLightColor, 1.0);
|
||||
vec4 glareColor = vec4(hazeParams.hazeGlareColor, 1.0);
|
||||
|
||||
// Use the haze colour for the belnd-out colour, if blend is not enabled
|
||||
// Use the haze colour for the glare colour, if blend is not enabled
|
||||
vec4 blendedHazeColor;
|
||||
if ((hazeParams.hazeMode & HAZE_MODE_IS_ENABLE_LIGHT_BLEND) == HAZE_MODE_IS_ENABLE_LIGHT_BLEND) {
|
||||
blendedHazeColor = mix(hazeColor, directionalLightColor, power);
|
||||
blendedHazeColor = mix(hazeColor, glareColor, power);
|
||||
} else {
|
||||
blendedHazeColor = hazeColor;
|
||||
}
|
||||
|
@ -86,14 +86,14 @@ void main(void) {
|
|||
// Note that the haze base reference affects only the haze density as function of altitude
|
||||
vec3 hazeDensityDistribution =
|
||||
hazeParams.colorModulationFactor *
|
||||
exp(-hazeParams.hazeAltitudeFactor * (worldEyePos.y - hazeParams.hazeBaseReference));
|
||||
exp(-hazeParams.hazeHeightFactor * (worldEyePos.y - hazeParams.hazeBaseReference));
|
||||
|
||||
vec3 hazeIntegral = hazeDensityDistribution * distance;
|
||||
|
||||
const float slopeThreshold = 0.01;
|
||||
float deltaHeight = worldFragPos.y - worldEyePos.y;
|
||||
if (abs(deltaHeight) > slopeThreshold) {
|
||||
float t = hazeParams.hazeAltitudeFactor * deltaHeight;
|
||||
float t = hazeParams.hazeHeightFactor * deltaHeight;
|
||||
hazeIntegral *= (1.0 - exp (-t)) / t;
|
||||
}
|
||||
|
||||
|
@ -117,14 +117,14 @@ void main(void) {
|
|||
// Note that the haze base reference affects only the haze density as function of altitude
|
||||
float hazeDensityDistribution =
|
||||
hazeParams.hazeRangeFactor *
|
||||
exp(-hazeParams.hazeAltitudeFactor * (worldEyePos.y - hazeParams.hazeBaseReference));
|
||||
exp(-hazeParams.hazeHeightFactor * (worldEyePos.y - hazeParams.hazeBaseReference));
|
||||
|
||||
float hazeIntegral = hazeDensityDistribution * distance;
|
||||
|
||||
const float slopeThreshold = 0.01;
|
||||
float deltaHeight = worldFragPos.y - worldEyePos.y;
|
||||
if (abs(deltaHeight) > slopeThreshold) {
|
||||
float t = hazeParams.hazeAltitudeFactor * deltaHeight;
|
||||
float t = hazeParams.hazeHeightFactor * deltaHeight;
|
||||
// Protect from wild values
|
||||
if (abs(t) > 0.0000001) {
|
||||
hazeIntegral *= (1.0 - exp (-t)) / t;
|
||||
|
@ -140,7 +140,7 @@ void main(void) {
|
|||
// Mix with background at far range
|
||||
const float BLEND_DISTANCE = 27000.0;
|
||||
if (distance > BLEND_DISTANCE) {
|
||||
outFragColor = mix(potentialFragColor, fragColor, hazeParams.backgroundBlendValue);
|
||||
outFragColor = mix(potentialFragColor, fragColor, hazeParams.backgroundBlend);
|
||||
} else {
|
||||
outFragColor = potentialFragColor;
|
||||
}
|
||||
|
|
|
@ -12,28 +12,28 @@
|
|||
|
||||
const int HAZE_MODE_IS_ACTIVE = 1 << 0;
|
||||
const int HAZE_MODE_IS_ALTITUDE_BASED = 1 << 1;
|
||||
const int HAZE_MODE_IS_DIRECTIONAL_LIGHT_ATTENUATED = 1 << 2;
|
||||
const int HAZE_MODE_IS_KEYLIGHT_ATTENUATED = 1 << 2;
|
||||
const int HAZE_MODE_IS_MODULATE_COLOR = 1 << 3;
|
||||
const int HAZE_MODE_IS_ENABLE_LIGHT_BLEND = 1 << 4;
|
||||
|
||||
struct HazeParams {
|
||||
vec3 hazeColor;
|
||||
float directionalLightBlend;
|
||||
float hazeGlareBlend;
|
||||
|
||||
vec3 directionalLightColor;
|
||||
vec3 hazeGlareColor;
|
||||
float hazeBaseReference;
|
||||
|
||||
vec3 colorModulationFactor;
|
||||
int hazeMode;
|
||||
|
||||
mat4 zoneTransform;
|
||||
float backgroundBlendValue;
|
||||
float backgroundBlend;
|
||||
|
||||
float hazeRangeFactor;
|
||||
float hazeAltitudeFactor;
|
||||
float hazeHeightFactor;
|
||||
|
||||
float hazeRangeFactorKeyLight;
|
||||
float hazeAltitudeFactorKeyLight;
|
||||
float hazeKeyLightRangeFactor;
|
||||
float hazeKeyLightAltitudeFactor;
|
||||
};
|
||||
|
||||
layout(std140) uniform hazeBuffer {
|
||||
|
|
|
@ -20,10 +20,10 @@ FetchHazeStage::FetchHazeStage() {
|
|||
}
|
||||
|
||||
void FetchHazeStage::configure(const Config& config) {
|
||||
_haze->setHazeColor(glm::vec3(config.hazeColorR, config.hazeColorG, config.hazeColorB));
|
||||
_haze->setDirectionalLightBlend(model::convertDirectionalLightAngleToPower(config.hazeDirectionalLightAngle_degs));
|
||||
_haze->setHazeColor(config.hazeColor);
|
||||
_haze->setHazeGlareBlend(model::Haze::convertGlareAngleToPower(config.hazeGlareAngle));
|
||||
|
||||
_haze->setDirectionalLightColor(glm::vec3(config.hazeDirectionalLightColorR, config.hazeDirectionalLightColorG, config.hazeDirectionalLightColorB));
|
||||
_haze->setHazeGlareColor(config.hazeGlareColor);
|
||||
_haze->setHazeBaseReference(config.hazeBaseReference);
|
||||
|
||||
_haze->setHazeActive(config.isHazeActive);
|
||||
|
@ -32,13 +32,13 @@ void FetchHazeStage::configure(const Config& config) {
|
|||
_haze->setModulateColorActive(config.isModulateColorActive);
|
||||
_haze->setHazeEnableGlare(config.isHazeEnableGlare);
|
||||
|
||||
_haze->setHazeRangeFactor(model::convertHazeRangeToHazeRangeFactor(config.hazeRange_m));
|
||||
_haze->setHazeAltitudeFactor(model::convertHazeAltitudeToHazeAltitudeFactor(config.hazeAltitude_m));
|
||||
_haze->setHazeRangeFactor(model::Haze::convertHazeRangeToHazeRangeFactor(config.hazeRange));
|
||||
_haze->setHazeAltitudeFactor(model::Haze::convertHazeAltitudeToHazeAltitudeFactor(config.hazeHeight));
|
||||
|
||||
_haze->setHazeKeyLightRangeFactor(model::convertHazeRangeToHazeRangeFactor(config.hazeKeyLightRange_m));
|
||||
_haze->setHazeKeyLightAltitudeFactor(model::convertHazeAltitudeToHazeAltitudeFactor(config.hazeKeyLightAltitude_m));
|
||||
_haze->setHazeKeyLightRangeFactor(model::Haze::convertHazeRangeToHazeRangeFactor(config.hazeKeyLightRange));
|
||||
_haze->setHazeKeyLightAltitudeFactor(model::Haze::convertHazeAltitudeToHazeAltitudeFactor(config.hazeKeyLightAltitude));
|
||||
|
||||
_haze->setHazeBackgroundBlendValue(config.hazeBackgroundBlendValue);
|
||||
_haze->setHazeBackgroundBlend(config.hazeBackgroundBlend);
|
||||
}
|
||||
|
||||
HazeStage::Index HazeStage::findHaze(const HazePointer& haze) const {
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
|
||||
#include <render/Forward.h>
|
||||
#include <render/DrawTask.h>
|
||||
#include "model/Haze.h"
|
||||
#include <model/Haze.h>
|
||||
|
||||
// Haze stage to set up haze-related rendering tasks
|
||||
class HazeStage : public render::Stage {
|
||||
|
@ -83,14 +83,10 @@ protected:
|
|||
class FetchHazeConfig : public render::Job::Config {
|
||||
Q_OBJECT
|
||||
|
||||
Q_PROPERTY(float hazeColorR MEMBER hazeColorR WRITE setHazeColorR NOTIFY dirty);
|
||||
Q_PROPERTY(float hazeColorG MEMBER hazeColorG WRITE setHazeColorG NOTIFY dirty);
|
||||
Q_PROPERTY(float hazeColorB MEMBER hazeColorB WRITE setHazeColorB NOTIFY dirty);
|
||||
Q_PROPERTY(float hazeDirectionalLightAngle_degs MEMBER hazeDirectionalLightAngle_degs WRITE setDirectionalLightAngle_degs NOTIFY dirty);
|
||||
Q_PROPERTY(glm::vec3 hazeColor MEMBER hazeColor WRITE setHazeColor NOTIFY dirty);
|
||||
Q_PROPERTY(float hazeGlareAngle MEMBER hazeGlareAngle WRITE setHazeGlareAngle NOTIFY dirty);
|
||||
|
||||
Q_PROPERTY(float hazeDirectionalLightColorR MEMBER hazeDirectionalLightColorR WRITE setDirectionalLightColorR NOTIFY dirty);
|
||||
Q_PROPERTY(float hazeDirectionalLightColorG MEMBER hazeDirectionalLightColorG WRITE setDirectionalLightColorG NOTIFY dirty);
|
||||
Q_PROPERTY(float hazeDirectionalLightColorB MEMBER hazeDirectionalLightColorB WRITE setDirectionalLightColorB NOTIFY dirty);
|
||||
Q_PROPERTY(glm::vec3 hazeGlareColor MEMBER hazeGlareColor WRITE setHazeGlareColor NOTIFY dirty);
|
||||
Q_PROPERTY(float hazeBaseReference MEMBER hazeBaseReference WRITE setHazeBaseReference NOTIFY dirty);
|
||||
|
||||
Q_PROPERTY(bool isHazeActive MEMBER isHazeActive WRITE setHazeActive NOTIFY dirty);
|
||||
|
@ -99,26 +95,22 @@ class FetchHazeConfig : public render::Job::Config {
|
|||
Q_PROPERTY(bool isModulateColorActive MEMBER isModulateColorActive WRITE setModulateColorActive NOTIFY dirty);
|
||||
Q_PROPERTY(bool isHazeEnableGlare MEMBER isHazeEnableGlare WRITE setHazeEnableGlare NOTIFY dirty);
|
||||
|
||||
Q_PROPERTY(float hazeRange_m MEMBER hazeRange_m WRITE setHazeRange_m NOTIFY dirty);
|
||||
Q_PROPERTY(float hazeAltitude_m MEMBER hazeAltitude_m WRITE setHazeAltitude_m NOTIFY dirty);
|
||||
Q_PROPERTY(float hazeRange MEMBER hazeRange WRITE setHazeRange NOTIFY dirty);
|
||||
Q_PROPERTY(float hazeHeight MEMBER hazeHeight WRITE setHazeAltitude NOTIFY dirty);
|
||||
|
||||
Q_PROPERTY(float hazeKeyLightRange_m MEMBER hazeKeyLightRange_m WRITE setHazeKeyLightRange_m NOTIFY dirty);
|
||||
Q_PROPERTY(float hazeKeyLightAltitude_m MEMBER hazeKeyLightAltitude_m WRITE setHazeKeyLightAltitude_m NOTIFY dirty);
|
||||
Q_PROPERTY(float hazeKeyLightRange MEMBER hazeKeyLightRange WRITE setHazeKeyLightRange NOTIFY dirty);
|
||||
Q_PROPERTY(float hazeKeyLightAltitude MEMBER hazeKeyLightAltitude WRITE setHazeKeyLightAltitude NOTIFY dirty);
|
||||
|
||||
Q_PROPERTY(float hazeBackgroundBlendValue MEMBER hazeBackgroundBlendValue WRITE setHazeBackgroundBlendValue NOTIFY dirty);
|
||||
Q_PROPERTY(float hazeBackgroundBlend MEMBER hazeBackgroundBlend WRITE setHazeBackgroundBlend NOTIFY dirty);
|
||||
|
||||
public:
|
||||
FetchHazeConfig() : render::Job::Config() {}
|
||||
|
||||
float hazeColorR{ model::initialHazeColor.r };
|
||||
float hazeColorG{ model::initialHazeColor.g };
|
||||
float hazeColorB{ model::initialHazeColor.b };
|
||||
float hazeDirectionalLightAngle_degs{ model::initialDirectionalLightAngle_degs };
|
||||
glm::vec3 hazeColor{ model::Haze::INITIAL_HAZE_COLOR };
|
||||
float hazeGlareAngle{ model::Haze::INITIAL_HAZE_GLARE_ANGLE };
|
||||
|
||||
float hazeDirectionalLightColorR{ model::initialDirectionalLightColor.r };
|
||||
float hazeDirectionalLightColorG{ model::initialDirectionalLightColor.g };
|
||||
float hazeDirectionalLightColorB{ model::initialDirectionalLightColor.b };
|
||||
float hazeBaseReference{ model::initialHazeBaseReference };
|
||||
glm::vec3 hazeGlareColor{ model::Haze::INITIAL_HAZE_GLARE_COLOR };
|
||||
float hazeBaseReference{ model::Haze::INITIAL_HAZE_BASE_REFERENCE };
|
||||
|
||||
bool isHazeActive{ false };
|
||||
bool isAltitudeBased{ false };
|
||||
|
@ -126,23 +118,19 @@ public:
|
|||
bool isModulateColorActive{ false };
|
||||
bool isHazeEnableGlare{ false };
|
||||
|
||||
float hazeRange_m{ model::initialHazeRange_m };
|
||||
float hazeAltitude_m{ model::initialHazeAltitude_m };
|
||||
float hazeRange{ model::Haze::INITIAL_HAZE_RANGE };
|
||||
float hazeHeight{ model::Haze::INITIAL_HAZE_HEIGHT };
|
||||
|
||||
float hazeKeyLightRange_m{ model::initialHazeKeyLightRange_m };
|
||||
float hazeKeyLightAltitude_m{ model::initialHazeKeyLightAltitude_m };
|
||||
float hazeKeyLightRange{ model::Haze::INITIAL_KEY_LIGHT_RANGE };
|
||||
float hazeKeyLightAltitude{ model::Haze::INITIAL_KEY_LIGHT_ALTITUDE };
|
||||
|
||||
float hazeBackgroundBlendValue{ model::initialHazeBackgroundBlendValue };
|
||||
float hazeBackgroundBlend{ model::Haze::INITIAL_HAZE_BACKGROUND_BLEND };
|
||||
|
||||
public slots:
|
||||
void setHazeColorR(const float value) { hazeColorR = value; emit dirty(); }
|
||||
void setHazeColorG(const float value) { hazeColorG = value; emit dirty(); }
|
||||
void setHazeColorB(const float value) { hazeColorB = value; emit dirty(); }
|
||||
void setDirectionalLightAngle_degs(const float value) { hazeDirectionalLightAngle_degs = value; emit dirty(); }
|
||||
void setHazeColor(const glm::vec3 value) { hazeColor = value; emit dirty(); }
|
||||
void setHazeGlareAngle(const float value) { hazeGlareAngle = value; emit dirty(); }
|
||||
|
||||
void setDirectionalLightColorR(const float value) { hazeDirectionalLightColorR = value; emit dirty(); }
|
||||
void setDirectionalLightColorG(const float value) { hazeDirectionalLightColorG = value; emit dirty(); }
|
||||
void setDirectionalLightColorB(const float value) { hazeDirectionalLightColorB = value; emit dirty(); }
|
||||
void setHazeGlareColor(const glm::vec3 value) { hazeGlareColor = value; emit dirty(); }
|
||||
void setHazeBaseReference(const float value) { hazeBaseReference = value; ; emit dirty(); }
|
||||
|
||||
void setHazeActive(const bool active) { isHazeActive = active; emit dirty(); }
|
||||
|
@ -151,13 +139,13 @@ public slots:
|
|||
void setModulateColorActive(const bool active) { isModulateColorActive = active; emit dirty(); }
|
||||
void setHazeEnableGlare(const bool active) { isHazeEnableGlare = active; emit dirty(); }
|
||||
|
||||
void setHazeRange_m(const float value) { hazeRange_m = value; emit dirty(); }
|
||||
void setHazeAltitude_m(const float value) { hazeAltitude_m = value; emit dirty(); }
|
||||
void setHazeRange(const float value) { hazeRange = value; emit dirty(); }
|
||||
void setHazeAltitude(const float value) { hazeHeight = value; emit dirty(); }
|
||||
|
||||
void setHazeKeyLightRange_m(const float value) { hazeKeyLightRange_m = value; emit dirty(); }
|
||||
void setHazeKeyLightAltitude_m(const float value) { hazeKeyLightAltitude_m = value; emit dirty(); }
|
||||
void setHazeKeyLightRange(const float value) { hazeKeyLightRange = value; emit dirty(); }
|
||||
void setHazeKeyLightAltitude(const float value) { hazeKeyLightAltitude = value; emit dirty(); }
|
||||
|
||||
void setHazeBackgroundBlendValue(const float value) { hazeBackgroundBlendValue = value; ; emit dirty(); }
|
||||
void setHazeBackgroundBlend(const float value) { hazeBackgroundBlend = value; ; emit dirty(); }
|
||||
|
||||
signals:
|
||||
void dirty();
|
||||
|
|
|
@ -129,101 +129,6 @@ QString SceneScripting::Stage::getBackgroundMode() const {
|
|||
};
|
||||
}
|
||||
|
||||
void SceneScripting::Stage::setHazeMode(const QString& mode) {
|
||||
if (mode == QString("haze off")) {
|
||||
_skyStage->setHazeMode(model::SunSkyStage::HAZE_OFF);
|
||||
} else if (mode == QString("haze on")) {
|
||||
_skyStage->setHazeMode(model::SunSkyStage::HAZE_ON);
|
||||
}
|
||||
}
|
||||
|
||||
QString SceneScripting::Stage::getHazeMode() const {
|
||||
switch (_skyStage->getHazeMode()) {
|
||||
case model::SunSkyStage::HAZE_OFF:
|
||||
return QString("haze off");
|
||||
case model::SunSkyStage::HAZE_ON:
|
||||
return QString("haze on");
|
||||
default:
|
||||
return QString("inherit");
|
||||
};
|
||||
}
|
||||
|
||||
void SceneScripting::Stage::setHazeRange(const float hazeRange) {
|
||||
_skyStage->setHazeRange(hazeRange);
|
||||
}
|
||||
float SceneScripting::Stage::getHazeRange() const {
|
||||
return _skyStage->getHazeRange();
|
||||
}
|
||||
void SceneScripting::Stage::setHazeColor(const xColor hazeColor) {
|
||||
_skyStage->setHazeColor(hazeColor);
|
||||
}
|
||||
xColor SceneScripting::Stage::getHazeColor() const {
|
||||
return _skyStage->getHazeColor();
|
||||
}
|
||||
void SceneScripting::Stage::setHazeGlareColor(const xColor hazeGlareColor) {
|
||||
_skyStage->setHazeGlareColor(hazeGlareColor);
|
||||
}
|
||||
xColor SceneScripting::Stage::getHazeGlareColor() const {
|
||||
return _skyStage->getHazeGlareColor();
|
||||
}
|
||||
void SceneScripting::Stage::setHazeEnableGlare(const bool hazeEnableGlare) {
|
||||
_skyStage->setHazeEnableGlare(hazeEnableGlare);
|
||||
}
|
||||
bool SceneScripting::Stage::getHazeEnableGlare() const {
|
||||
return _skyStage->getHazeEnableGlare();
|
||||
}
|
||||
void SceneScripting::Stage::setHazeGlareAngle(const float hazeGlareAngle) {
|
||||
_skyStage->setHazeGlareAngle(hazeGlareAngle);
|
||||
}
|
||||
float SceneScripting::Stage::getHazeGlareAngle() const {
|
||||
return _skyStage->getHazeGlareAngle();
|
||||
}
|
||||
|
||||
void SceneScripting::Stage::setHazeAltitudeEffect(const bool hazeAltitudeEffect) {
|
||||
_skyStage->setHazeAltitudeEffect(hazeAltitudeEffect);
|
||||
}
|
||||
bool SceneScripting::Stage::getHazeAltitudeEffect() const {
|
||||
return _skyStage->getHazeAltitudeEffect();
|
||||
}
|
||||
void SceneScripting::Stage::setHazeCeiling(const float hazeCeiling) {
|
||||
_skyStage->setHazeCeiling(hazeCeiling);
|
||||
}
|
||||
float SceneScripting::Stage::getHazeCeiling() const {
|
||||
return _skyStage->getHazeCeiling();
|
||||
}
|
||||
void SceneScripting::Stage::setHazeBaseRef(const float hazeBaseRef) {
|
||||
_skyStage->setHazeBaseRef(hazeBaseRef);
|
||||
}
|
||||
float SceneScripting::Stage::getHazeBaseRef() const {
|
||||
return _skyStage->getHazeBaseRef();
|
||||
}
|
||||
|
||||
void SceneScripting::Stage::setHazeBackgroundBlend(const float hazeBackgroundBlend) {
|
||||
_skyStage->setHazeBackgroundBlend(hazeBackgroundBlend);
|
||||
}
|
||||
float SceneScripting::Stage::getHazeBackgroundBlend() const {
|
||||
return _skyStage->getHazeBackgroundBlend();
|
||||
}
|
||||
|
||||
void SceneScripting::Stage::setHazeAttenuateKeyLight(const bool hazeAttenuateKeyLight) {
|
||||
_skyStage->setHazeAttenuateKeyLight(hazeAttenuateKeyLight);
|
||||
}
|
||||
bool SceneScripting::Stage::getHazeAttenuateKeyLight() const {
|
||||
return _skyStage->getHazeAttenuateKeyLight();
|
||||
}
|
||||
void SceneScripting::Stage::setHazeKeyLightRange(const float hazeKeyLightRange) {
|
||||
_skyStage->setHazeKeyLightRange(hazeKeyLightRange);
|
||||
}
|
||||
float SceneScripting::Stage::getHazeKeyLightRange() const {
|
||||
return _skyStage->getHazeKeyLightRange();
|
||||
}
|
||||
void SceneScripting::Stage::setHazeKeyLightAltitude(const float hazeKeyLightAltitude) {
|
||||
_skyStage->setHazeKeyLightAltitude(hazeKeyLightAltitude);
|
||||
}
|
||||
float SceneScripting::Stage::getHazeKeyLightAltitude() const {
|
||||
return _skyStage->getHazeKeyLightAltitude();
|
||||
}
|
||||
|
||||
SceneScriptingInterface::SceneScriptingInterface() : _stage{ new SceneScripting::Stage{ _skyStage } } {
|
||||
// Let's make sure the sunSkyStage is using a proceduralSkybox
|
||||
_skyStage->setSkybox(model::SkyboxPointer(new ProceduralSkybox()));
|
||||
|
|
|
@ -121,58 +121,6 @@ namespace SceneScripting {
|
|||
void setBackgroundMode(const QString& mode);
|
||||
QString getBackgroundMode() const;
|
||||
|
||||
Q_PROPERTY(QString hazeMode READ getHazeMode WRITE setHazeMode)
|
||||
void setHazeMode(const QString& mode);
|
||||
QString getHazeMode() const;
|
||||
|
||||
Q_PROPERTY(float hazeRange READ getHazeRange WRITE setHazeRange)
|
||||
void setHazeRange(float value);
|
||||
float getHazeRange() const;
|
||||
|
||||
Q_PROPERTY(xColor hazeColor READ getHazeColor WRITE setHazeColor)
|
||||
void setHazeColor(xColor value);
|
||||
xColor getHazeColor() const;
|
||||
|
||||
Q_PROPERTY(xColor hazeGlareColor READ getHazeGlareColor WRITE setHazeGlareColor)
|
||||
void setHazeGlareColor(xColor value);
|
||||
xColor getHazeGlareColor() const;
|
||||
|
||||
Q_PROPERTY(bool hazeEnableGlare READ getHazeEnableGlare WRITE setHazeEnableGlare)
|
||||
void setHazeEnableGlare(bool value);
|
||||
bool getHazeEnableGlare() const;
|
||||
|
||||
Q_PROPERTY(float hazeGlareAngle READ getHazeGlareAngle WRITE setHazeGlareAngle)
|
||||
void setHazeGlareAngle(float value);
|
||||
float getHazeGlareAngle() const;
|
||||
|
||||
Q_PROPERTY(bool hazeAltitudeEffect READ getHazeAltitudeEffect WRITE setHazeAltitudeEffect)
|
||||
void setHazeAltitudeEffect(bool value);
|
||||
bool getHazeAltitudeEffect() const;
|
||||
|
||||
Q_PROPERTY(float hazeCeiling READ getHazeCeiling WRITE setHazeCeiling)
|
||||
void setHazeCeiling(float value);
|
||||
float getHazeCeiling() const;
|
||||
|
||||
Q_PROPERTY(float hazeBaseRef READ getHazeBaseRef WRITE setHazeBaseRef)
|
||||
void setHazeBaseRef(float value);
|
||||
float getHazeBaseRef() const;
|
||||
|
||||
Q_PROPERTY(float hazeBackgroundBlend READ getHazeBackgroundBlend WRITE setHazeBackgroundBlend)
|
||||
void setHazeBackgroundBlend(float value);
|
||||
float getHazeBackgroundBlend() const;
|
||||
|
||||
Q_PROPERTY(bool hazeAttenuateKeyLight READ getHazeAttenuateKeyLight WRITE setHazeAttenuateKeyLight)
|
||||
void setHazeAttenuateKeyLight(bool value);
|
||||
bool getHazeAttenuateKeyLight() const;
|
||||
|
||||
Q_PROPERTY(float hazeKeyLightRange READ getHazeKeyLightRange WRITE setHazeKeyLightRange)
|
||||
void setHazeKeyLightRange(float value);
|
||||
float getHazeKeyLightRange() const;
|
||||
|
||||
Q_PROPERTY(float hazeKeyLightAltitude READ getHazeKeyLightAltitude WRITE setHazeKeyLightAltitude)
|
||||
void setHazeKeyLightAltitude(float value);
|
||||
float getHazeKeyLightAltitude() const;
|
||||
|
||||
protected:
|
||||
model::SunSkyStagePointer _skyStage;
|
||||
LocationPointer _location;
|
||||
|
|
|
@ -20,15 +20,16 @@
|
|||
namespace {
|
||||
|
||||
bool isAuthableHighFidelityURL(const QUrl& url) {
|
||||
auto metaverseServerURL = NetworkingConstants::METAVERSE_SERVER_URL;
|
||||
static const QStringList HF_HOSTS = {
|
||||
"highfidelity.com", "highfidelity.io",
|
||||
"metaverse.highfidelity.com", "metaverse.highfidelity.io"
|
||||
metaverseServerURL.toString(), "metaverse.highfidelity.io"
|
||||
};
|
||||
const auto& scheme = url.scheme();
|
||||
const auto& host = url.host();
|
||||
|
||||
return (scheme == "https" && HF_HOSTS.contains(host)) ||
|
||||
((scheme == NetworkingConstants::METAVERSE_SERVER_URL.scheme()) && (host == NetworkingConstants::METAVERSE_SERVER_URL.host()));
|
||||
((scheme == metaverseServerURL.scheme()) && (host == metaverseServerURL.host()));
|
||||
}
|
||||
|
||||
bool isScript(const QString filename) {
|
||||
|
|
|
@ -68,7 +68,7 @@ Item {
|
|||
label: "Haze Color R"
|
||||
integral: false
|
||||
config: root.hazeModel
|
||||
property: "hazeColorR"
|
||||
property: "hazeColor.r"
|
||||
max: 1.0
|
||||
min: 0.0
|
||||
width: 280
|
||||
|
@ -78,7 +78,7 @@ Item {
|
|||
label: "Haze Color G"
|
||||
integral: false
|
||||
config: root.hazeModel
|
||||
property: "hazeColorG"
|
||||
property: "hazeColor.g"
|
||||
max: 1.0
|
||||
min: 0.0
|
||||
width: 280
|
||||
|
@ -88,7 +88,7 @@ Item {
|
|||
label: "Haze Color B"
|
||||
integral: false
|
||||
config: root.hazeModel
|
||||
property: "hazeColorB"
|
||||
property: "hazeColor.b"
|
||||
max: 1.0
|
||||
min: 0.0
|
||||
width: 280
|
||||
|
@ -98,7 +98,7 @@ Item {
|
|||
label: "Sun R"
|
||||
integral: false
|
||||
config: root.hazeModel
|
||||
property: "hazeDirectionalLightColorR"
|
||||
property: "hazeGlareColor.r"
|
||||
max: 1.0
|
||||
min: 0.0
|
||||
width: 280
|
||||
|
@ -108,7 +108,7 @@ Item {
|
|||
label: "Sun G"
|
||||
integral: false
|
||||
config: root.hazeModel
|
||||
property: "hazeDirectionalLightColorG"
|
||||
property: "hazeGlareColor.g"
|
||||
max: 1.0
|
||||
min: 0.0
|
||||
width: 280
|
||||
|
@ -118,7 +118,7 @@ Item {
|
|||
label: "Sun B"
|
||||
integral: false
|
||||
config: root.hazeModel
|
||||
property: "hazeDirectionalLightColorB"
|
||||
property: "hazeGlareColor.b"
|
||||
max: 1.0
|
||||
min: 0.0
|
||||
width: 280
|
||||
|
@ -128,7 +128,7 @@ Item {
|
|||
label: "Sun glare angle"
|
||||
integral: false
|
||||
config: root.hazeModel
|
||||
property: "hazeDirectionalLightAngle_degs"
|
||||
property: "hazeGlareAngle_degs"
|
||||
max: 70.0
|
||||
min: 0.0
|
||||
width: 280
|
||||
|
@ -148,7 +148,7 @@ Item {
|
|||
label: "BG Blend"
|
||||
integral: false
|
||||
config: root.hazeModel
|
||||
property: "hazeBackgroundBlendValue"
|
||||
property: "hazeBackgroundBlend"
|
||||
max: 1.0
|
||||
min: 0.0
|
||||
width: 280
|
||||
|
@ -156,9 +156,9 @@ Item {
|
|||
|
||||
CheckBox {
|
||||
text: "Keylight Attenuation"
|
||||
checked: root.hazeModel["isDirectionaLightAttenuationActive"]
|
||||
checked: root.hazeModel["isKeyLightAttenuationActive"]
|
||||
onCheckedChanged: {
|
||||
root.hazeModel["isDirectionaLightAttenuationActive"] = checked;
|
||||
root.hazeModel["isKeyLightAttenuationActive"] = checked;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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 };
|
||||
}
|
||||
|
||||
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue