mirror of
https://github.com/overte-org/overte.git
synced 2025-04-19 15:03:53 +02:00
Merge branch 'master' of https://github.com/highfidelity/hifi into dk/makeMeSomeKeys
This commit is contained in:
commit
73e943daaf
51 changed files with 1715 additions and 219 deletions
|
@ -935,39 +935,7 @@ void OctreeServer::handleOctreeFileReplacement(QSharedPointer<ReceivedMessage> m
|
|||
// so here we just store a special file at our persist path
|
||||
// and then force a stop of the server so that it can pick it up when it relaunches
|
||||
if (!_persistAbsoluteFilePath.isEmpty()) {
|
||||
|
||||
// before we restart the server and make it try and load this data, let's make sure it is valid
|
||||
auto compressedOctree = message->getMessage();
|
||||
QByteArray jsonOctree;
|
||||
|
||||
// assume we have GZipped content
|
||||
bool wasCompressed = gunzip(compressedOctree, jsonOctree);
|
||||
if (!wasCompressed) {
|
||||
// the source was not compressed, assume we were sent regular JSON data
|
||||
jsonOctree = compressedOctree;
|
||||
}
|
||||
|
||||
// check the JSON data to verify it is an object
|
||||
if (QJsonDocument::fromJson(jsonOctree).isObject()) {
|
||||
if (!wasCompressed) {
|
||||
// source was not compressed, we compress it before we write it locally
|
||||
gzip(jsonOctree, compressedOctree);
|
||||
}
|
||||
|
||||
// write the compressed octree data to a special file
|
||||
auto replacementFilePath = _persistAbsoluteFilePath.append(OctreePersistThread::REPLACEMENT_FILE_EXTENSION);
|
||||
QFile replacementFile(replacementFilePath);
|
||||
if (replacementFile.open(QIODevice::WriteOnly) && replacementFile.write(compressedOctree) != -1) {
|
||||
// we've now written our replacement file, time to take the server down so it can
|
||||
// process it when it comes back up
|
||||
qInfo() << "Wrote octree replacement file to" << replacementFilePath << "- stopping server";
|
||||
setFinished(true);
|
||||
} else {
|
||||
qWarning() << "Could not write replacement octree data to file - refusing to process";
|
||||
}
|
||||
} else {
|
||||
qDebug() << "Received replacement octree file that is invalid - refusing to process";
|
||||
}
|
||||
replaceContentFromMessageData(message->getMessage());
|
||||
} else {
|
||||
qDebug() << "Cannot perform octree file replacement since current persist file path is not yet known";
|
||||
}
|
||||
|
@ -977,6 +945,68 @@ void OctreeServer::handleOctreeFileReplacement(QSharedPointer<ReceivedMessage> m
|
|||
}
|
||||
}
|
||||
|
||||
// Message->getMessage() contains a QByteArray representation of the URL to download from
|
||||
void OctreeServer::handleOctreeFileReplacementFromURL(QSharedPointer<ReceivedMessage> message) {
|
||||
qInfo() << "Received request to replace content from a url";
|
||||
if (!_isFinished && !_isShuttingDown) {
|
||||
// This call comes from Interface, so we skip our domain server check
|
||||
// but confirm that we have permissions to replace content sets
|
||||
if (DependencyManager::get<NodeList>()->getThisNodeCanReplaceContent()) {
|
||||
if (!_persistAbsoluteFilePath.isEmpty()) {
|
||||
// Convert message data into our URL
|
||||
QString url(message->getMessage());
|
||||
QUrl modelsURL = QUrl(url, QUrl::StrictMode);
|
||||
QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();
|
||||
QNetworkRequest request(modelsURL);
|
||||
QNetworkReply* reply = networkAccessManager.get(request);
|
||||
connect(reply, &QNetworkReply::finished, [this, reply, modelsURL]() {
|
||||
QNetworkReply::NetworkError networkError = reply->error();
|
||||
if (networkError == QNetworkReply::NoError) {
|
||||
QByteArray contents = reply->readAll();
|
||||
replaceContentFromMessageData(contents);
|
||||
} else {
|
||||
qDebug() << "Error downloading JSON from specified file";
|
||||
}
|
||||
});
|
||||
} else {
|
||||
qDebug() << "Cannot perform octree file replacement since current persist file path is not yet known";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void OctreeServer::replaceContentFromMessageData(QByteArray content) {
|
||||
//Assume we have compressed data
|
||||
auto compressedOctree = content;
|
||||
QByteArray jsonOctree;
|
||||
|
||||
bool wasCompressed = gunzip(compressedOctree, jsonOctree);
|
||||
if (!wasCompressed) {
|
||||
// the source was not compressed, assume we were sent regular JSON data
|
||||
jsonOctree = compressedOctree;
|
||||
}
|
||||
// check the JSON data to verify it is an object
|
||||
if (QJsonDocument::fromJson(jsonOctree).isObject()) {
|
||||
if (!wasCompressed) {
|
||||
// source was not compressed, we compress it before we write it locally
|
||||
gzip(jsonOctree, compressedOctree);
|
||||
}
|
||||
// write the compressed octree data to a special file
|
||||
auto replacementFilePath = _persistAbsoluteFilePath.append(OctreePersistThread::REPLACEMENT_FILE_EXTENSION);
|
||||
QFile replacementFile(replacementFilePath);
|
||||
if (replacementFile.open(QIODevice::WriteOnly) && replacementFile.write(compressedOctree) != -1) {
|
||||
// we've now written our replacement file, time to take the server down so it can
|
||||
// process it when it comes back up
|
||||
qInfo() << "Wrote octree replacement file to" << replacementFilePath << "- stopping server";
|
||||
setFinished(true);
|
||||
} else {
|
||||
qWarning() << "Could not write replacement octree data to file - refusing to process";
|
||||
}
|
||||
} else {
|
||||
qDebug() << "Received replacement octree file that is invalid - refusing to process";
|
||||
}
|
||||
}
|
||||
|
||||
bool OctreeServer::readOptionBool(const QString& optionName, const QJsonObject& settingsSectionObject, bool& result) {
|
||||
result = false; // assume it doesn't exist
|
||||
bool optionAvailable = false;
|
||||
|
@ -1202,6 +1232,7 @@ void OctreeServer::domainSettingsRequestComplete() {
|
|||
packetReceiver.registerListener(PacketType::OctreeDataNack, this, "handleOctreeDataNackPacket");
|
||||
packetReceiver.registerListener(PacketType::JurisdictionRequest, this, "handleJurisdictionRequestPacket");
|
||||
packetReceiver.registerListener(PacketType::OctreeFileReplacement, this, "handleOctreeFileReplacement");
|
||||
packetReceiver.registerListener(PacketType::OctreeFileReplacementFromUrl, this, "handleOctreeFileReplacementFromURL");
|
||||
|
||||
readConfiguration();
|
||||
|
||||
|
|
|
@ -137,6 +137,7 @@ private slots:
|
|||
void handleOctreeDataNackPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode);
|
||||
void handleJurisdictionRequestPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode);
|
||||
void handleOctreeFileReplacement(QSharedPointer<ReceivedMessage> message);
|
||||
void handleOctreeFileReplacementFromURL(QSharedPointer<ReceivedMessage> message);
|
||||
void removeSendThread();
|
||||
|
||||
protected:
|
||||
|
@ -161,6 +162,8 @@ protected:
|
|||
UniqueSendThread createSendThread(const SharedNodePointer& node);
|
||||
virtual UniqueSendThread newSendThread(const SharedNodePointer& node);
|
||||
|
||||
void replaceContentFromMessageData(QByteArray content);
|
||||
|
||||
int _argc;
|
||||
const char** _argv;
|
||||
char** _parsedArgV;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"version": 1.7,
|
||||
"version": 1.8,
|
||||
"settings": [
|
||||
{
|
||||
"name": "metaverse",
|
||||
|
@ -112,7 +112,6 @@
|
|||
"label": "Adult (18+)"
|
||||
}
|
||||
]
|
||||
|
||||
},
|
||||
{
|
||||
"name": "hosts",
|
||||
|
@ -161,15 +160,15 @@
|
|||
"label": "HTTP Password",
|
||||
"type": "password",
|
||||
"help": "Password used for basic HTTP authentication. Leave this alone if you do not want to change it.",
|
||||
"password_placeholder" : "******",
|
||||
"password_placeholder": "******",
|
||||
"value-hidden": true
|
||||
},
|
||||
{
|
||||
"name": "verify_http_password",
|
||||
"label": "Verify HTTP Password",
|
||||
"type": "password",
|
||||
"help": "Must match the password entered above for change to be saved.",
|
||||
"value-hidden": true
|
||||
"name": "verify_http_password",
|
||||
"label": "Verify HTTP Password",
|
||||
"type": "password",
|
||||
"help": "Must match the password entered above for change to be saved.",
|
||||
"value-hidden": true
|
||||
},
|
||||
{
|
||||
"name": "maximum_user_capacity",
|
||||
|
@ -208,21 +207,19 @@
|
|||
"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></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>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": [
|
||||
{
|
||||
"label": "Type of User",
|
||||
"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></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": 7
|
||||
"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
|
||||
}
|
||||
],
|
||||
|
||||
"columns": [
|
||||
{
|
||||
"name": "permissions_id",
|
||||
|
@ -276,9 +273,15 @@
|
|||
"type": "checkbox",
|
||||
"editable": true,
|
||||
"default": false
|
||||
},
|
||||
{
|
||||
"name": "id_can_replace_content",
|
||||
"label": "Replace Content",
|
||||
"type": "checkbox",
|
||||
"editable": true,
|
||||
"default": false
|
||||
}
|
||||
],
|
||||
|
||||
"non-deletable-row-key": "permissions_id",
|
||||
"non-deletable-row-values": ["localhost", "anonymous", "logged-in"]
|
||||
},
|
||||
|
@ -291,18 +294,16 @@
|
|||
"can_add_new_rows": false,
|
||||
"new_category_placeholder": "Add Group",
|
||||
"new_category_message": "Save and reload to see ranks",
|
||||
|
||||
"groups": [
|
||||
{
|
||||
"label": "Rank",
|
||||
"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></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": 7
|
||||
"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
|
||||
}
|
||||
],
|
||||
|
||||
"columns": [
|
||||
{
|
||||
"name": "permissions_id",
|
||||
|
@ -381,6 +382,13 @@
|
|||
"type": "checkbox",
|
||||
"editable": true,
|
||||
"default": false
|
||||
},
|
||||
{
|
||||
"name": "id_can_replace_content",
|
||||
"label": "Replace Content",
|
||||
"type": "checkbox",
|
||||
"editable": true,
|
||||
"default": false
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -393,18 +401,16 @@
|
|||
"can_add_new_rows": false,
|
||||
"new_category_placeholder": "Add Blacklist Group",
|
||||
"new_category_message": "Save and reload to see ranks",
|
||||
|
||||
"groups": [
|
||||
{
|
||||
"label": "Rank",
|
||||
"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></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": 7
|
||||
"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
|
||||
}
|
||||
],
|
||||
|
||||
"columns": [
|
||||
{
|
||||
"name": "permissions_id",
|
||||
|
@ -480,6 +486,13 @@
|
|||
"type": "checkbox",
|
||||
"editable": true,
|
||||
"default": false
|
||||
},
|
||||
{
|
||||
"name": "id_can_replace_content",
|
||||
"label": "Replace Content",
|
||||
"type": "checkbox",
|
||||
"editable": true,
|
||||
"default": false
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -488,18 +501,16 @@
|
|||
"type": "table",
|
||||
"caption": "Permissions for Specific Users",
|
||||
"can_add_new_rows": true,
|
||||
|
||||
"groups": [
|
||||
{
|
||||
"label": "User",
|
||||
"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></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": 7
|
||||
"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
|
||||
}
|
||||
],
|
||||
|
||||
"columns": [
|
||||
{
|
||||
"name": "permissions_id",
|
||||
|
@ -553,6 +564,13 @@
|
|||
"type": "checkbox",
|
||||
"editable": true,
|
||||
"default": false
|
||||
},
|
||||
{
|
||||
"name": "id_can_replace_content",
|
||||
"label": "Replace Content",
|
||||
"type": "checkbox",
|
||||
"editable": true,
|
||||
"default": false
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -567,11 +585,10 @@
|
|||
"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></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": 7
|
||||
"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
|
||||
}
|
||||
],
|
||||
|
||||
"columns": [
|
||||
{
|
||||
"name": "permissions_id",
|
||||
|
@ -625,6 +642,13 @@
|
|||
"type": "checkbox",
|
||||
"editable": true,
|
||||
"default": false
|
||||
},
|
||||
{
|
||||
"name": "id_can_replace_content",
|
||||
"label": "Replace Content",
|
||||
"type": "checkbox",
|
||||
"editable": true,
|
||||
"default": false
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -639,11 +663,10 @@
|
|||
"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></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": 7
|
||||
"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
|
||||
}
|
||||
],
|
||||
|
||||
"columns": [
|
||||
{
|
||||
"name": "permissions_id",
|
||||
|
@ -697,6 +720,13 @@
|
|||
"type": "checkbox",
|
||||
"editable": true,
|
||||
"default": false
|
||||
},
|
||||
{
|
||||
"name": "id_can_replace_content",
|
||||
"label": "Replace Content",
|
||||
"type": "checkbox",
|
||||
"editable": true,
|
||||
"default": false
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -711,11 +741,10 @@
|
|||
"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></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": 7
|
||||
"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
|
||||
}
|
||||
],
|
||||
|
||||
"columns": [
|
||||
{
|
||||
"name": "permissions_id",
|
||||
|
@ -769,6 +798,13 @@
|
|||
"type": "checkbox",
|
||||
"editable": true,
|
||||
"default": false
|
||||
},
|
||||
{
|
||||
"name": "id_can_replace_content",
|
||||
"label": "Replace Content",
|
||||
"type": "checkbox",
|
||||
"editable": true,
|
||||
"default": false
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@ -784,7 +820,6 @@
|
|||
"label": "Persistent Scripts",
|
||||
"help": "Add the URLs for scripts that you would like to ensure are always running in your domain.",
|
||||
"can_add_new_rows": true,
|
||||
|
||||
"columns": [
|
||||
{
|
||||
"name": "url",
|
||||
|
@ -946,7 +981,6 @@
|
|||
"help": "In this table you can define a set of zones in which you can specify various audio properties.",
|
||||
"numbered": false,
|
||||
"can_add_new_rows": true,
|
||||
|
||||
"key": {
|
||||
"name": "name",
|
||||
"label": "Name",
|
||||
|
@ -999,7 +1033,6 @@
|
|||
"numbered": true,
|
||||
"can_order": true,
|
||||
"can_add_new_rows": true,
|
||||
|
||||
"columns": [
|
||||
{
|
||||
"name": "source",
|
||||
|
@ -1028,7 +1061,6 @@
|
|||
"help": "In this table you can set reverb levels for audio zones. For a medium-sized (e.g., 100 square meter) meeting room, try a decay time of around 1.5 seconds and a wet/dry mix of 25%. For an airplane hangar or cathedral, try a decay time of 4 seconds and a wet/dry mix of 50%.",
|
||||
"numbered": true,
|
||||
"can_add_new_rows": true,
|
||||
|
||||
"columns": [
|
||||
{
|
||||
"name": "zone",
|
||||
|
@ -1063,7 +1095,9 @@
|
|||
{
|
||||
"name": "audio_buffer",
|
||||
"label": "Audio Buffers",
|
||||
"assignment-types": [0],
|
||||
"assignment-types": [
|
||||
0
|
||||
],
|
||||
"settings": [
|
||||
{
|
||||
"name": "dynamic_jitter_buffer",
|
||||
|
@ -1082,35 +1116,37 @@
|
|||
"advanced": true
|
||||
},
|
||||
{
|
||||
"name": "max_frames_over_desired",
|
||||
"deprecated": true
|
||||
"name": "max_frames_over_desired",
|
||||
"deprecated": true
|
||||
},
|
||||
{
|
||||
"name": "window_starve_threshold",
|
||||
"deprecated": true
|
||||
"name": "window_starve_threshold",
|
||||
"deprecated": true
|
||||
},
|
||||
{
|
||||
"name": "window_seconds_for_desired_calc_on_too_many_starves",
|
||||
"deprecated": true
|
||||
"name": "window_seconds_for_desired_calc_on_too_many_starves",
|
||||
"deprecated": true
|
||||
},
|
||||
{
|
||||
"name": "window_seconds_for_desired_reduction",
|
||||
"deprecated": true
|
||||
"name": "window_seconds_for_desired_reduction",
|
||||
"deprecated": true
|
||||
},
|
||||
{
|
||||
"name": "use_stdev_for_desired_calc",
|
||||
"deprecated": true
|
||||
"name": "use_stdev_for_desired_calc",
|
||||
"deprecated": true
|
||||
},
|
||||
{
|
||||
"name": "repetition_with_fade",
|
||||
"deprecated": true
|
||||
"name": "repetition_with_fade",
|
||||
"deprecated": true
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "entity_server_settings",
|
||||
"label": "Entity Server Settings",
|
||||
"assignment-types": [6],
|
||||
"assignment-types": [
|
||||
6
|
||||
],
|
||||
"settings": [
|
||||
{
|
||||
"name": "maxTmpLifetime",
|
||||
|
@ -1167,13 +1203,32 @@
|
|||
"help": "In this table you can define a set of rules for how frequently to backup copies of your entites content file.",
|
||||
"numbered": false,
|
||||
"can_add_new_rows": true,
|
||||
|
||||
"default": [
|
||||
{"Name":"Half Hourly Rolling","backupInterval":1800,"format":".backup.halfhourly.%N","maxBackupVersions":5},
|
||||
{"Name":"Daily Rolling","backupInterval":86400,"format":".backup.daily.%N","maxBackupVersions":7},
|
||||
{"Name":"Weekly Rolling","backupInterval":604800,"format":".backup.weekly.%N","maxBackupVersions":4},
|
||||
{"Name":"Thirty Day Rolling","backupInterval":2592000,"format":".backup.thirtyday.%N","maxBackupVersions":12}
|
||||
],
|
||||
"default": [
|
||||
{
|
||||
"Name": "Half Hourly Rolling",
|
||||
"backupInterval": 1800,
|
||||
"format": ".backup.halfhourly.%N",
|
||||
"maxBackupVersions": 5
|
||||
},
|
||||
{
|
||||
"Name": "Daily Rolling",
|
||||
"backupInterval": 86400,
|
||||
"format": ".backup.daily.%N",
|
||||
"maxBackupVersions": 7
|
||||
},
|
||||
{
|
||||
"Name": "Weekly Rolling",
|
||||
"backupInterval": 604800,
|
||||
"format": ".backup.weekly.%N",
|
||||
"maxBackupVersions": 4
|
||||
},
|
||||
{
|
||||
"Name": "Thirty Day Rolling",
|
||||
"backupInterval": 2592000,
|
||||
"format": ".backup.thirtyday.%N",
|
||||
"maxBackupVersions": 12
|
||||
}
|
||||
],
|
||||
"columns": [
|
||||
{
|
||||
"name": "Name",
|
||||
|
@ -1309,7 +1364,9 @@
|
|||
{
|
||||
"name": "avatar_mixer",
|
||||
"label": "Avatar Mixer",
|
||||
"assignment-types": [1],
|
||||
"assignment-types": [
|
||||
1
|
||||
],
|
||||
"settings": [
|
||||
{
|
||||
"name": "max_node_send_bandwidth",
|
||||
|
@ -1362,7 +1419,10 @@
|
|||
{
|
||||
"name": "downstream_servers",
|
||||
"label": "Receiving Servers",
|
||||
"assignment-types": [0,1],
|
||||
"assignment-types": [
|
||||
0,
|
||||
1
|
||||
],
|
||||
"type": "table",
|
||||
"advanced": true,
|
||||
"can_add_new_rows": true,
|
||||
|
@ -1402,7 +1462,10 @@
|
|||
{
|
||||
"name": "upstream_servers",
|
||||
"label": "Broadcasting Servers",
|
||||
"assignment-types": [0,1],
|
||||
"assignment-types": [
|
||||
0,
|
||||
1
|
||||
],
|
||||
"type": "table",
|
||||
"advanced": true,
|
||||
"can_add_new_rows": true,
|
||||
|
@ -1442,4 +1505,4 @@
|
|||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
|
@ -269,6 +269,7 @@ void DomainGatekeeper::updateNodePermissions() {
|
|||
userPerms.permissions |= NodePermissions::Permission::canRezPermanentEntities;
|
||||
userPerms.permissions |= NodePermissions::Permission::canRezTemporaryEntities;
|
||||
userPerms.permissions |= NodePermissions::Permission::canWriteToAssetServer;
|
||||
userPerms.permissions |= NodePermissions::Permission::canReplaceDomainContent;
|
||||
} else {
|
||||
// this node is an agent
|
||||
const QHostAddress& addr = node->getLocalSocket().getAddress();
|
||||
|
@ -357,6 +358,7 @@ SharedNodePointer DomainGatekeeper::processAssignmentConnectRequest(const NodeCo
|
|||
userPerms.permissions |= NodePermissions::Permission::canRezPermanentEntities;
|
||||
userPerms.permissions |= NodePermissions::Permission::canRezTemporaryEntities;
|
||||
userPerms.permissions |= NodePermissions::Permission::canWriteToAssetServer;
|
||||
userPerms.permissions |= NodePermissions::Permission::canReplaceDomainContent;
|
||||
newNode->setPermissions(userPerms);
|
||||
return newNode;
|
||||
}
|
||||
|
|
|
@ -112,6 +112,7 @@ void DomainServerSettingsManager::setupConfigMap(const QStringList& argumentList
|
|||
const QString RESTRICTED_ACCESS_SETTINGS_KEYPATH = "security.restricted_access";
|
||||
const QString ALLOWED_EDITORS_SETTINGS_KEYPATH = "security.allowed_editors";
|
||||
const QString EDITORS_ARE_REZZERS_KEYPATH = "security.editors_are_rezzers";
|
||||
const QString EDITORS_CAN_REPLACE_CONTENT_KEYPATH = "security.editors_can_replace_content";
|
||||
|
||||
qDebug() << "Previous domain-server settings version was"
|
||||
<< QString::number(oldVersion, 'g', 8) << "and the new version is"
|
||||
|
@ -294,6 +295,13 @@ void DomainServerSettingsManager::setupConfigMap(const QStringList& argumentList
|
|||
// persist the new config so the user config file has the correctly merged config
|
||||
persistToFile();
|
||||
}
|
||||
|
||||
if (oldVersion < 1.8) {
|
||||
unpackPermissions();
|
||||
// This was prior to addition of domain content replacement, add that to localhost permissions by default
|
||||
_standardAgentPermissions[NodePermissions::standardNameLocalhost]->set(NodePermissions::Permission::canReplaceDomainContent);
|
||||
packPermissions();
|
||||
}
|
||||
}
|
||||
|
||||
unpackPermissions();
|
||||
|
|
|
@ -27,6 +27,7 @@ TreeView {
|
|||
|
||||
model: treeModel
|
||||
selection: ItemSelectionModel {
|
||||
id: selectionModel
|
||||
model: treeModel
|
||||
}
|
||||
|
||||
|
@ -215,6 +216,10 @@ TreeView {
|
|||
|
||||
onDoubleClicked: isExpanded(index) ? collapse(index) : expand(index)
|
||||
|
||||
onClicked: {
|
||||
selectionModel.setCurrentIndex(index, ItemSelectionModel.ClearAndSelect);
|
||||
}
|
||||
|
||||
onActivated: {
|
||||
var path = scriptsModel.data(index, 0x100)
|
||||
if (path) {
|
||||
|
|
48
interface/resources/qml/controls-uit/WebGlyphButton.qml
Normal file
48
interface/resources/qml/controls-uit/WebGlyphButton.qml
Normal file
|
@ -0,0 +1,48 @@
|
|||
//
|
||||
// GlyphButton.qml
|
||||
//
|
||||
// Created by Vlad Stelmahovsky on 2017-06-21
|
||||
// Copyright 2017 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
import QtQuick 2.5
|
||||
import QtQuick.Controls 1.4 as Original
|
||||
import QtQuick.Controls.Styles 1.4
|
||||
|
||||
import "../styles-uit"
|
||||
|
||||
Original.Button {
|
||||
id: control
|
||||
|
||||
property int colorScheme: hifi.colorSchemes.light
|
||||
property string glyph: ""
|
||||
property int size: 32
|
||||
//colors
|
||||
readonly property color normalColor: "#AFAFAF"
|
||||
readonly property color hoverColor: "#00B4EF"
|
||||
readonly property color clickedColor: "#FFFFFF"
|
||||
readonly property color disabledColor: "#575757"
|
||||
|
||||
style: ButtonStyle {
|
||||
background: Item {}
|
||||
|
||||
|
||||
label: HiFiGlyphs {
|
||||
color: control.enabled ? (control.pressed ? control.clickedColor :
|
||||
(control.hovered ? control.hoverColor : control.normalColor)) :
|
||||
control.disabledColor
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
anchors {
|
||||
// Tweak horizontal alignment so that it looks right.
|
||||
left: parent.left
|
||||
leftMargin: -0.5
|
||||
}
|
||||
text: control.glyph
|
||||
size: control.size
|
||||
}
|
||||
}
|
||||
}
|
173
interface/resources/qml/hifi/SkyboxChanger.qml
Normal file
173
interface/resources/qml/hifi/SkyboxChanger.qml
Normal file
|
@ -0,0 +1,173 @@
|
|||
//
|
||||
// skyboxchanger.qml
|
||||
//
|
||||
//
|
||||
// Created by Cain Kilgore on 9th August 2017
|
||||
// Copyright 2017 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
import QtQuick.Layouts 1.3
|
||||
|
||||
Rectangle {
|
||||
id: root;
|
||||
|
||||
color: hifi.colors.baseGray;
|
||||
|
||||
Item {
|
||||
id: titleBarContainer;
|
||||
// Size
|
||||
width: parent.width;
|
||||
height: 50;
|
||||
// Anchors
|
||||
anchors.left: parent.left;
|
||||
anchors.top: parent.top;
|
||||
|
||||
RalewaySemiBold {
|
||||
id: titleBarText;
|
||||
text: "Skybox Changer";
|
||||
// Text size
|
||||
size: hifi.fontSizes.overlayTitle;
|
||||
// Anchors
|
||||
anchors.fill: parent;
|
||||
anchors.leftMargin: 16;
|
||||
// Style
|
||||
color: hifi.colors.lightGrayText;
|
||||
// Alignment
|
||||
horizontalAlignment: Text.AlignHCenter;
|
||||
verticalAlignment: Text.AlignVCenter;
|
||||
}
|
||||
RalewaySemiBold {
|
||||
id: titleBarDesc;
|
||||
text: "Click an image to choose a new Skybox.";
|
||||
wrapMode: Text.Wrap
|
||||
// Text size
|
||||
size: 14;
|
||||
// Anchors
|
||||
anchors.fill: parent;
|
||||
anchors.top: titleBarText.bottom
|
||||
anchors.leftMargin: 16;
|
||||
anchors.rightMargin: 16;
|
||||
// Style
|
||||
color: hifi.colors.lightGrayText;
|
||||
// Alignment
|
||||
horizontalAlignment: Text.AlignHCenter;
|
||||
verticalAlignment: Text.AlignVCenter;
|
||||
}
|
||||
}
|
||||
|
||||
// This RowLayout could be a GridLayout instead for further expandability.
|
||||
// As this SkyboxChanger task only required 6 images, implementing GridLayout wasn't necessary.
|
||||
// In the future if this is to be expanded to add more Skyboxes, it might be worth changing this.
|
||||
RowLayout {
|
||||
id: row1
|
||||
anchors.top: titleBarContainer.bottom
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: 30
|
||||
Layout.fillWidth: true
|
||||
anchors.topMargin: 30
|
||||
spacing: 10
|
||||
Image {
|
||||
width: 200; height: 200
|
||||
fillMode: Image.Stretch
|
||||
source: "http://mpassets.highfidelity.com/05904016-8f7d-4dfc-88e1-2bf9ba3fac20-v1/thumbnails/thumb_1.jpg"
|
||||
clip: true
|
||||
id: preview1
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
onClicked: {
|
||||
sendToScript({method: 'changeSkybox', url: 'http://mpassets.highfidelity.com/05904016-8f7d-4dfc-88e1-2bf9ba3fac20-v1/skyboxes/1.jpg'});
|
||||
}
|
||||
}
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
Image {
|
||||
width: 200; height: 200
|
||||
fillMode: Image.Stretch
|
||||
source: "http://mpassets.highfidelity.com/05904016-8f7d-4dfc-88e1-2bf9ba3fac20-v1/thumbnails/thumb_2.jpg"
|
||||
clip: true
|
||||
id: preview2
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
onClicked: {
|
||||
sendToScript({method: 'changeSkybox', url: 'http://mpassets.highfidelity.com/05904016-8f7d-4dfc-88e1-2bf9ba3fac20-v1/skyboxes/2.png'});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
RowLayout {
|
||||
id: row2
|
||||
anchors.top: row1.bottom
|
||||
anchors.topMargin: 10
|
||||
anchors.left: parent.left
|
||||
Layout.fillWidth: true
|
||||
anchors.leftMargin: 30
|
||||
spacing: 10
|
||||
Image {
|
||||
width: 200; height: 200
|
||||
fillMode: Image.Stretch
|
||||
source: "http://mpassets.highfidelity.com/05904016-8f7d-4dfc-88e1-2bf9ba3fac20-v1/thumbnails/thumb_3.jpg"
|
||||
clip: true
|
||||
id: preview3
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
onClicked: {
|
||||
sendToScript({method: 'changeSkybox', url: 'http://mpassets.highfidelity.com/05904016-8f7d-4dfc-88e1-2bf9ba3fac20-v1/skyboxes/3.jpg'});
|
||||
}
|
||||
}
|
||||
}
|
||||
Image {
|
||||
width: 200; height: 200
|
||||
fillMode: Image.Stretch
|
||||
source: "http://mpassets.highfidelity.com/05904016-8f7d-4dfc-88e1-2bf9ba3fac20-v1/thumbnails/thumb_4.jpg"
|
||||
clip: true
|
||||
id: preview4
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
onClicked: {
|
||||
sendToScript({method: 'changeSkybox', url: 'http://mpassets.highfidelity.com/05904016-8f7d-4dfc-88e1-2bf9ba3fac20-v1/skyboxes/4.jpg'});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
RowLayout {
|
||||
id: row3
|
||||
anchors.top: row2.bottom
|
||||
anchors.topMargin: 10
|
||||
anchors.left: parent.left
|
||||
Layout.fillWidth: true
|
||||
anchors.leftMargin: 30
|
||||
spacing: 10
|
||||
Image {
|
||||
width: 200; height: 200
|
||||
fillMode: Image.Stretch
|
||||
source: "http://mpassets.highfidelity.com/05904016-8f7d-4dfc-88e1-2bf9ba3fac20-v1/thumbnails/thumb_5.jpg"
|
||||
clip: true
|
||||
id: preview5
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
onClicked: {
|
||||
sendToScript({method: 'changeSkybox', url: 'http://mpassets.highfidelity.com/05904016-8f7d-4dfc-88e1-2bf9ba3fac20-v1/skyboxes/5.png'});
|
||||
}
|
||||
}
|
||||
}
|
||||
Image {
|
||||
width: 200; height: 200
|
||||
fillMode: Image.Stretch
|
||||
source: "http://mpassets.highfidelity.com/05904016-8f7d-4dfc-88e1-2bf9ba3fac20-v1/thumbnails/thumb_6.jpg"
|
||||
clip: true
|
||||
id: preview6
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
onClicked: {
|
||||
sendToScript({method: 'changeSkybox', url: 'http://mpassets.highfidelity.com/05904016-8f7d-4dfc-88e1-2bf9ba3fac20-v1/skyboxes/6.jpg'});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
signal sendToScript(var message);
|
||||
|
||||
}
|
253
interface/resources/qml/hifi/WebBrowser.qml
Normal file
253
interface/resources/qml/hifi/WebBrowser.qml
Normal file
|
@ -0,0 +1,253 @@
|
|||
//
|
||||
// WebBrowser.qml
|
||||
//
|
||||
//
|
||||
// Created by Vlad Stelmahovsky on 06/22/2017
|
||||
// Copyright 2017 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
import QtQuick 2.5
|
||||
import QtQuick.Controls 1.5 as QQControls
|
||||
import QtQuick.Layouts 1.3
|
||||
import QtQuick.Controls.Styles 1.4
|
||||
|
||||
import QtWebEngine 1.2
|
||||
import QtWebChannel 1.0
|
||||
|
||||
import "../styles-uit"
|
||||
import "../controls-uit" as HifiControls
|
||||
import "../windows"
|
||||
import "../controls"
|
||||
|
||||
Rectangle {
|
||||
id: root;
|
||||
|
||||
HifiConstants { id: hifi; }
|
||||
|
||||
property string title: "";
|
||||
signal sendToScript(var message);
|
||||
property bool keyboardEnabled: true // FIXME - Keyboard HMD only: Default to false
|
||||
property bool keyboardRaised: false
|
||||
property bool punctuationMode: false
|
||||
|
||||
|
||||
color: hifi.colors.baseGray;
|
||||
|
||||
// only show the title if loaded through a "loader"
|
||||
|
||||
Column {
|
||||
spacing: 2
|
||||
width: parent.width;
|
||||
|
||||
RowLayout {
|
||||
width: parent.width;
|
||||
height: 48
|
||||
|
||||
HifiControls.WebGlyphButton {
|
||||
enabled: webEngineView.canGoBack
|
||||
glyph: hifi.glyphs.backward;
|
||||
anchors.verticalCenter: parent.verticalCenter;
|
||||
size: 38;
|
||||
onClicked: {
|
||||
webEngineView.goBack()
|
||||
}
|
||||
}
|
||||
|
||||
HifiControls.WebGlyphButton {
|
||||
enabled: webEngineView.canGoForward
|
||||
glyph: hifi.glyphs.forward;
|
||||
anchors.verticalCenter: parent.verticalCenter;
|
||||
size: 38;
|
||||
onClicked: {
|
||||
webEngineView.goForward()
|
||||
}
|
||||
}
|
||||
|
||||
QQControls.TextField {
|
||||
id: addressBar
|
||||
|
||||
Image {
|
||||
anchors.verticalCenter: addressBar.verticalCenter;
|
||||
x: 5
|
||||
z: 2
|
||||
id: faviconImage
|
||||
width: 16; height: 16
|
||||
sourceSize: Qt.size(width, height)
|
||||
source: webEngineView.icon
|
||||
}
|
||||
|
||||
HifiControls.WebGlyphButton {
|
||||
glyph: webEngineView.loading ? hifi.glyphs.closeSmall : hifi.glyphs.reloadSmall;
|
||||
anchors.verticalCenter: parent.verticalCenter;
|
||||
width: hifi.dimensions.controlLineHeight
|
||||
z: 2
|
||||
x: addressBar.width - 28
|
||||
onClicked: {
|
||||
if (webEngineView.loading) {
|
||||
webEngineView.stop()
|
||||
} else {
|
||||
reloadTimer.start()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
style: TextFieldStyle {
|
||||
padding {
|
||||
left: 26;
|
||||
right: 26
|
||||
}
|
||||
}
|
||||
focus: true
|
||||
Layout.fillWidth: true
|
||||
text: webEngineView.url
|
||||
onAccepted: webEngineView.url = text
|
||||
}
|
||||
HifiControls.WebGlyphButton {
|
||||
checkable: true
|
||||
//only QtWebEngine 1.3
|
||||
//checked: webEngineView.audioMuted
|
||||
glyph: checked ? hifi.glyphs.unmuted : hifi.glyphs.muted
|
||||
anchors.verticalCenter: parent.verticalCenter;
|
||||
width: hifi.dimensions.controlLineHeight
|
||||
onClicked: {
|
||||
webEngineView.triggerWebAction(WebEngineView.ToggleMediaMute)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QQControls.ProgressBar {
|
||||
id: loadProgressBar
|
||||
style: ProgressBarStyle {
|
||||
background: Rectangle {
|
||||
color: "#6A6A6A"
|
||||
}
|
||||
progress: Rectangle{
|
||||
color: "#00B4EF"
|
||||
}
|
||||
}
|
||||
|
||||
width: parent.width;
|
||||
minimumValue: 0
|
||||
maximumValue: 100
|
||||
value: webEngineView.loadProgress
|
||||
height: 2
|
||||
}
|
||||
|
||||
HifiControls.BaseWebView {
|
||||
id: webEngineView
|
||||
focus: true
|
||||
objectName: "tabletWebEngineView"
|
||||
|
||||
url: "http://www.highfidelity.com"
|
||||
property real webViewHeight: root.height - loadProgressBar.height - 48 - 4
|
||||
|
||||
width: parent.width;
|
||||
height: keyboardEnabled && keyboardRaised ? webViewHeight - keyboard.height : webViewHeight
|
||||
|
||||
profile: HFTabletWebEngineProfile;
|
||||
|
||||
property string userScriptUrl: ""
|
||||
|
||||
// creates a global EventBridge object.
|
||||
WebEngineScript {
|
||||
id: createGlobalEventBridge
|
||||
sourceCode: eventBridgeJavaScriptToInject
|
||||
injectionPoint: WebEngineScript.DocumentCreation
|
||||
worldId: WebEngineScript.MainWorld
|
||||
}
|
||||
|
||||
// detects when to raise and lower virtual keyboard
|
||||
WebEngineScript {
|
||||
id: raiseAndLowerKeyboard
|
||||
injectionPoint: WebEngineScript.Deferred
|
||||
sourceUrl: resourceDirectoryUrl + "/html/raiseAndLowerKeyboard.js"
|
||||
worldId: WebEngineScript.MainWorld
|
||||
}
|
||||
|
||||
// User script.
|
||||
WebEngineScript {
|
||||
id: userScript
|
||||
sourceUrl: webEngineView.userScriptUrl
|
||||
injectionPoint: WebEngineScript.DocumentReady // DOM ready but page load may not be finished.
|
||||
worldId: WebEngineScript.MainWorld
|
||||
}
|
||||
|
||||
userScripts: [ createGlobalEventBridge, raiseAndLowerKeyboard, userScript ]
|
||||
|
||||
settings.autoLoadImages: true
|
||||
settings.javascriptEnabled: true
|
||||
settings.errorPageEnabled: true
|
||||
settings.pluginsEnabled: true
|
||||
settings.fullScreenSupportEnabled: false
|
||||
//from WebEngine 1.3
|
||||
// settings.autoLoadIconsForPage: false
|
||||
// settings.touchIconsEnabled: false
|
||||
|
||||
onCertificateError: {
|
||||
error.defer();
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
webChannel.registerObject("eventBridge", eventBridge);
|
||||
webChannel.registerObject("eventBridgeWrapper", eventBridgeWrapper);
|
||||
webEngineView.profile.httpUserAgent = "Mozilla/5.0 Chrome (HighFidelityInterface)";
|
||||
}
|
||||
|
||||
onFeaturePermissionRequested: {
|
||||
grantFeaturePermission(securityOrigin, feature, true);
|
||||
}
|
||||
|
||||
onNewViewRequested: {
|
||||
if (!request.userInitiated) {
|
||||
print("Warning: Blocked a popup window.");
|
||||
}
|
||||
}
|
||||
|
||||
onRenderProcessTerminated: {
|
||||
var status = "";
|
||||
switch (terminationStatus) {
|
||||
case WebEngineView.NormalTerminationStatus:
|
||||
status = "(normal exit)";
|
||||
break;
|
||||
case WebEngineView.AbnormalTerminationStatus:
|
||||
status = "(abnormal exit)";
|
||||
break;
|
||||
case WebEngineView.CrashedTerminationStatus:
|
||||
status = "(crashed)";
|
||||
break;
|
||||
case WebEngineView.KilledTerminationStatus:
|
||||
status = "(killed)";
|
||||
break;
|
||||
}
|
||||
|
||||
print("Render process exited with code " + exitCode + " " + status);
|
||||
reloadTimer.running = true;
|
||||
}
|
||||
|
||||
onWindowCloseRequested: {
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: reloadTimer
|
||||
interval: 0
|
||||
running: false
|
||||
repeat: false
|
||||
onTriggered: webEngineView.reload()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
HifiControls.Keyboard {
|
||||
id: keyboard
|
||||
raised: parent.keyboardEnabled && parent.keyboardRaised
|
||||
numeric: parent.punctuationMode
|
||||
anchors {
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
bottom: parent.bottom
|
||||
}
|
||||
}
|
||||
}
|
|
@ -66,6 +66,14 @@ Rectangle {
|
|||
}
|
||||
}
|
||||
}
|
||||
onSecurityImageResult: {
|
||||
securityImage.source = securityImageSelection.getImagePathFromImageID(imageID);
|
||||
}
|
||||
}
|
||||
|
||||
SecurityImageSelection {
|
||||
id: securityImageSelection;
|
||||
referrerURL: checkoutRoot.itemHref;
|
||||
}
|
||||
|
||||
//
|
||||
|
@ -80,6 +88,20 @@ Rectangle {
|
|||
anchors.left: parent.left;
|
||||
anchors.top: parent.top;
|
||||
|
||||
// Security Image
|
||||
Image {
|
||||
id: securityImage;
|
||||
// Anchors
|
||||
anchors.top: parent.top;
|
||||
anchors.left: parent.left;
|
||||
anchors.leftMargin: 16;
|
||||
height: parent.height - 5;
|
||||
width: height;
|
||||
anchors.verticalCenter: parent.verticalCenter;
|
||||
fillMode: Image.PreserveAspectFit;
|
||||
mipmap: true;
|
||||
}
|
||||
|
||||
// Title Bar text
|
||||
RalewaySemiBold {
|
||||
id: titleBarText;
|
||||
|
@ -87,8 +109,11 @@ Rectangle {
|
|||
// Text size
|
||||
size: hifi.fontSizes.overlayTitle;
|
||||
// Anchors
|
||||
anchors.fill: parent;
|
||||
anchors.top: parent.top;
|
||||
anchors.left: securityImage.right;
|
||||
anchors.leftMargin: 16;
|
||||
anchors.bottom: parent.bottom;
|
||||
width: paintedWidth;
|
||||
// Style
|
||||
color: hifi.colors.lightGrayText;
|
||||
// Alignment
|
||||
|
@ -381,7 +406,7 @@ Rectangle {
|
|||
// "Buy" button
|
||||
HifiControlsUit.Button {
|
||||
id: buyButton;
|
||||
enabled: balanceAfterPurchase >= 0 && !alreadyOwned && inventoryReceived && balanceReceived;
|
||||
enabled: balanceAfterPurchase >= 0 && inventoryReceived && balanceReceived;
|
||||
color: hifi.buttons.black;
|
||||
colorScheme: hifi.colorSchemes.dark;
|
||||
anchors.top: parent.top;
|
||||
|
@ -391,9 +416,16 @@ Rectangle {
|
|||
anchors.right: parent.right;
|
||||
anchors.rightMargin: 20;
|
||||
width: parent.width/2 - anchors.rightMargin*2;
|
||||
text: (inventoryReceived && balanceReceived) ? (alreadyOwned ? "Already Owned" : "Buy") : "--";
|
||||
text: (inventoryReceived && balanceReceived) ? (alreadyOwned ? "Already Owned: Get Item" : "Buy") : "--";
|
||||
onClicked: {
|
||||
commerce.buy(itemId, parseInt(itemPriceText.text));
|
||||
if (!alreadyOwned) {
|
||||
commerce.buy(itemId, parseInt(itemPriceText.text));
|
||||
} else {
|
||||
if (urlHandler.canHandleUrl(itemHref)) {
|
||||
urlHandler.handleUrl(itemHref);
|
||||
}
|
||||
sendToScript({method: 'checkout_buySuccess', itemId: itemId});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -427,6 +459,7 @@ Rectangle {
|
|||
itemHref = message.params.itemHref;
|
||||
commerce.balance();
|
||||
commerce.inventory();
|
||||
commerce.getSecurityImage();
|
||||
break;
|
||||
default:
|
||||
console.log('Unrecognized message from marketplaces.js:', JSON.stringify(message));
|
||||
|
|
|
@ -43,6 +43,14 @@ Rectangle {
|
|||
inventoryContentsList.model = inventory.assets;
|
||||
}
|
||||
}
|
||||
onSecurityImageResult: {
|
||||
securityImage.source = securityImageSelection.getImagePathFromImageID(imageID);
|
||||
}
|
||||
}
|
||||
|
||||
SecurityImageSelection {
|
||||
id: securityImageSelection;
|
||||
referrerURL: inventoryRoot.referrerURL;
|
||||
}
|
||||
|
||||
//
|
||||
|
@ -51,12 +59,26 @@ Rectangle {
|
|||
Item {
|
||||
id: titleBarContainer;
|
||||
// Size
|
||||
width: inventoryRoot.width;
|
||||
width: parent.width;
|
||||
height: 50;
|
||||
// Anchors
|
||||
anchors.left: parent.left;
|
||||
anchors.top: parent.top;
|
||||
|
||||
// Security Image
|
||||
Image {
|
||||
id: securityImage;
|
||||
// Anchors
|
||||
anchors.top: parent.top;
|
||||
anchors.left: parent.left;
|
||||
anchors.leftMargin: 16;
|
||||
height: parent.height - 5;
|
||||
width: height;
|
||||
anchors.verticalCenter: parent.verticalCenter;
|
||||
fillMode: Image.PreserveAspectFit;
|
||||
mipmap: true;
|
||||
}
|
||||
|
||||
// Title Bar text
|
||||
RalewaySemiBold {
|
||||
id: titleBarText;
|
||||
|
@ -64,8 +86,11 @@ Rectangle {
|
|||
// Text size
|
||||
size: hifi.fontSizes.overlayTitle;
|
||||
// Anchors
|
||||
anchors.fill: parent;
|
||||
anchors.top: parent.top;
|
||||
anchors.left: securityImage.right;
|
||||
anchors.leftMargin: 16;
|
||||
anchors.bottom: parent.bottom;
|
||||
width: paintedWidth;
|
||||
// Style
|
||||
color: hifi.colors.lightGrayText;
|
||||
// Alignment
|
||||
|
@ -73,6 +98,25 @@ Rectangle {
|
|||
verticalAlignment: Text.AlignVCenter;
|
||||
}
|
||||
|
||||
// "Change Security Image" button
|
||||
HifiControlsUit.Button {
|
||||
id: changeSecurityImageButton;
|
||||
color: hifi.buttons.black;
|
||||
colorScheme: hifi.colorSchemes.dark;
|
||||
anchors.top: parent.top;
|
||||
anchors.topMargin: 3;
|
||||
anchors.bottom: parent.bottom;
|
||||
anchors.bottomMargin: 3;
|
||||
anchors.right: parent.right;
|
||||
anchors.rightMargin: 20;
|
||||
width: 200;
|
||||
text: "Change Security Image"
|
||||
onClicked: {
|
||||
securityImageSelection.isManuallyChangingSecurityImage = true;
|
||||
securityImageSelection.visible = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Separator
|
||||
HifiControlsUit.Separator {
|
||||
anchors.left: parent.left;
|
||||
|
@ -166,6 +210,7 @@ Rectangle {
|
|||
}
|
||||
ListView {
|
||||
id: inventoryContentsList;
|
||||
clip: true;
|
||||
// Anchors
|
||||
anchors.top: inventoryContentsLabel.bottom;
|
||||
anchors.topMargin: 8;
|
||||
|
@ -262,6 +307,7 @@ Rectangle {
|
|||
referrerURL = message.referrerURL;
|
||||
commerce.balance();
|
||||
commerce.inventory();
|
||||
commerce.getSecurityImage();
|
||||
break;
|
||||
default:
|
||||
console.log('Unrecognized message from marketplaces.js:', JSON.stringify(message));
|
||||
|
|
42
interface/resources/qml/hifi/commerce/SecurityImageModel.qml
Normal file
42
interface/resources/qml/hifi/commerce/SecurityImageModel.qml
Normal file
|
@ -0,0 +1,42 @@
|
|||
//
|
||||
// SecurityImageModel.qml
|
||||
// qml/hifi/commerce
|
||||
//
|
||||
// SecurityImageModel
|
||||
//
|
||||
// Created by Zach Fox on 2017-08-15
|
||||
// Copyright 2017 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
import QtQuick 2.5
|
||||
|
||||
ListModel {
|
||||
id: root;
|
||||
ListElement{
|
||||
sourcePath: "images/01cat.jpg"
|
||||
securityImageEnumValue: 1;
|
||||
}
|
||||
ListElement{
|
||||
sourcePath: "images/02car.jpg"
|
||||
securityImageEnumValue: 2;
|
||||
}
|
||||
ListElement{
|
||||
sourcePath: "images/03dog.jpg"
|
||||
securityImageEnumValue: 3;
|
||||
}
|
||||
ListElement{
|
||||
sourcePath: "images/04stars.jpg"
|
||||
securityImageEnumValue: 4;
|
||||
}
|
||||
ListElement{
|
||||
sourcePath: "images/05plane.jpg"
|
||||
securityImageEnumValue: 5;
|
||||
}
|
||||
ListElement{
|
||||
sourcePath: "images/06gingerbread.jpg"
|
||||
securityImageEnumValue: 6;
|
||||
}
|
||||
}
|
271
interface/resources/qml/hifi/commerce/SecurityImageSelection.qml
Normal file
271
interface/resources/qml/hifi/commerce/SecurityImageSelection.qml
Normal file
|
@ -0,0 +1,271 @@
|
|||
//
|
||||
// SecurityImageSelection.qml
|
||||
// qml/hifi/commerce
|
||||
//
|
||||
// SecurityImageSelection
|
||||
//
|
||||
// Created by Zach Fox on 2017-08-15
|
||||
// Copyright 2017 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
import Hifi 1.0 as Hifi
|
||||
import QtQuick 2.5
|
||||
import QtQuick.Controls 1.4
|
||||
import "../../styles-uit"
|
||||
import "../../controls-uit" as HifiControlsUit
|
||||
import "../../controls" as HifiControls
|
||||
|
||||
// references XXX from root context
|
||||
|
||||
Rectangle {
|
||||
HifiConstants { id: hifi; }
|
||||
|
||||
id: securityImageSelectionRoot;
|
||||
property string referrerURL: "";
|
||||
property bool isManuallyChangingSecurityImage: false;
|
||||
anchors.fill: parent;
|
||||
// Style
|
||||
color: hifi.colors.baseGray;
|
||||
z:999; // On top of everything else
|
||||
visible: false;
|
||||
|
||||
Hifi.QmlCommerce {
|
||||
id: commerce;
|
||||
onSecurityImageResult: {
|
||||
if (!isManuallyChangingSecurityImage) {
|
||||
securityImageSelectionRoot.visible = (imageID == 0);
|
||||
}
|
||||
if (imageID > 0) {
|
||||
for (var itr = 0; itr < gridModel.count; itr++) {
|
||||
var thisValue = gridModel.get(itr).securityImageEnumValue;
|
||||
if (thisValue === imageID) {
|
||||
securityImageGrid.currentIndex = itr;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
commerce.getSecurityImage();
|
||||
}
|
||||
|
||||
//
|
||||
// TITLE BAR START
|
||||
//
|
||||
Item {
|
||||
id: titleBarContainer;
|
||||
// Size
|
||||
width: securityImageSelectionRoot.width;
|
||||
height: 30;
|
||||
// Anchors
|
||||
anchors.left: parent.left;
|
||||
anchors.top: parent.top;
|
||||
|
||||
// Title Bar text
|
||||
RalewaySemiBold {
|
||||
id: titleBarText;
|
||||
text: "Select a Security Image";
|
||||
// Text size
|
||||
size: hifi.fontSizes.overlayTitle;
|
||||
// Anchors
|
||||
anchors.fill: parent;
|
||||
anchors.leftMargin: 16;
|
||||
// Style
|
||||
color: hifi.colors.lightGrayText;
|
||||
// Alignment
|
||||
horizontalAlignment: Text.AlignHLeft;
|
||||
verticalAlignment: Text.AlignVCenter;
|
||||
}
|
||||
|
||||
// Separator
|
||||
HifiControlsUit.Separator {
|
||||
anchors.left: parent.left;
|
||||
anchors.right: parent.right;
|
||||
anchors.bottom: parent.bottom;
|
||||
}
|
||||
}
|
||||
//
|
||||
// TITLE BAR END
|
||||
//
|
||||
|
||||
//
|
||||
// EXPLANATION START
|
||||
//
|
||||
Item {
|
||||
id: explanationContainer;
|
||||
// Size
|
||||
width: securityImageSelectionRoot.width;
|
||||
height: 85;
|
||||
// Anchors
|
||||
anchors.top: titleBarContainer.bottom;
|
||||
anchors.left: parent.left;
|
||||
anchors.right: parent.right;
|
||||
|
||||
RalewayRegular {
|
||||
id: explanationText;
|
||||
text: "This image will be displayed on secure Inventory and Marketplace Checkout dialogs.<b><br>If you don't see your selected image on these dialogs, do not use them!</b>";
|
||||
// Text size
|
||||
size: 16;
|
||||
// Anchors
|
||||
anchors.top: parent.top;
|
||||
anchors.topMargin: 4;
|
||||
anchors.left: parent.left;
|
||||
anchors.leftMargin: 16;
|
||||
anchors.right: parent.right;
|
||||
anchors.rightMargin: 16;
|
||||
// Style
|
||||
color: hifi.colors.lightGrayText;
|
||||
wrapMode: Text.WordWrap;
|
||||
// Alignment
|
||||
horizontalAlignment: Text.AlignHLeft;
|
||||
verticalAlignment: Text.AlignVCenter;
|
||||
}
|
||||
|
||||
// Separator
|
||||
HifiControlsUit.Separator {
|
||||
anchors.left: parent.left;
|
||||
anchors.right: parent.right;
|
||||
anchors.bottom: parent.bottom;
|
||||
}
|
||||
}
|
||||
//
|
||||
// EXPLANATION END
|
||||
//
|
||||
|
||||
//
|
||||
// SECURITY IMAGE GRID START
|
||||
//
|
||||
Item {
|
||||
id: securityImageGridContainer;
|
||||
// Anchors
|
||||
anchors.left: parent.left;
|
||||
anchors.leftMargin: 8;
|
||||
anchors.right: parent.right;
|
||||
anchors.rightMargin: 8;
|
||||
anchors.top: explanationContainer.bottom;
|
||||
anchors.topMargin: 8;
|
||||
anchors.bottom: actionButtonsContainer.top;
|
||||
anchors.bottomMargin: 8;
|
||||
|
||||
SecurityImageModel {
|
||||
id: gridModel;
|
||||
}
|
||||
|
||||
GridView {
|
||||
id: securityImageGrid;
|
||||
clip: true;
|
||||
// Anchors
|
||||
anchors.fill: parent;
|
||||
currentIndex: -1;
|
||||
cellWidth: width / 2;
|
||||
cellHeight: height / 3;
|
||||
model: gridModel;
|
||||
delegate: Item {
|
||||
width: securityImageGrid.cellWidth;
|
||||
height: securityImageGrid.cellHeight;
|
||||
Item {
|
||||
anchors.fill: parent;
|
||||
Image {
|
||||
width: parent.width - 8;
|
||||
height: parent.height - 8;
|
||||
source: sourcePath;
|
||||
anchors.horizontalCenter: parent.horizontalCenter;
|
||||
anchors.verticalCenter: parent.verticalCenter;
|
||||
fillMode: Image.PreserveAspectFit;
|
||||
mipmap: true;
|
||||
}
|
||||
}
|
||||
MouseArea {
|
||||
anchors.fill: parent;
|
||||
onClicked: {
|
||||
securityImageGrid.currentIndex = index;
|
||||
}
|
||||
}
|
||||
}
|
||||
highlight: Rectangle {
|
||||
width: securityImageGrid.cellWidth;
|
||||
height: securityImageGrid.cellHeight;
|
||||
color: hifi.colors.blueHighlight;
|
||||
}
|
||||
}
|
||||
}
|
||||
//
|
||||
// SECURITY IMAGE GRID END
|
||||
//
|
||||
|
||||
|
||||
//
|
||||
// ACTION BUTTONS START
|
||||
//
|
||||
Item {
|
||||
id: actionButtonsContainer;
|
||||
// Size
|
||||
width: securityImageSelectionRoot.width;
|
||||
height: 40;
|
||||
// Anchors
|
||||
anchors.left: parent.left;
|
||||
anchors.bottom: parent.bottom;
|
||||
anchors.bottomMargin: 8;
|
||||
|
||||
// "Cancel" button
|
||||
HifiControlsUit.Button {
|
||||
id: cancelButton;
|
||||
color: hifi.buttons.black;
|
||||
colorScheme: hifi.colorSchemes.dark;
|
||||
anchors.top: parent.top;
|
||||
anchors.topMargin: 3;
|
||||
anchors.bottom: parent.bottom;
|
||||
anchors.bottomMargin: 3;
|
||||
anchors.left: parent.left;
|
||||
anchors.leftMargin: 20;
|
||||
width: parent.width/2 - anchors.leftMargin*2;
|
||||
text: "Cancel"
|
||||
onClicked: {
|
||||
if (!securityImageSelectionRoot.isManuallyChangingSecurityImage) {
|
||||
sendToScript({method: 'securityImageSelection_cancelClicked', referrerURL: securityImageSelectionRoot.referrerURL});
|
||||
} else {
|
||||
securityImageSelectionRoot.visible = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// "Confirm" button
|
||||
HifiControlsUit.Button {
|
||||
id: confirmButton;
|
||||
color: hifi.buttons.black;
|
||||
colorScheme: hifi.colorSchemes.dark;
|
||||
anchors.top: parent.top;
|
||||
anchors.topMargin: 3;
|
||||
anchors.bottom: parent.bottom;
|
||||
anchors.bottomMargin: 3;
|
||||
anchors.right: parent.right;
|
||||
anchors.rightMargin: 20;
|
||||
width: parent.width/2 - anchors.rightMargin*2;
|
||||
text: "Confirm";
|
||||
onClicked: {
|
||||
securityImageSelectionRoot.isManuallyChangingSecurityImage = false;
|
||||
commerce.chooseSecurityImage(gridModel.get(securityImageGrid.currentIndex).securityImageEnumValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
//
|
||||
// ACTION BUTTONS END
|
||||
//
|
||||
|
||||
//
|
||||
// FUNCTION DEFINITIONS START
|
||||
//
|
||||
signal sendToScript(var message);
|
||||
|
||||
function getImagePathFromImageID(imageID) {
|
||||
return (imageID ? gridModel.get(imageID - 1).sourcePath : "");
|
||||
}
|
||||
//
|
||||
// FUNCTION DEFINITIONS END
|
||||
//
|
||||
}
|
BIN
interface/resources/qml/hifi/commerce/images/01cat.jpg
Normal file
BIN
interface/resources/qml/hifi/commerce/images/01cat.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 62 KiB |
BIN
interface/resources/qml/hifi/commerce/images/02car.jpg
Normal file
BIN
interface/resources/qml/hifi/commerce/images/02car.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 99 KiB |
BIN
interface/resources/qml/hifi/commerce/images/03dog.jpg
Normal file
BIN
interface/resources/qml/hifi/commerce/images/03dog.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 113 KiB |
BIN
interface/resources/qml/hifi/commerce/images/04stars.jpg
Normal file
BIN
interface/resources/qml/hifi/commerce/images/04stars.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 86 KiB |
BIN
interface/resources/qml/hifi/commerce/images/05plane.jpg
Normal file
BIN
interface/resources/qml/hifi/commerce/images/05plane.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 61 KiB |
BIN
interface/resources/qml/hifi/commerce/images/06gingerbread.jpg
Normal file
BIN
interface/resources/qml/hifi/commerce/images/06gingerbread.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 74 KiB |
|
@ -225,6 +225,7 @@ static const int MIN_PROCESSING_THREAD_POOL_SIZE = 1;
|
|||
static const QString SNAPSHOT_EXTENSION = ".jpg";
|
||||
static const QString SVO_EXTENSION = ".svo";
|
||||
static const QString SVO_JSON_EXTENSION = ".svo.json";
|
||||
static const QString JSON_GZ_EXTENSION = ".json.gz";
|
||||
static const QString JSON_EXTENSION = ".json";
|
||||
static const QString JS_EXTENSION = ".js";
|
||||
static const QString FST_EXTENSION = ".fst";
|
||||
|
@ -258,6 +259,8 @@ static const QString DESKTOP_DISPLAY_PLUGIN_NAME = "Desktop";
|
|||
|
||||
static const QString SYSTEM_TABLET = "com.highfidelity.interface.tablet.system";
|
||||
|
||||
static const QString DOMAIN_SPAWNING_POINT = "/0, -10, 0";
|
||||
|
||||
const QHash<QString, Application::AcceptURLMethod> Application::_acceptedExtensions {
|
||||
{ SVO_EXTENSION, &Application::importSVOFromURL },
|
||||
{ SVO_JSON_EXTENSION, &Application::importSVOFromURL },
|
||||
|
@ -265,6 +268,7 @@ const QHash<QString, Application::AcceptURLMethod> Application::_acceptedExtensi
|
|||
{ JSON_EXTENSION, &Application::importJSONFromURL },
|
||||
{ JS_EXTENSION, &Application::askToLoadScript },
|
||||
{ FST_EXTENSION, &Application::askToSetAvatarUrl },
|
||||
{ JSON_GZ_EXTENSION, &Application::askToReplaceDomainContent },
|
||||
{ ZIP_EXTENSION, &Application::importFromZIP }
|
||||
};
|
||||
|
||||
|
@ -2811,7 +2815,6 @@ void Application::handleSandboxStatus(QNetworkReply* reply) {
|
|||
bool Application::importJSONFromURL(const QString& urlString) {
|
||||
// we only load files that terminate in just .json (not .svo.json and not .ava.json)
|
||||
// if they come from the High Fidelity Marketplace Assets CDN
|
||||
|
||||
QUrl jsonURL { urlString };
|
||||
|
||||
if (jsonURL.host().endsWith(MARKETPLACE_CDN_HOSTNAME)) {
|
||||
|
@ -6193,6 +6196,55 @@ bool Application::askToWearAvatarAttachmentUrl(const QString& url) {
|
|||
return true;
|
||||
}
|
||||
|
||||
bool Application::askToReplaceDomainContent(const QString& url) {
|
||||
QString methodDetails;
|
||||
if (DependencyManager::get<NodeList>()->getThisNodeCanReplaceContent()) {
|
||||
QUrl originURL { url };
|
||||
if (originURL.host().endsWith(MARKETPLACE_CDN_HOSTNAME)) {
|
||||
// Create a confirmation dialog when this call is made
|
||||
const int MAX_CHARACTERS_PER_LINE = 90;
|
||||
static const QString infoText = simpleWordWrap("Your domain's content will be replaced with a new content set. "
|
||||
"If you want to save what you have now, create a backup before proceeding. For more information about backing up "
|
||||
"and restoring content, visit the documentation page at: ", MAX_CHARACTERS_PER_LINE) +
|
||||
"\nhttps://docs.highfidelity.com/create-and-explore/start-working-in-your-sandbox/restoring-sandbox-content";
|
||||
|
||||
bool agreeToReplaceContent = false; // assume false
|
||||
agreeToReplaceContent = QMessageBox::Yes == OffscreenUi::question("Are you sure you want to replace this domain's content set?",
|
||||
infoText, QMessageBox::Yes | QMessageBox::No, QMessageBox::No);
|
||||
|
||||
if (agreeToReplaceContent) {
|
||||
// Given confirmation, send request to domain server to replace content
|
||||
qCDebug(interfaceapp) << "Attempting to replace domain content: " << url;
|
||||
QByteArray urlData(url.toUtf8());
|
||||
auto limitedNodeList = DependencyManager::get<LimitedNodeList>();
|
||||
limitedNodeList->eachMatchingNode([](const SharedNodePointer& node) {
|
||||
return node->getType() == NodeType::EntityServer && node->getActiveSocket();
|
||||
}, [&urlData, limitedNodeList](const SharedNodePointer& octreeNode) {
|
||||
auto octreeFilePacket = NLPacket::create(PacketType::OctreeFileReplacementFromUrl, urlData.size(), true);
|
||||
octreeFilePacket->write(urlData);
|
||||
limitedNodeList->sendPacket(std::move(octreeFilePacket), *octreeNode);
|
||||
});
|
||||
DependencyManager::get<AddressManager>()->handleLookupString(DOMAIN_SPAWNING_POINT);
|
||||
methodDetails = "SuccessfulRequestToReplaceContent";
|
||||
} else {
|
||||
methodDetails = "UserDeclinedToReplaceContent";
|
||||
}
|
||||
} else {
|
||||
methodDetails = "ContentSetDidNotOriginateFromMarketplace";
|
||||
}
|
||||
} else {
|
||||
methodDetails = "UserDoesNotHavePermissionToReplaceContent";
|
||||
OffscreenUi::warning("Unable to replace content", "You do not have permissions to replace domain content",
|
||||
QMessageBox::Ok, QMessageBox::Ok);
|
||||
}
|
||||
QJsonObject messageProperties = {
|
||||
{ "status", methodDetails },
|
||||
{ "content_set_url", url }
|
||||
};
|
||||
UserActivityLogger::getInstance().logAction("replace_domain_content", messageProperties);
|
||||
return true;
|
||||
}
|
||||
|
||||
void Application::displayAvatarAttachmentWarning(const QString& message) const {
|
||||
auto avatarAttachmentWarningTitle = tr("Avatar Attachment Failure");
|
||||
OffscreenUi::warning(avatarAttachmentWarningTitle, message);
|
||||
|
|
|
@ -431,6 +431,8 @@ private slots:
|
|||
void displayAvatarAttachmentWarning(const QString& message) const;
|
||||
bool displayAvatarAttachmentConfirmationDialog(const QString& name) const;
|
||||
|
||||
bool askToReplaceDomainContent(const QString& url);
|
||||
|
||||
void setSessionUUID(const QUuid& sessionUUID) const;
|
||||
|
||||
void domainChanged(const QString& domainHostname);
|
||||
|
|
|
@ -555,6 +555,8 @@ Menu::Menu() {
|
|||
avatar.get(), SLOT(setEnableDebugDrawIKConstraints(bool)));
|
||||
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::RenderIKChains, 0, false,
|
||||
avatar.get(), SLOT(setEnableDebugDrawIKChains(bool)));
|
||||
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::RenderDetailedCollision, 0, false,
|
||||
avatar.get(), SLOT(setEnableDebugDrawDetailedCollision(bool)));
|
||||
|
||||
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::ActionMotorControl,
|
||||
Qt::CTRL | Qt::SHIFT | Qt::Key_K, true, avatar.get(), SLOT(updateMotionBehaviorFromMenu()),
|
||||
|
|
|
@ -162,6 +162,7 @@ namespace MenuOption {
|
|||
const QString RenderIKTargets = "Show IK Targets";
|
||||
const QString RenderIKConstraints = "Show IK Constraints";
|
||||
const QString RenderIKChains = "Show IK Chains";
|
||||
const QString RenderDetailedCollision = "Show Detailed Collision";
|
||||
const QString ResetAvatarSize = "Reset Avatar Size";
|
||||
const QString ResetSensors = "Reset Sensors";
|
||||
const QString RunningScripts = "Running Scripts...";
|
||||
|
|
|
@ -1060,6 +1060,10 @@ void MyAvatar::setEnableDebugDrawIKConstraints(bool isEnabled) {
|
|||
_enableDebugDrawIKConstraints = isEnabled;
|
||||
}
|
||||
|
||||
void MyAvatar::setEnableDebugDrawDetailedCollision(bool isEnabled) {
|
||||
_enableDebugDrawDetailedCollision = isEnabled;
|
||||
}
|
||||
|
||||
void MyAvatar::setEnableDebugDrawIKChains(bool isEnabled) {
|
||||
_enableDebugDrawIKChains = isEnabled;
|
||||
}
|
||||
|
@ -1805,6 +1809,37 @@ void MyAvatar::postUpdate(float deltaTime) {
|
|||
AnimPose postUpdateRoomPose(_sensorToWorldMatrix);
|
||||
|
||||
updateHoldActions(_prePhysicsRoomPose, postUpdateRoomPose);
|
||||
|
||||
if (_enableDebugDrawDetailedCollision) {
|
||||
AnimPose rigToWorldPose(glm::vec3(1.0f), getRotation() * Quaternions::Y_180, getPosition());
|
||||
const int NUM_DEBUG_COLORS = 8;
|
||||
const glm::vec4 DEBUG_COLORS[NUM_DEBUG_COLORS] = {
|
||||
glm::vec4(1.0f, 1.0f, 1.0f, 1.0f),
|
||||
glm::vec4(1.0f, 0.0f, 0.0f, 1.0f),
|
||||
glm::vec4(0.0f, 1.0f, 0.0f, 1.0f),
|
||||
glm::vec4(0.25f, 0.25f, 1.0f, 1.0f),
|
||||
glm::vec4(1.0f, 1.0f, 0.0f, 1.0f),
|
||||
glm::vec4(0.25f, 1.0f, 1.0f, 1.0f),
|
||||
glm::vec4(1.0f, 0.25f, 1.0f, 1.0f),
|
||||
glm::vec4(1.0f, 0.65f, 0.0f, 1.0f) // Orange you glad I added this color?
|
||||
};
|
||||
|
||||
if (_skeletonModel && _skeletonModel->isLoaded()) {
|
||||
const Rig& rig = _skeletonModel->getRig();
|
||||
const FBXGeometry& geometry = _skeletonModel->getFBXGeometry();
|
||||
for (int i = 0; i < rig.getJointStateCount(); i++) {
|
||||
AnimPose jointPose;
|
||||
rig.getAbsoluteJointPoseInRigFrame(i, jointPose);
|
||||
const FBXJointShapeInfo& shapeInfo = geometry.joints[i].shapeInfo;
|
||||
const AnimPose pose = rigToWorldPose * jointPose;
|
||||
for (size_t j = 0; j < shapeInfo.debugLines.size() / 2; j++) {
|
||||
glm::vec3 pointA = pose.xformPoint(shapeInfo.debugLines[2 * j]);
|
||||
glm::vec3 pointB = pose.xformPoint(shapeInfo.debugLines[2 * j + 1]);
|
||||
DebugDraw::getInstance().drawRay(pointA, pointB, DEBUG_COLORS[i % NUM_DEBUG_COLORS]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MyAvatar::preDisplaySide(RenderArgs* renderArgs) {
|
||||
|
|
|
@ -553,6 +553,7 @@ public slots:
|
|||
void setEnableDebugDrawIKTargets(bool isEnabled);
|
||||
void setEnableDebugDrawIKConstraints(bool isEnabled);
|
||||
void setEnableDebugDrawIKChains(bool isEnabled);
|
||||
void setEnableDebugDrawDetailedCollision(bool isEnabled);
|
||||
|
||||
bool getEnableMeshVisible() const { return _skeletonModel->isVisible(); }
|
||||
void setEnableMeshVisible(bool isEnabled);
|
||||
|
@ -757,6 +758,7 @@ private:
|
|||
bool _enableDebugDrawIKTargets { false };
|
||||
bool _enableDebugDrawIKConstraints { false };
|
||||
bool _enableDebugDrawIKChains { false };
|
||||
bool _enableDebugDrawDetailedCollision { false };
|
||||
|
||||
AudioListenerMode _audioListenerMode;
|
||||
glm::vec3 _customListenPosition;
|
||||
|
|
|
@ -124,12 +124,26 @@ void MySkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) {
|
|||
}
|
||||
}
|
||||
|
||||
params.bodyCapsuleRadius = myAvatar->getCharacterController()->getCapsuleRadius();
|
||||
params.bodyCapsuleHalfHeight = myAvatar->getCharacterController()->getCapsuleHalfHeight();
|
||||
params.bodyCapsuleLocalOffset = myAvatar->getCharacterController()->getCapsuleLocalOffset();
|
||||
|
||||
params.isTalking = head->getTimeWithoutTalking() <= 1.5f;
|
||||
|
||||
// pass detailed torso k-dops to rig.
|
||||
int hipsJoint = _rig.indexOfJoint("Hips");
|
||||
if (hipsJoint >= 0) {
|
||||
params.hipsShapeInfo = geometry.joints[hipsJoint].shapeInfo;
|
||||
}
|
||||
int spineJoint = _rig.indexOfJoint("Spine");
|
||||
if (spineJoint >= 0) {
|
||||
params.spineShapeInfo = geometry.joints[spineJoint].shapeInfo;
|
||||
}
|
||||
int spine1Joint = _rig.indexOfJoint("Spine1");
|
||||
if (spine1Joint >= 0) {
|
||||
params.spine1ShapeInfo = geometry.joints[spine1Joint].shapeInfo;
|
||||
}
|
||||
int spine2Joint = _rig.indexOfJoint("Spine2");
|
||||
if (spine2Joint >= 0) {
|
||||
params.spine2ShapeInfo = geometry.joints[spine2Joint].shapeInfo;
|
||||
}
|
||||
|
||||
_rig.updateFromControllerParameters(params, deltaTime);
|
||||
|
||||
Rig::CharacterControllerState ccState = convertCharacterControllerState(myAvatar->getCharacterController()->getState());
|
||||
|
|
|
@ -19,9 +19,11 @@ HIFI_QML_DEF(QmlCommerce)
|
|||
|
||||
QmlCommerce::QmlCommerce(QQuickItem* parent) : OffscreenQmlDialog(parent) {
|
||||
auto ledger = DependencyManager::get<Ledger>();
|
||||
auto wallet = DependencyManager::get<Wallet>();
|
||||
connect(ledger.data(), &Ledger::buyResult, this, &QmlCommerce::buyResult);
|
||||
connect(ledger.data(), &Ledger::balanceResult, this, &QmlCommerce::balanceResult);
|
||||
connect(ledger.data(), &Ledger::inventoryResult, this, &QmlCommerce::inventoryResult);
|
||||
connect(wallet.data(), &Wallet::securityImageResult, this, &QmlCommerce::securityImageResult);
|
||||
}
|
||||
|
||||
void QmlCommerce::buy(const QString& assetId, int cost, const QString& buyerUsername) {
|
||||
|
@ -48,4 +50,13 @@ void QmlCommerce::inventory() {
|
|||
auto ledger = DependencyManager::get<Ledger>();
|
||||
auto wallet = DependencyManager::get<Wallet>();
|
||||
ledger->inventory(wallet->listPublicKeys());
|
||||
}
|
||||
}
|
||||
|
||||
void QmlCommerce::chooseSecurityImage(uint imageID) {
|
||||
auto wallet = DependencyManager::get<Wallet>();
|
||||
wallet->chooseSecurityImage(imageID);
|
||||
}
|
||||
void QmlCommerce::getSecurityImage() {
|
||||
auto wallet = DependencyManager::get<Wallet>();
|
||||
wallet->getSecurityImage();
|
||||
}
|
|
@ -30,11 +30,14 @@ signals:
|
|||
// because we can't scalably know of out-of-band changes (e.g., another machine interacting with the block chain).
|
||||
void balanceResult(int balance, const QString& failureMessage);
|
||||
void inventoryResult(QJsonObject inventory, const QString& failureMessage);
|
||||
void securityImageResult(uint imageID);
|
||||
|
||||
protected:
|
||||
Q_INVOKABLE void buy(const QString& assetId, int cost, const QString& buyerUsername = "");
|
||||
Q_INVOKABLE void balance();
|
||||
Q_INVOKABLE void inventory();
|
||||
Q_INVOKABLE void chooseSecurityImage(uint imageID);
|
||||
Q_INVOKABLE void getSecurityImage();
|
||||
};
|
||||
|
||||
#endif // hifi_QmlCommerce_h
|
||||
|
|
|
@ -266,3 +266,11 @@ QString Wallet::signWithKey(const QByteArray& text, const QString& key) {
|
|||
return QString();
|
||||
}
|
||||
|
||||
|
||||
void Wallet::chooseSecurityImage(uint imageID) {
|
||||
_chosenSecurityImage = (SecurityImage)imageID;
|
||||
emit securityImageResult(imageID);
|
||||
}
|
||||
void Wallet::getSecurityImage() {
|
||||
emit securityImageResult(_chosenSecurityImage);
|
||||
}
|
||||
|
|
|
@ -26,10 +26,29 @@ public:
|
|||
bool generateKeyPair();
|
||||
QStringList listPublicKeys();
|
||||
QString signWithKey(const QByteArray& text, const QString& key);
|
||||
void chooseSecurityImage(uint imageID);
|
||||
void getSecurityImage();
|
||||
|
||||
signals:
|
||||
void securityImageResult(uint imageID);
|
||||
|
||||
protected:
|
||||
// ALWAYS add SecurityImage enum values to the END of the enum.
|
||||
// They must be in the same order as the images are listed in
|
||||
// SecurityImageSelection.qml
|
||||
enum SecurityImage {
|
||||
NONE = 0,
|
||||
Cat,
|
||||
Car,
|
||||
Dog,
|
||||
Stars,
|
||||
Plane,
|
||||
Gingerbread
|
||||
};
|
||||
|
||||
private:
|
||||
QStringList _publicKeys{};
|
||||
|
||||
SecurityImage _chosenSecurityImage = SecurityImage::NONE;
|
||||
};
|
||||
|
||||
#endif // hifi_Wallet_h
|
||||
|
|
|
@ -315,21 +315,21 @@ void setupPreferences() {
|
|||
static const QString RENDER("Graphics");
|
||||
auto renderConfig = qApp->getRenderEngine()->getConfiguration();
|
||||
if (renderConfig) {
|
||||
auto ambientOcclusionConfig = renderConfig->getConfig<AmbientOcclusionEffect>();
|
||||
if (ambientOcclusionConfig) {
|
||||
auto getter = [ambientOcclusionConfig]()->QString { return ambientOcclusionConfig->getPreset(); };
|
||||
auto setter = [ambientOcclusionConfig](QString preset) { ambientOcclusionConfig->setPreset(preset); };
|
||||
auto mainViewAmbientOcclusionConfig = renderConfig->getConfig<AmbientOcclusionEffect>("RenderMainView.AmbientOcclusion");
|
||||
if (mainViewAmbientOcclusionConfig) {
|
||||
auto getter = [mainViewAmbientOcclusionConfig]()->QString { return mainViewAmbientOcclusionConfig->getPreset(); };
|
||||
auto setter = [mainViewAmbientOcclusionConfig](QString preset) { mainViewAmbientOcclusionConfig->setPreset(preset); };
|
||||
auto preference = new ComboBoxPreference(RENDER, "Ambient occlusion", getter, setter);
|
||||
preference->setItems(ambientOcclusionConfig->getPresetList());
|
||||
preference->setItems(mainViewAmbientOcclusionConfig->getPresetList());
|
||||
preferences->addPreference(preference);
|
||||
}
|
||||
|
||||
auto shadowConfig = renderConfig->getConfig<RenderShadowTask>();
|
||||
if (shadowConfig) {
|
||||
auto getter = [shadowConfig]()->QString { return shadowConfig->getPreset(); };
|
||||
auto setter = [shadowConfig](QString preset) { shadowConfig->setPreset(preset); };
|
||||
auto mainViewShadowConfig = renderConfig->getConfig<RenderShadowTask>("RenderMainView.RenderShadowTask");
|
||||
if (mainViewShadowConfig) {
|
||||
auto getter = [mainViewShadowConfig]()->QString { return mainViewShadowConfig->getPreset(); };
|
||||
auto setter = [mainViewShadowConfig](QString preset) { mainViewShadowConfig->setPreset(preset); };
|
||||
auto preference = new ComboBoxPreference(RENDER, "Shadows", getter, setter);
|
||||
preference->setItems(shadowConfig->getPresetList());
|
||||
preference->setItems(mainViewShadowConfig->getPresetList());
|
||||
preferences->addPreference(preference);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -55,6 +55,8 @@ ContextOverlayInterface::ContextOverlayInterface() {
|
|||
_contextOverlayJustClicked = false;
|
||||
}
|
||||
});
|
||||
auto entityScriptingInterface = DependencyManager::get<EntityScriptingInterface>().data();
|
||||
connect(entityScriptingInterface, &EntityScriptingInterface::deletingEntity, this, &ContextOverlayInterface::deletingEntity);
|
||||
}
|
||||
|
||||
static const uint32_t LEFT_HAND_HW_ID = 1;
|
||||
|
@ -278,3 +280,9 @@ void ContextOverlayInterface::disableEntityHighlight(const EntityItemID& entityI
|
|||
}
|
||||
});
|
||||
}
|
||||
|
||||
void ContextOverlayInterface::deletingEntity(const EntityItemID& entityID) {
|
||||
if (_currentEntityWithContextOverlay == entityID) {
|
||||
destroyContextOverlay(_currentEntityWithContextOverlay, PointerEvent());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -80,6 +80,7 @@ private:
|
|||
void enableEntityHighlight(const EntityItemID& entityItemID);
|
||||
void disableEntityHighlight(const EntityItemID& entityItemID);
|
||||
|
||||
void deletingEntity(const EntityItemID& entityItemID);
|
||||
};
|
||||
|
||||
#endif // hifi_ContextOverlayInterface_h
|
||||
|
|
|
@ -32,6 +32,8 @@ Line3DOverlay::Line3DOverlay(const Line3DOverlay* line3DOverlay) :
|
|||
_length = line3DOverlay->getLength();
|
||||
_endParentID = line3DOverlay->getEndParentID();
|
||||
_endParentJointIndex = line3DOverlay->getEndJointIndex();
|
||||
_glow = line3DOverlay->getGlow();
|
||||
_glowWidth = line3DOverlay->getGlowWidth();
|
||||
}
|
||||
|
||||
Line3DOverlay::~Line3DOverlay() {
|
||||
|
@ -138,11 +140,9 @@ void Line3DOverlay::render(RenderArgs* args) {
|
|||
// TODO: add support for color to renderDashedLine()
|
||||
geometryCache->bindSimpleProgram(*batch, false, false, false, true, true);
|
||||
geometryCache->renderDashedLine(*batch, start, end, colorv4, _geometryCacheID);
|
||||
} else if (_glow > 0.0f) {
|
||||
geometryCache->renderGlowLine(*batch, start, end, colorv4, _glow, _glowWidth, _geometryCacheID);
|
||||
} else {
|
||||
geometryCache->bindSimpleProgram(*batch, false, false, false, true, true);
|
||||
geometryCache->renderLine(*batch, start, end, colorv4, _geometryCacheID);
|
||||
// renderGlowLine handles both glow = 0 and glow > 0 cases
|
||||
geometryCache->renderGlowLine(*batch, start, end, colorv4, _glow, _glowWidth, _geometryCacheID);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -228,9 +228,9 @@ void Line3DOverlay::setProperties(const QVariantMap& originalProperties) {
|
|||
}
|
||||
}
|
||||
|
||||
auto glowWidth = properties["glow"];
|
||||
auto glowWidth = properties["glowWidth"];
|
||||
if (glowWidth.isValid()) {
|
||||
setGlow(glowWidth.toFloat());
|
||||
setGlowWidth(glowWidth.toFloat());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -168,6 +168,8 @@ void Rig::destroyAnimGraph() {
|
|||
void Rig::initJointStates(const FBXGeometry& geometry, const glm::mat4& modelOffset) {
|
||||
_geometryOffset = AnimPose(geometry.offset);
|
||||
_invGeometryOffset = _geometryOffset.inverse();
|
||||
_geometryToRigTransform = modelOffset * geometry.offset;
|
||||
_rigToGeometryTransform = glm::inverse(_geometryToRigTransform);
|
||||
setModelOffset(modelOffset);
|
||||
|
||||
_animSkeleton = std::make_shared<AnimSkeleton>(geometry);
|
||||
|
@ -1099,35 +1101,139 @@ void Rig::updateHead(bool headEnabled, bool hipsEnabled, const AnimPose& headPos
|
|||
}
|
||||
}
|
||||
|
||||
void Rig::updateHands(bool leftHandEnabled, bool rightHandEnabled, bool hipsEnabled, bool leftArmEnabled, bool rightArmEnabled, float dt,
|
||||
const AnimPose& leftHandPose, const AnimPose& rightHandPose,
|
||||
float bodyCapsuleRadius, float bodyCapsuleHalfHeight, const glm::vec3& bodyCapsuleLocalOffset) {
|
||||
const float INV_SQRT_3 = 1.0f / sqrtf(3.0f);
|
||||
const int DOP14_COUNT = 14;
|
||||
const glm::vec3 DOP14_NORMALS[DOP14_COUNT] = {
|
||||
Vectors::UNIT_X,
|
||||
-Vectors::UNIT_X,
|
||||
Vectors::UNIT_Y,
|
||||
-Vectors::UNIT_Y,
|
||||
Vectors::UNIT_Z,
|
||||
-Vectors::UNIT_Z,
|
||||
glm::vec3(INV_SQRT_3, INV_SQRT_3, INV_SQRT_3),
|
||||
-glm::vec3(INV_SQRT_3, INV_SQRT_3, INV_SQRT_3),
|
||||
glm::vec3(INV_SQRT_3, -INV_SQRT_3, INV_SQRT_3),
|
||||
-glm::vec3(INV_SQRT_3, -INV_SQRT_3, INV_SQRT_3),
|
||||
glm::vec3(INV_SQRT_3, INV_SQRT_3, -INV_SQRT_3),
|
||||
-glm::vec3(INV_SQRT_3, INV_SQRT_3, -INV_SQRT_3),
|
||||
glm::vec3(INV_SQRT_3, -INV_SQRT_3, -INV_SQRT_3),
|
||||
-glm::vec3(INV_SQRT_3, -INV_SQRT_3, -INV_SQRT_3)
|
||||
};
|
||||
|
||||
// Use this capsule to represent the avatar body.
|
||||
int hipsIndex = indexOfJoint("Hips");
|
||||
glm::vec3 hipsTrans;
|
||||
if (hipsIndex >= 0) {
|
||||
hipsTrans = _internalPoseSet._absolutePoses[hipsIndex].trans();
|
||||
// returns true if the given point lies inside of the k-dop, specified by shapeInfo & shapePose.
|
||||
// if the given point does lie within the k-dop, it also returns the amount of displacement necessary to push that point outward
|
||||
// such that it lies on the surface of the kdop.
|
||||
static bool findPointKDopDisplacement(const glm::vec3& point, const AnimPose& shapePose, const FBXJointShapeInfo& shapeInfo, glm::vec3& displacementOut) {
|
||||
|
||||
// transform point into local space of jointShape.
|
||||
glm::vec3 localPoint = shapePose.inverse().xformPoint(point);
|
||||
|
||||
// Only works for 14-dop shape infos.
|
||||
assert(shapeInfo.dots.size() == DOP14_COUNT);
|
||||
if (shapeInfo.dots.size() != DOP14_COUNT) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const glm::vec3 bodyCapsuleCenter = hipsTrans - bodyCapsuleLocalOffset;
|
||||
const glm::vec3 bodyCapsuleStart = bodyCapsuleCenter - glm::vec3(0, bodyCapsuleHalfHeight, 0);
|
||||
const glm::vec3 bodyCapsuleEnd = bodyCapsuleCenter + glm::vec3(0, bodyCapsuleHalfHeight, 0);
|
||||
glm::vec3 minDisplacement(FLT_MAX);
|
||||
float minDisplacementLen = FLT_MAX;
|
||||
glm::vec3 p = localPoint - shapeInfo.avgPoint;
|
||||
float pLen = glm::length(p);
|
||||
if (pLen > 0.0f) {
|
||||
int slabCount = 0;
|
||||
for (int i = 0; i < DOP14_COUNT; i++) {
|
||||
float dot = glm::dot(p, DOP14_NORMALS[i]);
|
||||
if (dot > 0.0f && dot < shapeInfo.dots[i]) {
|
||||
slabCount++;
|
||||
float distToPlane = pLen * (shapeInfo.dots[i] / dot);
|
||||
float displacementLen = distToPlane - pLen;
|
||||
|
||||
// keep track of the smallest displacement
|
||||
if (displacementLen < minDisplacementLen) {
|
||||
minDisplacementLen = displacementLen;
|
||||
minDisplacement = (p / pLen) * displacementLen;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (slabCount == (DOP14_COUNT / 2) && minDisplacementLen != FLT_MAX) {
|
||||
// we are within the k-dop so push the point along the minimum displacement found
|
||||
displacementOut = shapePose.xformVectorFast(minDisplacement);
|
||||
return true;
|
||||
} else {
|
||||
// point is outside of kdop
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
// point is directly on top of shapeInfo.avgPoint.
|
||||
// push the point out along the x axis.
|
||||
displacementOut = shapePose.xformVectorFast(shapeInfo.points[0]);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
glm::vec3 Rig::deflectHandFromTorso(const glm::vec3& handPosition, const FBXJointShapeInfo& hipsShapeInfo, const FBXJointShapeInfo& spineShapeInfo,
|
||||
const FBXJointShapeInfo& spine1ShapeInfo, const FBXJointShapeInfo& spine2ShapeInfo) const {
|
||||
glm::vec3 position = handPosition;
|
||||
glm::vec3 displacement;
|
||||
int hipsJoint = indexOfJoint("Hips");
|
||||
if (hipsJoint >= 0) {
|
||||
AnimPose hipsPose;
|
||||
if (getAbsoluteJointPoseInRigFrame(hipsJoint, hipsPose)) {
|
||||
if (findPointKDopDisplacement(position, hipsPose, hipsShapeInfo, displacement)) {
|
||||
position += displacement;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int spineJoint = indexOfJoint("Spine");
|
||||
if (spineJoint >= 0) {
|
||||
AnimPose spinePose;
|
||||
if (getAbsoluteJointPoseInRigFrame(spineJoint, spinePose)) {
|
||||
if (findPointKDopDisplacement(position, spinePose, spineShapeInfo, displacement)) {
|
||||
position += displacement;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int spine1Joint = indexOfJoint("Spine1");
|
||||
if (spine1Joint >= 0) {
|
||||
AnimPose spine1Pose;
|
||||
if (getAbsoluteJointPoseInRigFrame(spine1Joint, spine1Pose)) {
|
||||
if (findPointKDopDisplacement(position, spine1Pose, spine1ShapeInfo, displacement)) {
|
||||
position += displacement;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int spine2Joint = indexOfJoint("Spine2");
|
||||
if (spine2Joint >= 0) {
|
||||
AnimPose spine2Pose;
|
||||
if (getAbsoluteJointPoseInRigFrame(spine2Joint, spine2Pose)) {
|
||||
if (findPointKDopDisplacement(position, spine2Pose, spine2ShapeInfo, displacement)) {
|
||||
position += displacement;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return position;
|
||||
}
|
||||
|
||||
void Rig::updateHands(bool leftHandEnabled, bool rightHandEnabled, bool hipsEnabled, bool leftArmEnabled, bool rightArmEnabled, float dt,
|
||||
const AnimPose& leftHandPose, const AnimPose& rightHandPose,
|
||||
const FBXJointShapeInfo& hipsShapeInfo, const FBXJointShapeInfo& spineShapeInfo,
|
||||
const FBXJointShapeInfo& spine1ShapeInfo, const FBXJointShapeInfo& spine2ShapeInfo) {
|
||||
|
||||
const float HAND_RADIUS = 0.05f;
|
||||
const float ELBOW_POLE_VECTOR_BLEND_FACTOR = 0.95f;
|
||||
|
||||
int hipsIndex = indexOfJoint("Hips");
|
||||
|
||||
if (leftHandEnabled) {
|
||||
|
||||
glm::vec3 handPosition = leftHandPose.trans();
|
||||
glm::quat handRotation = leftHandPose.rot();
|
||||
|
||||
if (!hipsEnabled) {
|
||||
// prevent the hand IK targets from intersecting the body capsule
|
||||
glm::vec3 displacement;
|
||||
if (findSphereCapsulePenetration(handPosition, HAND_RADIUS, bodyCapsuleStart, bodyCapsuleEnd, bodyCapsuleRadius, displacement)) {
|
||||
handPosition -= displacement;
|
||||
}
|
||||
// prevent the hand IK targets from intersecting the torso
|
||||
handPosition = deflectHandFromTorso(handPosition, hipsShapeInfo, spineShapeInfo, spine1ShapeInfo, spine2ShapeInfo);
|
||||
}
|
||||
|
||||
_animVars.set("leftHandPosition", handPosition);
|
||||
|
@ -1173,11 +1279,8 @@ void Rig::updateHands(bool leftHandEnabled, bool rightHandEnabled, bool hipsEnab
|
|||
glm::quat handRotation = rightHandPose.rot();
|
||||
|
||||
if (!hipsEnabled) {
|
||||
// prevent the hand IK targets from intersecting the body capsule
|
||||
glm::vec3 displacement;
|
||||
if (findSphereCapsulePenetration(handPosition, HAND_RADIUS, bodyCapsuleStart, bodyCapsuleEnd, bodyCapsuleRadius, displacement)) {
|
||||
handPosition -= displacement;
|
||||
}
|
||||
// prevent the hand IK targets from intersecting the torso
|
||||
handPosition = deflectHandFromTorso(handPosition, hipsShapeInfo, spineShapeInfo, spine1ShapeInfo, spine2ShapeInfo);
|
||||
}
|
||||
|
||||
_animVars.set("rightHandPosition", handPosition);
|
||||
|
@ -1414,7 +1517,7 @@ void Rig::updateFromControllerParameters(const ControllerParameters& params, flo
|
|||
|
||||
updateHands(leftHandEnabled, rightHandEnabled, hipsEnabled, leftArmEnabled, rightArmEnabled, dt,
|
||||
params.primaryControllerPoses[PrimaryControllerType_LeftHand], params.primaryControllerPoses[PrimaryControllerType_RightHand],
|
||||
params.bodyCapsuleRadius, params.bodyCapsuleHalfHeight, params.bodyCapsuleLocalOffset);
|
||||
params.hipsShapeInfo, params.spineShapeInfo, params.spine1ShapeInfo, params.spine2ShapeInfo);
|
||||
|
||||
updateFeet(leftFootEnabled, rightFootEnabled,
|
||||
params.primaryControllerPoses[PrimaryControllerType_LeftFoot], params.primaryControllerPoses[PrimaryControllerType_RightFoot]);
|
||||
|
@ -1660,59 +1763,11 @@ void Rig::computeAvatarBoundingCapsule(
|
|||
return;
|
||||
}
|
||||
|
||||
AnimInverseKinematics ikNode("boundingShape");
|
||||
ikNode.setSkeleton(_animSkeleton);
|
||||
|
||||
ikNode.setTargetVars("LeftHand", "leftHandPosition", "leftHandRotation",
|
||||
"leftHandType", "leftHandWeight", 1.0f, {},
|
||||
QString(), QString(), QString());
|
||||
ikNode.setTargetVars("RightHand", "rightHandPosition", "rightHandRotation",
|
||||
"rightHandType", "rightHandWeight", 1.0f, {},
|
||||
QString(), QString(), QString());
|
||||
ikNode.setTargetVars("LeftFoot", "leftFootPosition", "leftFootRotation",
|
||||
"leftFootType", "leftFootWeight", 1.0f, {},
|
||||
QString(), QString(), QString());
|
||||
ikNode.setTargetVars("RightFoot", "rightFootPosition", "rightFootRotation",
|
||||
"rightFootType", "rightFootWeight", 1.0f, {},
|
||||
QString(), QString(), QString());
|
||||
glm::vec3 hipsPosition(0.0f);
|
||||
int hipsIndex = indexOfJoint("Hips");
|
||||
if (hipsIndex >= 0) {
|
||||
hipsPosition = transformPoint(_geometryToRigTransform, _animSkeleton->getAbsoluteDefaultPose(hipsIndex).trans());
|
||||
}
|
||||
AnimVariantMap animVars;
|
||||
animVars.setRigToGeometryTransform(_rigToGeometryTransform);
|
||||
glm::quat handRotation = glm::angleAxis(PI, Vectors::UNIT_X);
|
||||
animVars.set("leftHandPosition", hipsPosition);
|
||||
animVars.set("leftHandRotation", handRotation);
|
||||
animVars.set("leftHandType", (int)IKTarget::Type::RotationAndPosition);
|
||||
animVars.set("rightHandPosition", hipsPosition);
|
||||
animVars.set("rightHandRotation", handRotation);
|
||||
animVars.set("rightHandType", (int)IKTarget::Type::RotationAndPosition);
|
||||
|
||||
int rightFootIndex = indexOfJoint("RightFoot");
|
||||
int leftFootIndex = indexOfJoint("LeftFoot");
|
||||
if (rightFootIndex != -1 && leftFootIndex != -1) {
|
||||
glm::vec3 geomFootPosition = glm::vec3(0.0f, _animSkeleton->getAbsoluteDefaultPose(rightFootIndex).trans().y, 0.0f);
|
||||
glm::vec3 footPosition = transformPoint(_geometryToRigTransform, geomFootPosition);
|
||||
glm::quat footRotation = glm::angleAxis(0.5f * PI, Vectors::UNIT_X);
|
||||
animVars.set("leftFootPosition", footPosition);
|
||||
animVars.set("leftFootRotation", footRotation);
|
||||
animVars.set("leftFootType", (int)IKTarget::Type::RotationAndPosition);
|
||||
animVars.set("rightFootPosition", footPosition);
|
||||
animVars.set("rightFootRotation", footRotation);
|
||||
animVars.set("rightFootType", (int)IKTarget::Type::RotationAndPosition);
|
||||
}
|
||||
|
||||
// call overlay twice: once to verify AnimPoseVec joints and again to do the IK
|
||||
AnimNode::Triggers triggersOut;
|
||||
AnimContext context(false, false, false, _geometryToRigTransform, _rigToGeometryTransform);
|
||||
float dt = 1.0f; // the value of this does not matter
|
||||
ikNode.overlay(animVars, context, dt, triggersOut, _animSkeleton->getRelativeBindPoses());
|
||||
AnimPoseVec finalPoses = ikNode.overlay(animVars, context, dt, triggersOut, _animSkeleton->getRelativeBindPoses());
|
||||
|
||||
// convert relative poses to absolute
|
||||
_animSkeleton->convertRelativePosesToAbsolute(finalPoses);
|
||||
|
||||
// compute bounding box that encloses all points
|
||||
Extents totalExtents;
|
||||
|
@ -1723,15 +1778,15 @@ void Rig::computeAvatarBoundingCapsule(
|
|||
// even if they do not have legs (default robot)
|
||||
totalExtents.addPoint(glm::vec3(0.0f));
|
||||
|
||||
// HACK to reduce the radius of the bounding capsule to be tight with the torso, we only consider joints
|
||||
// To reduce the radius of the bounding capsule to be tight with the torso, we only consider joints
|
||||
// from the head to the hips when computing the rest of the bounding capsule.
|
||||
int index = indexOfJoint("Head");
|
||||
while (index != -1) {
|
||||
const FBXJointShapeInfo& shapeInfo = geometry.joints.at(index).shapeInfo;
|
||||
AnimPose pose = finalPoses[index];
|
||||
AnimPose pose = _animSkeleton->getAbsoluteDefaultPose(index);
|
||||
if (shapeInfo.points.size() > 0) {
|
||||
for (int j = 0; j < shapeInfo.points.size(); ++j) {
|
||||
totalExtents.addPoint((pose * shapeInfo.points[j]));
|
||||
for (auto& point : shapeInfo.points) {
|
||||
totalExtents.addPoint((pose * point));
|
||||
}
|
||||
}
|
||||
index = _animSkeleton->getParentIndex(index);
|
||||
|
@ -1745,7 +1800,6 @@ void Rig::computeAvatarBoundingCapsule(
|
|||
radiusOut = 0.5f * sqrtf(0.5f * (diagonal.x * diagonal.x + diagonal.z * diagonal.z));
|
||||
heightOut = diagonal.y - 2.0f * radiusOut;
|
||||
|
||||
glm::vec3 rootPosition = finalPoses[geometry.rootJointIndex].trans();
|
||||
glm::vec3 rigCenter = transformPoint(_geometryToRigTransform, (0.5f * (totalExtents.maximum + totalExtents.minimum)));
|
||||
localOffsetOut = rigCenter - transformPoint(_geometryToRigTransform, rootPosition);
|
||||
glm::vec3 capsuleCenter = transformPoint(_geometryToRigTransform, (0.5f * (totalExtents.maximum + totalExtents.minimum)));
|
||||
localOffsetOut = capsuleCenter - hipsPosition;
|
||||
}
|
||||
|
|
|
@ -75,9 +75,10 @@ public:
|
|||
AnimPose secondaryControllerPoses[NumSecondaryControllerTypes]; // rig space
|
||||
bool secondaryControllerActiveFlags[NumSecondaryControllerTypes];
|
||||
bool isTalking;
|
||||
float bodyCapsuleRadius;
|
||||
float bodyCapsuleHalfHeight;
|
||||
glm::vec3 bodyCapsuleLocalOffset;
|
||||
FBXJointShapeInfo hipsShapeInfo;
|
||||
FBXJointShapeInfo spineShapeInfo;
|
||||
FBXJointShapeInfo spine1ShapeInfo;
|
||||
FBXJointShapeInfo spine2ShapeInfo;
|
||||
};
|
||||
|
||||
struct EyeParameters {
|
||||
|
@ -249,7 +250,8 @@ protected:
|
|||
void updateHead(bool headEnabled, bool hipsEnabled, const AnimPose& headMatrix);
|
||||
void updateHands(bool leftHandEnabled, bool rightHandEnabled, bool hipsEnabled, bool leftArmEnabled, bool rightArmEnabled, float dt,
|
||||
const AnimPose& leftHandPose, const AnimPose& rightHandPose,
|
||||
float bodyCapsuleRadius, float bodyCapsuleHalfHeight, const glm::vec3& bodyCapsuleLocalOffset);
|
||||
const FBXJointShapeInfo& hipsShapeInfo, const FBXJointShapeInfo& spineShapeInfo,
|
||||
const FBXJointShapeInfo& spine1ShapeInfo, const FBXJointShapeInfo& spine2ShapeInfo);
|
||||
void updateFeet(bool leftFootEnabled, bool rightFootEnabled, const AnimPose& leftFootPose, const AnimPose& rightFootPose);
|
||||
|
||||
void updateEyeJoint(int index, const glm::vec3& modelTranslation, const glm::quat& modelRotation, const glm::vec3& lookAt, const glm::vec3& saccade);
|
||||
|
@ -257,6 +259,8 @@ protected:
|
|||
|
||||
glm::vec3 calculateElbowPoleVector(int handIndex, int elbowIndex, int armIndex, int hipsIndex, bool isLeft) const;
|
||||
glm::vec3 calculateKneePoleVector(int footJointIndex, int kneeJoint, int upLegIndex, int hipsIndex, const AnimPose& targetFootPose) const;
|
||||
glm::vec3 deflectHandFromTorso(const glm::vec3& handPosition, const FBXJointShapeInfo& hipsShapeInfo, const FBXJointShapeInfo& spineShapeInfo,
|
||||
const FBXJointShapeInfo& spine1ShapeInfo, const FBXJointShapeInfo& spine2ShapeInfo) const;
|
||||
|
||||
AnimPose _modelOffset; // model to rig space
|
||||
AnimPose _geometryOffset; // geometry to model space (includes unit offset & fst offsets)
|
||||
|
|
|
@ -544,7 +544,7 @@ public:
|
|||
Q_INVOKABLE void updateAvatarEntity(const QUuid& entityID, const QByteArray& entityData);
|
||||
Q_INVOKABLE void clearAvatarEntity(const QUuid& entityID);
|
||||
|
||||
void setForceFaceTrackerConnected(bool connected) { _forceFaceTrackerConnected = connected; }
|
||||
Q_INVOKABLE void setForceFaceTrackerConnected(bool connected) { _forceFaceTrackerConnected = connected; }
|
||||
|
||||
// key state
|
||||
void setKeyState(KeyState s) { _keyState = s; }
|
||||
|
|
|
@ -1682,8 +1682,8 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS
|
|||
int newIndex = it.value();
|
||||
|
||||
// remember vertices with at least 1/4 weight
|
||||
const float EXPANSION_WEIGHT_THRESHOLD = 0.99f;
|
||||
if (weight > EXPANSION_WEIGHT_THRESHOLD) {
|
||||
const float EXPANSION_WEIGHT_THRESHOLD = 0.25f;
|
||||
if (weight >= EXPANSION_WEIGHT_THRESHOLD) {
|
||||
// transform to joint-frame and save for later
|
||||
const glm::mat4 vertexTransform = meshToJoint * glm::translate(extracted.mesh.vertices.at(newIndex));
|
||||
points.push_back(extractTranslation(vertexTransform) * clusterScale);
|
||||
|
@ -1788,6 +1788,7 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS
|
|||
avgPoint += points[j];
|
||||
}
|
||||
avgPoint /= (float)points.size();
|
||||
joint.shapeInfo.avgPoint = avgPoint;
|
||||
|
||||
// compute a k-Dop bounding volume
|
||||
for (uint32_t j = 0; j < cardinalDirections.size(); ++j) {
|
||||
|
@ -1803,8 +1804,11 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS
|
|||
}
|
||||
}
|
||||
joint.shapeInfo.points.push_back(avgPoint + maxDot * cardinalDirections[j]);
|
||||
joint.shapeInfo.dots.push_back(maxDot);
|
||||
joint.shapeInfo.points.push_back(avgPoint + minDot * cardinalDirections[j]);
|
||||
joint.shapeInfo.dots.push_back(-minDot);
|
||||
}
|
||||
generateBoundryLinesForDop14(joint.shapeInfo.dots, joint.shapeInfo.avgPoint, joint.shapeInfo.debugLines);
|
||||
}
|
||||
}
|
||||
geometry.palmDirection = parseVec3(mapping.value("palmDirection", "0, -1, 0").toString());
|
||||
|
|
|
@ -56,7 +56,10 @@ public:
|
|||
|
||||
struct FBXJointShapeInfo {
|
||||
// same units and frame as FBXJoint.translation
|
||||
QVector<glm::vec3> points;
|
||||
glm::vec3 avgPoint;
|
||||
std::vector<float> dots;
|
||||
std::vector<glm::vec3> points;
|
||||
std::vector<glm::vec3> debugLines;
|
||||
};
|
||||
|
||||
/// A single joint (transformation node) extracted from an FBX document.
|
||||
|
|
|
@ -167,6 +167,10 @@ void LimitedNodeList::setPermissions(const NodePermissions& newPermissions) {
|
|||
newPermissions.can(NodePermissions::Permission::canKick)) {
|
||||
emit canKickChanged(_permissions.can(NodePermissions::Permission::canKick));
|
||||
}
|
||||
if (originalPermissions.can(NodePermissions::Permission::canReplaceDomainContent) !=
|
||||
newPermissions.can(NodePermissions::Permission::canReplaceDomainContent)) {
|
||||
emit canReplaceContentChanged(_permissions.can(NodePermissions::Permission::canReplaceDomainContent));
|
||||
}
|
||||
}
|
||||
|
||||
void LimitedNodeList::setSocketLocalPort(quint16 socketLocalPort) {
|
||||
|
|
|
@ -115,7 +115,8 @@ public:
|
|||
bool getThisNodeCanRezTmp() const { return _permissions.can(NodePermissions::Permission::canRezTemporaryEntities); }
|
||||
bool getThisNodeCanWriteAssets() const { return _permissions.can(NodePermissions::Permission::canWriteToAssetServer); }
|
||||
bool getThisNodeCanKick() const { return _permissions.can(NodePermissions::Permission::canKick); }
|
||||
|
||||
bool getThisNodeCanReplaceContent() const { return _permissions.can(NodePermissions::Permission::canReplaceDomainContent); }
|
||||
|
||||
quint16 getSocketLocalPort() const { return _nodeSocket.localPort(); }
|
||||
Q_INVOKABLE void setSocketLocalPort(quint16 socketLocalPort);
|
||||
|
||||
|
@ -329,6 +330,7 @@ signals:
|
|||
void canRezTmpChanged(bool canRezTmp);
|
||||
void canWriteAssetsChanged(bool canWriteAssets);
|
||||
void canKickChanged(bool canKick);
|
||||
void canReplaceContentChanged(bool canReplaceContent);
|
||||
|
||||
protected slots:
|
||||
void connectedForLocalSocketTest();
|
||||
|
|
|
@ -74,6 +74,7 @@ public:
|
|||
bool getCanRezTmp() const { return _permissions.can(NodePermissions::Permission::canRezTemporaryEntities); }
|
||||
bool getCanWriteToAssetServer() const { return _permissions.can(NodePermissions::Permission::canWriteToAssetServer); }
|
||||
bool getCanKick() const { return _permissions.can(NodePermissions::Permission::canKick); }
|
||||
bool getCanReplaceContent() const { return _permissions.can(NodePermissions::Permission::canReplaceDomainContent); }
|
||||
|
||||
void parseIgnoreRequestMessage(QSharedPointer<ReceivedMessage> message);
|
||||
void addIgnoredNode(const QUuid& otherNodeID);
|
||||
|
|
|
@ -45,6 +45,7 @@ NodePermissions::NodePermissions(QMap<QString, QVariant> perms) {
|
|||
permissions |= perms["id_can_connect_past_max_capacity"].toBool() ?
|
||||
Permission::canConnectPastMaxCapacity : Permission::none;
|
||||
permissions |= perms["id_can_kick"].toBool() ? Permission::canKick : Permission::none;
|
||||
permissions |= perms["id_can_replace_content"].toBool() ? Permission::canReplaceDomainContent : Permission::none;
|
||||
}
|
||||
|
||||
QVariant NodePermissions::toVariant(QHash<QUuid, GroupRank> groupRanks) {
|
||||
|
@ -65,6 +66,7 @@ QVariant NodePermissions::toVariant(QHash<QUuid, GroupRank> groupRanks) {
|
|||
values["id_can_write_to_asset_server"] = can(Permission::canWriteToAssetServer);
|
||||
values["id_can_connect_past_max_capacity"] = can(Permission::canConnectPastMaxCapacity);
|
||||
values["id_can_kick"] = can(Permission::canKick);
|
||||
values["id_can_replace_content"] = can(Permission::canReplaceDomainContent);
|
||||
return QVariant(values);
|
||||
}
|
||||
|
||||
|
@ -128,6 +130,9 @@ QDebug operator<<(QDebug debug, const NodePermissions& perms) {
|
|||
if (perms.can(NodePermissions::Permission::canKick)) {
|
||||
debug << " kick";
|
||||
}
|
||||
if (perms.can(NodePermissions::Permission::canReplaceDomainContent)) {
|
||||
debug << " can_replace_content";
|
||||
}
|
||||
debug.nospace() << "]";
|
||||
return debug.nospace();
|
||||
}
|
||||
|
|
|
@ -77,7 +77,8 @@ public:
|
|||
canRezTemporaryEntities = 8,
|
||||
canWriteToAssetServer = 16,
|
||||
canConnectPastMaxCapacity = 32,
|
||||
canKick = 64
|
||||
canKick = 64,
|
||||
canReplaceDomainContent = 128
|
||||
};
|
||||
Q_DECLARE_FLAGS(Permissions, Permission)
|
||||
Permissions permissions;
|
||||
|
|
|
@ -121,6 +121,7 @@ public:
|
|||
ReplicatedAvatarIdentity,
|
||||
ReplicatedKillAvatar,
|
||||
ReplicatedBulkAvatarData,
|
||||
OctreeFileReplacementFromUrl,
|
||||
NUM_PACKET_TYPE
|
||||
};
|
||||
|
||||
|
|
|
@ -1743,8 +1743,12 @@ void GeometryCache::renderGlowLine(gpu::Batch& batch, const glm::vec3& p1, const
|
|||
glowIntensity = 0.0f;
|
||||
#endif
|
||||
|
||||
if (glowIntensity <= 0) {
|
||||
bindSimpleProgram(batch, false, false, false, true, false);
|
||||
if (glowIntensity <= 0.0f) {
|
||||
if (color.a >= 1.0f) {
|
||||
bindSimpleProgram(batch, false, false, false, true, true);
|
||||
} else {
|
||||
bindSimpleProgram(batch, false, true, false, true, true);
|
||||
}
|
||||
renderLine(batch, p1, p2, color, id);
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -371,7 +371,7 @@ void DrawOverlay3D::run(const RenderContextPointer& renderContext, const Inputs&
|
|||
if (_opaquePass) {
|
||||
gpu::doInBatch(args->_context, [&](gpu::Batch& batch){
|
||||
batch.enableStereo(false);
|
||||
batch.clearFramebuffer(gpu::Framebuffer::BUFFER_DEPTH, glm::vec4(), 1.f, 0, true);
|
||||
batch.clearFramebuffer(gpu::Framebuffer::BUFFER_DEPTH, glm::vec4(), 1.f, 0, false);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
#include <glm/gtx/quaternion.hpp>
|
||||
|
||||
#include "NumericalConstants.h"
|
||||
#include "GLMHelpers.h"
|
||||
|
||||
glm::vec3 computeVectorFromPointToSegment(const glm::vec3& point, const glm::vec3& start, const glm::vec3& end) {
|
||||
// compute the projection of the point vector onto the segment vector
|
||||
|
@ -657,3 +658,150 @@ bool findPlaneFromPoints(const glm::vec3* points, size_t numPoints, glm::vec3& p
|
|||
planeNormalOut = glm::normalize(dir);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool findIntersectionOfThreePlanes(const glm::vec4& planeA, const glm::vec4& planeB, const glm::vec4& planeC, glm::vec3& intersectionPointOut) {
|
||||
glm::vec3 normalA(planeA);
|
||||
glm::vec3 normalB(planeB);
|
||||
glm::vec3 normalC(planeC);
|
||||
glm::vec3 u = glm::cross(normalB, normalC);
|
||||
float denom = glm::dot(normalA, u);
|
||||
if (fabsf(denom) < EPSILON) {
|
||||
return false; // planes do not intersect in a point.
|
||||
} else {
|
||||
intersectionPointOut = (planeA.w * u + glm::cross(normalA, planeC.w * normalB - planeB.w * normalC)) / denom;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
const float INV_SQRT_3 = 1.0f / sqrtf(3.0f);
|
||||
const int DOP14_COUNT = 14;
|
||||
const glm::vec3 DOP14_NORMALS[DOP14_COUNT] = {
|
||||
Vectors::UNIT_X,
|
||||
-Vectors::UNIT_X,
|
||||
Vectors::UNIT_Y,
|
||||
-Vectors::UNIT_Y,
|
||||
Vectors::UNIT_Z,
|
||||
-Vectors::UNIT_Z,
|
||||
glm::vec3(INV_SQRT_3, INV_SQRT_3, INV_SQRT_3),
|
||||
-glm::vec3(INV_SQRT_3, INV_SQRT_3, INV_SQRT_3),
|
||||
glm::vec3(INV_SQRT_3, -INV_SQRT_3, INV_SQRT_3),
|
||||
-glm::vec3(INV_SQRT_3, -INV_SQRT_3, INV_SQRT_3),
|
||||
glm::vec3(INV_SQRT_3, INV_SQRT_3, -INV_SQRT_3),
|
||||
-glm::vec3(INV_SQRT_3, INV_SQRT_3, -INV_SQRT_3),
|
||||
glm::vec3(INV_SQRT_3, -INV_SQRT_3, -INV_SQRT_3),
|
||||
-glm::vec3(INV_SQRT_3, -INV_SQRT_3, -INV_SQRT_3)
|
||||
};
|
||||
|
||||
typedef std::tuple<int, int, int> Int3Tuple;
|
||||
const std::tuple<int, int, int> DOP14_PLANE_COMBINATIONS[] = {
|
||||
Int3Tuple(0, 2, 4), Int3Tuple(0, 2, 5), Int3Tuple(0, 2, 6), Int3Tuple(0, 2, 7), Int3Tuple(0, 2, 8), Int3Tuple(0, 2, 9), Int3Tuple(0, 2, 10), Int3Tuple(0, 2, 11), Int3Tuple(0, 2, 12), Int3Tuple(0, 2, 13),
|
||||
Int3Tuple(0, 3, 4), Int3Tuple(0, 3, 5), Int3Tuple(0, 3, 6), Int3Tuple(0, 3, 7), Int3Tuple(0, 3, 8), Int3Tuple(0, 3, 9), Int3Tuple(0, 3, 10), Int3Tuple(0, 3, 11), Int3Tuple(0, 3, 12), Int3Tuple(0, 3, 13),
|
||||
Int3Tuple(0, 4, 6), Int3Tuple(0, 4, 7), Int3Tuple(0, 4, 8), Int3Tuple(0, 4, 9), Int3Tuple(0, 4, 10), Int3Tuple(0, 4, 11), Int3Tuple(0, 4, 12), Int3Tuple(0, 4, 13),
|
||||
Int3Tuple(0, 5, 6), Int3Tuple(0, 5, 7), Int3Tuple(0, 5, 8), Int3Tuple(0, 5, 9), Int3Tuple(0, 5, 10), Int3Tuple(0, 5, 11), Int3Tuple(0, 5, 12), Int3Tuple(0, 5, 13),
|
||||
Int3Tuple(0, 6, 8), Int3Tuple(0, 6, 9), Int3Tuple(0, 6, 10), Int3Tuple(0, 6, 11), Int3Tuple(0, 6, 12), Int3Tuple(0, 6, 13),
|
||||
Int3Tuple(0, 7, 8), Int3Tuple(0, 7, 9), Int3Tuple(0, 7, 10), Int3Tuple(0, 7, 11), Int3Tuple(0, 7, 12), Int3Tuple(0, 7, 13),
|
||||
Int3Tuple(0, 8, 10), Int3Tuple(0, 8, 11), Int3Tuple(0, 8, 12), Int3Tuple(0, 8, 13), Int3Tuple(0, 9, 10),
|
||||
Int3Tuple(0, 9, 11), Int3Tuple(0, 9, 12), Int3Tuple(0, 9, 13),
|
||||
Int3Tuple(0, 10, 12), Int3Tuple(0, 10, 13),
|
||||
Int3Tuple(0, 11, 12), Int3Tuple(0, 11, 13),
|
||||
Int3Tuple(1, 2, 4), Int3Tuple(1, 2, 5), Int3Tuple(1, 2, 6), Int3Tuple(1, 2, 7), Int3Tuple(1, 2, 8), Int3Tuple(1, 2, 9), Int3Tuple(1, 2, 10), Int3Tuple(1, 2, 11), Int3Tuple(1, 2, 12), Int3Tuple(1, 2, 13),
|
||||
Int3Tuple(1, 3, 4), Int3Tuple(1, 3, 5), Int3Tuple(1, 3, 6), Int3Tuple(1, 3, 7), Int3Tuple(1, 3, 8), Int3Tuple(1, 3, 9), Int3Tuple(1, 3, 10), Int3Tuple(1, 3, 11), Int3Tuple(1, 3, 12), Int3Tuple(1, 3, 13),
|
||||
Int3Tuple(1, 4, 6), Int3Tuple(1, 4, 7), Int3Tuple(1, 4, 8), Int3Tuple(1, 4, 9), Int3Tuple(1, 4, 10), Int3Tuple(1, 4, 11), Int3Tuple(1, 4, 12), Int3Tuple(1, 4, 13),
|
||||
Int3Tuple(1, 5, 6), Int3Tuple(1, 5, 7), Int3Tuple(1, 5, 8), Int3Tuple(1, 5, 9), Int3Tuple(1, 5, 10), Int3Tuple(1, 5, 11), Int3Tuple(1, 5, 12), Int3Tuple(1, 5, 13),
|
||||
Int3Tuple(1, 6, 8), Int3Tuple(1, 6, 9), Int3Tuple(1, 6, 10), Int3Tuple(1, 6, 11), Int3Tuple(1, 6, 12), Int3Tuple(1, 6, 13),
|
||||
Int3Tuple(1, 7, 8), Int3Tuple(1, 7, 9), Int3Tuple(1, 7, 10), Int3Tuple(1, 7, 11), Int3Tuple(1, 7, 12), Int3Tuple(1, 7, 13),
|
||||
Int3Tuple(1, 8, 10), Int3Tuple(1, 8, 11), Int3Tuple(1, 8, 12), Int3Tuple(1, 8, 13),
|
||||
Int3Tuple(1, 9, 10), Int3Tuple(1, 9, 11), Int3Tuple(1, 9, 12), Int3Tuple(1, 9, 13),
|
||||
Int3Tuple(1, 10, 12), Int3Tuple(1, 10, 13),
|
||||
Int3Tuple(1, 11, 12), Int3Tuple(1, 11, 13),
|
||||
Int3Tuple(2, 4, 6), Int3Tuple(2, 4, 7), Int3Tuple(2, 4, 8), Int3Tuple(2, 4, 9), Int3Tuple(2, 4, 10), Int3Tuple(2, 4, 11), Int3Tuple(2, 4, 12), Int3Tuple(2, 4, 13),
|
||||
Int3Tuple(2, 5, 6), Int3Tuple(2, 5, 7), Int3Tuple(2, 5, 8), Int3Tuple(2, 5, 9), Int3Tuple(2, 5, 10), Int3Tuple(2, 5, 11), Int3Tuple(2, 5, 12), Int3Tuple(2, 5, 13),
|
||||
Int3Tuple(2, 6, 8), Int3Tuple(2, 6, 9), Int3Tuple(2, 6, 10), Int3Tuple(2, 6, 11), Int3Tuple(2, 6, 12), Int3Tuple(2, 6, 13),
|
||||
Int3Tuple(2, 7, 8), Int3Tuple(2, 7, 9), Int3Tuple(2, 7, 10), Int3Tuple(2, 7, 11), Int3Tuple(2, 7, 12), Int3Tuple(2, 7, 13),
|
||||
Int3Tuple(2, 8, 10), Int3Tuple(2, 8, 11), Int3Tuple(2, 8, 12), Int3Tuple(2, 8, 13),
|
||||
Int3Tuple(2, 9, 10), Int3Tuple(2, 9, 11), Int3Tuple(2, 9, 12), Int3Tuple(2, 9, 13),
|
||||
Int3Tuple(2, 10, 12), Int3Tuple(2, 10, 13),
|
||||
Int3Tuple(2, 11, 12), Int3Tuple(2, 11, 13),
|
||||
Int3Tuple(3, 4, 6), Int3Tuple(3, 4, 7), Int3Tuple(3, 4, 8), Int3Tuple(3, 4, 9), Int3Tuple(3, 4, 10), Int3Tuple(3, 4, 11), Int3Tuple(3, 4, 12), Int3Tuple(3, 4, 13),
|
||||
Int3Tuple(3, 5, 6), Int3Tuple(3, 5, 7), Int3Tuple(3, 5, 8), Int3Tuple(3, 5, 9), Int3Tuple(3, 5, 10), Int3Tuple(3, 5, 11), Int3Tuple(3, 5, 12), Int3Tuple(3, 5, 13),
|
||||
Int3Tuple(3, 6, 8), Int3Tuple(3, 6, 9), Int3Tuple(3, 6, 10), Int3Tuple(3, 6, 11), Int3Tuple(3, 6, 12), Int3Tuple(3, 6, 13),
|
||||
Int3Tuple(3, 7, 8), Int3Tuple(3, 7, 9), Int3Tuple(3, 7, 10), Int3Tuple(3, 7, 11), Int3Tuple(3, 7, 12), Int3Tuple(3, 7, 13),
|
||||
Int3Tuple(3, 8, 10), Int3Tuple(3, 8, 11), Int3Tuple(3, 8, 12), Int3Tuple(3, 8, 13),
|
||||
Int3Tuple(3, 9, 10), Int3Tuple(3, 9, 11), Int3Tuple(3, 9, 12), Int3Tuple(3, 9, 13),
|
||||
Int3Tuple(3, 10, 12), Int3Tuple(3, 10, 13),
|
||||
Int3Tuple(3, 11, 12), Int3Tuple(3, 11, 13),
|
||||
Int3Tuple(4, 6, 8), Int3Tuple(4, 6, 9), Int3Tuple(4, 6, 10), Int3Tuple(4, 6, 11), Int3Tuple(4, 6, 12), Int3Tuple(4, 6, 13),
|
||||
Int3Tuple(4, 7, 8), Int3Tuple(4, 7, 9), Int3Tuple(4, 7, 10), Int3Tuple(4, 7, 11), Int3Tuple(4, 7, 12), Int3Tuple(4, 7, 13),
|
||||
Int3Tuple(4, 8, 10), Int3Tuple(4, 8, 11), Int3Tuple(4, 8, 12), Int3Tuple(4, 8, 13),
|
||||
Int3Tuple(4, 9, 10), Int3Tuple(4, 9, 11), Int3Tuple(4, 9, 12), Int3Tuple(4, 9, 13),
|
||||
Int3Tuple(4, 10, 12), Int3Tuple(4, 10, 13),
|
||||
Int3Tuple(4, 11, 12), Int3Tuple(4, 11, 13),
|
||||
Int3Tuple(5, 6, 8), Int3Tuple(5, 6, 9), Int3Tuple(5, 6, 10), Int3Tuple(5, 6, 11), Int3Tuple(5, 6, 12), Int3Tuple(5, 6, 13),
|
||||
Int3Tuple(5, 7, 8), Int3Tuple(5, 7, 9), Int3Tuple(5, 7, 10), Int3Tuple(5, 7, 11), Int3Tuple(5, 7, 12), Int3Tuple(5, 7, 13),
|
||||
Int3Tuple(5, 8, 10), Int3Tuple(5, 8, 11), Int3Tuple(5, 8, 12), Int3Tuple(5, 8, 13),
|
||||
Int3Tuple(5, 9, 10), Int3Tuple(5, 9, 11), Int3Tuple(5, 9, 12), Int3Tuple(5, 9, 13),
|
||||
Int3Tuple(5, 10, 12), Int3Tuple(5, 10, 13),
|
||||
Int3Tuple(5, 11, 12), Int3Tuple(5, 11, 13),
|
||||
Int3Tuple(6, 8, 10), Int3Tuple(6, 8, 11), Int3Tuple(6, 8, 12), Int3Tuple(6, 8, 13),
|
||||
Int3Tuple(6, 9, 10), Int3Tuple(6, 9, 11), Int3Tuple(6, 9, 12), Int3Tuple(6, 9, 13),
|
||||
Int3Tuple(6, 10, 12), Int3Tuple(6, 10, 13),
|
||||
Int3Tuple(6, 11, 12), Int3Tuple(6, 11, 13),
|
||||
Int3Tuple(7, 8, 10), Int3Tuple(7, 8, 11), Int3Tuple(7, 8, 12), Int3Tuple(7, 8, 13),
|
||||
Int3Tuple(7, 9, 10), Int3Tuple(7, 9, 11), Int3Tuple(7, 9, 12), Int3Tuple(7, 9, 13),
|
||||
Int3Tuple(7, 10, 12), Int3Tuple(7, 10, 13),
|
||||
Int3Tuple(7, 11, 12), Int3Tuple(7, 11, 13),
|
||||
Int3Tuple(8, 10, 12), Int3Tuple(8, 10, 13),
|
||||
Int3Tuple(8, 11, 12), Int3Tuple(8, 11, 13),
|
||||
Int3Tuple(9, 10, 12), Int3Tuple(9, 10, 13),
|
||||
Int3Tuple(9, 11, 12), Int3Tuple(9, 11, 13)
|
||||
};
|
||||
|
||||
void generateBoundryLinesForDop14(const std::vector<float>& dots, const glm::vec3& center, std::vector<glm::vec3>& linesOut) {
|
||||
if (dots.size() != DOP14_COUNT) {
|
||||
return;
|
||||
}
|
||||
|
||||
// iterate over all purmutations of non-parallel planes.
|
||||
// find all the vertices that lie on the surface of the k-dop
|
||||
std::vector<glm::vec3> vertices;
|
||||
for (auto& tuple : DOP14_PLANE_COMBINATIONS) {
|
||||
int i = std::get<0>(tuple);
|
||||
int j = std::get<1>(tuple);
|
||||
int k = std::get<2>(tuple);
|
||||
glm::vec4 planeA(DOP14_NORMALS[i], dots[i]);
|
||||
glm::vec4 planeB(DOP14_NORMALS[j], dots[j]);
|
||||
glm::vec4 planeC(DOP14_NORMALS[k], dots[k]);
|
||||
glm::vec3 intersectionPoint;
|
||||
const float IN_FRONT_MARGIN = 0.01f;
|
||||
if (findIntersectionOfThreePlanes(planeA, planeB, planeC, intersectionPoint)) {
|
||||
bool inFront = false;
|
||||
for (int p = 0; p < DOP14_COUNT; p++) {
|
||||
if (glm::dot(DOP14_NORMALS[p], intersectionPoint) > dots[p] + IN_FRONT_MARGIN) {
|
||||
inFront = true;
|
||||
}
|
||||
}
|
||||
if (!inFront) {
|
||||
vertices.push_back(intersectionPoint);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// build a set of lines between these vertices, that also lie on the surface of the k-dop.
|
||||
for (size_t i = 0; i < vertices.size(); i++) {
|
||||
for (size_t j = i; j < vertices.size(); j++) {
|
||||
glm::vec3 midPoint = (vertices[i] + vertices[j]) * 0.5f;
|
||||
int onSurfaceCount = 0;
|
||||
const float SURFACE_MARGIN = 0.01f;
|
||||
for (int p = 0; p < DOP14_COUNT; p++) {
|
||||
float d = glm::dot(DOP14_NORMALS[p], midPoint);
|
||||
if (d > dots[p] - SURFACE_MARGIN && d < dots[p] + SURFACE_MARGIN) {
|
||||
onSurfaceCount++;
|
||||
}
|
||||
}
|
||||
if (onSurfaceCount > 1) {
|
||||
linesOut.push_back(vertices[i] + center);
|
||||
linesOut.push_back(vertices[j] + center);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
#define hifi_GeometryUtil_h
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
#include <vector>
|
||||
|
||||
glm::vec3 computeVectorFromPointToSegment(const glm::vec3& point, const glm::vec3& start, const glm::vec3& end);
|
||||
|
||||
|
@ -166,4 +167,10 @@ private:
|
|||
// given a set of points, compute a best fit plane that passes as close as possible through all the points.
|
||||
bool findPlaneFromPoints(const glm::vec3* points, size_t numPoints, glm::vec3& planeNormalOut, glm::vec3& pointOnPlaneOut);
|
||||
|
||||
// plane equation is specified by ax + by + cz + d = 0.
|
||||
// the coefficents are passed in as a vec4. (a, b, c, d)
|
||||
bool findIntersectionOfThreePlanes(const glm::vec4& planeA, const glm::vec4& planeB, const glm::vec4& planeC, glm::vec3& intersectionPointOut);
|
||||
|
||||
void generateBoundryLinesForDop14(const std::vector<float>& dots, const glm::vec3& center, std::vector<glm::vec3>& linesOut);
|
||||
|
||||
#endif // hifi_GeometryUtil_h
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
var MARKETPLACES_INJECT_SCRIPT_URL = Script.resolvePath("../html/js/marketplacesInject.js");
|
||||
var MARKETPLACE_CHECKOUT_QML_PATH = Script.resourcesPath() + "qml/hifi/commerce/Checkout.qml";
|
||||
var MARKETPLACE_INVENTORY_QML_PATH = Script.resourcesPath() + "qml/hifi/commerce/Inventory.qml";
|
||||
var MARKETPLACE_SECURITY_QML_PATH = Script.resourcesPath() + "qml/hifi/commerce/SecurityImageSelection.qml";
|
||||
|
||||
var HOME_BUTTON_TEXTURE = "http://hifi-content.s3.amazonaws.com/alan/dev/tablet-with-home-button.fbx/tablet-with-home-button.fbm/button-root.png";
|
||||
// var HOME_BUTTON_TEXTURE = Script.resourcesPath() + "meshes/tablet-with-home-button.fbx/tablet-with-home-button.fbm/button-root.png";
|
||||
|
@ -87,7 +88,7 @@
|
|||
|
||||
function onScreenChanged(type, url) {
|
||||
onMarketplaceScreen = type === "Web" && url === MARKETPLACE_URL_INITIAL;
|
||||
wireEventBridge(type === "QML" && (url === MARKETPLACE_CHECKOUT_QML_PATH || url === MARKETPLACE_INVENTORY_QML_PATH));
|
||||
wireEventBridge(type === "QML" && (url === MARKETPLACE_CHECKOUT_QML_PATH || url === MARKETPLACE_INVENTORY_QML_PATH || url === MARKETPLACE_SECURITY_QML_PATH));
|
||||
// for toolbar mode: change button to active when window is first openend, false otherwise.
|
||||
marketplaceButton.editProperties({ isActive: onMarketplaceScreen });
|
||||
if (type === "Web" && url.indexOf(MARKETPLACE_URL) !== -1) {
|
||||
|
@ -217,8 +218,11 @@
|
|||
case 'inventory_backClicked':
|
||||
tablet.gotoWebScreen(message.referrerURL, MARKETPLACES_INJECT_SCRIPT_URL);
|
||||
break;
|
||||
case 'securityImageSelection_cancelClicked':
|
||||
tablet.gotoWebScreen(message.referrerURL, MARKETPLACES_INJECT_SCRIPT_URL);
|
||||
break;
|
||||
default:
|
||||
print('Unrecognized message from Checkout.qml or Inventory.qml: ' + JSON.stringify(message));
|
||||
print('Unrecognized message from Checkout.qml, Inventory.qml, or SecurityImageSelection.qml: ' + JSON.stringify(message));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
118
unpublishedScripts/marketplace/skyboxChanger/skyboxchanger.js
Normal file
118
unpublishedScripts/marketplace/skyboxChanger/skyboxchanger.js
Normal file
|
@ -0,0 +1,118 @@
|
|||
"use strict";
|
||||
|
||||
//
|
||||
// skyboxchanger.js
|
||||
//
|
||||
// Created by Cain Kilgore on 9th August 2017
|
||||
// Copyright 2017 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
(function() {
|
||||
var TABLET_BUTTON_NAME = "SKYBOX";
|
||||
|
||||
var ICONS = {
|
||||
icon: "http://mpassets.highfidelity.com/05904016-8f7d-4dfc-88e1-2bf9ba3fac20-v1/skyboxedit-i.svg",
|
||||
activeIcon: "http://mpassets.highfidelity.com/05904016-8f7d-4dfc-88e1-2bf9ba3fac20-v1/skyboxedit-i.svg"
|
||||
};
|
||||
|
||||
var onSkyboxChangerScreen = false;
|
||||
|
||||
function onClicked() {
|
||||
if (onSkyboxChangerScreen) {
|
||||
tablet.gotoHomeScreen();
|
||||
} else {
|
||||
tablet.loadQMLSource("../SkyboxChanger.qml");
|
||||
}
|
||||
}
|
||||
|
||||
var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system");
|
||||
var button = tablet.addButton({
|
||||
icon: ICONS.icon,
|
||||
activeIcon: ICONS.activeIcon,
|
||||
text: TABLET_BUTTON_NAME,
|
||||
sortOrder: 1
|
||||
});
|
||||
|
||||
var hasEventBridge = false;
|
||||
|
||||
function wireEventBridge(on) {
|
||||
if (!tablet) {
|
||||
print("Warning in wireEventBridge(): 'tablet' undefined!");
|
||||
return;
|
||||
}
|
||||
if (on) {
|
||||
if (!hasEventBridge) {
|
||||
tablet.fromQml.connect(fromQml);
|
||||
hasEventBridge = true;
|
||||
}
|
||||
} else {
|
||||
if (hasEventBridge) {
|
||||
tablet.fromQml.disconnect(fromQml);
|
||||
hasEventBridge = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function onScreenChanged(type, url) {
|
||||
if (url === "../SkyboxChanger.qml") {
|
||||
onSkyboxChangerScreen = true;
|
||||
} else {
|
||||
onSkyboxChangerScreen = false;
|
||||
}
|
||||
|
||||
button.editProperties({isActive: onSkyboxChangerScreen});
|
||||
wireEventBridge(onSkyboxChangerScreen);
|
||||
}
|
||||
|
||||
function fromQml(message) {
|
||||
switch (message.method) {
|
||||
case 'changeSkybox': // changeSkybox Code
|
||||
var standingZone;
|
||||
if (!Entities.canRez()) {
|
||||
Window.alert("You need to have rez permissions to change the Skybox.");
|
||||
break;
|
||||
}
|
||||
|
||||
var nearbyEntities = Entities.findEntities(MyAvatar.position, 5);
|
||||
for (var i = 0; i < nearbyEntities.length; i++) {
|
||||
if (Entities.getEntityProperties(nearbyEntities[i]).type === "Zone") {
|
||||
standingZone = nearbyEntities[i];
|
||||
}
|
||||
}
|
||||
|
||||
if (Entities.getEntityProperties(standingZone).locked) {
|
||||
Window.alert("This zone is currently locked; the Skybox can't be changed.");
|
||||
break;
|
||||
}
|
||||
|
||||
var newSkybox = {
|
||||
skybox: {
|
||||
url: message.url
|
||||
},
|
||||
keyLight: {
|
||||
ambientURL: message.url
|
||||
}
|
||||
};
|
||||
|
||||
Entities.editEntity(standingZone, newSkybox);
|
||||
break;
|
||||
default:
|
||||
print('Unrecognized message from QML: ' + JSON.stringify(message));
|
||||
}
|
||||
}
|
||||
|
||||
button.clicked.connect(onClicked);
|
||||
tablet.screenChanged.connect(onScreenChanged);
|
||||
|
||||
Script.scriptEnding.connect(function () {
|
||||
if (onSkyboxChangerScreen) {
|
||||
tablet.gotoHomeScreen();
|
||||
}
|
||||
button.clicked.disconnect(onClicked);
|
||||
tablet.screenChanged.disconnect(onScreenChanged);
|
||||
tablet.removeButton(button);
|
||||
});
|
||||
}());
|
Loading…
Reference in a new issue