mirror of
https://github.com/overte-org/overte.git
synced 2025-08-08 22:16:58 +02:00
Merge remote-tracking branch 'upstream/master' into homereset
This commit is contained in:
commit
02f69607d7
114 changed files with 2706 additions and 1224 deletions
|
@ -16,7 +16,7 @@ Contributing
|
||||||
git checkout -b new_branch_name
|
git checkout -b new_branch_name
|
||||||
```
|
```
|
||||||
4. Code
|
4. Code
|
||||||
* Follow the [coding standard](http://docs.highfidelity.io/v1.0/docs/coding-standard)
|
* Follow the [coding standard](https://readme.highfidelity.com/v1.0/docs/coding-standard)
|
||||||
5. Commit
|
5. Commit
|
||||||
* Use [well formed commit messages](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html)
|
* Use [well formed commit messages](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html)
|
||||||
6. Update your branch
|
6. Update your branch
|
||||||
|
|
|
@ -397,7 +397,7 @@ void Agent::processAgentAvatarAndAudio(float deltaTime) {
|
||||||
if (_numAvatarSoundSentBytes == soundByteArray.size()) {
|
if (_numAvatarSoundSentBytes == soundByteArray.size()) {
|
||||||
// we're done with this sound object - so set our pointer back to NULL
|
// we're done with this sound object - so set our pointer back to NULL
|
||||||
// and our sent bytes back to zero
|
// and our sent bytes back to zero
|
||||||
_avatarSound = NULL;
|
_avatarSound.clear();
|
||||||
_numAvatarSoundSentBytes = 0;
|
_numAvatarSoundSentBytes = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -56,7 +56,7 @@ public:
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void run();
|
void run();
|
||||||
void playAvatarSound(Sound* avatarSound) { setAvatarSound(avatarSound); }
|
void playAvatarSound(SharedSoundPointer avatarSound) { setAvatarSound(avatarSound); }
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void requestScript();
|
void requestScript();
|
||||||
|
@ -77,7 +77,7 @@ private:
|
||||||
MixedAudioStream _receivedAudioStream;
|
MixedAudioStream _receivedAudioStream;
|
||||||
float _lastReceivedAudioLoudness;
|
float _lastReceivedAudioLoudness;
|
||||||
|
|
||||||
void setAvatarSound(Sound* avatarSound) { _avatarSound = avatarSound; }
|
void setAvatarSound(SharedSoundPointer avatarSound) { _avatarSound = avatarSound; }
|
||||||
|
|
||||||
void sendAvatarIdentityPacket();
|
void sendAvatarIdentityPacket();
|
||||||
void sendAvatarBillboardPacket();
|
void sendAvatarBillboardPacket();
|
||||||
|
@ -85,7 +85,7 @@ private:
|
||||||
QString _scriptContents;
|
QString _scriptContents;
|
||||||
QTimer* _scriptRequestTimeout { nullptr };
|
QTimer* _scriptRequestTimeout { nullptr };
|
||||||
bool _isListeningToAudioStream = false;
|
bool _isListeningToAudioStream = false;
|
||||||
Sound* _avatarSound = nullptr;
|
SharedSoundPointer _avatarSound;
|
||||||
int _numAvatarSoundSentBytes = 0;
|
int _numAvatarSoundSentBytes = 0;
|
||||||
bool _isAvatar = false;
|
bool _isAvatar = false;
|
||||||
QTimer* _avatarIdentityTimer = nullptr;
|
QTimer* _avatarIdentityTimer = nullptr;
|
||||||
|
|
|
@ -235,6 +235,11 @@ void AssignmentClient::sendAssignmentRequest() {
|
||||||
void AssignmentClient::handleCreateAssignmentPacket(QSharedPointer<ReceivedMessage> message) {
|
void AssignmentClient::handleCreateAssignmentPacket(QSharedPointer<ReceivedMessage> message) {
|
||||||
qCDebug(assigmnentclient) << "Received a PacketType::CreateAssignment - attempting to unpack.";
|
qCDebug(assigmnentclient) << "Received a PacketType::CreateAssignment - attempting to unpack.";
|
||||||
|
|
||||||
|
if (_currentAssignment) {
|
||||||
|
qCWarning(assigmnentclient) << "Received a PacketType::CreateAssignment while still running an active assignment. Ignoring.";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// construct the deployed assignment from the packet data
|
// construct the deployed assignment from the packet data
|
||||||
_currentAssignment = AssignmentFactory::unpackAssignment(*message);
|
_currentAssignment = AssignmentFactory::unpackAssignment(*message);
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
{
|
{
|
||||||
"version": 1.1,
|
"version": 1.2,
|
||||||
"settings": [
|
"settings": [
|
||||||
{
|
{
|
||||||
"name": "metaverse",
|
"name": "metaverse",
|
||||||
|
@ -249,7 +249,7 @@
|
||||||
"label": "X end",
|
"label": "X end",
|
||||||
"can_set": true,
|
"can_set": true,
|
||||||
"placeholder": "16384.0"
|
"placeholder": "16384.0"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "y_min",
|
"name": "y_min",
|
||||||
"label": "Y start",
|
"label": "Y start",
|
||||||
|
|
|
@ -103,4 +103,5 @@
|
||||||
<script src='js/sweetalert.min.js'></script>
|
<script src='js/sweetalert.min.js'></script>
|
||||||
<script src='js/settings.js'></script>
|
<script src='js/settings.js'></script>
|
||||||
<script src='js/form2js.min.js'></script>
|
<script src='js/form2js.min.js'></script>
|
||||||
|
<script src='js/sha256.js'></script>
|
||||||
<!--#include virtual="page-end.html"-->
|
<!--#include virtual="page-end.html"-->
|
||||||
|
|
|
@ -867,6 +867,14 @@ function saveSettings() {
|
||||||
// grab a JSON representation of the form via form2js
|
// grab a JSON representation of the form via form2js
|
||||||
var formJSON = form2js('settings-form', ".", false, cleanupFormValues, true);
|
var formJSON = form2js('settings-form', ".", false, cleanupFormValues, true);
|
||||||
|
|
||||||
|
// check if we've set the basic http password - if so convert it to base64
|
||||||
|
if (formJSON["security"]) {
|
||||||
|
var password = formJSON["security"]["http_password"];
|
||||||
|
if (password.length > 0) {
|
||||||
|
formJSON["security"]["http_password"] = sha256_digest(password);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
console.log(formJSON);
|
console.log(formJSON);
|
||||||
|
|
||||||
// re-enable all inputs
|
// re-enable all inputs
|
||||||
|
|
247
domain-server/resources/web/settings/js/sha256.js
Normal file
247
domain-server/resources/web/settings/js/sha256.js
Normal file
|
@ -0,0 +1,247 @@
|
||||||
|
/*
|
||||||
|
* A JavaScript implementation of the SHA256 hash function.
|
||||||
|
*
|
||||||
|
* FILE: sha256.js
|
||||||
|
* VERSION: 0.8
|
||||||
|
* AUTHOR: Christoph Bichlmeier <informatik@zombiearena.de>
|
||||||
|
*
|
||||||
|
* NOTE: This version is not tested thoroughly!
|
||||||
|
*
|
||||||
|
* Copyright (c) 2003, Christoph Bichlmeier
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions
|
||||||
|
* are met:
|
||||||
|
* 1. Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
* 3. Neither the name of the copyright holder nor the names of contributors
|
||||||
|
* may be used to endorse or promote products derived from this software
|
||||||
|
* without specific prior written permission.
|
||||||
|
*
|
||||||
|
* ======================================================================
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS
|
||||||
|
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE
|
||||||
|
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||||
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||||
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
|
||||||
|
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||||
|
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
|
||||||
|
* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||||
|
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* SHA256 logical functions */
|
||||||
|
function rotateRight(n,x) {
|
||||||
|
return ((x >>> n) | (x << (32 - n)));
|
||||||
|
}
|
||||||
|
function choice(x,y,z) {
|
||||||
|
return ((x & y) ^ (~x & z));
|
||||||
|
}
|
||||||
|
function majority(x,y,z) {
|
||||||
|
return ((x & y) ^ (x & z) ^ (y & z));
|
||||||
|
}
|
||||||
|
function sha256_Sigma0(x) {
|
||||||
|
return (rotateRight(2, x) ^ rotateRight(13, x) ^ rotateRight(22, x));
|
||||||
|
}
|
||||||
|
function sha256_Sigma1(x) {
|
||||||
|
return (rotateRight(6, x) ^ rotateRight(11, x) ^ rotateRight(25, x));
|
||||||
|
}
|
||||||
|
function sha256_sigma0(x) {
|
||||||
|
return (rotateRight(7, x) ^ rotateRight(18, x) ^ (x >>> 3));
|
||||||
|
}
|
||||||
|
function sha256_sigma1(x) {
|
||||||
|
return (rotateRight(17, x) ^ rotateRight(19, x) ^ (x >>> 10));
|
||||||
|
}
|
||||||
|
function sha256_expand(W, j) {
|
||||||
|
return (W[j&0x0f] += sha256_sigma1(W[(j+14)&0x0f]) + W[(j+9)&0x0f] +
|
||||||
|
sha256_sigma0(W[(j+1)&0x0f]));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Hash constant words K: */
|
||||||
|
var K256 = new Array(
|
||||||
|
0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5,
|
||||||
|
0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
|
||||||
|
0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3,
|
||||||
|
0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
|
||||||
|
0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc,
|
||||||
|
0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
|
||||||
|
0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7,
|
||||||
|
0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
|
||||||
|
0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13,
|
||||||
|
0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
|
||||||
|
0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3,
|
||||||
|
0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
|
||||||
|
0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5,
|
||||||
|
0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
|
||||||
|
0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208,
|
||||||
|
0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2
|
||||||
|
);
|
||||||
|
|
||||||
|
/* global arrays */
|
||||||
|
var ihash, count, buffer;
|
||||||
|
var sha256_hex_digits = "0123456789abcdef";
|
||||||
|
|
||||||
|
/* Add 32-bit integers with 16-bit operations (bug in some JS-interpreters:
|
||||||
|
overflow) */
|
||||||
|
function safe_add(x, y)
|
||||||
|
{
|
||||||
|
var lsw = (x & 0xffff) + (y & 0xffff);
|
||||||
|
var msw = (x >> 16) + (y >> 16) + (lsw >> 16);
|
||||||
|
return (msw << 16) | (lsw & 0xffff);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Initialise the SHA256 computation */
|
||||||
|
function sha256_init() {
|
||||||
|
ihash = new Array(8);
|
||||||
|
count = new Array(2);
|
||||||
|
buffer = new Array(64);
|
||||||
|
count[0] = count[1] = 0;
|
||||||
|
ihash[0] = 0x6a09e667;
|
||||||
|
ihash[1] = 0xbb67ae85;
|
||||||
|
ihash[2] = 0x3c6ef372;
|
||||||
|
ihash[3] = 0xa54ff53a;
|
||||||
|
ihash[4] = 0x510e527f;
|
||||||
|
ihash[5] = 0x9b05688c;
|
||||||
|
ihash[6] = 0x1f83d9ab;
|
||||||
|
ihash[7] = 0x5be0cd19;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Transform a 512-bit message block */
|
||||||
|
function sha256_transform() {
|
||||||
|
var a, b, c, d, e, f, g, h, T1, T2;
|
||||||
|
var W = new Array(16);
|
||||||
|
|
||||||
|
/* Initialize registers with the previous intermediate value */
|
||||||
|
a = ihash[0];
|
||||||
|
b = ihash[1];
|
||||||
|
c = ihash[2];
|
||||||
|
d = ihash[3];
|
||||||
|
e = ihash[4];
|
||||||
|
f = ihash[5];
|
||||||
|
g = ihash[6];
|
||||||
|
h = ihash[7];
|
||||||
|
|
||||||
|
/* make 32-bit words */
|
||||||
|
for(var i=0; i<16; i++)
|
||||||
|
W[i] = ((buffer[(i<<2)+3]) | (buffer[(i<<2)+2] << 8) | (buffer[(i<<2)+1]
|
||||||
|
<< 16) | (buffer[i<<2] << 24));
|
||||||
|
|
||||||
|
for(var j=0; j<64; j++) {
|
||||||
|
T1 = h + sha256_Sigma1(e) + choice(e, f, g) + K256[j];
|
||||||
|
if(j < 16) T1 += W[j];
|
||||||
|
else T1 += sha256_expand(W, j);
|
||||||
|
T2 = sha256_Sigma0(a) + majority(a, b, c);
|
||||||
|
h = g;
|
||||||
|
g = f;
|
||||||
|
f = e;
|
||||||
|
e = safe_add(d, T1);
|
||||||
|
d = c;
|
||||||
|
c = b;
|
||||||
|
b = a;
|
||||||
|
a = safe_add(T1, T2);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Compute the current intermediate hash value */
|
||||||
|
ihash[0] += a;
|
||||||
|
ihash[1] += b;
|
||||||
|
ihash[2] += c;
|
||||||
|
ihash[3] += d;
|
||||||
|
ihash[4] += e;
|
||||||
|
ihash[5] += f;
|
||||||
|
ihash[6] += g;
|
||||||
|
ihash[7] += h;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Read the next chunk of data and update the SHA256 computation */
|
||||||
|
function sha256_update(data, inputLen) {
|
||||||
|
var i, index, curpos = 0;
|
||||||
|
/* Compute number of bytes mod 64 */
|
||||||
|
index = ((count[0] >> 3) & 0x3f);
|
||||||
|
var remainder = (inputLen & 0x3f);
|
||||||
|
|
||||||
|
/* Update number of bits */
|
||||||
|
if ((count[0] += (inputLen << 3)) < (inputLen << 3)) count[1]++;
|
||||||
|
count[1] += (inputLen >> 29);
|
||||||
|
|
||||||
|
/* Transform as many times as possible */
|
||||||
|
for(i=0; i+63<inputLen; i+=64) {
|
||||||
|
for(var j=index; j<64; j++)
|
||||||
|
buffer[j] = data.charCodeAt(curpos++);
|
||||||
|
sha256_transform();
|
||||||
|
index = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Buffer remaining input */
|
||||||
|
for(var j=0; j<remainder; j++)
|
||||||
|
buffer[j] = data.charCodeAt(curpos++);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Finish the computation by operations such as padding */
|
||||||
|
function sha256_final() {
|
||||||
|
var index = ((count[0] >> 3) & 0x3f);
|
||||||
|
buffer[index++] = 0x80;
|
||||||
|
if(index <= 56) {
|
||||||
|
for(var i=index; i<56; i++)
|
||||||
|
buffer[i] = 0;
|
||||||
|
} else {
|
||||||
|
for(var i=index; i<64; i++)
|
||||||
|
buffer[i] = 0;
|
||||||
|
sha256_transform();
|
||||||
|
for(var i=0; i<56; i++)
|
||||||
|
buffer[i] = 0;
|
||||||
|
}
|
||||||
|
buffer[56] = (count[1] >>> 24) & 0xff;
|
||||||
|
buffer[57] = (count[1] >>> 16) & 0xff;
|
||||||
|
buffer[58] = (count[1] >>> 8) & 0xff;
|
||||||
|
buffer[59] = count[1] & 0xff;
|
||||||
|
buffer[60] = (count[0] >>> 24) & 0xff;
|
||||||
|
buffer[61] = (count[0] >>> 16) & 0xff;
|
||||||
|
buffer[62] = (count[0] >>> 8) & 0xff;
|
||||||
|
buffer[63] = count[0] & 0xff;
|
||||||
|
sha256_transform();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Split the internal hash values into an array of bytes */
|
||||||
|
function sha256_encode_bytes() {
|
||||||
|
var j=0;
|
||||||
|
var output = new Array(32);
|
||||||
|
for(var i=0; i<8; i++) {
|
||||||
|
output[j++] = ((ihash[i] >>> 24) & 0xff);
|
||||||
|
output[j++] = ((ihash[i] >>> 16) & 0xff);
|
||||||
|
output[j++] = ((ihash[i] >>> 8) & 0xff);
|
||||||
|
output[j++] = (ihash[i] & 0xff);
|
||||||
|
}
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Get the internal hash as a hex string */
|
||||||
|
function sha256_encode_hex() {
|
||||||
|
var output = new String();
|
||||||
|
for(var i=0; i<8; i++) {
|
||||||
|
for(var j=28; j>=0; j-=4)
|
||||||
|
output += sha256_hex_digits.charAt((ihash[i] >>> j) & 0x0f);
|
||||||
|
}
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Main function: returns a hex string representing the SHA256 value of the
|
||||||
|
given data */
|
||||||
|
function sha256_digest(data) {
|
||||||
|
sha256_init();
|
||||||
|
sha256_update(data, data.length);
|
||||||
|
sha256_final();
|
||||||
|
return sha256_encode_hex();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* test if the JS-interpreter is working properly */
|
||||||
|
function sha256_self_test() {
|
||||||
|
return sha256_digest("message digest") ==
|
||||||
|
"f7846f55cf23e14eebeab5b4e1550cad5b509e3348fbc4efa3a1413d393cb650";
|
||||||
|
}
|
|
@ -1679,8 +1679,9 @@ bool DomainServer::isAuthenticatedRequest(HTTPConnection* connection, const QUrl
|
||||||
QString settingsUsername = valueForKeyPath(settingsMap, BASIC_AUTH_USERNAME_KEY_PATH)->toString();
|
QString settingsUsername = valueForKeyPath(settingsMap, BASIC_AUTH_USERNAME_KEY_PATH)->toString();
|
||||||
const QVariant* settingsPasswordVariant = valueForKeyPath(settingsMap, BASIC_AUTH_PASSWORD_KEY_PATH);
|
const QVariant* settingsPasswordVariant = valueForKeyPath(settingsMap, BASIC_AUTH_PASSWORD_KEY_PATH);
|
||||||
QString settingsPassword = settingsPasswordVariant ? settingsPasswordVariant->toString() : "";
|
QString settingsPassword = settingsPasswordVariant ? settingsPasswordVariant->toString() : "";
|
||||||
|
QString hexHeaderPassword = QCryptographicHash::hash(headerPassword.toUtf8(), QCryptographicHash::Sha256).toHex();
|
||||||
|
|
||||||
if (settingsUsername == headerUsername && headerPassword == settingsPassword) {
|
if (settingsUsername == headerUsername && hexHeaderPassword == settingsPassword) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -129,7 +129,9 @@ void DomainServerSettingsManager::setupConfigMap(const QStringList& argumentList
|
||||||
// reload the master and user config so that the merged config is right
|
// reload the master and user config so that the merged config is right
|
||||||
_configMap.loadMasterAndUserConfig(argumentList);
|
_configMap.loadMasterAndUserConfig(argumentList);
|
||||||
}
|
}
|
||||||
} else if (oldVersion < 1.1) {
|
}
|
||||||
|
|
||||||
|
if (oldVersion < 1.1) {
|
||||||
static const QString ENTITY_SERVER_SETTINGS_KEY = "entity_server_settings";
|
static const QString ENTITY_SERVER_SETTINGS_KEY = "entity_server_settings";
|
||||||
static const QString ENTITY_FILE_NAME_KEY = "persistFilename";
|
static const QString ENTITY_FILE_NAME_KEY = "persistFilename";
|
||||||
static const QString ENTITY_FILE_PATH_KEYPATH = ENTITY_SERVER_SETTINGS_KEY + ".persistFilePath";
|
static const QString ENTITY_FILE_PATH_KEYPATH = ENTITY_SERVER_SETTINGS_KEY + ".persistFilePath";
|
||||||
|
@ -165,6 +167,28 @@ void DomainServerSettingsManager::setupConfigMap(const QStringList& argumentList
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (oldVersion < 1.2) {
|
||||||
|
// This was prior to the base64 encoding of password for HTTP Basic Authentication.
|
||||||
|
// If we have a password in the previous settings file, make it base 64
|
||||||
|
static const QString BASIC_AUTH_PASSWORD_KEY_PATH { "security.http_password" };
|
||||||
|
|
||||||
|
QVariant* passwordVariant = valueForKeyPath(_configMap.getUserConfig(), BASIC_AUTH_PASSWORD_KEY_PATH);
|
||||||
|
|
||||||
|
if (passwordVariant && passwordVariant->canConvert(QMetaType::QString)) {
|
||||||
|
QString plaintextPassword = passwordVariant->toString();
|
||||||
|
|
||||||
|
qDebug() << "Migrating plaintext password to SHA256 hash in domain-server settings.";
|
||||||
|
|
||||||
|
*passwordVariant = QCryptographicHash::hash(plaintextPassword.toUtf8(), QCryptographicHash::Sha256).toHex();
|
||||||
|
|
||||||
|
// write the new settings to file
|
||||||
|
persistToFile();
|
||||||
|
|
||||||
|
// reload the master and user config so the merged config is correct
|
||||||
|
_configMap.loadMasterAndUserConfig(argumentList);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// write the current description version to our settings
|
// write the current description version to our settings
|
||||||
|
|
|
@ -12,7 +12,7 @@
|
||||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
/*global print, MyAvatar, Entities, AnimationCache, SoundCache, Scene, Camera, Overlays, Audio, HMD, AvatarList, AvatarManager, Controller, UndoStack, Window, Account, GlobalServices, Script, ScriptDiscoveryService, LODManager, Menu, Vec3, Quat, AudioDevice, Paths, Clipboard, Settings, XMLHttpRequest, randFloat, randInt, pointInExtents, vec3equal, setEntityCustomData, getEntityCustomData */
|
/*global print, MyAvatar, Entities, AnimationCache, SoundCache, Scene, Camera, Overlays, Audio, HMD, AvatarList, AvatarManager, Controller, UndoStack, Window, Account, GlobalServices, Script, ScriptDiscoveryService, LODManager, Menu, Vec3, Quat, AudioDevice, Paths, Clipboard, Settings, XMLHttpRequest, randFloat, randInt, pointInExtents, vec3equal, setEntityCustomData, getEntityCustomData */
|
||||||
|
|
||||||
Script.include("../libraries/utils.js");
|
Script.include("/~/libraries/utils.js");
|
||||||
|
|
||||||
|
|
||||||
//
|
//
|
||||||
|
@ -79,6 +79,7 @@ var NEAR_PICK_MAX_DISTANCE = 0.3; // max length of pick-ray for close grabbing t
|
||||||
var PICK_BACKOFF_DISTANCE = 0.2; // helps when hand is intersecting the grabble object
|
var PICK_BACKOFF_DISTANCE = 0.2; // helps when hand is intersecting the grabble object
|
||||||
var NEAR_GRABBING_KINEMATIC = true; // force objects to be kinematic when near-grabbed
|
var NEAR_GRABBING_KINEMATIC = true; // force objects to be kinematic when near-grabbed
|
||||||
var SHOW_GRAB_SPHERE = false; // draw a green sphere to show the grab search position and size
|
var SHOW_GRAB_SPHERE = false; // draw a green sphere to show the grab search position and size
|
||||||
|
var CHECK_TOO_FAR_UNEQUIP_TIME = 1.0; // seconds
|
||||||
|
|
||||||
//
|
//
|
||||||
// equip
|
// equip
|
||||||
|
@ -290,11 +291,13 @@ function MyController(hand) {
|
||||||
this.intersectionDistance = 0.0;
|
this.intersectionDistance = 0.0;
|
||||||
this.searchSphereDistance = DEFAULT_SEARCH_SPHERE_DISTANCE;
|
this.searchSphereDistance = DEFAULT_SEARCH_SPHERE_DISTANCE;
|
||||||
|
|
||||||
|
|
||||||
this.ignoreIK = false;
|
this.ignoreIK = false;
|
||||||
this.offsetPosition = Vec3.ZERO;
|
this.offsetPosition = Vec3.ZERO;
|
||||||
this.offsetRotation = Quat.IDENTITY;
|
this.offsetRotation = Quat.IDENTITY;
|
||||||
|
|
||||||
|
this.lastPickTime = 0;
|
||||||
|
this.lastUnequipCheckTime = 0;
|
||||||
|
|
||||||
var _this = this;
|
var _this = this;
|
||||||
|
|
||||||
this.update = function() {
|
this.update = function() {
|
||||||
|
@ -1523,18 +1526,30 @@ function MyController(hand) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (props.parentID == MyAvatar.sessionUUID &&
|
|
||||||
Vec3.length(props.localPosition) > NEAR_PICK_MAX_DISTANCE * 2.0) {
|
var now = Date.now();
|
||||||
// for whatever reason, the held/equipped entity has been pulled away. ungrab or unequip.
|
if (now - this.lastUnequipCheckTime > MSECS_PER_SEC * CHECK_TOO_FAR_UNEQUIP_TIME) {
|
||||||
print("handControllerGrab -- autoreleasing held or equipped item because it is far from hand." +
|
this.lastUnequipCheckTime = now;
|
||||||
props.parentID + " " + vec3toStr(props.position));
|
|
||||||
this.setState(STATE_RELEASE);
|
if (props.parentID == MyAvatar.sessionUUID &&
|
||||||
if (this.state == STATE_CONTINUE_NEAR_GRABBING) {
|
Vec3.length(props.localPosition) > NEAR_PICK_MAX_DISTANCE * 2.0) {
|
||||||
this.callEntityMethodOnGrabbed("releaseGrab");
|
var handPosition = this.getHandPosition();
|
||||||
} else { // (this.state == STATE_CONTINUE_EQUIP || this.state == STATE_CONTINUE_HOLD)
|
// the center of the equipped object being far from the hand isn't enough to autoequip -- we also
|
||||||
this.callEntityMethodOnGrabbed("releaseEquip");
|
// need to fail the findEntities test.
|
||||||
|
nearPickedCandidateEntities = Entities.findEntities(handPosition, GRAB_RADIUS);
|
||||||
|
if (nearPickedCandidateEntities.indexOf(this.grabbedEntity) == -1) {
|
||||||
|
// for whatever reason, the held/equipped entity has been pulled away. ungrab or unequip.
|
||||||
|
print("handControllerGrab -- autoreleasing held or equipped item because it is far from hand." +
|
||||||
|
props.parentID + " " + vec3toStr(props.position));
|
||||||
|
this.setState(STATE_RELEASE);
|
||||||
|
if (this.state == STATE_CONTINUE_NEAR_GRABBING) {
|
||||||
|
this.callEntityMethodOnGrabbed("releaseGrab");
|
||||||
|
} else { // (this.state == STATE_CONTINUE_EQUIP || this.state == STATE_CONTINUE_HOLD)
|
||||||
|
this.callEntityMethodOnGrabbed("releaseEquip");
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Keep track of the fingertip velocity to impart when we release the object.
|
// Keep track of the fingertip velocity to impart when we release the object.
|
||||||
|
|
|
@ -11,7 +11,7 @@
|
||||||
Script.load("away.js");
|
Script.load("away.js");
|
||||||
Script.load("progress.js");
|
Script.load("progress.js");
|
||||||
Script.load("edit.js");
|
Script.load("edit.js");
|
||||||
Script.load("marketplace.js");
|
Script.load("examples.js");
|
||||||
Script.load("selectAudioDevice.js");
|
Script.load("selectAudioDevice.js");
|
||||||
Script.load("notifications.js");
|
Script.load("notifications.js");
|
||||||
Script.load("users.js");
|
Script.load("users.js");
|
||||||
|
|
|
@ -98,10 +98,21 @@ function seekToLookAt() {
|
||||||
}
|
}
|
||||||
|
|
||||||
function autoHideReticle() {
|
function autoHideReticle() {
|
||||||
|
var now = Date.now();
|
||||||
|
|
||||||
|
// sometimes we don't actually get mouse move messages (for example, if the focus has been set
|
||||||
|
// to an overlay or web page 'overlay') in but the mouse can still be moving, and we don't want
|
||||||
|
// to autohide in these cases, so we will take this opportunity to also check if the reticle
|
||||||
|
// position has changed.
|
||||||
|
if (lastMouseX != Reticle.position.x || lastMouseY != Reticle.position.y) {
|
||||||
|
lastMouseMoveOrClick = now;
|
||||||
|
lastMouseX = Reticle.position.x;
|
||||||
|
lastMouseY = Reticle.position.y;
|
||||||
|
}
|
||||||
|
|
||||||
// if we haven't moved in a long period of time, and we're not pointing at some
|
// if we haven't moved in a long period of time, and we're not pointing at some
|
||||||
// system overlay (like a window), then hide the reticle
|
// system overlay (like a window), then hide the reticle
|
||||||
if (Reticle.visible && !Reticle.pointingAtSystemOverlay) {
|
if (Reticle.visible && !Reticle.pointingAtSystemOverlay) {
|
||||||
var now = Date.now();
|
|
||||||
var timeSinceLastMouseMove = now - lastMouseMoveOrClick;
|
var timeSinceLastMouseMove = now - lastMouseMoveOrClick;
|
||||||
if (timeSinceLastMouseMove > HIDE_STATIC_MOUSE_AFTER) {
|
if (timeSinceLastMouseMove > HIDE_STATIC_MOUSE_AFTER) {
|
||||||
Reticle.visible = false;
|
Reticle.visible = false;
|
||||||
|
|
78
examples/example/audio/largeHall.js
Normal file
78
examples/example/audio/largeHall.js
Normal file
|
@ -0,0 +1,78 @@
|
||||||
|
//
|
||||||
|
// largeHall.js
|
||||||
|
// examples
|
||||||
|
//
|
||||||
|
// Created by Freidrica on 4/1/16.
|
||||||
|
// Copyright 2016 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// This entity script invokes reverb upon entering an entity acting as a trigger zone
|
||||||
|
|
||||||
|
//
|
||||||
|
// 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 _this = this;
|
||||||
|
print("EBL PRELOADING NEW VERSION ")
|
||||||
|
var audioOptions = new AudioEffectOptions({
|
||||||
|
bandwidth: 7000,
|
||||||
|
preDelay: 80,
|
||||||
|
lateDelay: 0,
|
||||||
|
reverbTime: 3,
|
||||||
|
earlyDiffusion: 100,
|
||||||
|
lateDiffusion: 100,
|
||||||
|
roomSize: 50,
|
||||||
|
density: 100,
|
||||||
|
bassMult: 1.5,
|
||||||
|
bassFreq: 250,
|
||||||
|
highGain: -12,
|
||||||
|
highFreq: 3000,
|
||||||
|
modRate: 2.3,
|
||||||
|
modDepth: 50,
|
||||||
|
earlyGain: -12,
|
||||||
|
lateGain: -12,
|
||||||
|
earlyMixLeft: 20,
|
||||||
|
earlyMixRight: 20,
|
||||||
|
lateMixLeft: 90,
|
||||||
|
lateMixRight: 90,
|
||||||
|
wetDryMix: 90,
|
||||||
|
});
|
||||||
|
|
||||||
|
function setter(name) {
|
||||||
|
return function(value) {
|
||||||
|
audioOptions[name] = value;
|
||||||
|
AudioDevice.setReverbOptions(audioOptions);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getter(name) {
|
||||||
|
return function() {
|
||||||
|
return audioOptions[name];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function displayer(units) {
|
||||||
|
return function(value) {
|
||||||
|
return (value).toFixed(1) + units;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function scriptEnding() {
|
||||||
|
AudioDevice.setReverb(false);
|
||||||
|
print("Reverb is OFF.");
|
||||||
|
}
|
||||||
|
_this.enterEntity = function(entityID) {
|
||||||
|
print('EBL I am insiude');
|
||||||
|
|
||||||
|
AudioDevice.setReverbOptions(audioOptions);
|
||||||
|
AudioDevice.setReverb(true);
|
||||||
|
print("Reverb is ON.");
|
||||||
|
};
|
||||||
|
|
||||||
|
_this.leaveEntity = function(entityID) {
|
||||||
|
print('EBL I am outsidee');
|
||||||
|
AudioDevice.setReverb(false);
|
||||||
|
print("Reverb is OFF.");
|
||||||
|
// Messages.sendMessage('PlayBackOnAssignment', 'BowShootingGameWelcome');
|
||||||
|
};
|
||||||
|
});
|
78
examples/example/audio/smallRoom.js
Normal file
78
examples/example/audio/smallRoom.js
Normal file
|
@ -0,0 +1,78 @@
|
||||||
|
//
|
||||||
|
// smallRoom.js
|
||||||
|
// examples
|
||||||
|
//
|
||||||
|
// Created by Freidrica on 4/1/16.
|
||||||
|
// Copyright 2016 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// This entity script invokes reverb upon entering an entity acting as a trigger zone
|
||||||
|
|
||||||
|
//
|
||||||
|
// 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 _this = this;
|
||||||
|
print("EBL PRELOADING NEW VERSION ")
|
||||||
|
var audioOptions = new AudioEffectOptions({
|
||||||
|
bandwidth: 7000,
|
||||||
|
preDelay: 20,
|
||||||
|
lateDelay: 0,
|
||||||
|
reverbTime: 1.5,
|
||||||
|
earlyDiffusion: 100,
|
||||||
|
lateDiffusion: 100,
|
||||||
|
roomSize: 50,
|
||||||
|
density: 100,
|
||||||
|
bassMult: 1.5,
|
||||||
|
bassFreq: 250,
|
||||||
|
highGain: -12,
|
||||||
|
highFreq: 3000,
|
||||||
|
modRate: 2.3,
|
||||||
|
modDepth: 50,
|
||||||
|
earlyGain: -24,
|
||||||
|
lateGain: -24,
|
||||||
|
earlyMixLeft: 20,
|
||||||
|
earlyMixRight: 20,
|
||||||
|
lateMixLeft: 90,
|
||||||
|
lateMixRight: 90,
|
||||||
|
wetDryMix: 70,
|
||||||
|
});
|
||||||
|
|
||||||
|
function setter(name) {
|
||||||
|
return function(value) {
|
||||||
|
audioOptions[name] = value;
|
||||||
|
AudioDevice.setReverbOptions(audioOptions);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getter(name) {
|
||||||
|
return function() {
|
||||||
|
return audioOptions[name];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function displayer(units) {
|
||||||
|
return function(value) {
|
||||||
|
return (value).toFixed(1) + units;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function scriptEnding() {
|
||||||
|
AudioDevice.setReverb(false);
|
||||||
|
print("Reverb is OFF.");
|
||||||
|
}
|
||||||
|
_this.enterEntity = function(entityID) {
|
||||||
|
print('EBL I am insiude');
|
||||||
|
// create a slider for each parameter
|
||||||
|
AudioDevice.setReverbOptions(audioOptions);
|
||||||
|
AudioDevice.setReverb(true);
|
||||||
|
print("Reverb is ON.");
|
||||||
|
};
|
||||||
|
|
||||||
|
_this.leaveEntity = function(entityID) {
|
||||||
|
print('EBL I am outside');
|
||||||
|
AudioDevice.setReverb(false);
|
||||||
|
print("Reverb is OFF.");
|
||||||
|
};
|
||||||
|
});
|
|
@ -1,5 +1,5 @@
|
||||||
//
|
//
|
||||||
// marketplace.js
|
// examples.js
|
||||||
// examples
|
// examples
|
||||||
//
|
//
|
||||||
// Created by Eric Levin on 8 Jan 2016
|
// Created by Eric Levin on 8 Jan 2016
|
||||||
|
@ -16,9 +16,9 @@ Script.include([
|
||||||
HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/";
|
HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/";
|
||||||
var toolIconUrl = HIFI_PUBLIC_BUCKET + "images/tools/";
|
var toolIconUrl = HIFI_PUBLIC_BUCKET + "images/tools/";
|
||||||
|
|
||||||
var MARKETPLACE_URL = "https://metaverse.highfidelity.com/marketplace";
|
var EXAMPLES_URL = "https://metaverse.highfidelity.com/examples";
|
||||||
var marketplaceWindow = new OverlayWebWindow({
|
var examplesWindow = new OverlayWebWindow({
|
||||||
title: 'Marketplace',
|
title: 'Examples',
|
||||||
source: "about:blank",
|
source: "about:blank",
|
||||||
width: 900,
|
width: 900,
|
||||||
height: 700,
|
height: 700,
|
||||||
|
@ -29,43 +29,43 @@ var toolHeight = 50;
|
||||||
var toolWidth = 50;
|
var toolWidth = 50;
|
||||||
|
|
||||||
|
|
||||||
function showMarketplace(marketplaceID) {
|
function showExamples(marketplaceID) {
|
||||||
var url = MARKETPLACE_URL;
|
var url = EXAMPLES_URL;
|
||||||
if (marketplaceID) {
|
if (marketplaceID) {
|
||||||
url = url + "/items/" + marketplaceID;
|
url = url + "/items/" + marketplaceID;
|
||||||
}
|
}
|
||||||
print("setting marketplace URL to " + url);
|
print("setting examples URL to " + url);
|
||||||
marketplaceWindow.setURL(url);
|
examplesWindow.setURL(url);
|
||||||
marketplaceWindow.setVisible(true);
|
examplesWindow.setVisible(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
function hideMarketplace() {
|
function hideExamples() {
|
||||||
marketplaceWindow.setVisible(false);
|
examplesWindow.setVisible(false);
|
||||||
marketplaceWindow.setURL("about:blank");
|
examplesWindow.setURL("about:blank");
|
||||||
}
|
}
|
||||||
|
|
||||||
function toggleMarketplace() {
|
function toggleExamples() {
|
||||||
if (marketplaceWindow.visible) {
|
if (examplesWindow.visible) {
|
||||||
hideMarketplace();
|
hideExamples();
|
||||||
} else {
|
} else {
|
||||||
showMarketplace();
|
showExamples();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var toolBar = (function() {
|
var toolBar = (function() {
|
||||||
var that = {},
|
var that = {},
|
||||||
toolBar,
|
toolBar,
|
||||||
browseMarketplaceButton;
|
browseExamplesButton;
|
||||||
|
|
||||||
function initialize() {
|
function initialize() {
|
||||||
toolBar = new ToolBar(0, 0, ToolBar.VERTICAL, "highfidelity.marketplace.toolbar", function(windowDimensions, toolbar) {
|
toolBar = new ToolBar(0, 0, ToolBar.VERTICAL, "highfidelity.examples.toolbar", function(windowDimensions, toolbar) {
|
||||||
return {
|
return {
|
||||||
x: windowDimensions.x - 8 - toolbar.width,
|
x: windowDimensions.x - 8 - toolbar.width,
|
||||||
y: 135
|
y: 135
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
browseMarketplaceButton = toolBar.addTool({
|
browseExamplesButton = toolBar.addTool({
|
||||||
imageURL: toolIconUrl + "market-01.svg",
|
imageURL: toolIconUrl + "examples-01.svg",
|
||||||
subImage: {
|
subImage: {
|
||||||
x: 0,
|
x: 0,
|
||||||
y: Tool.IMAGE_WIDTH,
|
y: Tool.IMAGE_WIDTH,
|
||||||
|
@ -79,10 +79,10 @@ var toolBar = (function() {
|
||||||
showButtonDown: true
|
showButtonDown: true
|
||||||
});
|
});
|
||||||
|
|
||||||
toolBar.showTool(browseMarketplaceButton, true);
|
toolBar.showTool(browseExamplesButton, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
var browseMarketplaceButtonDown = false;
|
var browseExamplesButtonDown = false;
|
||||||
that.mousePressEvent = function(event) {
|
that.mousePressEvent = function(event) {
|
||||||
var clickedOverlay,
|
var clickedOverlay,
|
||||||
url,
|
url,
|
||||||
|
@ -98,10 +98,8 @@ var toolBar = (function() {
|
||||||
y: event.y
|
y: event.y
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (browseExamplesButton === toolBar.clicked(clickedOverlay)) {
|
||||||
|
toggleExamples();
|
||||||
if (browseMarketplaceButton === toolBar.clicked(clickedOverlay)) {
|
|
||||||
toggleMarketplace();
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -112,7 +110,7 @@ var toolBar = (function() {
|
||||||
var handled = false;
|
var handled = false;
|
||||||
|
|
||||||
|
|
||||||
if (browseMarketplaceButtonDown) {
|
if (browseExamplesButtonDown) {
|
||||||
var clickedOverlay = Overlays.getOverlayAtPoint({
|
var clickedOverlay = Overlays.getOverlayAtPoint({
|
||||||
x: event.x,
|
x: event.x,
|
||||||
y: event.y
|
y: event.y
|
||||||
|
@ -120,7 +118,7 @@ var toolBar = (function() {
|
||||||
}
|
}
|
||||||
|
|
||||||
newModelButtonDown = false;
|
newModelButtonDown = false;
|
||||||
browseMarketplaceButtonDown = false;
|
browseExamplesButtonDown = false;
|
||||||
|
|
||||||
return handled;
|
return handled;
|
||||||
}
|
}
|
|
@ -10,43 +10,50 @@
|
||||||
|
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: Raleway-Regular;
|
font-family: Raleway-Regular;
|
||||||
src: url(../../resources/fonts/Raleway-Regular.ttf), /* Production */
|
src: url(../../resources/fonts/Raleway-Regular.ttf), /* Windows production */
|
||||||
url(../../interface/resources/fonts/Raleway-Regular.ttf); /* Development */
|
url(../../fonts/Raleway-Regular.ttf), /* OSX production */
|
||||||
|
url(../../interface/resources/fonts/Raleway-Regular.ttf); /* Development, running script in /HiFi/examples */
|
||||||
}
|
}
|
||||||
|
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: Raleway-Light;
|
font-family: Raleway-Light;
|
||||||
src: url(../../resources/fonts/Raleway-Light.ttf),
|
src: url(../../resources/fonts/Raleway-Light.ttf),
|
||||||
|
url(../../fonts/Raleway-Light.ttf),
|
||||||
url(../../interface/resources/fonts/Raleway-Light.ttf);
|
url(../../interface/resources/fonts/Raleway-Light.ttf);
|
||||||
}
|
}
|
||||||
|
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: Raleway-Bold;
|
font-family: Raleway-Bold;
|
||||||
src: url(../../resources/fonts/Raleway-Bold.ttf),
|
src: url(../../resources/fonts/Raleway-Bold.ttf),
|
||||||
|
url(../../fonts/Raleway-Bold.ttf),
|
||||||
url(../../interface/resources/fonts/Raleway-Bold.ttf);
|
url(../../interface/resources/fonts/Raleway-Bold.ttf);
|
||||||
}
|
}
|
||||||
|
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: Raleway-SemiBold;
|
font-family: Raleway-SemiBold;
|
||||||
src: url(../../resources/fonts/Raleway-SemiBold.ttf),
|
src: url(../../resources/fonts/Raleway-SemiBold.ttf),
|
||||||
|
url(../../fonts/Raleway-SemiBold.ttf),
|
||||||
url(../../interface/resources/fonts/Raleway-SemiBold.ttf);
|
url(../../interface/resources/fonts/Raleway-SemiBold.ttf);
|
||||||
}
|
}
|
||||||
|
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: FiraSans-SemiBold;
|
font-family: FiraSans-SemiBold;
|
||||||
src: url(../../resources/fonts/FiraSans-SemiBold.ttf),
|
src: url(../../resources/fonts/FiraSans-SemiBold.ttf),
|
||||||
|
url(../../fonts/FiraSans-SemiBold.ttf),
|
||||||
url(../../interface/resources/fonts/FiraSans-SemiBold.ttf);
|
url(../../interface/resources/fonts/FiraSans-SemiBold.ttf);
|
||||||
}
|
}
|
||||||
|
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: AnonymousPro-Regular;
|
font-family: AnonymousPro-Regular;
|
||||||
src: url(../../resources/fonts/AnonymousPro-Regular.ttf),
|
src: url(../../resources/fonts/AnonymousPro-Regular.ttf),
|
||||||
|
url(../../fonts/AnonymousPro-Regular.ttf),
|
||||||
url(../../interface/resources/fonts/AnonymousPro-Regular.ttf);
|
url(../../interface/resources/fonts/AnonymousPro-Regular.ttf);
|
||||||
}
|
}
|
||||||
|
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: HiFi-Glyphs;
|
font-family: HiFi-Glyphs;
|
||||||
src: url(../../resources/fonts/hifi-glyphs.ttf),
|
src: url(../../resources/fonts/hifi-glyphs.ttf),
|
||||||
|
url(../../fonts/hifi-glyphs.ttf),
|
||||||
url(../../interface/resources/fonts/hifi-glyphs.ttf);
|
url(../../interface/resources/fonts/hifi-glyphs.ttf);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -69,8 +76,10 @@ body {
|
||||||
-moz-user-select: none;
|
-moz-user-select: none;
|
||||||
-ms-user-select: none;
|
-ms-user-select: none;
|
||||||
user-select: none;
|
user-select: none;
|
||||||
}
|
|
||||||
|
|
||||||
|
overflow-x: hidden;
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
|
||||||
table {
|
table {
|
||||||
font-family: FiraSans-SemiBold;
|
font-family: FiraSans-SemiBold;
|
||||||
|
@ -231,11 +240,11 @@ input[type="text"] {
|
||||||
input[type="number"] {
|
input[type="number"] {
|
||||||
position: relative;
|
position: relative;
|
||||||
height: 28px;
|
height: 28px;
|
||||||
width: 120px;
|
width: 124px;
|
||||||
}
|
}
|
||||||
|
|
||||||
input[type=number] {
|
input[type=number] {
|
||||||
padding-right: 6px;
|
padding-right: 3px;
|
||||||
}
|
}
|
||||||
input[type=number]::-webkit-inner-spin-button {
|
input[type=number]::-webkit-inner-spin-button {
|
||||||
-webkit-appearance: none;
|
-webkit-appearance: none;
|
||||||
|
@ -243,6 +252,7 @@ input[type=number]::-webkit-inner-spin-button {
|
||||||
display: block;
|
display: block;
|
||||||
position: relative;
|
position: relative;
|
||||||
width: 10px;
|
width: 10px;
|
||||||
|
height: 100%;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
font-family: hifi-glyphs;
|
font-family: hifi-glyphs;
|
||||||
font-size: 50px;
|
font-size: 50px;
|
||||||
|
@ -452,6 +462,10 @@ input[type=checkbox]:checked + label:hover {
|
||||||
min-height: 29px;
|
min-height: 29px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.property.checkbox {
|
||||||
|
width: auto;
|
||||||
|
}
|
||||||
|
|
||||||
.property label {
|
.property label {
|
||||||
display: table-cell;
|
display: table-cell;
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
|
@ -657,7 +671,7 @@ div.refresh input[type="button"] {
|
||||||
padding-left: 25px;
|
padding-left: 25px;
|
||||||
}
|
}
|
||||||
.pyr .tuple input {
|
.pyr .tuple input {
|
||||||
padding-left: 45px;
|
padding-left: 40px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tuple div > label:first-child {
|
.tuple div > label:first-child {
|
||||||
|
@ -779,6 +793,7 @@ textarea:enabled[scrolling="true"]::-webkit-resizer {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
overflow-x: hidden;
|
overflow-x: hidden;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
|
box-sizing: border-box;
|
||||||
padding-top: 28px; /* Space for header and footer outside of scroll region. */
|
padding-top: 28px; /* Space for header and footer outside of scroll region. */
|
||||||
margin-top: 28px;
|
margin-top: 28px;
|
||||||
border-left: 2px solid #575757;
|
border-left: 2px solid #575757;
|
||||||
|
@ -797,6 +812,7 @@ textarea:enabled[scrolling="true"]::-webkit-resizer {
|
||||||
}
|
}
|
||||||
|
|
||||||
#entity-table thead {
|
#entity-table thead {
|
||||||
|
box-sizing: border-box;
|
||||||
border: 2px solid #575757;
|
border: 2px solid #575757;
|
||||||
border-top-left-radius: 7px;
|
border-top-left-radius: 7px;
|
||||||
border-top-right-radius: 7px;
|
border-top-right-radius: 7px;
|
||||||
|
@ -804,6 +820,7 @@ textarea:enabled[scrolling="true"]::-webkit-resizer {
|
||||||
}
|
}
|
||||||
|
|
||||||
#entity-table tfoot {
|
#entity-table tfoot {
|
||||||
|
box-sizing: border-box;
|
||||||
border: 2px solid #575757;
|
border: 2px solid #575757;
|
||||||
border-bottom-left-radius: 7px;
|
border-bottom-left-radius: 7px;
|
||||||
border-bottom-right-radius: 7px;
|
border-bottom-right-radius: 7px;
|
||||||
|
|
|
@ -254,7 +254,7 @@
|
||||||
|
|
||||||
function resize() {
|
function resize() {
|
||||||
// Take up available window space
|
// Take up available window space
|
||||||
elEntityTableScroll.style.height = window.innerHeight - 232;
|
elEntityTableScroll.style.height = window.innerHeight - 200;
|
||||||
|
|
||||||
// Update the widths of the header cells to match the body
|
// Update the widths of the header cells to match the body
|
||||||
var tds = document.querySelectorAll("#entity-table-body tr:first-child td");
|
var tds = document.querySelectorAll("#entity-table-body tr:first-child td");
|
||||||
|
|
|
@ -467,37 +467,6 @@
|
||||||
var elZTextureURL = document.getElementById("property-z-texture-url");
|
var elZTextureURL = document.getElementById("property-z-texture-url");
|
||||||
|
|
||||||
var elPreviewCameraButton = document.getElementById("preview-camera-button");
|
var elPreviewCameraButton = document.getElementById("preview-camera-button");
|
||||||
|
|
||||||
var urlUpdaters = document.getElementsByClassName("update-url-version");
|
|
||||||
var PARAM_REGEXP = /(?:\?)(\S+)/; // Check if this has any parameters.
|
|
||||||
var TIMESTAMP_REGEXP = /(&?HFTime=\d+)/;
|
|
||||||
|
|
||||||
var refreshEvent = function (event) {
|
|
||||||
var urlElement = event.target.parentElement.getElementsByTagName("INPUT")[0];
|
|
||||||
var content = urlElement.value;
|
|
||||||
var date = new Date();
|
|
||||||
var timeStamp = date.getTime();
|
|
||||||
|
|
||||||
if(content.length > 0){
|
|
||||||
if(PARAM_REGEXP.test(content)){
|
|
||||||
// Has params, so lets remove existing definition and append again.
|
|
||||||
content = content.replace(TIMESTAMP_REGEXP,"") + "&";
|
|
||||||
}else{
|
|
||||||
content += "?";
|
|
||||||
}
|
|
||||||
content = content.replace("?&","?");
|
|
||||||
urlElement.value = content + "HFTime=" + timeStamp;
|
|
||||||
}
|
|
||||||
|
|
||||||
var evt = document.createEvent("HTMLEvents");
|
|
||||||
evt.initEvent("change", true, true );
|
|
||||||
urlElement.dispatchEvent(evt);
|
|
||||||
};
|
|
||||||
|
|
||||||
for(var index = 0; index < urlUpdaters.length; index++){
|
|
||||||
var urlUpdater = urlUpdaters[index];
|
|
||||||
urlUpdater.addEventListener("click", refreshEvent, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (window.EventBridge !== undefined) {
|
if (window.EventBridge !== undefined) {
|
||||||
var properties;
|
var properties;
|
||||||
|
@ -1397,20 +1366,17 @@
|
||||||
<option value="3">Edged marching cubes</option>
|
<option value="3">Edged marching cubes</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div class="spatial-group poly-vox-section property url refresh">
|
<div class="spatial-group poly-vox-section property url ">
|
||||||
<label for="property-x-texture-url">X-axis texture URL</label>
|
<label for="property-x-texture-url">X-axis texture URL</label>
|
||||||
<input type="text" id="property-x-texture-url">
|
<input type="text" id="property-x-texture-url">
|
||||||
<input type="button" class="update-url-version glyph" value="F" />
|
|
||||||
</div>
|
</div>
|
||||||
<div class="spatial-group poly-vox-section property url refresh">
|
<div class="spatial-group poly-vox-section property url ">
|
||||||
<label for="property-y-texture-url">Y-axis texture URL</label>
|
<label for="property-y-texture-url">Y-axis texture URL</label>
|
||||||
<input type="text" id="property-y-texture-url">
|
<input type="text" id="property-y-texture-url">
|
||||||
<input type="button" class="update-url-version glyph" value="F" />
|
|
||||||
</div>
|
</div>
|
||||||
<div class="spatial-group poly-vox-section property url refresh">
|
<div class="spatial-group poly-vox-section property url ">
|
||||||
<label for="property-z-texture-url">Z-axis texture URL</label>
|
<label for="property-z-texture-url">Z-axis texture URL</label>
|
||||||
<input type="text" id="property-z-texture-url">
|
<input type="text" id="property-z-texture-url">
|
||||||
<input type="button" class="update-url-version glyph" value="F" />
|
|
||||||
</div>
|
</div>
|
||||||
<div class="spatial-group property pyr">
|
<div class="spatial-group property pyr">
|
||||||
<label>Rotation</label>
|
<label>Rotation</label>
|
||||||
|
@ -1545,16 +1511,15 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="behavior-group property url refresh">
|
<div class="behavior-group property url ">
|
||||||
<label for="property-collision-sound-url">Collision sound URL</label>
|
<label for="property-collision-sound-url">Collision sound URL</label>
|
||||||
<input type="text" id="property-collision-sound-url">
|
<input type="text" id="property-collision-sound-url">
|
||||||
<input type="button" class="update-url-version glyph" value="F" />
|
|
||||||
</div>
|
</div>
|
||||||
<div class="behavior-group property number">
|
<div class="behavior-group property number">
|
||||||
<label>Lifetime</label>
|
<label>Lifetime</label>
|
||||||
<input type="number" id="property-lifetime">
|
<input type="number" id="property-lifetime">
|
||||||
</div>
|
</div>
|
||||||
<div class="behavior-group property url refresh">
|
<div class="behavior-group property url ">
|
||||||
<!--
|
<!--
|
||||||
FIXME: If reload buttons at the end of each URL continue to work OK during beta, this reload button and associated
|
FIXME: If reload buttons at the end of each URL continue to work OK during beta, this reload button and associated
|
||||||
code should be removed.
|
code should be removed.
|
||||||
|
@ -1563,17 +1528,15 @@
|
||||||
-->
|
-->
|
||||||
<label for="property-script-url">Script URL</label>
|
<label for="property-script-url">Script URL</label>
|
||||||
<input type="text" id="property-script-url">
|
<input type="text" id="property-script-url">
|
||||||
<input type="button" class="update-url-version glyph" value="F" />
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
<div class="section-header model-group model-section zone-section">
|
<div class="section-header model-group model-section zone-section">
|
||||||
<label>Model</label><span>M</span>
|
<label>Model</label><span>M</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="model-group model-section property url refresh">
|
<div class="model-group model-section property url ">
|
||||||
<label for="property-model-url">Model URL</label>
|
<label for="property-model-url">Model URL</label>
|
||||||
<input type="text" id="property-model-url">
|
<input type="text" id="property-model-url">
|
||||||
<input type="button" class="update-url-version glyph" value="F" />
|
|
||||||
</div>
|
</div>
|
||||||
<div class="model-group model-section zone-section property dropdown">
|
<div class="model-group model-section zone-section property dropdown">
|
||||||
<label>Collision shape type</label>
|
<label>Collision shape type</label>
|
||||||
|
@ -1584,15 +1547,13 @@
|
||||||
<option value="compound">Compound</option>
|
<option value="compound">Compound</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div class="model-group model-section zone-section property url refresh">
|
<div class="model-group model-section zone-section property url ">
|
||||||
<label for="property-compound-shape-url">Compound shape URL</label>
|
<label for="property-compound-shape-url">Compound shape URL</label>
|
||||||
<input type="text" id="property-compound-shape-url">
|
<input type="text" id="property-compound-shape-url">
|
||||||
<input type="button" class="update-url-version glyph" value="F" />
|
|
||||||
</div>
|
</div>
|
||||||
<div class="model-group model-section property url refresh">
|
<div class="model-group model-section property url ">
|
||||||
<label for="property-model-animation-url">Animation URL</label>
|
<label for="property-model-animation-url">Animation URL</label>
|
||||||
<input type="text" id="property-model-animation-url">
|
<input type="text" id="property-model-animation-url">
|
||||||
<input type="button" class="update-url-version glyph" value="F" />
|
|
||||||
</div>
|
</div>
|
||||||
<div class="model-group model-section property checkbox">
|
<div class="model-group model-section property checkbox">
|
||||||
<input type="checkbox" id="property-model-animation-playing">
|
<input type="checkbox" id="property-model-animation-playing">
|
||||||
|
@ -1693,10 +1654,9 @@
|
||||||
<label>Ambient intensity</label>
|
<label>Ambient intensity</label>
|
||||||
<input type="number" id="property-zone-key-ambient-intensity" min="0" max="10" step="0.1">
|
<input type="number" id="property-zone-key-ambient-intensity" min="0" max="10" step="0.1">
|
||||||
</div>
|
</div>
|
||||||
<div class="zone-group zone-section keylight-section property url refresh">
|
<div class="zone-group zone-section keylight-section property url ">
|
||||||
<label for="property-zone-key-ambient-url">Ambient URL</label>
|
<label for="property-zone-key-ambient-url">Ambient URL</label>
|
||||||
<input type="text" id="property-zone-key-ambient-url">
|
<input type="text" id="property-zone-key-ambient-url">
|
||||||
<input type="button" class="update-url-version glyph" value="F" />
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="sub-section-header zone-group zone-section stage-section">
|
<div class="sub-section-header zone-group zone-section stage-section">
|
||||||
|
@ -1745,20 +1705,18 @@
|
||||||
<div><input type="number" class="blue" id="property-zone-skybox-color-blue"><label for="property-zone-skybox-color-blue">Blue:</label></div>
|
<div><input type="number" class="blue" id="property-zone-skybox-color-blue"><label for="property-zone-skybox-color-blue">Blue:</label></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="zone-group zone-section skybox-section property url refresh">
|
<div class="zone-group zone-section skybox-section property url ">
|
||||||
<label for="property-zone-skybox-url">Skybox URL</label>
|
<label for="property-zone-skybox-url">Skybox URL</label>
|
||||||
<input type="text" id="property-zone-skybox-url">
|
<input type="text" id="property-zone-skybox-url">
|
||||||
<input type="button" class="update-url-version glyph" value="F" />
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
<div class="section-header web-group web-section">
|
<div class="section-header web-group web-section">
|
||||||
<label>Web</label><span>M</span>
|
<label>Web</label><span>M</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="web-group web-section property url refresh">
|
<div class="web-group web-section property url ">
|
||||||
<label for="property-web-source-url">Source URL</label>
|
<label for="property-web-source-url">Source URL</label>
|
||||||
<input type="text" id="property-web-source-url">
|
<input type="text" id="property-web-source-url">
|
||||||
<input type="button" class="update-url-version glyph" value="F" />
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
21
examples/utilities/cache/cacheStats.js
vendored
Normal file
21
examples/utilities/cache/cacheStats.js
vendored
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
//
|
||||||
|
// cacheStats.js
|
||||||
|
// examples/utilities/cache
|
||||||
|
//
|
||||||
|
// Zach Pomerantz, created on 4/1/2016.
|
||||||
|
// Copyright 2016 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
|
||||||
|
//
|
||||||
|
|
||||||
|
// Set up the qml ui
|
||||||
|
var qml = Script.resolvePath('stats.qml');
|
||||||
|
var window = new OverlayWindow({
|
||||||
|
title: 'Cache Stats',
|
||||||
|
source: qml,
|
||||||
|
width: 300,
|
||||||
|
height: 200
|
||||||
|
});
|
||||||
|
window.setPosition(500, 50);
|
||||||
|
window.closed.connect(function() { Script.stop(); });
|
77
examples/utilities/cache/stats.qml
vendored
Normal file
77
examples/utilities/cache/stats.qml
vendored
Normal file
|
@ -0,0 +1,77 @@
|
||||||
|
//
|
||||||
|
// stats.qml
|
||||||
|
// examples/utilities/cache
|
||||||
|
//
|
||||||
|
// Created by Zach Pomerantz on 4/1/2016
|
||||||
|
// Copyright 2016 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or https://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
import QtQuick 2.5
|
||||||
|
import QtQuick.Controls 1.4
|
||||||
|
import "../lib/plotperf"
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: root
|
||||||
|
anchors.fill: parent
|
||||||
|
|
||||||
|
property var caches: [["Animation", AnimationCache], ["Model", ModelCache], ["Texture", TextureCache], ["Sound", SoundCache]]
|
||||||
|
|
||||||
|
Grid {
|
||||||
|
id: grid
|
||||||
|
rows: root.caches.length; columns: 1; spacing: 8
|
||||||
|
anchors.fill: parent
|
||||||
|
|
||||||
|
Repeater {
|
||||||
|
id: repeater
|
||||||
|
|
||||||
|
model: root.caches
|
||||||
|
|
||||||
|
Row {
|
||||||
|
PlotPerf {
|
||||||
|
title: modelData[0] + " Count"
|
||||||
|
anchors.left: parent
|
||||||
|
height: (grid.height - (grid.spacing * (root.caches.length + 1))) / root.caches.length
|
||||||
|
width: grid.width / 2 - grid.spacing * 1.5
|
||||||
|
object: modelData[1]
|
||||||
|
valueNumDigits: "1"
|
||||||
|
plots: [
|
||||||
|
{
|
||||||
|
prop: "numTotal",
|
||||||
|
label: "total",
|
||||||
|
color: "#00B4EF"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
prop: "numCached",
|
||||||
|
label: "cached",
|
||||||
|
color: "#1AC567"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
PlotPerf {
|
||||||
|
title: modelData[0] + " Size"
|
||||||
|
anchors.right: parent
|
||||||
|
height: (grid.height - (grid.spacing * (root.caches.length + 1))) / root.caches.length
|
||||||
|
width: grid.width / 2 - grid.spacing * 1.5
|
||||||
|
object: modelData[1]
|
||||||
|
valueScale: 1048576
|
||||||
|
valueUnit: "Mb"
|
||||||
|
valueNumDigits: "1"
|
||||||
|
plots: [
|
||||||
|
{
|
||||||
|
prop: "sizeTotal",
|
||||||
|
label: "total",
|
||||||
|
color: "#00B4EF"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
prop: "sizeCached",
|
||||||
|
label: "cached",
|
||||||
|
color: "#1AC567"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -19,12 +19,8 @@ Item {
|
||||||
// The title of the graph
|
// The title of the graph
|
||||||
property string title
|
property string title
|
||||||
|
|
||||||
// THe object used as the default source object for the prop plots
|
// The object used as the default source object for the prop plots
|
||||||
property var object
|
property var object
|
||||||
|
|
||||||
// THis is my hack to get a property and assign it to a trigger var in order to get
|
|
||||||
// a signal called whenever the value changed
|
|
||||||
property var trigger
|
|
||||||
|
|
||||||
// Plots is an array of plot descriptor
|
// Plots is an array of plot descriptor
|
||||||
// a default plot descriptor expects the following object:
|
// a default plot descriptor expects the following object:
|
||||||
|
@ -55,45 +51,38 @@ Item {
|
||||||
property var tick : 0
|
property var tick : 0
|
||||||
|
|
||||||
function createValues() {
|
function createValues() {
|
||||||
print("trigger is: " + JSON.stringify(trigger))
|
for (var i =0; i < plots.length; i++) {
|
||||||
if (Array.isArray(plots)) {
|
var plot = plots[i];
|
||||||
for (var i =0; i < plots.length; i++) {
|
_values.push( {
|
||||||
var plot = plots[i];
|
object: (plot["object"] !== undefined ? plot["object"] : root.object),
|
||||||
print(" a pnew Plot:" + JSON.stringify(plot));
|
value: plot["prop"],
|
||||||
_values.push( {
|
valueMax: 1,
|
||||||
object: (plot["object"] !== undefined ? plot["object"] : root.object),
|
numSamplesConstantMax: 0,
|
||||||
value: plot["prop"],
|
valueHistory: new Array(),
|
||||||
valueMax: 1,
|
label: (plot["label"] !== undefined ? plot["label"] : ""),
|
||||||
numSamplesConstantMax: 0,
|
color: (plot["color"] !== undefined ? plot["color"] : "white"),
|
||||||
valueHistory: new Array(),
|
scale: (plot["scale"] !== undefined ? plot["scale"] : 1),
|
||||||
label: (plot["label"] !== undefined ? plot["label"] : ""),
|
unit: (plot["unit"] !== undefined ? plot["unit"] : valueUnit)
|
||||||
color: (plot["color"] !== undefined ? plot["color"] : "white"),
|
})
|
||||||
scale: (plot["scale"] !== undefined ? plot["scale"] : 1),
|
|
||||||
unit: (plot["unit"] !== undefined ? plot["unit"] : valueUnit)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
print("in creator" + JSON.stringify(_values));
|
pullFreshValues();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Component.onCompleted: {
|
Component.onCompleted: {
|
||||||
createValues();
|
createValues();
|
||||||
print(JSON.stringify(_values));
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function pullFreshValues() {
|
function pullFreshValues() {
|
||||||
//print("pullFreshValues");
|
// Wait until values are created to begin pulling
|
||||||
|
if (!_values) { return; }
|
||||||
|
|
||||||
var VALUE_HISTORY_SIZE = 100;
|
var VALUE_HISTORY_SIZE = 100;
|
||||||
var UPDATE_CANVAS_RATE = 20;
|
|
||||||
tick++;
|
tick++;
|
||||||
|
|
||||||
|
|
||||||
var currentValueMax = 0
|
var currentValueMax = 0
|
||||||
for (var i = 0; i < _values.length; i++) {
|
for (var i = 0; i < _values.length; i++) {
|
||||||
|
|
||||||
var currentVal = _values[i].object[_values[i].value] * _values[i].scale;
|
var currentVal = (+_values[i].object[_values[i].value]) * _values[i].scale;
|
||||||
|
|
||||||
_values[i].valueHistory.push(currentVal)
|
_values[i].valueHistory.push(currentVal)
|
||||||
_values[i].numSamplesConstantMax++;
|
_values[i].numSamplesConstantMax++;
|
||||||
|
@ -125,11 +114,13 @@ Item {
|
||||||
valueMax = currentValueMax;
|
valueMax = currentValueMax;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tick % UPDATE_CANVAS_RATE == 0) {
|
mycanvas.requestPaint()
|
||||||
mycanvas.requestPaint()
|
}
|
||||||
}
|
|
||||||
|
Timer {
|
||||||
|
interval: 100; running: true; repeat: true
|
||||||
|
onTriggered: pullFreshValues()
|
||||||
}
|
}
|
||||||
onTriggerChanged: pullFreshValues()
|
|
||||||
|
|
||||||
Canvas {
|
Canvas {
|
||||||
id: mycanvas
|
id: mycanvas
|
||||||
|
@ -165,9 +156,9 @@ Item {
|
||||||
ctx.fillStyle = val.color;
|
ctx.fillStyle = val.color;
|
||||||
var bestValue = val.valueHistory[val.valueHistory.length -1];
|
var bestValue = val.valueHistory[val.valueHistory.length -1];
|
||||||
ctx.textAlign = "right";
|
ctx.textAlign = "right";
|
||||||
ctx.fillText(displayValue(bestValue, val.unit), width, (num + 2) * lineHeight * 1.5);
|
ctx.fillText(displayValue(bestValue, val.unit), width, (num + 2) * lineHeight * 1);
|
||||||
ctx.textAlign = "left";
|
ctx.textAlign = "left";
|
||||||
ctx.fillText(val.label, 0, (num + 2) * lineHeight * 1.5);
|
ctx.fillText(val.label, 0, (num + 2) * lineHeight * 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
function displayTitle(ctx, text, maxVal) {
|
function displayTitle(ctx, text, maxVal) {
|
|
@ -10,7 +10,7 @@
|
||||||
//
|
//
|
||||||
import QtQuick 2.5
|
import QtQuick 2.5
|
||||||
import QtQuick.Controls 1.4
|
import QtQuick.Controls 1.4
|
||||||
import "plotperf"
|
import "../lib/plotperf"
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
id: statsUI
|
id: statsUI
|
||||||
|
@ -32,7 +32,6 @@ Item {
|
||||||
title: "Num Buffers"
|
title: "Num Buffers"
|
||||||
height: parent.evalEvenHeight()
|
height: parent.evalEvenHeight()
|
||||||
object: stats.config
|
object: stats.config
|
||||||
trigger: stats.config["bufferCPUCount"]
|
|
||||||
plots: [
|
plots: [
|
||||||
{
|
{
|
||||||
prop: "bufferCPUCount",
|
prop: "bufferCPUCount",
|
||||||
|
@ -50,7 +49,6 @@ Item {
|
||||||
title: "gpu::Buffer Memory"
|
title: "gpu::Buffer Memory"
|
||||||
height: parent.evalEvenHeight()
|
height: parent.evalEvenHeight()
|
||||||
object: stats.config
|
object: stats.config
|
||||||
trigger: stats.config["bufferCPUMemoryUsage"]
|
|
||||||
valueScale: 1048576
|
valueScale: 1048576
|
||||||
valueUnit: "Mb"
|
valueUnit: "Mb"
|
||||||
valueNumDigits: "1"
|
valueNumDigits: "1"
|
||||||
|
@ -71,7 +69,6 @@ Item {
|
||||||
title: "Num Textures"
|
title: "Num Textures"
|
||||||
height: parent.evalEvenHeight()
|
height: parent.evalEvenHeight()
|
||||||
object: stats.config
|
object: stats.config
|
||||||
trigger: stats.config["textureCPUCount"]
|
|
||||||
plots: [
|
plots: [
|
||||||
{
|
{
|
||||||
prop: "textureCPUCount",
|
prop: "textureCPUCount",
|
||||||
|
@ -94,7 +91,6 @@ Item {
|
||||||
title: "gpu::Texture Memory"
|
title: "gpu::Texture Memory"
|
||||||
height: parent.evalEvenHeight()
|
height: parent.evalEvenHeight()
|
||||||
object: stats.config
|
object: stats.config
|
||||||
trigger: stats.config["textureCPUMemoryUsage"]
|
|
||||||
valueScale: 1048576
|
valueScale: 1048576
|
||||||
valueUnit: "Mb"
|
valueUnit: "Mb"
|
||||||
valueNumDigits: "1"
|
valueNumDigits: "1"
|
||||||
|
@ -116,7 +112,6 @@ Item {
|
||||||
title: "Triangles"
|
title: "Triangles"
|
||||||
height: parent.evalEvenHeight()
|
height: parent.evalEvenHeight()
|
||||||
object: stats.config
|
object: stats.config
|
||||||
trigger: stats.config["frameTriangleCount"]
|
|
||||||
valueScale: 1000
|
valueScale: 1000
|
||||||
valueUnit: "K"
|
valueUnit: "K"
|
||||||
plots: [
|
plots: [
|
||||||
|
@ -138,7 +133,6 @@ Item {
|
||||||
title: "Drawcalls"
|
title: "Drawcalls"
|
||||||
height: parent.evalEvenHeight()
|
height: parent.evalEvenHeight()
|
||||||
object: stats.config
|
object: stats.config
|
||||||
trigger: stats.config["frameDrawcallCount"]
|
|
||||||
plots: [
|
plots: [
|
||||||
{
|
{
|
||||||
prop: "frameAPIDrawcallCount",
|
prop: "frameAPIDrawcallCount",
|
||||||
|
@ -168,7 +162,6 @@ Item {
|
||||||
title: "Items"
|
title: "Items"
|
||||||
height: parent.evalEvenHeight()
|
height: parent.evalEvenHeight()
|
||||||
object: parent.drawOpaqueConfig
|
object: parent.drawOpaqueConfig
|
||||||
trigger: Render.getConfig("DrawOpaqueDeferred")["numDrawn"]
|
|
||||||
plots: [
|
plots: [
|
||||||
{
|
{
|
||||||
object: Render.getConfig("DrawOpaqueDeferred"),
|
object: Render.getConfig("DrawOpaqueDeferred"),
|
||||||
|
|
|
@ -7,7 +7,8 @@ Script.include([
|
||||||
]);
|
]);
|
||||||
|
|
||||||
var isActive = false;
|
var isActive = false;
|
||||||
var toolIconUrl = "http://headache.hungry.com/~seth/hifi/";
|
var toolIconUrl = "https://s3-us-west-1.amazonaws.com/hifi-content/seth/production/icons/"
|
||||||
|
|
||||||
var toolHeight = 50;
|
var toolHeight = 50;
|
||||||
var toolWidth = 50;
|
var toolWidth = 50;
|
||||||
|
|
||||||
|
|
|
@ -53,6 +53,10 @@ IceServer::IceServer(int argc, char* argv[]) :
|
||||||
QTimer* inactivePeerTimer = new QTimer(this);
|
QTimer* inactivePeerTimer = new QTimer(this);
|
||||||
connect(inactivePeerTimer, &QTimer::timeout, this, &IceServer::clearInactivePeers);
|
connect(inactivePeerTimer, &QTimer::timeout, this, &IceServer::clearInactivePeers);
|
||||||
inactivePeerTimer->start(CLEAR_INACTIVE_PEERS_INTERVAL_MSECS);
|
inactivePeerTimer->start(CLEAR_INACTIVE_PEERS_INTERVAL_MSECS);
|
||||||
|
|
||||||
|
// handle public keys when they arrive from the QNetworkAccessManager
|
||||||
|
auto& networkAccessManager = NetworkAccessManager::getInstance();
|
||||||
|
connect(&networkAccessManager, &QNetworkAccessManager::finished, this, &IceServer::publicKeyReplyFinished);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IceServer::packetVersionMatch(const udt::Packet& packet) {
|
bool IceServer::packetVersionMatch(const udt::Packet& packet) {
|
||||||
|
@ -62,8 +66,6 @@ bool IceServer::packetVersionMatch(const udt::Packet& packet) {
|
||||||
if (headerVersion == versionForPacketType(headerType)) {
|
if (headerVersion == versionForPacketType(headerType)) {
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
qDebug() << "Packet version mismatch for packet" << headerType << " from" << packet.getSenderSockAddr();
|
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -202,7 +204,6 @@ bool IceServer::isVerifiedHeartbeat(const QUuid& domainID, const QByteArray& pla
|
||||||
void IceServer::requestDomainPublicKey(const QUuid& domainID) {
|
void IceServer::requestDomainPublicKey(const QUuid& domainID) {
|
||||||
// send a request to the metaverse API for the public key for this domain
|
// send a request to the metaverse API for the public key for this domain
|
||||||
auto& networkAccessManager = NetworkAccessManager::getInstance();
|
auto& networkAccessManager = NetworkAccessManager::getInstance();
|
||||||
connect(&networkAccessManager, &QNetworkAccessManager::finished, this, &IceServer::publicKeyReplyFinished);
|
|
||||||
|
|
||||||
QUrl publicKeyURL { NetworkingConstants::METAVERSE_SERVER_URL };
|
QUrl publicKeyURL { NetworkingConstants::METAVERSE_SERVER_URL };
|
||||||
QString publicKeyPath = QString("/api/v1/domains/%1/public_key").arg(uuidStringWithoutCurlyBraces(domainID));
|
QString publicKeyPath = QString("/api/v1/domains/%1/public_key").arg(uuidStringWithoutCurlyBraces(domainID));
|
||||||
|
|
|
@ -107,12 +107,15 @@ elseif(WIN32)
|
||||||
# add an executable that also has the icon itself and the configured rc file as resources
|
# add an executable that also has the icon itself and the configured rc file as resources
|
||||||
add_executable(${TARGET_NAME} WIN32 ${INTERFACE_SRCS} ${QM} ${CONFIGURE_ICON_RC_OUTPUT})
|
add_executable(${TARGET_NAME} WIN32 ${INTERFACE_SRCS} ${QM} ${CONFIGURE_ICON_RC_OUTPUT})
|
||||||
|
|
||||||
add_custom_command(
|
if ( NOT DEV_BUILD )
|
||||||
TARGET ${TARGET_NAME}
|
add_custom_command(
|
||||||
POST_BUILD
|
TARGET ${TARGET_NAME}
|
||||||
COMMAND "mt.exe" -manifest "${CMAKE_CURRENT_SOURCE_DIR}/interface.exe.manifest" -inputresource:"$<TARGET_FILE:${TARGET_NAME}>"\;\#1 -outputresource:"$<TARGET_FILE:${TARGET_NAME}>"\;\#1
|
POST_BUILD
|
||||||
COMMENT "Adding OS version support manifest to exe"
|
COMMAND "mt.exe" -manifest "${CMAKE_CURRENT_SOURCE_DIR}/interface.exe.manifest" -inputresource:"$<TARGET_FILE:${TARGET_NAME}>"\;\#1 -outputresource:"$<TARGET_FILE:${TARGET_NAME}>"\;\#1
|
||||||
)
|
COMMENT "Adding OS version support manifest to exe"
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
else()
|
else()
|
||||||
add_executable(${TARGET_NAME} ${INTERFACE_SRCS} ${QM})
|
add_executable(${TARGET_NAME} ${INTERFACE_SRCS} ${QM})
|
||||||
endif()
|
endif()
|
||||||
|
|
13
interface/resources/controllers/oculus_remote.json
Normal file
13
interface/resources/controllers/oculus_remote.json
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
{
|
||||||
|
"name": "Oculus Remote to Standard",
|
||||||
|
"channels": [
|
||||||
|
{ "from": "OculusRemote.Start", "to": "Actions.UiNavSelect" },
|
||||||
|
{ "from": "OculusRemote.Back", "to": "Actions.UiNavBack" },
|
||||||
|
{ "from": "OculusRemote.DU", "to": "Standard.DU" },
|
||||||
|
{ "from": "OculusRemote.DD", "to": "Standard.DD" },
|
||||||
|
{ "from": "OculusRemote.DL", "to": "Standard.DL" },
|
||||||
|
{ "from": "OculusRemote.DR", "to": "Standard.DR" }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
|
23
interface/resources/controllers/oculus_touch.json
Normal file
23
interface/resources/controllers/oculus_touch.json
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
{
|
||||||
|
"name": "Oculus Touch to Standard",
|
||||||
|
"channels": [
|
||||||
|
{ "from": "OculusTouch.LY", "filters": "invert", "to": "Standard.LY" },
|
||||||
|
{ "from": "OculusTouch.LX", "to": "Standard.LX" },
|
||||||
|
{ "from": "OculusTouch.LT", "to": "Standard.LT" },
|
||||||
|
|
||||||
|
{ "from": "OculusTouch.RY", "filters": "invert", "to": "Standard.RY" },
|
||||||
|
{ "from": "OculusTouch.RX", "to": "Standard.RX" },
|
||||||
|
|
||||||
|
{ "from": "OculusTouch.RT", "to": "Standard.RT" },
|
||||||
|
{ "from": "OculusTouch.RB", "to": "Standard.RB" },
|
||||||
|
{ "from": "OculusTouch.RS", "to": "Standard.RS" },
|
||||||
|
|
||||||
|
{ "from": "OculusTouch.LeftApplicationMenu", "to": "Standard.Back" },
|
||||||
|
{ "from": "OculusTouch.RightApplicationMenu", "to": "Standard.Start" },
|
||||||
|
|
||||||
|
{ "from": "OculusTouch.LeftHand", "to": "Standard.LeftHand" },
|
||||||
|
{ "from": "OculusTouch.RightHand", "to": "Standard.RightHand" }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,9 +2,6 @@
|
||||||
"name": "Standard to Action",
|
"name": "Standard to Action",
|
||||||
"when": "Application.NavigationFocused",
|
"when": "Application.NavigationFocused",
|
||||||
"channels": [
|
"channels": [
|
||||||
{ "disabled_from": { "makeAxis" : [ "Standard.DD", "Standard.DU" ] }, "to": "Actions.UiNavVertical" },
|
|
||||||
{ "disabled_from": { "makeAxis" : [ "Standard.DL", "Standard.DR" ] }, "to": "Actions.UiNavLateral" },
|
|
||||||
{ "disabled_from": { "makeAxis" : [ "Standard.LB", "Standard.RB" ] }, "to": "Actions.UiNavGroup" },
|
|
||||||
{ "from": "Standard.DU", "to": "Actions.UiNavVertical" },
|
{ "from": "Standard.DU", "to": "Actions.UiNavVertical" },
|
||||||
{ "from": "Standard.DD", "to": "Actions.UiNavVertical", "filters": "invert" },
|
{ "from": "Standard.DD", "to": "Actions.UiNavVertical", "filters": "invert" },
|
||||||
{ "from": "Standard.DL", "to": "Actions.UiNavLateral", "filters": "invert" },
|
{ "from": "Standard.DL", "to": "Actions.UiNavLateral", "filters": "invert" },
|
||||||
|
|
|
@ -30,7 +30,7 @@ Window {
|
||||||
title: "Edit"
|
title: "Edit"
|
||||||
property alias tabView: tabView
|
property alias tabView: tabView
|
||||||
implicitWidth: 520; implicitHeight: 695
|
implicitWidth: 520; implicitHeight: 695
|
||||||
minSize: Qt.vector2d(400, 500)
|
minSize: Qt.vector2d(412, 500)
|
||||||
|
|
||||||
HifiConstants { id: hifi }
|
HifiConstants { id: hifi }
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,8 @@ WebEngineView {
|
||||||
id: root
|
id: root
|
||||||
property var newUrl;
|
property var newUrl;
|
||||||
|
|
||||||
|
profile.httpUserAgent: "Mozilla/5.0 Chrome (HighFidelityInterface)"
|
||||||
|
|
||||||
Component.onCompleted: {
|
Component.onCompleted: {
|
||||||
console.log("Connecting JS messaging to Hifi Logging")
|
console.log("Connecting JS messaging to Hifi Logging")
|
||||||
// Ensure the JS from the web-engine makes it to our logging
|
// Ensure the JS from the web-engine makes it to our logging
|
||||||
|
|
|
@ -21,7 +21,7 @@ Desktop {
|
||||||
Component.onCompleted: {
|
Component.onCompleted: {
|
||||||
WebEngine.settings.javascriptCanOpenWindows = true;
|
WebEngine.settings.javascriptCanOpenWindows = true;
|
||||||
WebEngine.settings.javascriptCanAccessClipboard = false;
|
WebEngine.settings.javascriptCanAccessClipboard = false;
|
||||||
WebEngine.settings.spatialNavigationEnabled = true;
|
WebEngine.settings.spatialNavigationEnabled = false;
|
||||||
WebEngine.settings.localContentCanAccessRemoteUrls = true;
|
WebEngine.settings.localContentCanAccessRemoteUrls = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -135,14 +135,14 @@ public:
|
||||||
const ViewFrustum* getDisplayViewFrustum() const;
|
const ViewFrustum* getDisplayViewFrustum() const;
|
||||||
ViewFrustum* getShadowViewFrustum() override { return &_shadowViewFrustum; }
|
ViewFrustum* getShadowViewFrustum() override { return &_shadowViewFrustum; }
|
||||||
const OctreePacketProcessor& getOctreePacketProcessor() const { return _octreeProcessor; }
|
const OctreePacketProcessor& getOctreePacketProcessor() const { return _octreeProcessor; }
|
||||||
EntityTreeRenderer* getEntities() { return DependencyManager::get<EntityTreeRenderer>().data(); }
|
EntityTreeRenderer* getEntities() const { return DependencyManager::get<EntityTreeRenderer>().data(); }
|
||||||
QUndoStack* getUndoStack() { return &_undoStack; }
|
QUndoStack* getUndoStack() { return &_undoStack; }
|
||||||
MainWindow* getWindow() { return _window; }
|
MainWindow* getWindow() const { return _window; }
|
||||||
EntityTreePointer getEntityClipboard() { return _entityClipboard; }
|
EntityTreePointer getEntityClipboard() const { return _entityClipboard; }
|
||||||
EntityTreeRenderer* getEntityClipboardRenderer() { return &_entityClipboardRenderer; }
|
EntityTreeRenderer* getEntityClipboardRenderer() { return &_entityClipboardRenderer; }
|
||||||
EntityEditPacketSender* getEntityEditPacketSender() { return &_entityEditSender; }
|
EntityEditPacketSender* getEntityEditPacketSender() { return &_entityEditSender; }
|
||||||
|
|
||||||
ivec2 getMouse();
|
ivec2 getMouse() const;
|
||||||
|
|
||||||
FaceTracker* getActiveFaceTracker();
|
FaceTracker* getActiveFaceTracker();
|
||||||
FaceTracker* getSelectedFaceTracker();
|
FaceTracker* getSelectedFaceTracker();
|
||||||
|
@ -155,7 +155,7 @@ public:
|
||||||
|
|
||||||
bool isForeground() const { return _isForeground; }
|
bool isForeground() const { return _isForeground; }
|
||||||
|
|
||||||
uint32_t getFrameCount() { return _frameCount; }
|
uint32_t getFrameCount() const { return _frameCount; }
|
||||||
float getFps() const { return _fps; }
|
float getFps() const { return _fps; }
|
||||||
float getTargetFrameRate(); // frames/second
|
float getTargetFrameRate(); // frames/second
|
||||||
float getLastInstanteousFps() const { return _lastInstantaneousFps; }
|
float getLastInstanteousFps() const { return _lastInstantaneousFps; }
|
||||||
|
@ -179,7 +179,7 @@ public:
|
||||||
DisplayPlugin* getActiveDisplayPlugin();
|
DisplayPlugin* getActiveDisplayPlugin();
|
||||||
const DisplayPlugin* getActiveDisplayPlugin() const;
|
const DisplayPlugin* getActiveDisplayPlugin() const;
|
||||||
|
|
||||||
FileLogger* getLogger() { return _logger; }
|
FileLogger* getLogger() const { return _logger; }
|
||||||
|
|
||||||
glm::vec2 getViewportDimensions() const;
|
glm::vec2 getViewportDimensions() const;
|
||||||
|
|
||||||
|
@ -189,7 +189,7 @@ public:
|
||||||
|
|
||||||
bool isAboutToQuit() const { return _aboutToQuit; }
|
bool isAboutToQuit() const { return _aboutToQuit; }
|
||||||
|
|
||||||
// the isHMDmode is true whenever we use the interface from an HMD and not a standard flat display
|
// the isHMDMode is true whenever we use the interface from an HMD and not a standard flat display
|
||||||
// rendering of several elements depend on that
|
// rendering of several elements depend on that
|
||||||
// TODO: carry that information on the Camera as a setting
|
// TODO: carry that information on the Camera as a setting
|
||||||
bool isHMDMode() const;
|
bool isHMDMode() const;
|
||||||
|
@ -197,14 +197,14 @@ public:
|
||||||
glm::mat4 getEyeOffset(int eye) const;
|
glm::mat4 getEyeOffset(int eye) const;
|
||||||
glm::mat4 getEyeProjection(int eye) const;
|
glm::mat4 getEyeProjection(int eye) const;
|
||||||
|
|
||||||
QRect getDesirableApplicationGeometry();
|
QRect getDesirableApplicationGeometry() const;
|
||||||
Bookmarks* getBookmarks() const { return _bookmarks; }
|
Bookmarks* getBookmarks() const { return _bookmarks; }
|
||||||
|
|
||||||
virtual bool canAcceptURL(const QString& url) const override;
|
virtual bool canAcceptURL(const QString& url) const override;
|
||||||
virtual bool acceptURL(const QString& url, bool defaultUpload = false) override;
|
virtual bool acceptURL(const QString& url, bool defaultUpload = false) override;
|
||||||
|
|
||||||
void setMaxOctreePacketsPerSecond(int maxOctreePPS);
|
void setMaxOctreePacketsPerSecond(int maxOctreePPS);
|
||||||
int getMaxOctreePacketsPerSecond();
|
int getMaxOctreePacketsPerSecond() const;
|
||||||
|
|
||||||
render::ScenePointer getMain3DScene() override { return _main3DScene; }
|
render::ScenePointer getMain3DScene() override { return _main3DScene; }
|
||||||
render::ScenePointer getMain3DScene() const { return _main3DScene; }
|
render::ScenePointer getMain3DScene() const { return _main3DScene; }
|
||||||
|
@ -239,22 +239,23 @@ public slots:
|
||||||
bool exportEntities(const QString& filename, float x, float y, float z, float scale);
|
bool exportEntities(const QString& filename, float x, float y, float z, float scale);
|
||||||
bool importEntities(const QString& url);
|
bool importEntities(const QString& url);
|
||||||
|
|
||||||
void setLowVelocityFilter(bool lowVelocityFilter);
|
static void setLowVelocityFilter(bool lowVelocityFilter);
|
||||||
Q_INVOKABLE void loadDialog();
|
Q_INVOKABLE void loadDialog();
|
||||||
Q_INVOKABLE void loadScriptURLDialog();
|
Q_INVOKABLE void loadScriptURLDialog() const;
|
||||||
void toggleLogDialog();
|
void toggleLogDialog();
|
||||||
void toggleRunningScriptsWidget();
|
void toggleRunningScriptsWidget() const;
|
||||||
void toggleAssetServerWidget(QString filePath = "");
|
void toggleAssetServerWidget(QString filePath = "");
|
||||||
|
|
||||||
void handleLocalServerConnection();
|
void handleLocalServerConnection() const;
|
||||||
void readArgumentsFromLocalSocket();
|
void readArgumentsFromLocalSocket() const;
|
||||||
|
|
||||||
void packageModel();
|
static void packageModel();
|
||||||
|
|
||||||
void openUrl(const QUrl& url);
|
void openUrl(const QUrl& url) const;
|
||||||
|
|
||||||
void resetSensors(bool andReload = false);
|
void resetSensors(bool andReload = false);
|
||||||
void setActiveFaceTracker();
|
void setActiveFaceTracker() const;
|
||||||
|
void toggleSuppressDeadlockWatchdogStatus(bool checked);
|
||||||
|
|
||||||
#ifdef HAVE_IVIEWHMD
|
#ifdef HAVE_IVIEWHMD
|
||||||
void setActiveEyeTracker();
|
void setActiveEyeTracker();
|
||||||
|
@ -264,7 +265,7 @@ public slots:
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
void aboutApp();
|
void aboutApp();
|
||||||
void showHelp();
|
static void showHelp();
|
||||||
|
|
||||||
void cycleCamera();
|
void cycleCamera();
|
||||||
void cameraMenuChanged();
|
void cameraMenuChanged();
|
||||||
|
@ -273,14 +274,14 @@ public slots:
|
||||||
|
|
||||||
void reloadResourceCaches();
|
void reloadResourceCaches();
|
||||||
|
|
||||||
void updateHeartbeat();
|
void updateHeartbeat() const;
|
||||||
|
|
||||||
void crashApplication();
|
static void crashApplication();
|
||||||
void deadlockApplication();
|
static void deadlockApplication();
|
||||||
|
|
||||||
void rotationModeChanged();
|
void rotationModeChanged() const;
|
||||||
|
|
||||||
void runTests();
|
static void runTests();
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void showDesktop();
|
void showDesktop();
|
||||||
|
@ -290,7 +291,7 @@ private slots:
|
||||||
|
|
||||||
void resettingDomain();
|
void resettingDomain();
|
||||||
|
|
||||||
void audioMuteToggled();
|
void audioMuteToggled() const;
|
||||||
void faceTrackerMuteToggled();
|
void faceTrackerMuteToggled();
|
||||||
|
|
||||||
void activeChanged(Qt::ApplicationState state);
|
void activeChanged(Qt::ApplicationState state);
|
||||||
|
@ -298,7 +299,7 @@ private slots:
|
||||||
void notifyPacketVersionMismatch();
|
void notifyPacketVersionMismatch();
|
||||||
|
|
||||||
void loadSettings();
|
void loadSettings();
|
||||||
void saveSettings();
|
void saveSettings() const;
|
||||||
|
|
||||||
bool acceptSnapshot(const QString& urlString);
|
bool acceptSnapshot(const QString& urlString);
|
||||||
bool askToSetAvatarUrl(const QString& url);
|
bool askToSetAvatarUrl(const QString& url);
|
||||||
|
@ -308,18 +309,18 @@ private slots:
|
||||||
void displayAvatarAttachmentWarning(const QString& message) const;
|
void displayAvatarAttachmentWarning(const QString& message) const;
|
||||||
bool displayAvatarAttachmentConfirmationDialog(const QString& name) const;
|
bool displayAvatarAttachmentConfirmationDialog(const QString& name) const;
|
||||||
|
|
||||||
void setSessionUUID(const QUuid& sessionUUID);
|
void setSessionUUID(const QUuid& sessionUUID) const;
|
||||||
void domainChanged(const QString& domainHostname);
|
void domainChanged(const QString& domainHostname);
|
||||||
void updateWindowTitle();
|
void updateWindowTitle() const;
|
||||||
void nodeAdded(SharedNodePointer node);
|
void nodeAdded(SharedNodePointer node) const;
|
||||||
void nodeActivated(SharedNodePointer node);
|
void nodeActivated(SharedNodePointer node);
|
||||||
void nodeKilled(SharedNodePointer node);
|
void nodeKilled(SharedNodePointer node);
|
||||||
void packetSent(quint64 length);
|
static void packetSent(quint64 length);
|
||||||
void updateDisplayMode();
|
void updateDisplayMode();
|
||||||
void updateInputModes();
|
void updateInputModes();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void initDisplay();
|
static void initDisplay();
|
||||||
void init();
|
void init();
|
||||||
|
|
||||||
void cleanupBeforeQuit();
|
void cleanupBeforeQuit();
|
||||||
|
@ -327,14 +328,14 @@ private:
|
||||||
void update(float deltaTime);
|
void update(float deltaTime);
|
||||||
|
|
||||||
// Various helper functions called during update()
|
// Various helper functions called during update()
|
||||||
void updateLOD();
|
void updateLOD() const;
|
||||||
void updateThreads(float deltaTime);
|
void updateThreads(float deltaTime);
|
||||||
void updateDialogs(float deltaTime);
|
void updateDialogs(float deltaTime) const;
|
||||||
|
|
||||||
void queryOctree(NodeType_t serverType, PacketType packetType, NodeToJurisdictionMap& jurisdictions);
|
void queryOctree(NodeType_t serverType, PacketType packetType, NodeToJurisdictionMap& jurisdictions, bool forceResend = false);
|
||||||
void loadViewFrustum(Camera& camera, ViewFrustum& viewFrustum);
|
static void loadViewFrustum(Camera& camera, ViewFrustum& viewFrustum);
|
||||||
|
|
||||||
glm::vec3 getSunDirection();
|
glm::vec3 getSunDirection() const;
|
||||||
|
|
||||||
void renderRearViewMirror(RenderArgs* renderArgs, const QRect& region);
|
void renderRearViewMirror(RenderArgs* renderArgs, const QRect& region);
|
||||||
|
|
||||||
|
@ -344,7 +345,7 @@ private:
|
||||||
|
|
||||||
MyAvatar* getMyAvatar() const;
|
MyAvatar* getMyAvatar() const;
|
||||||
|
|
||||||
void checkSkeleton();
|
void checkSkeleton() const;
|
||||||
|
|
||||||
void initializeAcceptedFiles();
|
void initializeAcceptedFiles();
|
||||||
|
|
||||||
|
@ -366,18 +367,18 @@ private:
|
||||||
|
|
||||||
void mouseMoveEvent(QMouseEvent* event);
|
void mouseMoveEvent(QMouseEvent* event);
|
||||||
void mousePressEvent(QMouseEvent* event);
|
void mousePressEvent(QMouseEvent* event);
|
||||||
void mouseDoublePressEvent(QMouseEvent* event);
|
void mouseDoublePressEvent(QMouseEvent* event) const;
|
||||||
void mouseReleaseEvent(QMouseEvent* event);
|
void mouseReleaseEvent(QMouseEvent* event);
|
||||||
|
|
||||||
void touchBeginEvent(QTouchEvent* event);
|
void touchBeginEvent(QTouchEvent* event);
|
||||||
void touchEndEvent(QTouchEvent* event);
|
void touchEndEvent(QTouchEvent* event);
|
||||||
void touchUpdateEvent(QTouchEvent* event);
|
void touchUpdateEvent(QTouchEvent* event);
|
||||||
|
|
||||||
void wheelEvent(QWheelEvent* event);
|
void wheelEvent(QWheelEvent* event) const;
|
||||||
void dropEvent(QDropEvent* event);
|
void dropEvent(QDropEvent* event);
|
||||||
void dragEnterEvent(QDragEnterEvent* event);
|
static void dragEnterEvent(QDragEnterEvent* event);
|
||||||
|
|
||||||
void maybeToggleMenuVisible(QMouseEvent* event);
|
void maybeToggleMenuVisible(QMouseEvent* event) const;
|
||||||
|
|
||||||
MainWindow* _window;
|
MainWindow* _window;
|
||||||
QElapsedTimer& _sessionRunTimer;
|
QElapsedTimer& _sessionRunTimer;
|
||||||
|
@ -423,7 +424,7 @@ private:
|
||||||
int _avatarSimsPerSecondReport {0};
|
int _avatarSimsPerSecondReport {0};
|
||||||
quint64 _lastAvatarSimsPerSecondUpdate {0};
|
quint64 _lastAvatarSimsPerSecondUpdate {0};
|
||||||
Camera _myCamera; // My view onto the world
|
Camera _myCamera; // My view onto the world
|
||||||
Camera _mirrorCamera; // Cammera for mirror view
|
Camera _mirrorCamera; // Camera for mirror view
|
||||||
QRect _mirrorViewRect;
|
QRect _mirrorViewRect;
|
||||||
|
|
||||||
Setting::Handle<QString> _previousScriptLocation;
|
Setting::Handle<QString> _previousScriptLocation;
|
||||||
|
@ -518,6 +519,12 @@ private:
|
||||||
std::mutex _preRenderLambdasLock;
|
std::mutex _preRenderLambdasLock;
|
||||||
|
|
||||||
std::atomic<uint32_t> _processOctreeStatsCounter { 0 };
|
std::atomic<uint32_t> _processOctreeStatsCounter { 0 };
|
||||||
|
|
||||||
|
bool _keyboardDeviceHasFocus { true };
|
||||||
|
|
||||||
|
bool _recentlyClearedDomain { false };
|
||||||
|
|
||||||
|
QString _returnFromFullScreenMirrorTo;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // hifi_Application_h
|
#endif // hifi_Application_h
|
||||||
|
|
|
@ -63,6 +63,11 @@ bool Bookmarks::contains(const QString& name) const {
|
||||||
void Bookmarks::readFromFile() {
|
void Bookmarks::readFromFile() {
|
||||||
QFile loadFile(_bookmarksFilename);
|
QFile loadFile(_bookmarksFilename);
|
||||||
|
|
||||||
|
if (!loadFile.exists()) {
|
||||||
|
// User has not yet saved bookmarks
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (!loadFile.open(QIODevice::ReadOnly)) {
|
if (!loadFile.open(QIODevice::ReadOnly)) {
|
||||||
qWarning("Couldn't open bookmarks file for reading");
|
qWarning("Couldn't open bookmarks file for reading");
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -219,12 +219,12 @@ Menu::Menu() {
|
||||||
// View > First Person
|
// View > First Person
|
||||||
cameraModeGroup->addAction(addCheckableActionToQMenuAndActionHash(viewMenu,
|
cameraModeGroup->addAction(addCheckableActionToQMenuAndActionHash(viewMenu,
|
||||||
MenuOption::FirstPerson, 0, // QML Qt:: Key_P
|
MenuOption::FirstPerson, 0, // QML Qt:: Key_P
|
||||||
false, qApp, SLOT(cameraMenuChanged())));
|
true, qApp, SLOT(cameraMenuChanged())));
|
||||||
|
|
||||||
// View > Third Person
|
// View > Third Person
|
||||||
cameraModeGroup->addAction(addCheckableActionToQMenuAndActionHash(viewMenu,
|
cameraModeGroup->addAction(addCheckableActionToQMenuAndActionHash(viewMenu,
|
||||||
MenuOption::ThirdPerson, 0,
|
MenuOption::ThirdPerson, 0,
|
||||||
true, qApp, SLOT(cameraMenuChanged())));
|
false, qApp, SLOT(cameraMenuChanged())));
|
||||||
|
|
||||||
// View > Mirror
|
// View > Mirror
|
||||||
cameraModeGroup->addAction(addCheckableActionToQMenuAndActionHash(viewMenu,
|
cameraModeGroup->addAction(addCheckableActionToQMenuAndActionHash(viewMenu,
|
||||||
|
@ -530,6 +530,9 @@ Menu::Menu() {
|
||||||
addCheckableActionToQMenuAndActionHash(timingMenu, MenuOption::PipelineWarnings);
|
addCheckableActionToQMenuAndActionHash(timingMenu, MenuOption::PipelineWarnings);
|
||||||
addCheckableActionToQMenuAndActionHash(timingMenu, MenuOption::LogExtraTimings);
|
addCheckableActionToQMenuAndActionHash(timingMenu, MenuOption::LogExtraTimings);
|
||||||
addCheckableActionToQMenuAndActionHash(timingMenu, MenuOption::SuppressShortTimings);
|
addCheckableActionToQMenuAndActionHash(timingMenu, MenuOption::SuppressShortTimings);
|
||||||
|
addCheckableActionToQMenuAndActionHash(timingMenu, MenuOption::SupressDeadlockWatchdogStatus, 0, false,
|
||||||
|
qApp, SLOT(toggleSuppressDeadlockWatchdogStatus(bool)));
|
||||||
|
|
||||||
|
|
||||||
// Developer > Audio >>>
|
// Developer > Audio >>>
|
||||||
MenuWrapper* audioDebugMenu = developerMenu->addMenu("Audio");
|
MenuWrapper* audioDebugMenu = developerMenu->addMenu("Audio");
|
||||||
|
|
|
@ -162,6 +162,7 @@ namespace MenuOption {
|
||||||
const QString Stats = "Stats";
|
const QString Stats = "Stats";
|
||||||
const QString StopAllScripts = "Stop All Scripts";
|
const QString StopAllScripts = "Stop All Scripts";
|
||||||
const QString SuppressShortTimings = "Suppress Timings Less than 10ms";
|
const QString SuppressShortTimings = "Suppress Timings Less than 10ms";
|
||||||
|
const QString SupressDeadlockWatchdogStatus = "Supress Deadlock Watchdog Status";
|
||||||
const QString ThirdPerson = "Third Person";
|
const QString ThirdPerson = "Third Person";
|
||||||
const QString ThreePointCalibration = "3 Point Calibration";
|
const QString ThreePointCalibration = "3 Point Calibration";
|
||||||
const QString ThrottleFPSIfNotFocus = "Throttle FPS If Not Focus"; // FIXME - this value duplicated in Basic2DWindowOpenGLDisplayPlugin.cpp
|
const QString ThrottleFPSIfNotFocus = "Throttle FPS If Not Focus"; // FIXME - this value duplicated in Basic2DWindowOpenGLDisplayPlugin.cpp
|
||||||
|
|
|
@ -32,8 +32,8 @@ AudioInjector::AudioInjector(QObject* parent) :
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
AudioInjector::AudioInjector(Sound* sound, const AudioInjectorOptions& injectorOptions) :
|
AudioInjector::AudioInjector(const Sound& sound, const AudioInjectorOptions& injectorOptions) :
|
||||||
_audioData(sound->getByteArray()),
|
_audioData(sound.getByteArray()),
|
||||||
_options(injectorOptions)
|
_options(injectorOptions)
|
||||||
{
|
{
|
||||||
|
|
||||||
|
|
|
@ -45,7 +45,7 @@ public:
|
||||||
};
|
};
|
||||||
|
|
||||||
AudioInjector(QObject* parent);
|
AudioInjector(QObject* parent);
|
||||||
AudioInjector(Sound* sound, const AudioInjectorOptions& injectorOptions);
|
AudioInjector(const Sound& sound, const AudioInjectorOptions& injectorOptions);
|
||||||
AudioInjector(const QByteArray& audioData, const AudioInjectorOptions& injectorOptions);
|
AudioInjector(const QByteArray& audioData, const AudioInjectorOptions& injectorOptions);
|
||||||
|
|
||||||
bool isFinished() const { return _state == State::Finished; }
|
bool isFinished() const { return _state == State::Finished; }
|
||||||
|
|
|
@ -23,13 +23,15 @@
|
||||||
|
|
||||||
#include "AudioRingBuffer.h"
|
#include "AudioRingBuffer.h"
|
||||||
|
|
||||||
|
static const QString RING_BUFFER_OVERFLOW_DEBUG { "AudioRingBuffer::writeData has overflown the buffer. Overwriting old data." };
|
||||||
|
|
||||||
AudioRingBuffer::AudioRingBuffer(int numFrameSamples, bool randomAccessMode, int numFramesCapacity) :
|
AudioRingBuffer::AudioRingBuffer(int numFrameSamples, bool randomAccessMode, int numFramesCapacity) :
|
||||||
_frameCapacity(numFramesCapacity),
|
_frameCapacity(numFramesCapacity),
|
||||||
_sampleCapacity(numFrameSamples * numFramesCapacity),
|
_sampleCapacity(numFrameSamples * numFramesCapacity),
|
||||||
_bufferLength(numFrameSamples * (numFramesCapacity + 1)),
|
_bufferLength(numFrameSamples * (numFramesCapacity + 1)),
|
||||||
_numFrameSamples(numFrameSamples),
|
_numFrameSamples(numFrameSamples),
|
||||||
_randomAccessMode(randomAccessMode),
|
_randomAccessMode(randomAccessMode),
|
||||||
_overflowCount(0)
|
_overflowCount(0)
|
||||||
{
|
{
|
||||||
if (numFrameSamples) {
|
if (numFrameSamples) {
|
||||||
_buffer = new int16_t[_bufferLength];
|
_buffer = new int16_t[_bufferLength];
|
||||||
|
@ -41,6 +43,8 @@ _overflowCount(0)
|
||||||
_nextOutput = NULL;
|
_nextOutput = NULL;
|
||||||
_endOfLastWrite = NULL;
|
_endOfLastWrite = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static QString repeatedMessage = LogHandler::getInstance().addRepeatedMessageRegex(RING_BUFFER_OVERFLOW_DEBUG);
|
||||||
};
|
};
|
||||||
|
|
||||||
AudioRingBuffer::~AudioRingBuffer() {
|
AudioRingBuffer::~AudioRingBuffer() {
|
||||||
|
@ -131,8 +135,6 @@ int AudioRingBuffer::writeData(const char* data, int maxSize) {
|
||||||
_nextOutput = shiftedPositionAccomodatingWrap(_nextOutput, samplesToDelete);
|
_nextOutput = shiftedPositionAccomodatingWrap(_nextOutput, samplesToDelete);
|
||||||
_overflowCount++;
|
_overflowCount++;
|
||||||
|
|
||||||
const QString RING_BUFFER_OVERFLOW_DEBUG { "AudioRingBuffer::writeData has overflown the buffer. Overwriting old data." };
|
|
||||||
static QString repeatedMessage = LogHandler::getInstance().addRepeatedMessageRegex(RING_BUFFER_OVERFLOW_DEBUG);
|
|
||||||
qCDebug(audio) << qPrintable(RING_BUFFER_OVERFLOW_DEBUG);
|
qCDebug(audio) << qPrintable(RING_BUFFER_OVERFLOW_DEBUG);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -179,7 +181,12 @@ int AudioRingBuffer::addSilentSamples(int silentSamples) {
|
||||||
if (silentSamples > samplesRoomFor) {
|
if (silentSamples > samplesRoomFor) {
|
||||||
// there's not enough room for this write. write as many silent samples as we have room for
|
// there's not enough room for this write. write as many silent samples as we have room for
|
||||||
silentSamples = samplesRoomFor;
|
silentSamples = samplesRoomFor;
|
||||||
qCDebug(audio) << "Dropping some silent samples to prevent ring buffer overflow";
|
|
||||||
|
static const QString DROPPED_SILENT_DEBUG {
|
||||||
|
"AudioRingBuffer::addSilentSamples dropping silent samples to prevent overflow."
|
||||||
|
};
|
||||||
|
static QString repeatedMessage = LogHandler::getInstance().addRepeatedMessageRegex(DROPPED_SILENT_DEBUG);
|
||||||
|
qCDebug(audio) << qPrintable(DROPPED_SILENT_DEBUG);
|
||||||
}
|
}
|
||||||
|
|
||||||
// memset zeroes into the buffer, accomodate a wrap around the end
|
// memset zeroes into the buffer, accomodate a wrap around the end
|
||||||
|
@ -243,7 +250,7 @@ int AudioRingBuffer::writeSamples(ConstIterator source, int maxSamples) {
|
||||||
int samplesToDelete = samplesToCopy - samplesRoomFor;
|
int samplesToDelete = samplesToCopy - samplesRoomFor;
|
||||||
_nextOutput = shiftedPositionAccomodatingWrap(_nextOutput, samplesToDelete);
|
_nextOutput = shiftedPositionAccomodatingWrap(_nextOutput, samplesToDelete);
|
||||||
_overflowCount++;
|
_overflowCount++;
|
||||||
qCDebug(audio) << "Overflowed ring buffer! Overwriting old data";
|
qCDebug(audio) << qPrintable(RING_BUFFER_OVERFLOW_DEBUG);
|
||||||
}
|
}
|
||||||
|
|
||||||
int16_t* bufferLast = _buffer + _bufferLength - 1;
|
int16_t* bufferLast = _buffer + _bufferLength - 1;
|
||||||
|
@ -264,7 +271,7 @@ int AudioRingBuffer::writeSamplesWithFade(ConstIterator source, int maxSamples,
|
||||||
int samplesToDelete = samplesToCopy - samplesRoomFor;
|
int samplesToDelete = samplesToCopy - samplesRoomFor;
|
||||||
_nextOutput = shiftedPositionAccomodatingWrap(_nextOutput, samplesToDelete);
|
_nextOutput = shiftedPositionAccomodatingWrap(_nextOutput, samplesToDelete);
|
||||||
_overflowCount++;
|
_overflowCount++;
|
||||||
qCDebug(audio) << "Overflowed ring buffer! Overwriting old data";
|
qCDebug(audio) << qPrintable(RING_BUFFER_OVERFLOW_DEBUG);
|
||||||
}
|
}
|
||||||
|
|
||||||
int16_t* bufferLast = _buffer + _bufferLength - 1;
|
int16_t* bufferLast = _buffer + _bufferLength - 1;
|
||||||
|
|
|
@ -27,22 +27,18 @@
|
||||||
#include "AudioLogging.h"
|
#include "AudioLogging.h"
|
||||||
#include "Sound.h"
|
#include "Sound.h"
|
||||||
|
|
||||||
static int soundMetaTypeId = qRegisterMetaType<Sound*>();
|
QScriptValue soundSharedPointerToScriptValue(QScriptEngine* engine, const SharedSoundPointer& in) {
|
||||||
|
return engine->newQObject(new SoundScriptingInterface(in), QScriptEngine::ScriptOwnership);
|
||||||
QScriptValue soundSharedPointerToScriptValue(QScriptEngine* engine, SharedSoundPointer const& in) {
|
|
||||||
return engine->newQObject(in.data());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void soundSharedPointerFromScriptValue(const QScriptValue& object, SharedSoundPointer &out) {
|
void soundSharedPointerFromScriptValue(const QScriptValue& object, SharedSoundPointer& out) {
|
||||||
out = SharedSoundPointer(qobject_cast<Sound*>(object.toQObject()));
|
if (auto soundInterface = qobject_cast<SoundScriptingInterface*>(object.toQObject())) {
|
||||||
|
out = soundInterface->getSound();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QScriptValue soundPointerToScriptValue(QScriptEngine* engine, Sound* const& in) {
|
SoundScriptingInterface::SoundScriptingInterface(SharedSoundPointer sound) : _sound(sound) {
|
||||||
return engine->newQObject(in);
|
QObject::connect(sound.data(), &Sound::ready, this, &SoundScriptingInterface::ready);
|
||||||
}
|
|
||||||
|
|
||||||
void soundPointerFromScriptValue(const QScriptValue &object, Sound* &out) {
|
|
||||||
out = qobject_cast<Sound*>(object.toQObject());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Sound::Sound(const QUrl& url, bool isStereo) :
|
Sound::Sound(const QUrl& url, bool isStereo) :
|
||||||
|
|
|
@ -20,18 +20,16 @@
|
||||||
|
|
||||||
class Sound : public Resource {
|
class Sound : public Resource {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
Q_PROPERTY(bool downloaded READ isReady)
|
|
||||||
Q_PROPERTY(float duration READ getDuration)
|
|
||||||
public:
|
public:
|
||||||
Sound(const QUrl& url, bool isStereo = false);
|
Sound(const QUrl& url, bool isStereo = false);
|
||||||
|
|
||||||
bool isStereo() const { return _isStereo; }
|
bool isStereo() const { return _isStereo; }
|
||||||
bool isReady() const { return _isReady; }
|
bool isReady() const { return _isReady; }
|
||||||
float getDuration() { return _duration; }
|
float getDuration() const { return _duration; }
|
||||||
|
|
||||||
|
|
||||||
const QByteArray& getByteArray() { return _byteArray; }
|
const QByteArray& getByteArray() const { return _byteArray; }
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void ready();
|
void ready();
|
||||||
|
@ -50,13 +48,28 @@ private:
|
||||||
|
|
||||||
typedef QSharedPointer<Sound> SharedSoundPointer;
|
typedef QSharedPointer<Sound> SharedSoundPointer;
|
||||||
|
|
||||||
|
class SoundScriptingInterface : public QObject {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
Q_PROPERTY(bool downloaded READ isReady)
|
||||||
|
Q_PROPERTY(float duration READ getDuration)
|
||||||
|
|
||||||
|
public:
|
||||||
|
SoundScriptingInterface(SharedSoundPointer sound);
|
||||||
|
SharedSoundPointer getSound() { return _sound; }
|
||||||
|
|
||||||
|
bool isReady() const { return _sound->isReady(); }
|
||||||
|
float getDuration() { return _sound->getDuration(); }
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void ready();
|
||||||
|
|
||||||
|
private:
|
||||||
|
SharedSoundPointer _sound;
|
||||||
|
};
|
||||||
|
|
||||||
Q_DECLARE_METATYPE(SharedSoundPointer)
|
Q_DECLARE_METATYPE(SharedSoundPointer)
|
||||||
QScriptValue soundSharedPointerToScriptValue(QScriptEngine* engine, SharedSoundPointer const& in);
|
QScriptValue soundSharedPointerToScriptValue(QScriptEngine* engine, const SharedSoundPointer& in);
|
||||||
void soundSharedPointerFromScriptValue(const QScriptValue& object, SharedSoundPointer &out);
|
void soundSharedPointerFromScriptValue(const QScriptValue& object, SharedSoundPointer& out);
|
||||||
|
|
||||||
Q_DECLARE_METATYPE(Sound*)
|
|
||||||
QScriptValue soundPointerToScriptValue(QScriptEngine* engine, Sound* const& in);
|
|
||||||
void soundPointerFromScriptValue(const QScriptValue& object, Sound* &out);
|
|
||||||
|
|
||||||
|
|
||||||
#endif // hifi_Sound_h
|
#endif // hifi_Sound_h
|
||||||
|
|
|
@ -33,8 +33,10 @@ static QVariantMap createDeviceMap(const controller::InputDevice::Pointer device
|
||||||
for (const auto& inputMapping : userInputMapper->getAvailableInputs(device->getDeviceID())) {
|
for (const auto& inputMapping : userInputMapper->getAvailableInputs(device->getDeviceID())) {
|
||||||
const auto& input = inputMapping.first;
|
const auto& input = inputMapping.first;
|
||||||
const auto inputName = QString(inputMapping.second).remove(SANITIZE_NAME_EXPRESSION);
|
const auto inputName = QString(inputMapping.second).remove(SANITIZE_NAME_EXPRESSION);
|
||||||
|
#ifdef DEBUG
|
||||||
qCDebug(controllers) << "\tInput " << input.getChannel() << (int)input.getType()
|
qCDebug(controllers) << "\tInput " << input.getChannel() << (int)input.getType()
|
||||||
<< QString::number(input.getID(), 16) << ": " << inputName;
|
<< QString::number(input.getID(), 16) << ": " << inputName;
|
||||||
|
#endif
|
||||||
deviceMap.insert(inputName, input.getID());
|
deviceMap.insert(inputName, input.getID());
|
||||||
}
|
}
|
||||||
return deviceMap;
|
return deviceMap;
|
||||||
|
|
|
@ -19,32 +19,39 @@
|
||||||
|
|
||||||
namespace controller {
|
namespace controller {
|
||||||
|
|
||||||
|
static QStringList stateVariables;
|
||||||
|
|
||||||
|
void StateController::setStateVariables(const QStringList& newStateVariables) {
|
||||||
|
stateVariables = newStateVariables;
|
||||||
|
}
|
||||||
|
|
||||||
StateController::StateController() : InputDevice("Application") {
|
StateController::StateController() : InputDevice("Application") {
|
||||||
|
_deviceID = UserInputMapper::STATE_DEVICE;
|
||||||
|
for (const auto& variable : stateVariables) {
|
||||||
|
_namedReadLambdas[variable] = []()->float{ return 0; };
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
StateController::~StateController() {
|
void StateController::setInputVariant(const QString& name, ReadLambda lambda) {
|
||||||
}
|
// All state variables must be predeclared;
|
||||||
|
Q_ASSERT(_namedReadLambdas.contains(name));
|
||||||
void StateController::update(float deltaTime, const InputCalibrationData& inputCalibrationData, bool jointsCaptured) {}
|
_namedReadLambdas[name] = lambda;
|
||||||
|
|
||||||
void StateController::focusOutEvent() {}
|
|
||||||
|
|
||||||
void StateController::addInputVariant(QString name, ReadLambda lambda) {
|
|
||||||
_namedReadLambdas.push_back(NamedReadLambda(name, lambda));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Input::NamedVector StateController::getAvailableInputs() const {
|
Input::NamedVector StateController::getAvailableInputs() const {
|
||||||
Input::NamedVector availableInputs;
|
Input::NamedVector availableInputs;
|
||||||
int i = 0;
|
int i = 0;
|
||||||
for (auto& pair : _namedReadLambdas) {
|
for (const auto& name : stateVariables) {
|
||||||
availableInputs.push_back(Input::NamedPair(Input(_deviceID, i, ChannelType::BUTTON), pair.first));
|
availableInputs.push_back(Input::NamedPair(Input(_deviceID, i, ChannelType::BUTTON), name));
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
return availableInputs;
|
return availableInputs;
|
||||||
}
|
}
|
||||||
|
|
||||||
EndpointPointer StateController::createEndpoint(const Input& input) const {
|
EndpointPointer StateController::createEndpoint(const Input& input) const {
|
||||||
return std::make_shared<LambdaEndpoint>(_namedReadLambdas[input.getChannel()].second);
|
auto name = stateVariables[input.getChannel()];
|
||||||
|
ReadLambda& readLambda = const_cast<QHash<QString, ReadLambda>&>(_namedReadLambdas)[name];
|
||||||
|
return std::make_shared<LambdaRefEndpoint>(readLambda);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -24,26 +24,28 @@ class StateController : public QObject, public InputDevice {
|
||||||
Q_PROPERTY(QString name READ getName)
|
Q_PROPERTY(QString name READ getName)
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
using Pointer = std::shared_ptr<StateController>;
|
||||||
|
using ReadLambda = std::function<float()>;
|
||||||
|
using NamedReadLambda = QPair<QString, ReadLambda>;
|
||||||
|
|
||||||
|
static void setStateVariables(const QStringList& stateVariables);
|
||||||
|
|
||||||
|
StateController();
|
||||||
|
|
||||||
const QString& getName() const { return _name; }
|
const QString& getName() const { return _name; }
|
||||||
|
|
||||||
// Device functions
|
// Device functions
|
||||||
virtual Input::NamedVector getAvailableInputs() const override;
|
virtual Input::NamedVector getAvailableInputs() const override;
|
||||||
virtual void update(float deltaTime, const InputCalibrationData& inputCalibrationData, bool jointsCaptured) override;
|
|
||||||
virtual void focusOutEvent() override;
|
|
||||||
|
|
||||||
StateController();
|
void update(float deltaTime, const InputCalibrationData& inputCalibrationData, bool jointsCaptured) override {}
|
||||||
virtual ~StateController();
|
void focusOutEvent() override {}
|
||||||
|
|
||||||
using ReadLambda = std::function<float()>;
|
void setInputVariant(const QString& name, ReadLambda lambda);
|
||||||
using NamedReadLambda = QPair<QString, ReadLambda>;
|
|
||||||
|
|
||||||
void addInputVariant(QString name, ReadLambda lambda);
|
|
||||||
|
|
||||||
virtual EndpointPointer createEndpoint(const Input& input) const override;
|
|
||||||
|
|
||||||
|
EndpointPointer createEndpoint(const Input& input) const override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
QVector<NamedReadLambda> _namedReadLambdas;
|
QHash<QString, ReadLambda> _namedReadLambdas;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,13 +44,15 @@
|
||||||
|
|
||||||
|
|
||||||
namespace controller {
|
namespace controller {
|
||||||
const uint16_t UserInputMapper::ACTIONS_DEVICE = Input::INVALID_DEVICE - 0xFF;
|
|
||||||
const uint16_t UserInputMapper::STANDARD_DEVICE = 0;
|
const uint16_t UserInputMapper::STANDARD_DEVICE = 0;
|
||||||
|
const uint16_t UserInputMapper::ACTIONS_DEVICE = Input::INVALID_DEVICE - 0x00FF;
|
||||||
|
const uint16_t UserInputMapper::STATE_DEVICE = Input::INVALID_DEVICE - 0x0100;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Default contruct allocate the poutput size with the current hardcoded action channels
|
// Default contruct allocate the poutput size with the current hardcoded action channels
|
||||||
controller::UserInputMapper::UserInputMapper() {
|
controller::UserInputMapper::UserInputMapper() {
|
||||||
registerDevice(std::make_shared<ActionsDevice>());
|
registerDevice(std::make_shared<ActionsDevice>());
|
||||||
|
registerDevice(_stateDevice = std::make_shared<StateController>());
|
||||||
registerDevice(std::make_shared<StandardController>());
|
registerDevice(std::make_shared<StandardController>());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -138,7 +140,6 @@ void UserInputMapper::loadDefaultMapping(uint16 deviceID) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
auto mapping = loadMappings(proxyEntry->second->getDefaultMappingConfigs());
|
auto mapping = loadMappings(proxyEntry->second->getDefaultMappingConfigs());
|
||||||
if (mapping) {
|
if (mapping) {
|
||||||
auto prevMapping = _mappingsByDevice[deviceID];
|
auto prevMapping = _mappingsByDevice[deviceID];
|
||||||
|
@ -235,6 +236,10 @@ void fixBisectedAxis(float& full, float& negative, float& positive) {
|
||||||
|
|
||||||
void UserInputMapper::update(float deltaTime) {
|
void UserInputMapper::update(float deltaTime) {
|
||||||
Locker locker(_lock);
|
Locker locker(_lock);
|
||||||
|
|
||||||
|
static uint64_t updateCount = 0;
|
||||||
|
++updateCount;
|
||||||
|
|
||||||
// Reset the axis state for next loop
|
// Reset the axis state for next loop
|
||||||
for (auto& channel : _actionStates) {
|
for (auto& channel : _actionStates) {
|
||||||
channel = 0.0f;
|
channel = 0.0f;
|
||||||
|
@ -694,11 +699,17 @@ Pose UserInputMapper::getPose(const Input& input) const {
|
||||||
return getPose(endpoint);
|
return getPose(endpoint);
|
||||||
}
|
}
|
||||||
|
|
||||||
Mapping::Pointer UserInputMapper::loadMapping(const QString& jsonFile) {
|
Mapping::Pointer UserInputMapper::loadMapping(const QString& jsonFile, bool enable) {
|
||||||
Locker locker(_lock);
|
Locker locker(_lock);
|
||||||
if (jsonFile.isEmpty()) {
|
if (jsonFile.isEmpty()) {
|
||||||
return Mapping::Pointer();
|
return Mapping::Pointer();
|
||||||
}
|
}
|
||||||
|
// Each mapping only needs to be loaded once
|
||||||
|
static QSet<QString> loaded;
|
||||||
|
if (loaded.contains(jsonFile)) {
|
||||||
|
return Mapping::Pointer();
|
||||||
|
}
|
||||||
|
loaded.insert(jsonFile);
|
||||||
QString json;
|
QString json;
|
||||||
{
|
{
|
||||||
QFile file(jsonFile);
|
QFile file(jsonFile);
|
||||||
|
@ -707,7 +718,11 @@ Mapping::Pointer UserInputMapper::loadMapping(const QString& jsonFile) {
|
||||||
}
|
}
|
||||||
file.close();
|
file.close();
|
||||||
}
|
}
|
||||||
return parseMapping(json);
|
auto result = parseMapping(json);
|
||||||
|
if (enable) {
|
||||||
|
enableMapping(result->name);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
MappingPointer UserInputMapper::loadMappings(const QStringList& jsonFiles) {
|
MappingPointer UserInputMapper::loadMappings(const QStringList& jsonFiles) {
|
||||||
|
@ -961,7 +976,7 @@ Route::Pointer UserInputMapper::parseRoute(const QJsonValue& value) {
|
||||||
result->json = QString(QJsonDocument(obj).toJson());
|
result->json = QString(QJsonDocument(obj).toJson());
|
||||||
result->source = parseSource(obj[JSON_CHANNEL_FROM]);
|
result->source = parseSource(obj[JSON_CHANNEL_FROM]);
|
||||||
result->debug = obj[JSON_CHANNEL_DEBUG].toBool();
|
result->debug = obj[JSON_CHANNEL_DEBUG].toBool();
|
||||||
result->debug = obj[JSON_CHANNEL_PEEK].toBool();
|
result->peek = obj[JSON_CHANNEL_PEEK].toBool();
|
||||||
if (!result->source) {
|
if (!result->source) {
|
||||||
qWarning() << "Invalid route source " << obj[JSON_CHANNEL_FROM];
|
qWarning() << "Invalid route source " << obj[JSON_CHANNEL_FROM];
|
||||||
return Route::Pointer();
|
return Route::Pointer();
|
||||||
|
@ -1033,7 +1048,7 @@ Mapping::Pointer UserInputMapper::parseMapping(const QJsonValue& json) {
|
||||||
Route::Pointer route = parseRoute(channelIt);
|
Route::Pointer route = parseRoute(channelIt);
|
||||||
|
|
||||||
if (!route) {
|
if (!route) {
|
||||||
qWarning() << "Couldn't parse route";
|
qWarning() << "Couldn't parse route:" << mapping->name << QString(QJsonDocument(channelIt.toObject()).toJson());
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -30,6 +30,7 @@
|
||||||
#include "DeviceProxy.h"
|
#include "DeviceProxy.h"
|
||||||
#include "StandardControls.h"
|
#include "StandardControls.h"
|
||||||
#include "Actions.h"
|
#include "Actions.h"
|
||||||
|
#include "StateController.h"
|
||||||
|
|
||||||
namespace controller {
|
namespace controller {
|
||||||
|
|
||||||
|
@ -55,8 +56,9 @@ namespace controller {
|
||||||
using uint16 = uint16_t;
|
using uint16 = uint16_t;
|
||||||
using uint32 = uint32_t;
|
using uint32 = uint32_t;
|
||||||
|
|
||||||
static const uint16_t ACTIONS_DEVICE;
|
|
||||||
static const uint16_t STANDARD_DEVICE;
|
static const uint16_t STANDARD_DEVICE;
|
||||||
|
static const uint16_t ACTIONS_DEVICE;
|
||||||
|
static const uint16_t STATE_DEVICE;
|
||||||
|
|
||||||
UserInputMapper();
|
UserInputMapper();
|
||||||
virtual ~UserInputMapper();
|
virtual ~UserInputMapper();
|
||||||
|
@ -100,10 +102,11 @@ namespace controller {
|
||||||
const DevicesMap& getDevices() { return _registeredDevices; }
|
const DevicesMap& getDevices() { return _registeredDevices; }
|
||||||
uint16 getStandardDeviceID() const { return STANDARD_DEVICE; }
|
uint16 getStandardDeviceID() const { return STANDARD_DEVICE; }
|
||||||
InputDevice::Pointer getStandardDevice() { return _registeredDevices[getStandardDeviceID()]; }
|
InputDevice::Pointer getStandardDevice() { return _registeredDevices[getStandardDeviceID()]; }
|
||||||
|
StateController::Pointer getStateDevice() { return _stateDevice; }
|
||||||
|
|
||||||
MappingPointer newMapping(const QString& mappingName);
|
MappingPointer newMapping(const QString& mappingName);
|
||||||
MappingPointer parseMapping(const QString& json);
|
MappingPointer parseMapping(const QString& json);
|
||||||
MappingPointer loadMapping(const QString& jsonFile);
|
MappingPointer loadMapping(const QString& jsonFile, bool enable = false);
|
||||||
MappingPointer loadMappings(const QStringList& jsonFiles);
|
MappingPointer loadMappings(const QStringList& jsonFiles);
|
||||||
|
|
||||||
void loadDefaultMapping(uint16 deviceID);
|
void loadDefaultMapping(uint16 deviceID);
|
||||||
|
@ -120,6 +123,7 @@ namespace controller {
|
||||||
// GetFreeDeviceID should be called before registering a device to use an ID not used by a different device.
|
// GetFreeDeviceID should be called before registering a device to use an ID not used by a different device.
|
||||||
uint16 getFreeDeviceID() { return _nextFreeDeviceID++; }
|
uint16 getFreeDeviceID() { return _nextFreeDeviceID++; }
|
||||||
DevicesMap _registeredDevices;
|
DevicesMap _registeredDevices;
|
||||||
|
StateController::Pointer _stateDevice;
|
||||||
uint16 _nextFreeDeviceID = STANDARD_DEVICE + 1;
|
uint16 _nextFreeDeviceID = STANDARD_DEVICE + 1;
|
||||||
|
|
||||||
std::vector<float> _actionStates = std::vector<float>(toInt(Action::NUM_ACTIONS), 0.0f);
|
std::vector<float> _actionStates = std::vector<float>(toInt(Action::NUM_ACTIONS), 0.0f);
|
||||||
|
|
|
@ -12,5 +12,8 @@
|
||||||
// warning LNK4221: This object file does not define any previously undefined public symbols,
|
// warning LNK4221: This object file does not define any previously undefined public symbols,
|
||||||
// so it will not be used by any link operation that consumes this library
|
// so it will not be used by any link operation that consumes this library
|
||||||
//
|
//
|
||||||
//#include "Endpoint.h"
|
#include "Endpoint.h"
|
||||||
|
|
||||||
|
namespace controller {
|
||||||
|
Endpoint::WriteLambda DEFAULT_WRITE_LAMBDA = [](float) {};
|
||||||
|
}
|
||||||
|
|
|
@ -67,6 +67,23 @@ namespace controller {
|
||||||
WriteLambda _writeLambda;
|
WriteLambda _writeLambda;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
extern Endpoint::WriteLambda DEFAULT_WRITE_LAMBDA;
|
||||||
|
|
||||||
|
class LambdaRefEndpoint : public Endpoint {
|
||||||
|
public:
|
||||||
|
using Endpoint::apply;
|
||||||
|
LambdaRefEndpoint(const ReadLambda& readLambda, const WriteLambda& writeLambda = DEFAULT_WRITE_LAMBDA)
|
||||||
|
: Endpoint(Input::INVALID_INPUT), _readLambda(readLambda), _writeLambda(writeLambda) {
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual float peek() const override { return _readLambda(); }
|
||||||
|
virtual void apply(float value, const Pointer& source) override { _writeLambda(value); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
const ReadLambda& _readLambda;
|
||||||
|
const WriteLambda& _writeLambda;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
class VirtualEndpoint : public Endpoint {
|
class VirtualEndpoint : public Endpoint {
|
||||||
public:
|
public:
|
||||||
|
|
|
@ -467,8 +467,6 @@ void CompositorHelper::toggle() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
glm::mat4 CompositorHelper::getReticleTransform(const glm::mat4& eyePose, const glm::vec3& headPosition) const {
|
glm::mat4 CompositorHelper::getReticleTransform(const glm::mat4& eyePose, const glm::vec3& headPosition) const {
|
||||||
glm::mat4 result;
|
glm::mat4 result;
|
||||||
if (isHMD()) {
|
if (isHMD()) {
|
||||||
|
@ -487,7 +485,7 @@ glm::mat4 CompositorHelper::getReticleTransform(const glm::mat4& eyePose, const
|
||||||
pointerTransform[3] = vec4(cursorRay + headPosition, 1);
|
pointerTransform[3] = vec4(cursorRay + headPosition, 1);
|
||||||
// Scale up the cursor because of distance
|
// Scale up the cursor because of distance
|
||||||
reticleScale *= reticleDepth;
|
reticleScale *= reticleDepth;
|
||||||
}
|
}
|
||||||
glm::mat4 overlayXfm;
|
glm::mat4 overlayXfm;
|
||||||
_modelTransform.getMatrix(overlayXfm);
|
_modelTransform.getMatrix(overlayXfm);
|
||||||
pointerTransform = overlayXfm * pointerTransform;
|
pointerTransform = overlayXfm * pointerTransform;
|
||||||
|
@ -503,7 +501,7 @@ glm::mat4 CompositorHelper::getReticleTransform(const glm::mat4& eyePose, const
|
||||||
mousePosition.y *= -1.0f;
|
mousePosition.y *= -1.0f;
|
||||||
|
|
||||||
vec2 mouseSize = CURSOR_PIXEL_SIZE / canvasSize;
|
vec2 mouseSize = CURSOR_PIXEL_SIZE / canvasSize;
|
||||||
return glm::scale(glm::translate(glm::mat4(), vec3(mousePosition, 0.0f)), vec3(mouseSize, 1.0f));
|
result = glm::scale(glm::translate(glm::mat4(), vec3(mousePosition, 0.0f)), vec3(mouseSize, 1.0f));
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
|
@ -176,7 +176,7 @@ private:
|
||||||
|
|
||||||
bool _reticleOverQml { false };
|
bool _reticleOverQml { false };
|
||||||
|
|
||||||
bool _allowMouseCapture { true };
|
std::atomic<bool> _allowMouseCapture { true };
|
||||||
|
|
||||||
bool _fakeMouseEvent { false };
|
bool _fakeMouseEvent { false };
|
||||||
|
|
||||||
|
|
|
@ -245,8 +245,10 @@ bool OpenGLDisplayPlugin::activate() {
|
||||||
|
|
||||||
#if THREADED_PRESENT
|
#if THREADED_PRESENT
|
||||||
// Start the present thread if necessary
|
// Start the present thread if necessary
|
||||||
auto presentThread = DependencyManager::get<PresentThread>();
|
QSharedPointer<PresentThread> presentThread;
|
||||||
if (!presentThread) {
|
if (DependencyManager::isSet<PresentThread>()) {
|
||||||
|
presentThread = DependencyManager::get<PresentThread>();
|
||||||
|
} else {
|
||||||
auto widget = _container->getPrimaryWidget();
|
auto widget = _container->getPrimaryWidget();
|
||||||
DependencyManager::set<PresentThread>();
|
DependencyManager::set<PresentThread>();
|
||||||
presentThread = DependencyManager::get<PresentThread>();
|
presentThread = DependencyManager::get<PresentThread>();
|
||||||
|
|
|
@ -71,8 +71,8 @@ EntityTreeRenderer::EntityTreeRenderer(bool wantScripts, AbstractViewStateInterf
|
||||||
}
|
}
|
||||||
|
|
||||||
EntityTreeRenderer::~EntityTreeRenderer() {
|
EntityTreeRenderer::~EntityTreeRenderer() {
|
||||||
// NOTE: we don't need to delete _entitiesScriptEngine because it is registered with the application and has a
|
// NOTE: We don't need to delete _entitiesScriptEngine because
|
||||||
// signal tied to call it's deleteLater on doneRunning
|
// it is registered with ScriptEngines, which will call deleteLater for us.
|
||||||
}
|
}
|
||||||
|
|
||||||
void EntityTreeRenderer::clear() {
|
void EntityTreeRenderer::clear() {
|
||||||
|
@ -297,7 +297,14 @@ void EntityTreeRenderer::applyZonePropertiesToScene(std::shared_ptr<ZoneEntityIt
|
||||||
auto sceneLocation = sceneStage->getLocation();
|
auto sceneLocation = sceneStage->getLocation();
|
||||||
auto sceneTime = sceneStage->getTime();
|
auto sceneTime = sceneStage->getTime();
|
||||||
|
|
||||||
|
// Skybox and procedural skybox data
|
||||||
|
auto skybox = std::dynamic_pointer_cast<ProceduralSkybox>(skyStage->getSkybox());
|
||||||
|
static QString userData;
|
||||||
|
|
||||||
if (!zone) {
|
if (!zone) {
|
||||||
|
userData = QString();
|
||||||
|
skybox->clear();
|
||||||
|
|
||||||
_pendingSkyboxTexture = false;
|
_pendingSkyboxTexture = false;
|
||||||
_skyboxTexture.clear();
|
_skyboxTexture.clear();
|
||||||
|
|
||||||
|
@ -373,9 +380,7 @@ void EntityTreeRenderer::applyZonePropertiesToScene(std::shared_ptr<ZoneEntityIt
|
||||||
|
|
||||||
switch (zone->getBackgroundMode()) {
|
switch (zone->getBackgroundMode()) {
|
||||||
case BACKGROUND_MODE_SKYBOX: {
|
case BACKGROUND_MODE_SKYBOX: {
|
||||||
auto skybox = std::dynamic_pointer_cast<ProceduralSkybox>(skyStage->getSkybox());
|
|
||||||
skybox->setColor(zone->getSkyboxProperties().getColorVec3());
|
skybox->setColor(zone->getSkyboxProperties().getColorVec3());
|
||||||
static QString userData;
|
|
||||||
if (userData != zone->getUserData()) {
|
if (userData != zone->getUserData()) {
|
||||||
userData = zone->getUserData();
|
userData = zone->getUserData();
|
||||||
skybox->parse(userData);
|
skybox->parse(userData);
|
||||||
|
@ -414,9 +419,15 @@ void EntityTreeRenderer::applyZonePropertiesToScene(std::shared_ptr<ZoneEntityIt
|
||||||
|
|
||||||
case BACKGROUND_MODE_INHERIT:
|
case BACKGROUND_MODE_INHERIT:
|
||||||
default:
|
default:
|
||||||
skyStage->setBackgroundMode(model::SunSkyStage::SKY_DOME); // let the application background through
|
// Clear the skybox to release its textures
|
||||||
_pendingSkyboxTexture = false;
|
userData = QString();
|
||||||
|
skybox->clear();
|
||||||
|
|
||||||
_skyboxTexture.clear();
|
_skyboxTexture.clear();
|
||||||
|
_pendingSkyboxTexture = false;
|
||||||
|
|
||||||
|
// Let the application background through
|
||||||
|
skyStage->setBackgroundMode(model::SunSkyStage::SKY_DOME);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -92,7 +92,7 @@ private:
|
||||||
public: \
|
public: \
|
||||||
virtual bool addToScene(EntityItemPointer self, std::shared_ptr<render::Scene> scene, render::PendingChanges& pendingChanges) override { return _renderHelper.addToScene(self, scene, pendingChanges); } \
|
virtual bool addToScene(EntityItemPointer self, std::shared_ptr<render::Scene> scene, render::PendingChanges& pendingChanges) override { return _renderHelper.addToScene(self, scene, pendingChanges); } \
|
||||||
virtual void removeFromScene(EntityItemPointer self, std::shared_ptr<render::Scene> scene, render::PendingChanges& pendingChanges) override { _renderHelper.removeFromScene(self, scene, pendingChanges); } \
|
virtual void removeFromScene(EntityItemPointer self, std::shared_ptr<render::Scene> scene, render::PendingChanges& pendingChanges) override { _renderHelper.removeFromScene(self, scene, pendingChanges); } \
|
||||||
virtual void locationChanged() override { EntityItem::locationChanged(); _renderHelper.notifyChanged(); } \
|
virtual void locationChanged(bool tellPhysics = true) override { EntityItem::locationChanged(tellPhysics); _renderHelper.notifyChanged(); } \
|
||||||
virtual void dimensionsChanged() override { EntityItem::dimensionsChanged(); _renderHelper.notifyChanged(); } \
|
virtual void dimensionsChanged() override { EntityItem::dimensionsChanged(); _renderHelper.notifyChanged(); } \
|
||||||
private: \
|
private: \
|
||||||
SimpleRenderableEntityItem _renderHelper;
|
SimpleRenderableEntityItem _renderHelper;
|
||||||
|
|
|
@ -271,10 +271,10 @@ bool RenderableModelEntityItem::getAnimationFrame() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!hasAnimation() || !_jointMappingCompleted) {
|
if (!hasRenderAnimation() || !_jointMappingCompleted) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
AnimationPointer myAnimation = getAnimation(_animationProperties.getURL()); // FIXME: this could be optimized
|
AnimationPointer myAnimation = getAnimation(getRenderAnimationURL()); // FIXME: this could be optimized
|
||||||
if (myAnimation && myAnimation->isLoaded()) {
|
if (myAnimation && myAnimation->isLoaded()) {
|
||||||
|
|
||||||
const QVector<FBXAnimationFrame>& frames = myAnimation->getFramesReference(); // NOTE: getFrames() is too heavy
|
const QVector<FBXAnimationFrame>& frames = myAnimation->getFramesReference(); // NOTE: getFrames() is too heavy
|
||||||
|
@ -384,7 +384,7 @@ void RenderableModelEntityItem::render(RenderArgs* args) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_model) {
|
if (_model) {
|
||||||
if (hasAnimation()) {
|
if (hasRenderAnimation()) {
|
||||||
if (!jointsMapped()) {
|
if (!jointsMapped()) {
|
||||||
QStringList modelJointNames = _model->getJointNames();
|
QStringList modelJointNames = _model->getJointNames();
|
||||||
mapJoints(modelJointNames);
|
mapJoints(modelJointNames);
|
||||||
|
@ -434,6 +434,8 @@ void RenderableModelEntityItem::render(RenderArgs* args) {
|
||||||
_showCollisionHull = shouldShowCollisionHull;
|
_showCollisionHull = shouldShowCollisionHull;
|
||||||
render::PendingChanges pendingChanges;
|
render::PendingChanges pendingChanges;
|
||||||
|
|
||||||
|
_model->removeFromScene(scene, pendingChanges);
|
||||||
|
|
||||||
render::Item::Status::Getters statusGetters;
|
render::Item::Status::Getters statusGetters;
|
||||||
makeEntityItemStatusGetters(getThisPointer(), statusGetters);
|
makeEntityItemStatusGetters(getThisPointer(), statusGetters);
|
||||||
_model->addToScene(scene, pendingChanges, statusGetters, _showCollisionHull);
|
_model->addToScene(scene, pendingChanges, statusGetters, _showCollisionHull);
|
||||||
|
@ -526,6 +528,9 @@ void RenderableModelEntityItem::update(const quint64& now) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// make a copy of the animation properites
|
||||||
|
_renderAnimationProperties = _animationProperties;
|
||||||
|
|
||||||
ModelEntityItem::update(now);
|
ModelEntityItem::update(now);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -793,8 +798,8 @@ void RenderableModelEntityItem::setJointTranslationsSet(const QVector<bool>& tra
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void RenderableModelEntityItem::locationChanged() {
|
void RenderableModelEntityItem::locationChanged(bool tellPhysics) {
|
||||||
EntityItem::locationChanged();
|
EntityItem::locationChanged(tellPhysics);
|
||||||
if (_model && _model->isActive()) {
|
if (_model && _model->isActive()) {
|
||||||
_model->setRotation(getRotation());
|
_model->setRotation(getRotation());
|
||||||
_model->setTranslation(getPosition());
|
_model->setTranslation(getPosition());
|
||||||
|
|
|
@ -75,13 +75,18 @@ public:
|
||||||
virtual void setJointTranslationsSet(const QVector<bool>& translationsSet) override;
|
virtual void setJointTranslationsSet(const QVector<bool>& translationsSet) override;
|
||||||
|
|
||||||
virtual void loader() override;
|
virtual void loader() override;
|
||||||
virtual void locationChanged() override;
|
virtual void locationChanged(bool tellPhysics = true) override;
|
||||||
|
|
||||||
virtual void resizeJointArrays(int newSize = -1) override;
|
virtual void resizeJointArrays(int newSize = -1) override;
|
||||||
|
|
||||||
virtual int getJointIndex(const QString& name) const override;
|
virtual int getJointIndex(const QString& name) const override;
|
||||||
virtual QStringList getJointNames() const override;
|
virtual QStringList getJointNames() const override;
|
||||||
|
|
||||||
|
// These operate on a copy of the renderAnimationProperties, so they can be accessed
|
||||||
|
// without having the entityTree lock.
|
||||||
|
bool hasRenderAnimation() const { return !_renderAnimationProperties.getURL().isEmpty(); }
|
||||||
|
const QString& getRenderAnimationURL() const { return _renderAnimationProperties.getURL(); }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QVariantMap parseTexturesToMap(QString textures);
|
QVariantMap parseTexturesToMap(QString textures);
|
||||||
void remapTextures();
|
void remapTextures();
|
||||||
|
@ -97,6 +102,8 @@ private:
|
||||||
QVector<QVector<glm::vec3>> _points;
|
QVector<QVector<glm::vec3>> _points;
|
||||||
bool _dimensionsInitialized = true;
|
bool _dimensionsInitialized = true;
|
||||||
|
|
||||||
|
AnimationPropertyGroup _renderAnimationProperties;
|
||||||
|
|
||||||
render::ItemID _myMetaItem{ render::Item::INVALID_ITEM_ID };
|
render::ItemID _myMetaItem{ render::Item::INVALID_ITEM_ID };
|
||||||
|
|
||||||
bool _showCollisionHull = false;
|
bool _showCollisionHull = false;
|
||||||
|
|
|
@ -39,6 +39,7 @@ public:
|
||||||
InterpolationData<float> radius;
|
InterpolationData<float> radius;
|
||||||
InterpolationData<glm::vec4> color; // rgba
|
InterpolationData<glm::vec4> color; // rgba
|
||||||
float lifespan;
|
float lifespan;
|
||||||
|
glm::vec3 spare;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ParticlePrimitive {
|
struct ParticlePrimitive {
|
||||||
|
|
|
@ -29,7 +29,7 @@ public:
|
||||||
virtual void removeFromScene(EntityItemPointer self, render::ScenePointer scene, render::PendingChanges& pendingChanges) override;
|
virtual void removeFromScene(EntityItemPointer self, render::ScenePointer scene, render::PendingChanges& pendingChanges) override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual void locationChanged() override { EntityItem::locationChanged(); notifyBoundChanged(); }
|
virtual void locationChanged(bool tellPhysics = true) override { EntityItem::locationChanged(tellPhysics); notifyBoundChanged(); }
|
||||||
virtual void dimensionsChanged() override { EntityItem::dimensionsChanged(); notifyBoundChanged(); }
|
virtual void dimensionsChanged() override { EntityItem::dimensionsChanged(); notifyBoundChanged(); }
|
||||||
|
|
||||||
void notifyBoundChanged();
|
void notifyBoundChanged();
|
||||||
|
|
|
@ -42,7 +42,7 @@ public:
|
||||||
virtual void removeFromScene(EntityItemPointer self, std::shared_ptr<render::Scene> scene, render::PendingChanges& pendingChanges);
|
virtual void removeFromScene(EntityItemPointer self, std::shared_ptr<render::Scene> scene, render::PendingChanges& pendingChanges);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
virtual void locationChanged() override { EntityItem::locationChanged(); notifyBoundChanged(); }
|
virtual void locationChanged(bool tellPhysics = true) override { EntityItem::locationChanged(tellPhysics); notifyBoundChanged(); }
|
||||||
virtual void dimensionsChanged() override { EntityItem::dimensionsChanged(); notifyBoundChanged(); }
|
virtual void dimensionsChanged() override { EntityItem::dimensionsChanged(); notifyBoundChanged(); }
|
||||||
void notifyBoundChanged();
|
void notifyBoundChanged();
|
||||||
|
|
||||||
|
|
|
@ -30,10 +30,10 @@ struct Colors {
|
||||||
struct ParticleUniforms {
|
struct ParticleUniforms {
|
||||||
Radii radius;
|
Radii radius;
|
||||||
Colors color;
|
Colors color;
|
||||||
float lifespan;
|
vec4 lifespan; // x is lifespan, 3 spare floats
|
||||||
};
|
};
|
||||||
|
|
||||||
uniform particleBuffer {
|
layout(std140) uniform particleBuffer {
|
||||||
ParticleUniforms particle;
|
ParticleUniforms particle;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -112,7 +112,7 @@ void main(void) {
|
||||||
int twoTriID = gl_VertexID - particleID * NUM_VERTICES_PER_PARTICLE;
|
int twoTriID = gl_VertexID - particleID * NUM_VERTICES_PER_PARTICLE;
|
||||||
|
|
||||||
// Particle properties
|
// Particle properties
|
||||||
float age = inColor.x / particle.lifespan;
|
float age = inColor.x / particle.lifespan.x;
|
||||||
float seed = inColor.y;
|
float seed = inColor.y;
|
||||||
|
|
||||||
// Pass the texcoord and the z texcoord is representing the texture icon
|
// Pass the texcoord and the z texcoord is representing the texture icon
|
||||||
|
|
|
@ -888,6 +888,9 @@ void EntityItem::simulateKinematicMotion(float timeElapsed, bool setFlags) {
|
||||||
if (hasActions()) {
|
if (hasActions()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (!_parentID.isNull()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (hasLocalAngularVelocity()) {
|
if (hasLocalAngularVelocity()) {
|
||||||
glm::vec3 localAngularVelocity = getLocalAngularVelocity();
|
glm::vec3 localAngularVelocity = getLocalAngularVelocity();
|
||||||
|
@ -1973,9 +1976,16 @@ QList<EntityActionPointer> EntityItem::getActionsOfType(EntityActionType typeToG
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
void EntityItem::locationChanged() {
|
void EntityItem::locationChanged(bool tellPhysics) {
|
||||||
requiresRecalcBoxes();
|
requiresRecalcBoxes();
|
||||||
SpatiallyNestable::locationChanged(); // tell all the children, also
|
if (tellPhysics) {
|
||||||
|
_dirtyFlags |= Simulation::DIRTY_TRANSFORM;
|
||||||
|
}
|
||||||
|
EntityTreePointer tree = getTree();
|
||||||
|
if (tree) {
|
||||||
|
tree->entityChanged(getThisPointer());
|
||||||
|
}
|
||||||
|
SpatiallyNestable::locationChanged(tellPhysics); // tell all the children, also
|
||||||
}
|
}
|
||||||
|
|
||||||
void EntityItem::dimensionsChanged() {
|
void EntityItem::dimensionsChanged() {
|
||||||
|
@ -1984,6 +1994,7 @@ void EntityItem::dimensionsChanged() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void EntityItem::globalizeProperties(EntityItemProperties& properties, const QString& messageTemplate, const glm::vec3& offset) const {
|
void EntityItem::globalizeProperties(EntityItemProperties& properties, const QString& messageTemplate, const glm::vec3& offset) const {
|
||||||
|
// TODO -- combine this with convertLocationToScriptSemantics
|
||||||
bool success;
|
bool success;
|
||||||
auto globalPosition = getPosition(success);
|
auto globalPosition = getPosition(success);
|
||||||
if (success) {
|
if (success) {
|
||||||
|
|
|
@ -432,7 +432,7 @@ protected:
|
||||||
const QByteArray getActionDataInternal() const;
|
const QByteArray getActionDataInternal() const;
|
||||||
void setActionDataInternal(QByteArray actionData);
|
void setActionDataInternal(QByteArray actionData);
|
||||||
|
|
||||||
virtual void locationChanged() override;
|
virtual void locationChanged(bool tellPhysics = true) override;
|
||||||
virtual void dimensionsChanged() override;
|
virtual void dimensionsChanged() override;
|
||||||
|
|
||||||
EntityTypes::EntityType _type;
|
EntityTypes::EntityType _type;
|
||||||
|
|
|
@ -475,13 +475,20 @@ QVector<QUuid> EntityScriptingInterface::findEntitiesInBox(const glm::vec3& corn
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
RayToEntityIntersectionResult EntityScriptingInterface::findRayIntersection(const PickRay& ray, bool precisionPicking, const QScriptValue& entityIdsToInclude, const QScriptValue& entityIdsToDiscard) {
|
RayToEntityIntersectionResult EntityScriptingInterface::findRayIntersection(const PickRay& ray, bool precisionPicking,
|
||||||
|
const QScriptValue& entityIdsToInclude, const QScriptValue& entityIdsToDiscard) {
|
||||||
|
|
||||||
QVector<EntityItemID> entitiesToInclude = qVectorEntityItemIDFromScriptValue(entityIdsToInclude);
|
QVector<EntityItemID> entitiesToInclude = qVectorEntityItemIDFromScriptValue(entityIdsToInclude);
|
||||||
QVector<EntityItemID> entitiesToDiscard = qVectorEntityItemIDFromScriptValue(entityIdsToDiscard);
|
QVector<EntityItemID> entitiesToDiscard = qVectorEntityItemIDFromScriptValue(entityIdsToDiscard);
|
||||||
return findRayIntersectionWorker(ray, Octree::TryLock, precisionPicking, entitiesToInclude, entitiesToDiscard);
|
return findRayIntersectionWorker(ray, Octree::Lock, precisionPicking, entitiesToInclude, entitiesToDiscard);
|
||||||
}
|
}
|
||||||
|
|
||||||
RayToEntityIntersectionResult EntityScriptingInterface::findRayIntersectionBlocking(const PickRay& ray, bool precisionPicking, const QScriptValue& entityIdsToInclude, const QScriptValue& entityIdsToDiscard) {
|
// FIXME - we should remove this API and encourage all users to use findRayIntersection() instead. We've changed
|
||||||
|
// findRayIntersection() to be blocking because it never makes sense for a script to get back a non-answer
|
||||||
|
RayToEntityIntersectionResult EntityScriptingInterface::findRayIntersectionBlocking(const PickRay& ray, bool precisionPicking,
|
||||||
|
const QScriptValue& entityIdsToInclude, const QScriptValue& entityIdsToDiscard) {
|
||||||
|
|
||||||
|
qWarning() << "Entities.findRayIntersectionBlocking() is obsolete, use Entities.findRayIntersection() instead.";
|
||||||
const QVector<EntityItemID>& entitiesToInclude = qVectorEntityItemIDFromScriptValue(entityIdsToInclude);
|
const QVector<EntityItemID>& entitiesToInclude = qVectorEntityItemIDFromScriptValue(entityIdsToInclude);
|
||||||
const QVector<EntityItemID> entitiesToDiscard = qVectorEntityItemIDFromScriptValue(entityIdsToDiscard);
|
const QVector<EntityItemID> entitiesToDiscard = qVectorEntityItemIDFromScriptValue(entityIdsToDiscard);
|
||||||
return findRayIntersectionWorker(ray, Octree::Lock, precisionPicking, entitiesToInclude, entitiesToDiscard);
|
return findRayIntersectionWorker(ray, Octree::Lock, precisionPicking, entitiesToInclude, entitiesToDiscard);
|
||||||
|
|
|
@ -1323,7 +1323,9 @@ QVector<EntityItemID> EntityTree::sendEntities(EntityEditPacketSender* packetSen
|
||||||
// We need to keep a map so that we can map parent identifiers correctly.
|
// We need to keep a map so that we can map parent identifiers correctly.
|
||||||
QHash<EntityItemID, EntityItemID> map;
|
QHash<EntityItemID, EntityItemID> map;
|
||||||
args.map = ↦
|
args.map = ↦
|
||||||
recurseTreeWithOperation(sendEntitiesOperation, &args);
|
withReadLock([&] {
|
||||||
|
recurseTreeWithOperation(sendEntitiesOperation, &args);
|
||||||
|
});
|
||||||
packetSender->releaseQueuedMessages();
|
packetSender->releaseQueuedMessages();
|
||||||
|
|
||||||
return map.values().toVector();
|
return map.values().toVector();
|
||||||
|
|
|
@ -9,17 +9,20 @@
|
||||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
//
|
//
|
||||||
|
|
||||||
#include <iostream>
|
|
||||||
#include <QBuffer>
|
|
||||||
#include <QDataStream>
|
|
||||||
#include <QIODevice>
|
|
||||||
#include <QStringList>
|
|
||||||
#include <QTextStream>
|
|
||||||
#include <QtDebug>
|
|
||||||
#include <QtEndian>
|
|
||||||
#include <QFileInfo>
|
|
||||||
#include "FBXReader.h"
|
#include "FBXReader.h"
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
#include <QtCore/QBuffer>
|
||||||
|
#include <QtCore/QDataStream>
|
||||||
|
#include <QtCore/QIODevice>
|
||||||
|
#include <QtCore/QStringList>
|
||||||
|
#include <QtCore/QTextStream>
|
||||||
|
#include <QtCore/QDebug>
|
||||||
|
#include <QtCore/QtEndian>
|
||||||
|
#include <QtCore/QFileInfo>
|
||||||
|
|
||||||
|
#include <shared/NsightHelpers.h>
|
||||||
|
|
||||||
template<class T> int streamSize() {
|
template<class T> int streamSize() {
|
||||||
return sizeof(T);
|
return sizeof(T);
|
||||||
}
|
}
|
||||||
|
@ -299,6 +302,7 @@ FBXNode parseTextFBXNode(Tokenizer& tokenizer) {
|
||||||
}
|
}
|
||||||
|
|
||||||
FBXNode FBXReader::parseFBX(QIODevice* device) {
|
FBXNode FBXReader::parseFBX(QIODevice* device) {
|
||||||
|
PROFILE_RANGE_EX(__FUNCTION__, 0xff0000ff, device);
|
||||||
// verify the prolog
|
// verify the prolog
|
||||||
const QByteArray BINARY_PROLOG = "Kaydara FBX Binary ";
|
const QByteArray BINARY_PROLOG = "Kaydara FBX Binary ";
|
||||||
if (device->peek(BINARY_PROLOG.size()) != BINARY_PROLOG) {
|
if (device->peek(BINARY_PROLOG.size()) != BINARY_PROLOG) {
|
||||||
|
|
|
@ -12,17 +12,20 @@
|
||||||
// http://www.scratchapixel.com/old/lessons/3d-advanced-lessons/obj-file-format/obj-file-format/
|
// http://www.scratchapixel.com/old/lessons/3d-advanced-lessons/obj-file-format/obj-file-format/
|
||||||
// http://paulbourke.net/dataformats/obj/
|
// http://paulbourke.net/dataformats/obj/
|
||||||
|
|
||||||
|
#include "OBJReader.h"
|
||||||
|
|
||||||
#include <QBuffer>
|
|
||||||
#include <QIODevice>
|
|
||||||
#include <QtNetwork/QNetworkAccessManager>
|
|
||||||
#include <QtNetwork/QNetworkRequest>
|
|
||||||
#include <QEventLoop>
|
|
||||||
#include <ctype.h> // .obj files are not locale-specific. The C/ASCII charset applies.
|
#include <ctype.h> // .obj files are not locale-specific. The C/ASCII charset applies.
|
||||||
|
|
||||||
|
#include <QtCore/QBuffer>
|
||||||
|
#include <QtCore/QIODevice>
|
||||||
|
#include <QtCore/QEventLoop>
|
||||||
|
#include <QtNetwork/QNetworkAccessManager>
|
||||||
|
#include <QtNetwork/QNetworkRequest>
|
||||||
|
|
||||||
|
#include <shared/NsightHelpers.h>
|
||||||
#include <NetworkAccessManager.h>
|
#include <NetworkAccessManager.h>
|
||||||
|
|
||||||
#include "FBXReader.h"
|
#include "FBXReader.h"
|
||||||
#include "OBJReader.h"
|
|
||||||
#include "ModelFormatLogging.h"
|
#include "ModelFormatLogging.h"
|
||||||
|
|
||||||
QHash<QString, float> COMMENT_SCALE_HINTS = {{"This file uses centimeters as units", 1.0f / 100.0f},
|
QHash<QString, float> COMMENT_SCALE_HINTS = {{"This file uses centimeters as units", 1.0f / 100.0f},
|
||||||
|
@ -404,7 +407,7 @@ done:
|
||||||
|
|
||||||
|
|
||||||
FBXGeometry* OBJReader::readOBJ(QByteArray& model, const QVariantHash& mapping, const QUrl& url) {
|
FBXGeometry* OBJReader::readOBJ(QByteArray& model, const QVariantHash& mapping, const QUrl& url) {
|
||||||
|
PROFILE_RANGE_EX(__FUNCTION__, 0xffff0000, nullptr);
|
||||||
QBuffer buffer { &model };
|
QBuffer buffer { &model };
|
||||||
buffer.open(QIODevice::ReadOnly);
|
buffer.open(QIODevice::ReadOnly);
|
||||||
|
|
||||||
|
|
|
@ -12,7 +12,7 @@ const QSurfaceFormat& getDefaultOpenGLSurfaceFormat() {
|
||||||
// Qt Quick may need a depth and stencil buffer. Always make sure these are available.
|
// Qt Quick may need a depth and stencil buffer. Always make sure these are available.
|
||||||
format.setDepthBufferSize(DEFAULT_GL_DEPTH_BUFFER_BITS);
|
format.setDepthBufferSize(DEFAULT_GL_DEPTH_BUFFER_BITS);
|
||||||
format.setStencilBufferSize(DEFAULT_GL_STENCIL_BUFFER_BITS);
|
format.setStencilBufferSize(DEFAULT_GL_STENCIL_BUFFER_BITS);
|
||||||
format.setVersion(4, 1);
|
format.setVersion(4, 5);
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
format.setOption(QSurfaceFormat::DebugContext);
|
format.setOption(QSurfaceFormat::DebugContext);
|
||||||
#endif
|
#endif
|
||||||
|
@ -27,7 +27,7 @@ const QGLFormat& getDefaultGLFormat() {
|
||||||
static QGLFormat glFormat;
|
static QGLFormat glFormat;
|
||||||
static std::once_flag once;
|
static std::once_flag once;
|
||||||
std::call_once(once, [] {
|
std::call_once(once, [] {
|
||||||
glFormat.setVersion(4, 1);
|
glFormat.setVersion(4, 5);
|
||||||
glFormat.setProfile(QGLFormat::CoreProfile); // Requires >=Qt-4.8.0
|
glFormat.setProfile(QGLFormat::CoreProfile); // Requires >=Qt-4.8.0
|
||||||
glFormat.setSampleBuffers(false);
|
glFormat.setSampleBuffers(false);
|
||||||
glFormat.setDepth(false);
|
glFormat.setDepth(false);
|
||||||
|
|
|
@ -83,3 +83,8 @@ void OffscreenGLCanvas::doneCurrent() {
|
||||||
QObject* OffscreenGLCanvas::getContextObject() {
|
QObject* OffscreenGLCanvas::getContextObject() {
|
||||||
return _context;
|
return _context;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void OffscreenGLCanvas::moveToThreadWithContext(QThread* thread) {
|
||||||
|
moveToThread(thread);
|
||||||
|
_context->moveToThread(thread);
|
||||||
|
}
|
|
@ -26,6 +26,7 @@ public:
|
||||||
bool create(QOpenGLContext* sharedContext = nullptr);
|
bool create(QOpenGLContext* sharedContext = nullptr);
|
||||||
bool makeCurrent();
|
bool makeCurrent();
|
||||||
void doneCurrent();
|
void doneCurrent();
|
||||||
|
void moveToThreadWithContext(QThread* thread);
|
||||||
QOpenGLContext* getContext() {
|
QOpenGLContext* getContext() {
|
||||||
return _context;
|
return _context;
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,8 +6,10 @@
|
||||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
//
|
//
|
||||||
#include "OglplusHelpers.h"
|
#include "OglplusHelpers.h"
|
||||||
#include <QSharedPointer>
|
|
||||||
#include <set>
|
#include <set>
|
||||||
|
#include <oglplus/shapes/plane.hpp>
|
||||||
|
#include <oglplus/shapes/sky_box.hpp>
|
||||||
|
|
||||||
using namespace oglplus;
|
using namespace oglplus;
|
||||||
using namespace oglplus::shapes;
|
using namespace oglplus::shapes;
|
||||||
|
@ -20,11 +22,13 @@ uniform mat4 mvp = mat4(1);
|
||||||
in vec3 Position;
|
in vec3 Position;
|
||||||
in vec2 TexCoord;
|
in vec2 TexCoord;
|
||||||
|
|
||||||
|
out vec3 vPosition;
|
||||||
out vec2 vTexCoord;
|
out vec2 vTexCoord;
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
gl_Position = mvp * vec4(Position, 1);
|
gl_Position = mvp * vec4(Position, 1);
|
||||||
vTexCoord = TexCoord ;
|
vTexCoord = TexCoord;
|
||||||
|
vPosition = Position;
|
||||||
}
|
}
|
||||||
|
|
||||||
)VS";
|
)VS";
|
||||||
|
@ -35,7 +39,9 @@ static const char * SIMPLE_TEXTURED_FS = R"FS(#version 410 core
|
||||||
uniform sampler2D sampler;
|
uniform sampler2D sampler;
|
||||||
uniform float alpha = 1.0;
|
uniform float alpha = 1.0;
|
||||||
|
|
||||||
|
in vec3 vPosition;
|
||||||
in vec2 vTexCoord;
|
in vec2 vTexCoord;
|
||||||
|
|
||||||
out vec4 FragColor;
|
out vec4 FragColor;
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
|
@ -47,12 +53,38 @@ void main() {
|
||||||
)FS";
|
)FS";
|
||||||
|
|
||||||
|
|
||||||
|
static const char * SIMPLE_TEXTURED_CUBEMAP_FS = R"FS(#version 410 core
|
||||||
|
#pragma line __LINE__
|
||||||
|
|
||||||
|
uniform samplerCube sampler;
|
||||||
|
uniform float alpha = 1.0;
|
||||||
|
|
||||||
|
in vec3 vPosition;
|
||||||
|
in vec3 vTexCoord;
|
||||||
|
|
||||||
|
out vec4 FragColor;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
|
||||||
|
FragColor = texture(sampler, vPosition);
|
||||||
|
FragColor.a *= alpha;
|
||||||
|
}
|
||||||
|
|
||||||
|
)FS";
|
||||||
|
|
||||||
|
|
||||||
ProgramPtr loadDefaultShader() {
|
ProgramPtr loadDefaultShader() {
|
||||||
ProgramPtr result;
|
ProgramPtr result;
|
||||||
compileProgram(result, SIMPLE_TEXTURED_VS, SIMPLE_TEXTURED_FS);
|
compileProgram(result, SIMPLE_TEXTURED_VS, SIMPLE_TEXTURED_FS);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ProgramPtr loadCubemapShader() {
|
||||||
|
ProgramPtr result;
|
||||||
|
compileProgram(result, SIMPLE_TEXTURED_VS, SIMPLE_TEXTURED_CUBEMAP_FS);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
void compileProgram(ProgramPtr & result, const std::string& vs, const std::string& fs) {
|
void compileProgram(ProgramPtr & result, const std::string& vs, const std::string& fs) {
|
||||||
using namespace oglplus;
|
using namespace oglplus;
|
||||||
try {
|
try {
|
||||||
|
@ -93,6 +125,10 @@ ShapeWrapperPtr loadPlane(ProgramPtr program, float aspect) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ShapeWrapperPtr loadSkybox(ProgramPtr program) {
|
||||||
|
return ShapeWrapperPtr(new shapes::ShapeWrapper({ { "Position" } }, shapes::SkyBox(), *program));
|
||||||
|
}
|
||||||
|
|
||||||
// Return a point's cartesian coordinates on a sphere from pitch and yaw
|
// Return a point's cartesian coordinates on a sphere from pitch and yaw
|
||||||
static glm::vec3 getPoint(float yaw, float pitch) {
|
static glm::vec3 getPoint(float yaw, float pitch) {
|
||||||
return glm::vec3(glm::cos(-pitch) * (-glm::sin(yaw)),
|
return glm::vec3(glm::cos(-pitch) * (-glm::sin(yaw)),
|
||||||
|
|
|
@ -37,7 +37,6 @@
|
||||||
#include <oglplus/bound/framebuffer.hpp>
|
#include <oglplus/bound/framebuffer.hpp>
|
||||||
#include <oglplus/bound/renderbuffer.hpp>
|
#include <oglplus/bound/renderbuffer.hpp>
|
||||||
#include <oglplus/shapes/wrapper.hpp>
|
#include <oglplus/shapes/wrapper.hpp>
|
||||||
#include <oglplus/shapes/plane.hpp>
|
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
#pragma warning(pop)
|
#pragma warning(pop)
|
||||||
|
@ -55,7 +54,9 @@ using ProgramPtr = std::shared_ptr<oglplus::Program>;
|
||||||
using Mat4Uniform = oglplus::Uniform<mat4>;
|
using Mat4Uniform = oglplus::Uniform<mat4>;
|
||||||
|
|
||||||
ProgramPtr loadDefaultShader();
|
ProgramPtr loadDefaultShader();
|
||||||
|
ProgramPtr loadCubemapShader();
|
||||||
void compileProgram(ProgramPtr & result, const std::string& vs, const std::string& fs);
|
void compileProgram(ProgramPtr & result, const std::string& vs, const std::string& fs);
|
||||||
|
ShapeWrapperPtr loadSkybox(ProgramPtr program);
|
||||||
ShapeWrapperPtr loadPlane(ProgramPtr program, float aspect = 1.0f);
|
ShapeWrapperPtr loadPlane(ProgramPtr program, float aspect = 1.0f);
|
||||||
ShapeWrapperPtr loadSphereSection(ProgramPtr program, float fov = PI / 3.0f * 2.0f, float aspect = 16.0f / 9.0f, int slices = 32, int stacks = 32);
|
ShapeWrapperPtr loadSphereSection(ProgramPtr program, float fov = PI / 3.0f * 2.0f, float aspect = 16.0f / 9.0f, int slices = 32, int stacks = 32);
|
||||||
|
|
||||||
|
|
|
@ -13,6 +13,9 @@
|
||||||
|
|
||||||
#include <QOpenGLContext>
|
#include <QOpenGLContext>
|
||||||
|
|
||||||
|
QOpenGLContext* QOpenGLContextWrapper::currentContext() {
|
||||||
|
return QOpenGLContext::currentContext();
|
||||||
|
}
|
||||||
|
|
||||||
QOpenGLContextWrapper::QOpenGLContextWrapper() :
|
QOpenGLContextWrapper::QOpenGLContextWrapper() :
|
||||||
_context(new QOpenGLContext)
|
_context(new QOpenGLContext)
|
||||||
|
|
|
@ -19,7 +19,6 @@ class QSurfaceFormat;
|
||||||
class QOpenGLContextWrapper {
|
class QOpenGLContextWrapper {
|
||||||
public:
|
public:
|
||||||
QOpenGLContextWrapper();
|
QOpenGLContextWrapper();
|
||||||
|
|
||||||
void setFormat(const QSurfaceFormat& format);
|
void setFormat(const QSurfaceFormat& format);
|
||||||
bool create();
|
bool create();
|
||||||
void swapBuffers(QSurface* surface);
|
void swapBuffers(QSurface* surface);
|
||||||
|
@ -27,6 +26,8 @@ public:
|
||||||
void doneCurrent();
|
void doneCurrent();
|
||||||
void setShareContext(QOpenGLContext* otherContext);
|
void setShareContext(QOpenGLContext* otherContext);
|
||||||
|
|
||||||
|
static QOpenGLContext* currentContext();
|
||||||
|
|
||||||
QOpenGLContext* getContext() {
|
QOpenGLContext* getContext() {
|
||||||
return _context;
|
return _context;
|
||||||
}
|
}
|
||||||
|
|
|
@ -125,6 +125,7 @@ GLBackend::GLBackend() {
|
||||||
glGetIntegerv(GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT, &_uboAlignment);
|
glGetIntegerv(GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT, &_uboAlignment);
|
||||||
initInput();
|
initInput();
|
||||||
initTransform();
|
initTransform();
|
||||||
|
initTextureTransferHelper();
|
||||||
}
|
}
|
||||||
|
|
||||||
GLBackend::~GLBackend() {
|
GLBackend::~GLBackend() {
|
||||||
|
|
|
@ -24,6 +24,8 @@
|
||||||
|
|
||||||
namespace gpu {
|
namespace gpu {
|
||||||
|
|
||||||
|
class GLTextureTransferHelper;
|
||||||
|
|
||||||
class GLBackend : public Backend {
|
class GLBackend : public Backend {
|
||||||
|
|
||||||
// Context Backend static interface required
|
// Context Backend static interface required
|
||||||
|
@ -35,7 +37,6 @@ class GLBackend : public Backend {
|
||||||
explicit GLBackend(bool syncCache);
|
explicit GLBackend(bool syncCache);
|
||||||
GLBackend();
|
GLBackend();
|
||||||
public:
|
public:
|
||||||
|
|
||||||
virtual ~GLBackend();
|
virtual ~GLBackend();
|
||||||
|
|
||||||
virtual void render(Batch& batch);
|
virtual void render(Batch& batch);
|
||||||
|
@ -75,25 +76,63 @@ public:
|
||||||
|
|
||||||
class GLTexture : public GPUObject {
|
class GLTexture : public GPUObject {
|
||||||
public:
|
public:
|
||||||
Stamp _storageStamp;
|
const Stamp _storageStamp;
|
||||||
Stamp _contentStamp;
|
Stamp _contentStamp { 0 };
|
||||||
GLuint _texture;
|
const GLuint _texture;
|
||||||
GLenum _target;
|
const GLenum _target;
|
||||||
|
|
||||||
GLTexture();
|
GLTexture(const gpu::Texture& gpuTexture);
|
||||||
~GLTexture();
|
~GLTexture();
|
||||||
|
|
||||||
void setSize(GLuint size);
|
|
||||||
GLuint size() const { return _size; }
|
GLuint size() const { return _size; }
|
||||||
|
|
||||||
|
enum SyncState {
|
||||||
|
// The texture is currently undergoing no processing, although it's content
|
||||||
|
// may be out of date, or it's storage may be invalid relative to the
|
||||||
|
// owning GPU texture
|
||||||
|
Idle,
|
||||||
|
// The texture has been queued for transfer to the GPU
|
||||||
|
Pending,
|
||||||
|
// The texture has been transferred to the GPU, but is awaiting
|
||||||
|
// any post transfer operations that may need to occur on the
|
||||||
|
// primary rendering thread
|
||||||
|
Transferred,
|
||||||
|
};
|
||||||
|
|
||||||
|
void setSyncState(SyncState syncState) { _syncState = syncState; }
|
||||||
|
SyncState getSyncState() const { return _syncState; }
|
||||||
|
|
||||||
|
// Is the storage out of date relative to the gpu texture?
|
||||||
|
bool isInvalid() const;
|
||||||
|
|
||||||
|
// Is the content out of date relative to the gpu texture?
|
||||||
|
bool isOutdated() const;
|
||||||
|
|
||||||
|
// Is the texture in a state where it can be rendered with no work?
|
||||||
|
bool isReady() const;
|
||||||
|
|
||||||
|
// Move the image bits from the CPU to the GPU
|
||||||
|
void transfer() const;
|
||||||
|
|
||||||
|
// Execute any post-move operations that must occur only on the main thread
|
||||||
|
void postTransfer();
|
||||||
|
|
||||||
|
static const size_t CUBE_NUM_FACES = 6;
|
||||||
|
static const GLenum CUBE_FACE_LAYOUT[6];
|
||||||
|
|
||||||
private:
|
private:
|
||||||
GLuint _size;
|
void transferMip(GLenum target, const Texture::PixelsPointer& mip) const;
|
||||||
|
|
||||||
|
const GLuint _size;
|
||||||
|
// The owning texture
|
||||||
|
const Texture& _gpuTexture;
|
||||||
|
std::atomic<SyncState> _syncState { SyncState::Idle };
|
||||||
};
|
};
|
||||||
static GLTexture* syncGPUObject(const Texture& texture);
|
static GLTexture* syncGPUObject(const TexturePointer& texture);
|
||||||
static GLuint getTextureID(const TexturePointer& texture, bool sync = true);
|
static GLuint getTextureID(const TexturePointer& texture, bool sync = true);
|
||||||
|
|
||||||
// very specific for now
|
// very specific for now
|
||||||
static void syncSampler(const Sampler& sampler, Texture::Type type, GLTexture* object);
|
static void syncSampler(const Sampler& sampler, Texture::Type type, const GLTexture* object);
|
||||||
|
|
||||||
class GLShader : public GPUObject {
|
class GLShader : public GPUObject {
|
||||||
public:
|
public:
|
||||||
|
@ -241,6 +280,11 @@ protected:
|
||||||
void renderPassTransfer(Batch& batch);
|
void renderPassTransfer(Batch& batch);
|
||||||
void renderPassDraw(Batch& batch);
|
void renderPassDraw(Batch& batch);
|
||||||
|
|
||||||
|
void initTextureTransferHelper();
|
||||||
|
static void transferGPUObject(const TexturePointer& texture);
|
||||||
|
|
||||||
|
static std::shared_ptr<GLTextureTransferHelper> _textureTransferHelper;
|
||||||
|
|
||||||
// Draw Stage
|
// Draw Stage
|
||||||
void do_draw(Batch& batch, size_t paramOffset);
|
void do_draw(Batch& batch, size_t paramOffset);
|
||||||
void do_drawIndexed(Batch& batch, size_t paramOffset);
|
void do_drawIndexed(Batch& batch, size_t paramOffset);
|
||||||
|
@ -484,6 +528,7 @@ protected:
|
||||||
|
|
||||||
typedef void (GLBackend::*CommandCall)(Batch&, size_t);
|
typedef void (GLBackend::*CommandCall)(Batch&, size_t);
|
||||||
static CommandCall _commandCalls[Batch::NUM_COMMANDS];
|
static CommandCall _commandCalls[Batch::NUM_COMMANDS];
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -83,7 +83,7 @@ GLBackend::GLFramebuffer* GLBackend::syncGPUObject(const Framebuffer& framebuffe
|
||||||
for (auto& b : framebuffer.getRenderBuffers()) {
|
for (auto& b : framebuffer.getRenderBuffers()) {
|
||||||
surface = b._texture;
|
surface = b._texture;
|
||||||
if (surface) {
|
if (surface) {
|
||||||
gltexture = GLBackend::syncGPUObject(*surface);
|
gltexture = GLBackend::syncGPUObject(surface);
|
||||||
} else {
|
} else {
|
||||||
gltexture = nullptr;
|
gltexture = nullptr;
|
||||||
}
|
}
|
||||||
|
@ -123,7 +123,7 @@ GLBackend::GLFramebuffer* GLBackend::syncGPUObject(const Framebuffer& framebuffe
|
||||||
if (framebuffer.getDepthStamp() != object->_depthStamp) {
|
if (framebuffer.getDepthStamp() != object->_depthStamp) {
|
||||||
auto surface = framebuffer.getDepthStencilBuffer();
|
auto surface = framebuffer.getDepthStencilBuffer();
|
||||||
if (framebuffer.hasDepthStencil() && surface) {
|
if (framebuffer.hasDepthStencil() && surface) {
|
||||||
gltexture = GLBackend::syncGPUObject(*surface);
|
gltexture = GLBackend::syncGPUObject(surface);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (gltexture) {
|
if (gltexture) {
|
||||||
|
|
|
@ -255,7 +255,7 @@ void GLBackend::do_setResourceTexture(Batch& batch, size_t paramOffset) {
|
||||||
_stats._RSNumTextureBounded++;
|
_stats._RSNumTextureBounded++;
|
||||||
|
|
||||||
// Always make sure the GLObject is in sync
|
// Always make sure the GLObject is in sync
|
||||||
GLTexture* object = GLBackend::syncGPUObject(*resourceTexture);
|
GLTexture* object = GLBackend::syncGPUObject(resourceTexture);
|
||||||
if (object) {
|
if (object) {
|
||||||
GLuint to = object->_texture;
|
GLuint to = object->_texture;
|
||||||
GLuint target = object->_target;
|
GLuint target = object->_target;
|
||||||
|
|
|
@ -9,18 +9,79 @@
|
||||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
//
|
//
|
||||||
#include "GPULogging.h"
|
#include "GPULogging.h"
|
||||||
|
|
||||||
|
#include <QtCore/QThread>
|
||||||
|
|
||||||
#include "GLBackendShared.h"
|
#include "GLBackendShared.h"
|
||||||
|
#include "GLTexelFormat.h"
|
||||||
|
#include "GLBackendTextureTransfer.h"
|
||||||
|
|
||||||
using namespace gpu;
|
using namespace gpu;
|
||||||
|
|
||||||
GLBackend::GLTexture::GLTexture() :
|
GLenum gpuToGLTextureType(const Texture& texture) {
|
||||||
_storageStamp(0),
|
switch (texture.getType()) {
|
||||||
_contentStamp(0),
|
case Texture::TEX_2D:
|
||||||
_texture(0),
|
return GL_TEXTURE_2D;
|
||||||
_target(GL_TEXTURE_2D),
|
break;
|
||||||
_size(0)
|
|
||||||
|
case Texture::TEX_CUBE:
|
||||||
|
return GL_TEXTURE_CUBE_MAP;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
qFatal("Unsupported texture type");
|
||||||
|
}
|
||||||
|
Q_UNREACHABLE();
|
||||||
|
return GL_TEXTURE_2D;
|
||||||
|
}
|
||||||
|
|
||||||
|
GLuint allocateSingleTexture() {
|
||||||
|
GLuint result;
|
||||||
|
glGenTextures(1, &result);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
const GLenum GLBackend::GLTexture::CUBE_FACE_LAYOUT[6] = {
|
||||||
|
GL_TEXTURE_CUBE_MAP_POSITIVE_X, GL_TEXTURE_CUBE_MAP_NEGATIVE_X,
|
||||||
|
GL_TEXTURE_CUBE_MAP_POSITIVE_Y, GL_TEXTURE_CUBE_MAP_NEGATIVE_Y,
|
||||||
|
GL_TEXTURE_CUBE_MAP_POSITIVE_Z, GL_TEXTURE_CUBE_MAP_NEGATIVE_Z
|
||||||
|
};
|
||||||
|
|
||||||
|
// Create the texture and allocate storage
|
||||||
|
GLBackend::GLTexture::GLTexture(const Texture& texture) :
|
||||||
|
_storageStamp(texture.getStamp()), _texture(allocateSingleTexture()),
|
||||||
|
_target(gpuToGLTextureType(texture)), _size((GLuint)texture.getSize()), _gpuTexture(texture)
|
||||||
{
|
{
|
||||||
Backend::incrementTextureGPUCount();
|
Backend::incrementTextureGPUCount();
|
||||||
|
Backend::updateTextureGPUMemoryUsage(0, _size);
|
||||||
|
Backend::setGPUObject(texture, this);
|
||||||
|
|
||||||
|
GLsizei width = texture.getWidth();
|
||||||
|
GLsizei height = texture.getHeight();
|
||||||
|
GLsizei levels = 1;
|
||||||
|
if (texture.maxMip() > 0) {
|
||||||
|
if (texture.isAutogenerateMips()) {
|
||||||
|
while ((width | height) >> levels) {
|
||||||
|
++levels;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
levels = std::max(1, std::min(texture.maxMip() + 1, levels));
|
||||||
|
}
|
||||||
|
|
||||||
|
GLTexelFormat texelFormat = GLTexelFormat::evalGLTexelFormat(texture.getTexelFormat());
|
||||||
|
withPreservedTexture(_target, [&] {
|
||||||
|
glBindTexture(_target, _texture);
|
||||||
|
(void)CHECK_GL_ERROR();
|
||||||
|
// GO through the process of allocating the correct storage
|
||||||
|
if (GLEW_VERSION_4_2) {
|
||||||
|
glTexStorage2D(_target, levels, texelFormat.internalFormat, width, height);
|
||||||
|
} else {
|
||||||
|
glTexImage2D(_target, 0, texelFormat.internalFormat, width, height, 0, texelFormat.format, texelFormat.type, 0);
|
||||||
|
}
|
||||||
|
(void)CHECK_GL_ERROR();
|
||||||
|
syncSampler(texture.getSampler(), texture.getType(), this);
|
||||||
|
(void)CHECK_GL_ERROR();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
GLBackend::GLTexture::~GLTexture() {
|
GLBackend::GLTexture::~GLTexture() {
|
||||||
|
@ -31,562 +92,163 @@ GLBackend::GLTexture::~GLTexture() {
|
||||||
Backend::decrementTextureGPUCount();
|
Backend::decrementTextureGPUCount();
|
||||||
}
|
}
|
||||||
|
|
||||||
void GLBackend::GLTexture::setSize(GLuint size) {
|
bool GLBackend::GLTexture::isInvalid() const {
|
||||||
Backend::updateTextureGPUMemoryUsage(_size, size);
|
return _storageStamp < _gpuTexture.getStamp();
|
||||||
_size = size;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class GLTexelFormat {
|
bool GLBackend::GLTexture::isOutdated() const {
|
||||||
public:
|
return _contentStamp < _gpuTexture.getDataStamp();
|
||||||
GLenum internalFormat;
|
}
|
||||||
GLenum format;
|
|
||||||
GLenum type;
|
|
||||||
|
|
||||||
static GLTexelFormat evalGLTexelFormat(const Element& dstFormat, const Element& srcFormat) {
|
bool GLBackend::GLTexture::isReady() const {
|
||||||
if (dstFormat != srcFormat) {
|
// If we have an invalid texture, we're never ready
|
||||||
GLTexelFormat texel = {GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE};
|
if (isInvalid()) {
|
||||||
|
return false;
|
||||||
switch(dstFormat.getDimension()) {
|
|
||||||
case gpu::SCALAR: {
|
|
||||||
texel.format = GL_RED;
|
|
||||||
texel.type = _elementTypeToGLType[dstFormat.getType()];
|
|
||||||
|
|
||||||
switch(dstFormat.getSemantic()) {
|
|
||||||
case gpu::RGB:
|
|
||||||
case gpu::RGBA:
|
|
||||||
texel.internalFormat = GL_RED;
|
|
||||||
break;
|
|
||||||
case gpu::DEPTH:
|
|
||||||
texel.internalFormat = GL_DEPTH_COMPONENT;
|
|
||||||
break;
|
|
||||||
case gpu::DEPTH_STENCIL:
|
|
||||||
texel.type = GL_UNSIGNED_INT_24_8;
|
|
||||||
texel.format = GL_DEPTH_STENCIL;
|
|
||||||
texel.internalFormat = GL_DEPTH24_STENCIL8;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
qCDebug(gpulogging) << "Unknown combination of texel format";
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case gpu::VEC2: {
|
|
||||||
texel.format = GL_RG;
|
|
||||||
texel.type = _elementTypeToGLType[dstFormat.getType()];
|
|
||||||
|
|
||||||
switch(dstFormat.getSemantic()) {
|
|
||||||
case gpu::RGB:
|
|
||||||
case gpu::RGBA:
|
|
||||||
texel.internalFormat = GL_RG;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
qCDebug(gpulogging) << "Unknown combination of texel format";
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case gpu::VEC3: {
|
|
||||||
texel.format = GL_RGB;
|
|
||||||
|
|
||||||
texel.type = _elementTypeToGLType[dstFormat.getType()];
|
|
||||||
|
|
||||||
switch(dstFormat.getSemantic()) {
|
|
||||||
case gpu::RGB:
|
|
||||||
case gpu::RGBA:
|
|
||||||
texel.internalFormat = GL_RGB;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
qCDebug(gpulogging) << "Unknown combination of texel format";
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case gpu::VEC4: {
|
|
||||||
texel.format = GL_RGBA;
|
|
||||||
texel.type = _elementTypeToGLType[dstFormat.getType()];
|
|
||||||
|
|
||||||
switch(srcFormat.getSemantic()) {
|
|
||||||
case gpu::BGRA:
|
|
||||||
case gpu::SBGRA:
|
|
||||||
texel.format = GL_BGRA;
|
|
||||||
break;
|
|
||||||
case gpu::RGB:
|
|
||||||
case gpu::RGBA:
|
|
||||||
case gpu::SRGB:
|
|
||||||
case gpu::SRGBA:
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
};
|
|
||||||
|
|
||||||
switch(dstFormat.getSemantic()) {
|
|
||||||
case gpu::RGB:
|
|
||||||
texel.internalFormat = GL_RGB;
|
|
||||||
break;
|
|
||||||
case gpu::RGBA:
|
|
||||||
texel.internalFormat = GL_RGBA;
|
|
||||||
break;
|
|
||||||
case gpu::SRGB:
|
|
||||||
texel.internalFormat = GL_SRGB;
|
|
||||||
break;
|
|
||||||
case gpu::SRGBA:
|
|
||||||
texel.internalFormat = GL_SRGB_ALPHA;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
qCDebug(gpulogging) << "Unknown combination of texel format";
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
|
||||||
qCDebug(gpulogging) << "Unknown combination of texel format";
|
|
||||||
}
|
|
||||||
return texel;
|
|
||||||
} else {
|
|
||||||
GLTexelFormat texel = {GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE};
|
|
||||||
|
|
||||||
switch(dstFormat.getDimension()) {
|
|
||||||
case gpu::SCALAR: {
|
|
||||||
texel.format = GL_RED;
|
|
||||||
texel.type = _elementTypeToGLType[dstFormat.getType()];
|
|
||||||
|
|
||||||
switch(dstFormat.getSemantic()) {
|
|
||||||
case gpu::RGB:
|
|
||||||
case gpu::RGBA:
|
|
||||||
case gpu::SRGB:
|
|
||||||
case gpu::SRGBA:
|
|
||||||
texel.internalFormat = GL_RED;
|
|
||||||
switch (dstFormat.getType()) {
|
|
||||||
case gpu::UINT32: {
|
|
||||||
texel.internalFormat = GL_R32UI;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case gpu::INT32: {
|
|
||||||
texel.internalFormat = GL_R32I;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case gpu::NUINT32: {
|
|
||||||
texel.internalFormat = GL_RED;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case gpu::NINT32: {
|
|
||||||
texel.internalFormat = GL_RED_SNORM;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case gpu::FLOAT: {
|
|
||||||
texel.internalFormat = GL_R32F;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case gpu::UINT16: {
|
|
||||||
texel.internalFormat = GL_R16UI;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case gpu::INT16: {
|
|
||||||
texel.internalFormat = GL_R16I;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case gpu::NUINT16: {
|
|
||||||
texel.internalFormat = GL_R16;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case gpu::NINT16: {
|
|
||||||
texel.internalFormat = GL_R16_SNORM;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case gpu::HALF: {
|
|
||||||
texel.internalFormat = GL_R16F;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case gpu::UINT8: {
|
|
||||||
texel.internalFormat = GL_R8UI;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case gpu::INT8: {
|
|
||||||
texel.internalFormat = GL_R8I;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case gpu::NUINT8: {
|
|
||||||
if ((dstFormat.getSemantic() == gpu::SRGB || dstFormat.getSemantic() == gpu::SRGBA)) {
|
|
||||||
texel.internalFormat = GL_SLUMINANCE;
|
|
||||||
} else {
|
|
||||||
texel.internalFormat = GL_R8;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case gpu::NINT8: {
|
|
||||||
texel.internalFormat = GL_R8_SNORM;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case gpu::NUM_TYPES: { // quiet compiler
|
|
||||||
Q_UNREACHABLE();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case gpu::R11G11B10:
|
|
||||||
texel.format = GL_RGB;
|
|
||||||
// the type should be float
|
|
||||||
texel.internalFormat = GL_R11F_G11F_B10F;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case gpu::DEPTH:
|
|
||||||
texel.format = GL_DEPTH_COMPONENT; // It's depth component to load it
|
|
||||||
texel.internalFormat = GL_DEPTH_COMPONENT;
|
|
||||||
switch (dstFormat.getType()) {
|
|
||||||
case gpu::UINT32:
|
|
||||||
case gpu::INT32:
|
|
||||||
case gpu::NUINT32:
|
|
||||||
case gpu::NINT32: {
|
|
||||||
texel.internalFormat = GL_DEPTH_COMPONENT32;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case gpu::FLOAT: {
|
|
||||||
texel.internalFormat = GL_DEPTH_COMPONENT32F;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case gpu::UINT16:
|
|
||||||
case gpu::INT16:
|
|
||||||
case gpu::NUINT16:
|
|
||||||
case gpu::NINT16:
|
|
||||||
case gpu::HALF: {
|
|
||||||
texel.internalFormat = GL_DEPTH_COMPONENT16;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case gpu::UINT8:
|
|
||||||
case gpu::INT8:
|
|
||||||
case gpu::NUINT8:
|
|
||||||
case gpu::NINT8: {
|
|
||||||
texel.internalFormat = GL_DEPTH_COMPONENT24;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case gpu::NUM_TYPES: { // quiet compiler
|
|
||||||
Q_UNREACHABLE();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case gpu::DEPTH_STENCIL:
|
|
||||||
texel.type = GL_UNSIGNED_INT_24_8;
|
|
||||||
texel.format = GL_DEPTH_STENCIL;
|
|
||||||
texel.internalFormat = GL_DEPTH24_STENCIL8;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
qCDebug(gpulogging) << "Unknown combination of texel format";
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case gpu::VEC2: {
|
|
||||||
texel.format = GL_RG;
|
|
||||||
texel.type = _elementTypeToGLType[dstFormat.getType()];
|
|
||||||
|
|
||||||
switch(dstFormat.getSemantic()) {
|
|
||||||
case gpu::RGB:
|
|
||||||
case gpu::RGBA:
|
|
||||||
texel.internalFormat = GL_RG;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
qCDebug(gpulogging) << "Unknown combination of texel format";
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case gpu::VEC3: {
|
|
||||||
texel.format = GL_RGB;
|
|
||||||
|
|
||||||
texel.type = _elementTypeToGLType[dstFormat.getType()];
|
|
||||||
|
|
||||||
switch(dstFormat.getSemantic()) {
|
|
||||||
case gpu::RGB:
|
|
||||||
case gpu::RGBA:
|
|
||||||
texel.internalFormat = GL_RGB;
|
|
||||||
break;
|
|
||||||
case gpu::SRGB:
|
|
||||||
case gpu::SRGBA:
|
|
||||||
texel.internalFormat = GL_SRGB; // standard 2.2 gamma correction color
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
qCDebug(gpulogging) << "Unknown combination of texel format";
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case gpu::VEC4: {
|
|
||||||
texel.format = GL_RGBA;
|
|
||||||
texel.type = _elementTypeToGLType[dstFormat.getType()];
|
|
||||||
|
|
||||||
switch(dstFormat.getSemantic()) {
|
|
||||||
case gpu::RGB:
|
|
||||||
texel.internalFormat = GL_RGB;
|
|
||||||
break;
|
|
||||||
case gpu::RGBA:
|
|
||||||
texel.internalFormat = GL_RGBA;
|
|
||||||
switch (dstFormat.getType()) {
|
|
||||||
case gpu::UINT32:
|
|
||||||
texel.format = GL_RGBA_INTEGER;
|
|
||||||
texel.internalFormat = GL_RGBA32UI;
|
|
||||||
break;
|
|
||||||
case gpu::INT32:
|
|
||||||
texel.format = GL_RGBA_INTEGER;
|
|
||||||
texel.internalFormat = GL_RGBA32I;
|
|
||||||
break;
|
|
||||||
case gpu::FLOAT:
|
|
||||||
texel.internalFormat = GL_RGBA32F;
|
|
||||||
break;
|
|
||||||
case gpu::UINT16:
|
|
||||||
texel.format = GL_RGBA_INTEGER;
|
|
||||||
texel.internalFormat = GL_RGBA16UI;
|
|
||||||
break;
|
|
||||||
case gpu::INT16:
|
|
||||||
texel.format = GL_RGBA_INTEGER;
|
|
||||||
texel.internalFormat = GL_RGBA16I;
|
|
||||||
break;
|
|
||||||
case gpu::NUINT16:
|
|
||||||
texel.format = GL_RGBA;
|
|
||||||
texel.internalFormat = GL_RGBA16;
|
|
||||||
break;
|
|
||||||
case gpu::NINT16:
|
|
||||||
texel.format = GL_RGBA;
|
|
||||||
texel.internalFormat = GL_RGBA16_SNORM;
|
|
||||||
break;
|
|
||||||
case gpu::HALF:
|
|
||||||
texel.format = GL_RGBA;
|
|
||||||
texel.internalFormat = GL_RGBA16F;
|
|
||||||
break;
|
|
||||||
case gpu::UINT8:
|
|
||||||
texel.format = GL_RGBA_INTEGER;
|
|
||||||
texel.internalFormat = GL_RGBA8UI;
|
|
||||||
break;
|
|
||||||
case gpu::INT8:
|
|
||||||
texel.format = GL_RGBA_INTEGER;
|
|
||||||
texel.internalFormat = GL_RGBA8I;
|
|
||||||
break;
|
|
||||||
case gpu::NUINT8:
|
|
||||||
texel.format = GL_RGBA;
|
|
||||||
texel.internalFormat = GL_RGBA8;
|
|
||||||
break;
|
|
||||||
case gpu::NINT8:
|
|
||||||
texel.format = GL_RGBA;
|
|
||||||
texel.internalFormat = GL_RGBA8_SNORM;
|
|
||||||
break;
|
|
||||||
case gpu::NUINT32:
|
|
||||||
case gpu::NINT32:
|
|
||||||
case gpu::NUM_TYPES: // quiet compiler
|
|
||||||
Q_UNREACHABLE();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case gpu::SRGB:
|
|
||||||
texel.internalFormat = GL_SRGB;
|
|
||||||
break;
|
|
||||||
case gpu::SRGBA:
|
|
||||||
texel.internalFormat = GL_SRGB_ALPHA; // standard 2.2 gamma correction color
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
qCDebug(gpulogging) << "Unknown combination of texel format";
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
|
||||||
qCDebug(gpulogging) << "Unknown combination of texel format";
|
|
||||||
}
|
|
||||||
return texel;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
|
// If we're out of date, but the transfer is in progress, report ready
|
||||||
|
// as a special case
|
||||||
|
auto syncState = _syncState.load();
|
||||||
|
|
||||||
GLBackend::GLTexture* GLBackend::syncGPUObject(const Texture& texture) {
|
if (isOutdated()) {
|
||||||
GLTexture* object = Backend::getGPUObject<GLBackend::GLTexture>(texture);
|
return Pending == syncState;
|
||||||
|
}
|
||||||
|
|
||||||
// If GPU object already created and in sync
|
return Idle == syncState;
|
||||||
bool needUpdate = false;
|
}
|
||||||
if (object && (object->_storageStamp == texture.getStamp())) {
|
|
||||||
// If gpu object info is in sync with sysmem version
|
//#define USE_PBO
|
||||||
if (object->_contentStamp >= texture.getDataStamp()) {
|
|
||||||
// Then all good, GPU object is ready to be used
|
// Move content bits from the CPU to the GPU for a given mip / face
|
||||||
return object;
|
void GLBackend::GLTexture::transferMip(GLenum target, const Texture::PixelsPointer& mip) const {
|
||||||
} else {
|
GLTexelFormat texelFormat = GLTexelFormat::evalGLTexelFormat(_gpuTexture.getTexelFormat(), mip->getFormat());
|
||||||
// Need to update the content of the GPU object from the source sysmem of the texture
|
#ifdef USE_PBO
|
||||||
needUpdate = true;
|
GLuint pixelBufferID;
|
||||||
}
|
glGenBuffers(1, &pixelBufferID);
|
||||||
} else if (!texture.isDefined()) {
|
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pixelBufferID);
|
||||||
|
//if (GLEW_VERSION_4_4) {
|
||||||
|
// glBufferStorage(GL_PIXEL_UNPACK_BUFFER, mip->getSize(), nullptr, GL_STREAM_DRAW);
|
||||||
|
//} else {
|
||||||
|
glBufferData(GL_PIXEL_UNPACK_BUFFER, mip->getSize(), nullptr, GL_STREAM_DRAW);
|
||||||
|
//}
|
||||||
|
void* mappedBuffer = glMapBuffer(GL_PIXEL_UNPACK_BUFFER, GL_WRITE_ONLY);
|
||||||
|
memcpy(mappedBuffer, mip->readData(), mip->getSize());
|
||||||
|
//// use while PBO is still bound, assumes GL_TEXTURE_2D and offset 0
|
||||||
|
glTexSubImage2D(target, 0, 0, 0, _gpuTexture.getWidth(), _gpuTexture.getHeight(), texelFormat.format, texelFormat.type, 0);
|
||||||
|
glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER);
|
||||||
|
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
|
||||||
|
glDeleteBuffers(1, &pixelBufferID);
|
||||||
|
#else
|
||||||
|
//glTexImage2D(target, 0, internalFormat, texture.getWidth(), texture.getHeight(), 0, texelFormat.format, texelFormat.type, bytes);
|
||||||
|
glTexSubImage2D(target, 0, 0, 0, _gpuTexture.getWidth(), _gpuTexture.getHeight(), texelFormat.format, texelFormat.type, mip->readData());
|
||||||
|
(void)CHECK_GL_ERROR();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
// Move content bits from the CPU to the GPU
|
||||||
|
void GLBackend::GLTexture::transfer() const {
|
||||||
|
PROFILE_RANGE(__FUNCTION__);
|
||||||
|
qDebug() << "Transferring texture: " << _texture;
|
||||||
|
// Need to update the content of the GPU object from the source sysmem of the texture
|
||||||
|
if (_contentStamp >= _gpuTexture.getDataStamp()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
glBindTexture(_target, _texture);
|
||||||
|
// GO through the process of allocating the correct storage and/or update the content
|
||||||
|
switch (_gpuTexture.getType()) {
|
||||||
|
case Texture::TEX_2D:
|
||||||
|
if (_gpuTexture.isStoredMipFaceAvailable(0)) {
|
||||||
|
transferMip(GL_TEXTURE_2D, _gpuTexture.accessStoredMipFace(0));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Texture::TEX_CUBE:
|
||||||
|
// transfer pixels from each faces
|
||||||
|
for (uint8_t f = 0; f < CUBE_NUM_FACES; f++) {
|
||||||
|
if (_gpuTexture.isStoredMipFaceAvailable(0, f)) {
|
||||||
|
transferMip(CUBE_FACE_LAYOUT[f], _gpuTexture.accessStoredMipFace(0, f));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
qCWarning(gpulogging) << __FUNCTION__ << " case for Texture Type " << _gpuTexture.getType() << " not supported";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_gpuTexture.isAutogenerateMips()) {
|
||||||
|
glGenerateMipmap(_target);
|
||||||
|
(void)CHECK_GL_ERROR();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do any post-transfer operations that might be required on the main context / rendering thread
|
||||||
|
void GLBackend::GLTexture::postTransfer() {
|
||||||
|
setSyncState(GLTexture::Idle);
|
||||||
|
// At this point the mip pixels have been loaded, we can notify the gpu texture to abandon it's memory
|
||||||
|
switch (_gpuTexture.getType()) {
|
||||||
|
case Texture::TEX_2D:
|
||||||
|
_gpuTexture.notifyMipFaceGPULoaded(0, 0);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Texture::TEX_CUBE:
|
||||||
|
for (uint8_t f = 0; f < CUBE_NUM_FACES; ++f) {
|
||||||
|
_gpuTexture.notifyMipFaceGPULoaded(0, f);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
qCWarning(gpulogging) << __FUNCTION__ << " case for Texture Type " << _gpuTexture.getType() << " not supported";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
GLBackend::GLTexture* GLBackend::syncGPUObject(const TexturePointer& texturePointer) {
|
||||||
|
const Texture& texture = *texturePointer;
|
||||||
|
if (!texture.isDefined()) {
|
||||||
// NO texture definition yet so let's avoid thinking
|
// NO texture definition yet so let's avoid thinking
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If the object hasn't been created, or the object definition is out of date, drop and re-create
|
||||||
|
GLTexture* object = Backend::getGPUObject<GLBackend::GLTexture>(texture);
|
||||||
|
if (object && object->isReady()) {
|
||||||
|
return object;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Object isn't ready, check what we need to do...
|
||||||
|
|
||||||
|
// Create the texture if need be (force re-creation if the storage stamp changes
|
||||||
|
// for easier use of immutable storage)
|
||||||
|
if (!object || object->isInvalid()) {
|
||||||
|
// This automatically destroys the old texture
|
||||||
|
object = new GLTexture(texture);
|
||||||
|
}
|
||||||
|
|
||||||
// need to have a gpu object?
|
// need to have a gpu object?
|
||||||
if (!object) {
|
if (texture.getNumSlices() != 1) {
|
||||||
object = new GLTexture();
|
return object;
|
||||||
glGenTextures(1, &object->_texture);
|
|
||||||
(void) CHECK_GL_ERROR();
|
|
||||||
Backend::setGPUObject(texture, object);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// GO through the process of allocating the correct storage and/or update the content
|
// Object might be outdated, if so, start the transfer
|
||||||
switch (texture.getType()) {
|
// (outdated objects that are already in transfer will have reported 'true' for ready()
|
||||||
case Texture::TEX_2D: {
|
if (object->isOutdated()) {
|
||||||
if (texture.getNumSlices() == 1) {
|
_textureTransferHelper->transferTexture(texturePointer);
|
||||||
GLint boundTex = -1;
|
|
||||||
glGetIntegerv(GL_TEXTURE_BINDING_2D, &boundTex);
|
|
||||||
glBindTexture(GL_TEXTURE_2D, object->_texture);
|
|
||||||
|
|
||||||
if (needUpdate) {
|
|
||||||
if (texture.isStoredMipFaceAvailable(0)) {
|
|
||||||
Texture::PixelsPointer mip = texture.accessStoredMipFace(0);
|
|
||||||
const GLvoid* bytes = mip->readData();
|
|
||||||
Element srcFormat = mip->getFormat();
|
|
||||||
|
|
||||||
GLTexelFormat texelFormat = GLTexelFormat::evalGLTexelFormat(texture.getTexelFormat(), srcFormat);
|
|
||||||
|
|
||||||
glBindTexture(GL_TEXTURE_2D, object->_texture);
|
|
||||||
glTexSubImage2D(GL_TEXTURE_2D, 0,
|
|
||||||
texelFormat.internalFormat, texture.getWidth(), texture.getHeight(), 0,
|
|
||||||
texelFormat.format, texelFormat.type, bytes);
|
|
||||||
|
|
||||||
if (texture.isAutogenerateMips()) {
|
|
||||||
glGenerateMipmap(GL_TEXTURE_2D);
|
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
|
|
||||||
}
|
|
||||||
|
|
||||||
object->_target = GL_TEXTURE_2D;
|
|
||||||
|
|
||||||
syncSampler(texture.getSampler(), texture.getType(), object);
|
|
||||||
|
|
||||||
|
|
||||||
// At this point the mip piels have been loaded, we can notify
|
|
||||||
texture.notifyMipFaceGPULoaded(0, 0);
|
|
||||||
|
|
||||||
object->_contentStamp = texture.getDataStamp();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
const GLvoid* bytes = 0;
|
|
||||||
Element srcFormat = texture.getTexelFormat();
|
|
||||||
if (texture.isStoredMipFaceAvailable(0)) {
|
|
||||||
Texture::PixelsPointer mip = texture.accessStoredMipFace(0);
|
|
||||||
|
|
||||||
bytes = mip->readData();
|
|
||||||
srcFormat = mip->getFormat();
|
|
||||||
|
|
||||||
object->_contentStamp = texture.getDataStamp();
|
|
||||||
}
|
|
||||||
|
|
||||||
GLTexelFormat texelFormat = GLTexelFormat::evalGLTexelFormat(texture.getTexelFormat(), srcFormat);
|
|
||||||
|
|
||||||
glTexImage2D(GL_TEXTURE_2D, 0,
|
|
||||||
texelFormat.internalFormat, texture.getWidth(), texture.getHeight(), 0,
|
|
||||||
texelFormat.format, texelFormat.type, bytes);
|
|
||||||
|
|
||||||
if (bytes && texture.isAutogenerateMips()) {
|
|
||||||
glGenerateMipmap(GL_TEXTURE_2D);
|
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
|
|
||||||
}
|
|
||||||
object->_target = GL_TEXTURE_2D;
|
|
||||||
|
|
||||||
syncSampler(texture.getSampler(), texture.getType(), object);
|
|
||||||
|
|
||||||
// At this point the mip pixels have been loaded, we can notify
|
|
||||||
texture.notifyMipFaceGPULoaded(0, 0);
|
|
||||||
|
|
||||||
object->_storageStamp = texture.getStamp();
|
|
||||||
object->_contentStamp = texture.getDataStamp();
|
|
||||||
object->setSize((GLuint)texture.getSize());
|
|
||||||
}
|
|
||||||
|
|
||||||
glBindTexture(GL_TEXTURE_2D, boundTex);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
case Texture::TEX_CUBE: {
|
|
||||||
if (texture.getNumSlices() == 1) {
|
|
||||||
GLint boundTex = -1;
|
|
||||||
glGetIntegerv(GL_TEXTURE_BINDING_CUBE_MAP, &boundTex);
|
|
||||||
glBindTexture(GL_TEXTURE_CUBE_MAP, object->_texture);
|
|
||||||
const int NUM_FACES = 6;
|
|
||||||
const GLenum FACE_LAYOUT[] = {
|
|
||||||
GL_TEXTURE_CUBE_MAP_POSITIVE_X, GL_TEXTURE_CUBE_MAP_NEGATIVE_X,
|
|
||||||
GL_TEXTURE_CUBE_MAP_POSITIVE_Y, GL_TEXTURE_CUBE_MAP_NEGATIVE_Y,
|
|
||||||
GL_TEXTURE_CUBE_MAP_POSITIVE_Z, GL_TEXTURE_CUBE_MAP_NEGATIVE_Z };
|
|
||||||
if (needUpdate) {
|
|
||||||
glBindTexture(GL_TEXTURE_CUBE_MAP, object->_texture);
|
|
||||||
|
|
||||||
// transfer pixels from each faces
|
if (GLTexture::Transferred == object->getSyncState()) {
|
||||||
for (int f = 0; f < NUM_FACES; f++) {
|
object->postTransfer();
|
||||||
if (texture.isStoredMipFaceAvailable(0, f)) {
|
|
||||||
Texture::PixelsPointer mipFace = texture.accessStoredMipFace(0, f);
|
|
||||||
Element srcFormat = mipFace->getFormat();
|
|
||||||
GLTexelFormat texelFormat = GLTexelFormat::evalGLTexelFormat(texture.getTexelFormat(), srcFormat);
|
|
||||||
|
|
||||||
glTexSubImage2D(FACE_LAYOUT[f], 0, texelFormat.internalFormat, texture.getWidth(), texture.getWidth(), 0,
|
|
||||||
texelFormat.format, texelFormat.type, (GLvoid*) (mipFace->readData()));
|
|
||||||
|
|
||||||
// At this point the mip pixels have been loaded, we can notify
|
|
||||||
texture.notifyMipFaceGPULoaded(0, f);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (texture.isAutogenerateMips()) {
|
|
||||||
glGenerateMipmap(GL_TEXTURE_CUBE_MAP);
|
|
||||||
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
|
|
||||||
}
|
|
||||||
|
|
||||||
object->_target = GL_TEXTURE_CUBE_MAP;
|
|
||||||
|
|
||||||
syncSampler(texture.getSampler(), texture.getType(), object);
|
|
||||||
|
|
||||||
object->_contentStamp = texture.getDataStamp();
|
|
||||||
|
|
||||||
} else {
|
|
||||||
glBindTexture(GL_TEXTURE_CUBE_MAP, object->_texture);
|
|
||||||
|
|
||||||
// transfer pixels from each faces
|
|
||||||
for (int f = 0; f < NUM_FACES; f++) {
|
|
||||||
if (texture.isStoredMipFaceAvailable(0, f)) {
|
|
||||||
Texture::PixelsPointer mipFace = texture.accessStoredMipFace(0, f);
|
|
||||||
Element srcFormat = mipFace->getFormat();
|
|
||||||
GLTexelFormat texelFormat = GLTexelFormat::evalGLTexelFormat(texture.getTexelFormat(), srcFormat);
|
|
||||||
|
|
||||||
glTexImage2D(FACE_LAYOUT[f], 0, texelFormat.internalFormat, texture.getWidth(), texture.getWidth(), 0,
|
|
||||||
texelFormat.format, texelFormat.type, (GLvoid*) (mipFace->readData()));
|
|
||||||
|
|
||||||
// At this point the mip pixels have been loaded, we can notify
|
|
||||||
texture.notifyMipFaceGPULoaded(0, f);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (texture.isAutogenerateMips()) {
|
|
||||||
glGenerateMipmap(GL_TEXTURE_CUBE_MAP);
|
|
||||||
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
|
|
||||||
} else {
|
|
||||||
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
|
||||||
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
|
||||||
}
|
|
||||||
|
|
||||||
object->_target = GL_TEXTURE_CUBE_MAP;
|
|
||||||
|
|
||||||
syncSampler(texture.getSampler(), texture.getType(), object);
|
|
||||||
|
|
||||||
object->_storageStamp = texture.getStamp();
|
|
||||||
object->_contentStamp = texture.getDataStamp();
|
|
||||||
object->setSize((GLuint)texture.getSize());
|
|
||||||
}
|
|
||||||
|
|
||||||
glBindTexture(GL_TEXTURE_CUBE_MAP, boundTex);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
default:
|
|
||||||
qCDebug(gpulogging) << "GLBackend::syncGPUObject(const Texture&) case for Texture Type " << texture.getType() << " not supported";
|
|
||||||
}
|
|
||||||
(void) CHECK_GL_ERROR();
|
|
||||||
|
|
||||||
return object;
|
return object;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<GLTextureTransferHelper> GLBackend::_textureTransferHelper;
|
||||||
|
|
||||||
|
void GLBackend::initTextureTransferHelper() {
|
||||||
|
_textureTransferHelper = std::make_shared<GLTextureTransferHelper>();
|
||||||
|
}
|
||||||
|
|
||||||
GLuint GLBackend::getTextureID(const TexturePointer& texture, bool sync) {
|
GLuint GLBackend::getTextureID(const TexturePointer& texture, bool sync) {
|
||||||
if (!texture) {
|
if (!texture) {
|
||||||
|
@ -594,7 +256,7 @@ GLuint GLBackend::getTextureID(const TexturePointer& texture, bool sync) {
|
||||||
}
|
}
|
||||||
GLTexture* object { nullptr };
|
GLTexture* object { nullptr };
|
||||||
if (sync) {
|
if (sync) {
|
||||||
object = GLBackend::syncGPUObject(*texture);
|
object = GLBackend::syncGPUObject(texture);
|
||||||
} else {
|
} else {
|
||||||
object = Backend::getGPUObject<GLBackend::GLTexture>(*texture);
|
object = Backend::getGPUObject<GLBackend::GLTexture>(*texture);
|
||||||
}
|
}
|
||||||
|
@ -605,38 +267,37 @@ GLuint GLBackend::getTextureID(const TexturePointer& texture, bool sync) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void GLBackend::syncSampler(const Sampler& sampler, Texture::Type type, GLTexture* object) {
|
void GLBackend::syncSampler(const Sampler& sampler, Texture::Type type, const GLTexture* object) {
|
||||||
if (!object) return;
|
if (!object) return;
|
||||||
if (!object->_texture) return;
|
|
||||||
|
|
||||||
class GLFilterMode {
|
class GLFilterMode {
|
||||||
public:
|
public:
|
||||||
GLint minFilter;
|
GLint minFilter;
|
||||||
GLint magFilter;
|
GLint magFilter;
|
||||||
};
|
};
|
||||||
static const GLFilterMode filterModes[] = {
|
static const GLFilterMode filterModes[] = {
|
||||||
{GL_NEAREST, GL_NEAREST}, //FILTER_MIN_MAG_POINT,
|
{ GL_NEAREST, GL_NEAREST }, //FILTER_MIN_MAG_POINT,
|
||||||
{GL_NEAREST, GL_LINEAR}, //FILTER_MIN_POINT_MAG_LINEAR,
|
{ GL_NEAREST, GL_LINEAR }, //FILTER_MIN_POINT_MAG_LINEAR,
|
||||||
{GL_LINEAR, GL_NEAREST}, //FILTER_MIN_LINEAR_MAG_POINT,
|
{ GL_LINEAR, GL_NEAREST }, //FILTER_MIN_LINEAR_MAG_POINT,
|
||||||
{GL_LINEAR, GL_LINEAR}, //FILTER_MIN_MAG_LINEAR,
|
{ GL_LINEAR, GL_LINEAR }, //FILTER_MIN_MAG_LINEAR,
|
||||||
|
|
||||||
{GL_NEAREST_MIPMAP_NEAREST, GL_NEAREST}, //FILTER_MIN_MAG_MIP_POINT,
|
{ GL_NEAREST_MIPMAP_NEAREST, GL_NEAREST }, //FILTER_MIN_MAG_MIP_POINT,
|
||||||
{GL_NEAREST_MIPMAP_NEAREST, GL_NEAREST}, //FILTER_MIN_MAG_MIP_POINT,
|
{ GL_NEAREST_MIPMAP_NEAREST, GL_NEAREST }, //FILTER_MIN_MAG_MIP_POINT,
|
||||||
{GL_NEAREST_MIPMAP_LINEAR, GL_NEAREST}, //FILTER_MIN_MAG_POINT_MIP_LINEAR,
|
{ GL_NEAREST_MIPMAP_LINEAR, GL_NEAREST }, //FILTER_MIN_MAG_POINT_MIP_LINEAR,
|
||||||
{GL_NEAREST_MIPMAP_NEAREST, GL_LINEAR}, //FILTER_MIN_POINT_MAG_LINEAR_MIP_POINT,
|
{ GL_NEAREST_MIPMAP_NEAREST, GL_LINEAR }, //FILTER_MIN_POINT_MAG_LINEAR_MIP_POINT,
|
||||||
{GL_NEAREST_MIPMAP_LINEAR, GL_LINEAR}, //FILTER_MIN_POINT_MAG_MIP_LINEAR,
|
{ GL_NEAREST_MIPMAP_LINEAR, GL_LINEAR }, //FILTER_MIN_POINT_MAG_MIP_LINEAR,
|
||||||
{GL_LINEAR_MIPMAP_NEAREST, GL_NEAREST}, //FILTER_MIN_LINEAR_MAG_MIP_POINT,
|
{ GL_LINEAR_MIPMAP_NEAREST, GL_NEAREST }, //FILTER_MIN_LINEAR_MAG_MIP_POINT,
|
||||||
{GL_LINEAR_MIPMAP_LINEAR, GL_NEAREST}, //FILTER_MIN_LINEAR_MAG_POINT_MIP_LINEAR,
|
{ GL_LINEAR_MIPMAP_LINEAR, GL_NEAREST }, //FILTER_MIN_LINEAR_MAG_POINT_MIP_LINEAR,
|
||||||
{GL_LINEAR_MIPMAP_NEAREST, GL_LINEAR}, //FILTER_MIN_MAG_LINEAR_MIP_POINT,
|
{ GL_LINEAR_MIPMAP_NEAREST, GL_LINEAR }, //FILTER_MIN_MAG_LINEAR_MIP_POINT,
|
||||||
{GL_LINEAR_MIPMAP_LINEAR, GL_LINEAR}, //FILTER_MIN_MAG_MIP_LINEAR,
|
{ GL_LINEAR_MIPMAP_LINEAR, GL_LINEAR }, //FILTER_MIN_MAG_MIP_LINEAR,
|
||||||
{GL_LINEAR_MIPMAP_LINEAR, GL_LINEAR} //FILTER_ANISOTROPIC,
|
{ GL_LINEAR_MIPMAP_LINEAR, GL_LINEAR } //FILTER_ANISOTROPIC,
|
||||||
};
|
};
|
||||||
|
|
||||||
auto fm = filterModes[sampler.getFilter()];
|
auto fm = filterModes[sampler.getFilter()];
|
||||||
glTexParameteri(object->_target, GL_TEXTURE_MIN_FILTER, fm.minFilter);
|
glTexParameteri(object->_target, GL_TEXTURE_MIN_FILTER, fm.minFilter);
|
||||||
glTexParameteri(object->_target, GL_TEXTURE_MAG_FILTER, fm.magFilter);
|
glTexParameteri(object->_target, GL_TEXTURE_MAG_FILTER, fm.magFilter);
|
||||||
|
|
||||||
static const GLenum comparisonFuncs[] = {
|
static const GLenum comparisonFuncs[] = {
|
||||||
GL_NEVER,
|
GL_NEVER,
|
||||||
GL_LESS,
|
GL_LESS,
|
||||||
GL_EQUAL,
|
GL_EQUAL,
|
||||||
|
@ -653,7 +314,7 @@ void GLBackend::syncSampler(const Sampler& sampler, Texture::Type type, GLTextur
|
||||||
glTexParameteri(object->_target, GL_TEXTURE_COMPARE_MODE, GL_NONE);
|
glTexParameteri(object->_target, GL_TEXTURE_COMPARE_MODE, GL_NONE);
|
||||||
}
|
}
|
||||||
|
|
||||||
static const GLenum wrapModes[] = {
|
static const GLenum wrapModes[] = {
|
||||||
GL_REPEAT, // WRAP_REPEAT,
|
GL_REPEAT, // WRAP_REPEAT,
|
||||||
GL_MIRRORED_REPEAT, // WRAP_MIRROR,
|
GL_MIRRORED_REPEAT, // WRAP_MIRROR,
|
||||||
GL_CLAMP_TO_EDGE, // WRAP_CLAMP,
|
GL_CLAMP_TO_EDGE, // WRAP_CLAMP,
|
||||||
|
@ -664,23 +325,20 @@ void GLBackend::syncSampler(const Sampler& sampler, Texture::Type type, GLTextur
|
||||||
glTexParameteri(object->_target, GL_TEXTURE_WRAP_T, wrapModes[sampler.getWrapModeV()]);
|
glTexParameteri(object->_target, GL_TEXTURE_WRAP_T, wrapModes[sampler.getWrapModeV()]);
|
||||||
glTexParameteri(object->_target, GL_TEXTURE_WRAP_R, wrapModes[sampler.getWrapModeW()]);
|
glTexParameteri(object->_target, GL_TEXTURE_WRAP_R, wrapModes[sampler.getWrapModeW()]);
|
||||||
|
|
||||||
glTexParameterfv(object->_target, GL_TEXTURE_BORDER_COLOR, (const float*) &sampler.getBorderColor());
|
glTexParameterfv(object->_target, GL_TEXTURE_BORDER_COLOR, (const float*)&sampler.getBorderColor());
|
||||||
glTexParameteri(object->_target, GL_TEXTURE_BASE_LEVEL, sampler.getMipOffset());
|
glTexParameteri(object->_target, GL_TEXTURE_BASE_LEVEL, sampler.getMipOffset());
|
||||||
glTexParameterf(object->_target, GL_TEXTURE_MIN_LOD, (float) sampler.getMinMip());
|
glTexParameterf(object->_target, GL_TEXTURE_MIN_LOD, (float)sampler.getMinMip());
|
||||||
glTexParameterf(object->_target, GL_TEXTURE_MAX_LOD, (sampler.getMaxMip() == Sampler::MAX_MIP_LEVEL ? 1000.f : sampler.getMaxMip()));
|
glTexParameterf(object->_target, GL_TEXTURE_MAX_LOD, (sampler.getMaxMip() == Sampler::MAX_MIP_LEVEL ? 1000.f : sampler.getMaxMip()));
|
||||||
glTexParameterf(object->_target, GL_TEXTURE_MAX_ANISOTROPY_EXT, sampler.getMaxAnisotropy());
|
glTexParameterf(object->_target, GL_TEXTURE_MAX_ANISOTROPY_EXT, sampler.getMaxAnisotropy());
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void GLBackend::do_generateTextureMips(Batch& batch, size_t paramOffset) {
|
void GLBackend::do_generateTextureMips(Batch& batch, size_t paramOffset) {
|
||||||
TexturePointer resourceTexture = batch._textures.get(batch._params[paramOffset + 0]._uint);
|
TexturePointer resourceTexture = batch._textures.get(batch._params[paramOffset + 0]._uint);
|
||||||
if (!resourceTexture) {
|
if (!resourceTexture) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
GLTexture* object = GLBackend::syncGPUObject(*resourceTexture);
|
GLTexture* object = GLBackend::syncGPUObject(resourceTexture);
|
||||||
if (!object) {
|
if (!object) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -695,7 +353,7 @@ void GLBackend::do_generateTextureMips(Batch& batch, size_t paramOffset) {
|
||||||
|
|
||||||
if (freeSlot < 0) {
|
if (freeSlot < 0) {
|
||||||
// If had to use slot 0 then restore state
|
// If had to use slot 0 then restore state
|
||||||
GLTexture* boundObject = GLBackend::syncGPUObject(*_resource._textures[0]);
|
GLTexture* boundObject = GLBackend::syncGPUObject(_resource._textures[0]);
|
||||||
if (boundObject) {
|
if (boundObject) {
|
||||||
glBindTexture(boundObject->_target, boundObject->_texture);
|
glBindTexture(boundObject->_target, boundObject->_texture);
|
||||||
}
|
}
|
||||||
|
|
132
libraries/gpu/src/gpu/GLBackendTextureTransfer.cpp
Normal file
132
libraries/gpu/src/gpu/GLBackendTextureTransfer.cpp
Normal file
|
@ -0,0 +1,132 @@
|
||||||
|
//
|
||||||
|
// Created by Bradley Austin Davis on 2016/04/03
|
||||||
|
// Copyright 2013-2016 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
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "GLBackendTextureTransfer.h"
|
||||||
|
|
||||||
|
#include "GLBackendShared.h"
|
||||||
|
#include "GLTexelFormat.h"
|
||||||
|
|
||||||
|
#ifdef THREADED_TEXTURE_TRANSFER
|
||||||
|
|
||||||
|
#include <gl/OffscreenGLCanvas.h>
|
||||||
|
#include <gl/QOpenGLContextWrapper.h>
|
||||||
|
|
||||||
|
//#define FORCE_DRAW_AFTER_TRANSFER
|
||||||
|
|
||||||
|
#ifdef FORCE_DRAW_AFTER_TRANSFER
|
||||||
|
|
||||||
|
#include <gl/OglplusHelpers.h>
|
||||||
|
|
||||||
|
static ProgramPtr _program;
|
||||||
|
static ProgramPtr _cubeProgram;
|
||||||
|
static ShapeWrapperPtr _plane;
|
||||||
|
static ShapeWrapperPtr _skybox;
|
||||||
|
static BasicFramebufferWrapperPtr _framebuffer;
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
using namespace gpu;
|
||||||
|
|
||||||
|
GLTextureTransferHelper::GLTextureTransferHelper() {
|
||||||
|
#ifdef THREADED_TEXTURE_TRANSFER
|
||||||
|
_canvas = std::make_shared<OffscreenGLCanvas>();
|
||||||
|
_canvas->create(QOpenGLContextWrapper::currentContext());
|
||||||
|
if (!_canvas->makeCurrent()) {
|
||||||
|
qFatal("Unable to create texture transfer context");
|
||||||
|
}
|
||||||
|
_canvas->doneCurrent();
|
||||||
|
initialize(true, QThread::LowPriority);
|
||||||
|
_canvas->moveToThreadWithContext(_thread);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void GLTextureTransferHelper::transferTexture(const gpu::TexturePointer& texturePointer) {
|
||||||
|
GLBackend::GLTexture* object = Backend::getGPUObject<GLBackend::GLTexture>(*texturePointer);
|
||||||
|
#ifdef THREADED_TEXTURE_TRANSFER
|
||||||
|
TextureTransferPackage package { texturePointer, glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0) };
|
||||||
|
glFlush();
|
||||||
|
object->setSyncState(GLBackend::GLTexture::Pending);
|
||||||
|
queueItem(package);
|
||||||
|
#else
|
||||||
|
object->transfer();
|
||||||
|
object->postTransfer();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void GLTextureTransferHelper::setup() {
|
||||||
|
#ifdef THREADED_TEXTURE_TRANSFER
|
||||||
|
_canvas->makeCurrent();
|
||||||
|
|
||||||
|
#ifdef FORCE_DRAW_AFTER_TRANSFER
|
||||||
|
_program = loadDefaultShader();
|
||||||
|
_plane = loadPlane(_program);
|
||||||
|
_cubeProgram = loadCubemapShader();
|
||||||
|
_skybox = loadSkybox(_cubeProgram);
|
||||||
|
_framebuffer = std::make_shared<BasicFramebufferWrapper>();
|
||||||
|
_framebuffer->Init({ 100, 100 });
|
||||||
|
_framebuffer->fbo.Bind(oglplus::FramebufferTarget::Draw);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void GLTextureTransferHelper::shutdown() {
|
||||||
|
_canvas->doneCurrent();
|
||||||
|
_canvas->moveToThreadWithContext(qApp->thread());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool GLTextureTransferHelper::processQueueItems(const Queue& messages) {
|
||||||
|
for (auto package : messages) {
|
||||||
|
glWaitSync(package.fence, 0, GL_TIMEOUT_IGNORED);
|
||||||
|
glDeleteSync(package.fence);
|
||||||
|
TexturePointer texturePointer = package.texture.lock();
|
||||||
|
// Texture no longer exists, move on to the next
|
||||||
|
if (!texturePointer) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
GLBackend::GLTexture* object = Backend::getGPUObject<GLBackend::GLTexture>(*texturePointer);
|
||||||
|
object->transfer();
|
||||||
|
|
||||||
|
#ifdef FORCE_DRAW_AFTER_TRANSFER
|
||||||
|
// Now force a draw using the texture
|
||||||
|
try {
|
||||||
|
switch (texturePointer->getType()) {
|
||||||
|
case Texture::TEX_2D:
|
||||||
|
_program->Use();
|
||||||
|
_plane->Use();
|
||||||
|
_plane->Draw();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Texture::TEX_CUBE:
|
||||||
|
_cubeProgram->Use();
|
||||||
|
_skybox->Use();
|
||||||
|
_skybox->Draw();
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
qCWarning(gpulogging) << __FUNCTION__ << " case for Texture Type " << texturePointer->getType() << " not supported";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} catch (const std::runtime_error& error) {
|
||||||
|
qWarning() << "Failed to render texture on background thread: " << error.what();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
glBindTexture(object->_target, 0);
|
||||||
|
auto writeSync = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
|
||||||
|
glClientWaitSync(writeSync, GL_SYNC_FLUSH_COMMANDS_BIT, GL_TIMEOUT_IGNORED);
|
||||||
|
glDeleteSync(writeSync);
|
||||||
|
object->_contentStamp = texturePointer->getDataStamp();
|
||||||
|
object->setSyncState(GLBackend::GLTexture::Transferred);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
62
libraries/gpu/src/gpu/GLBackendTextureTransfer.h
Normal file
62
libraries/gpu/src/gpu/GLBackendTextureTransfer.h
Normal file
|
@ -0,0 +1,62 @@
|
||||||
|
//
|
||||||
|
// Created by Bradley Austin Davis on 2016/04/03
|
||||||
|
// Copyright 2013-2016 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
|
||||||
|
//
|
||||||
|
|
||||||
|
#include <GenericQueueThread.h>
|
||||||
|
#include "GLBackendShared.h"
|
||||||
|
|
||||||
|
#define THREADED_TEXTURE_TRANSFER
|
||||||
|
|
||||||
|
class OffscreenGLCanvas;
|
||||||
|
|
||||||
|
namespace gpu {
|
||||||
|
|
||||||
|
struct TextureTransferPackage {
|
||||||
|
std::weak_ptr<Texture> texture;
|
||||||
|
GLsync fence;
|
||||||
|
};
|
||||||
|
|
||||||
|
class GLTextureTransferHelper : public GenericQueueThread<TextureTransferPackage> {
|
||||||
|
public:
|
||||||
|
GLTextureTransferHelper();
|
||||||
|
void transferTexture(const gpu::TexturePointer& texturePointer);
|
||||||
|
void postTransfer(const gpu::TexturePointer& texturePointer);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void setup() override;
|
||||||
|
void shutdown() override;
|
||||||
|
bool processQueueItems(const Queue& messages) override;
|
||||||
|
void transferTextureSynchronous(const gpu::Texture& texture);
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::shared_ptr<OffscreenGLCanvas> _canvas;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename F>
|
||||||
|
void withPreservedTexture(GLenum target, F f) {
|
||||||
|
GLint boundTex = -1;
|
||||||
|
switch (target) {
|
||||||
|
case GL_TEXTURE_2D:
|
||||||
|
glGetIntegerv(GL_TEXTURE_BINDING_2D, &boundTex);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case GL_TEXTURE_CUBE_MAP:
|
||||||
|
glGetIntegerv(GL_TEXTURE_BINDING_CUBE_MAP, &boundTex);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
qFatal("Unsupported texture type");
|
||||||
|
}
|
||||||
|
(void)CHECK_GL_ERROR();
|
||||||
|
|
||||||
|
f();
|
||||||
|
|
||||||
|
glBindTexture(target, boundTex);
|
||||||
|
(void)CHECK_GL_ERROR();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
375
libraries/gpu/src/gpu/GLTexelFormat.h
Normal file
375
libraries/gpu/src/gpu/GLTexelFormat.h
Normal file
|
@ -0,0 +1,375 @@
|
||||||
|
//
|
||||||
|
// Created by Bradley Austin Davis on 2016/04/03
|
||||||
|
// Copyright 2013-2016 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
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "GLBackendShared.h"
|
||||||
|
|
||||||
|
class GLTexelFormat {
|
||||||
|
public:
|
||||||
|
GLenum internalFormat;
|
||||||
|
GLenum format;
|
||||||
|
GLenum type;
|
||||||
|
|
||||||
|
static GLTexelFormat evalGLTexelFormat(const gpu::Element& dstFormat) {
|
||||||
|
return evalGLTexelFormat(dstFormat, dstFormat);
|
||||||
|
}
|
||||||
|
static GLTexelFormat evalGLTexelFormat(const gpu::Element& dstFormat, const gpu::Element& srcFormat) {
|
||||||
|
using namespace gpu;
|
||||||
|
if (dstFormat != srcFormat) {
|
||||||
|
GLTexelFormat texel = { GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE };
|
||||||
|
|
||||||
|
switch (dstFormat.getDimension()) {
|
||||||
|
case gpu::SCALAR: {
|
||||||
|
texel.format = GL_RED;
|
||||||
|
texel.type = _elementTypeToGLType[dstFormat.getType()];
|
||||||
|
|
||||||
|
switch (dstFormat.getSemantic()) {
|
||||||
|
case gpu::RGB:
|
||||||
|
case gpu::RGBA:
|
||||||
|
texel.internalFormat = GL_R8;
|
||||||
|
break;
|
||||||
|
case gpu::DEPTH:
|
||||||
|
texel.internalFormat = GL_DEPTH_COMPONENT32;
|
||||||
|
break;
|
||||||
|
case gpu::DEPTH_STENCIL:
|
||||||
|
texel.type = GL_UNSIGNED_INT_24_8;
|
||||||
|
texel.format = GL_DEPTH_STENCIL;
|
||||||
|
texel.internalFormat = GL_DEPTH24_STENCIL8;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
qCDebug(gpulogging) << "Unknown combination of texel format";
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case gpu::VEC2: {
|
||||||
|
texel.format = GL_RG;
|
||||||
|
texel.type = _elementTypeToGLType[dstFormat.getType()];
|
||||||
|
|
||||||
|
switch (dstFormat.getSemantic()) {
|
||||||
|
case gpu::RGB:
|
||||||
|
case gpu::RGBA:
|
||||||
|
texel.internalFormat = GL_RG8;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
qCDebug(gpulogging) << "Unknown combination of texel format";
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case gpu::VEC3: {
|
||||||
|
texel.format = GL_RGB;
|
||||||
|
|
||||||
|
texel.type = _elementTypeToGLType[dstFormat.getType()];
|
||||||
|
|
||||||
|
switch (dstFormat.getSemantic()) {
|
||||||
|
case gpu::RGB:
|
||||||
|
case gpu::RGBA:
|
||||||
|
texel.internalFormat = GL_RGB8;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
qCDebug(gpulogging) << "Unknown combination of texel format";
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case gpu::VEC4: {
|
||||||
|
texel.format = GL_RGBA;
|
||||||
|
texel.type = _elementTypeToGLType[dstFormat.getType()];
|
||||||
|
|
||||||
|
switch (srcFormat.getSemantic()) {
|
||||||
|
case gpu::BGRA:
|
||||||
|
case gpu::SBGRA:
|
||||||
|
texel.format = GL_BGRA;
|
||||||
|
break;
|
||||||
|
case gpu::RGB:
|
||||||
|
case gpu::RGBA:
|
||||||
|
case gpu::SRGB:
|
||||||
|
case gpu::SRGBA:
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
};
|
||||||
|
|
||||||
|
switch (dstFormat.getSemantic()) {
|
||||||
|
case gpu::RGB:
|
||||||
|
texel.internalFormat = GL_RGB8;
|
||||||
|
break;
|
||||||
|
case gpu::RGBA:
|
||||||
|
texel.internalFormat = GL_RGBA8;
|
||||||
|
break;
|
||||||
|
case gpu::SRGB:
|
||||||
|
texel.internalFormat = GL_SRGB8;
|
||||||
|
break;
|
||||||
|
case gpu::SRGBA:
|
||||||
|
texel.internalFormat = GL_SRGB8_ALPHA8;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
qCDebug(gpulogging) << "Unknown combination of texel format";
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
qCDebug(gpulogging) << "Unknown combination of texel format";
|
||||||
|
}
|
||||||
|
return texel;
|
||||||
|
} else {
|
||||||
|
GLTexelFormat texel = { GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE };
|
||||||
|
|
||||||
|
switch (dstFormat.getDimension()) {
|
||||||
|
case gpu::SCALAR: {
|
||||||
|
texel.format = GL_RED;
|
||||||
|
texel.type = _elementTypeToGLType[dstFormat.getType()];
|
||||||
|
|
||||||
|
switch (dstFormat.getSemantic()) {
|
||||||
|
case gpu::RGB:
|
||||||
|
case gpu::RGBA:
|
||||||
|
case gpu::SRGB:
|
||||||
|
case gpu::SRGBA:
|
||||||
|
texel.internalFormat = GL_R8;
|
||||||
|
switch (dstFormat.getType()) {
|
||||||
|
case gpu::UINT32: {
|
||||||
|
texel.internalFormat = GL_R32UI;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case gpu::INT32: {
|
||||||
|
texel.internalFormat = GL_R32I;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case gpu::NUINT32: {
|
||||||
|
texel.internalFormat = GL_R8;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case gpu::NINT32: {
|
||||||
|
texel.internalFormat = GL_R8_SNORM;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case gpu::FLOAT: {
|
||||||
|
texel.internalFormat = GL_R32F;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case gpu::UINT16: {
|
||||||
|
texel.internalFormat = GL_R16UI;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case gpu::INT16: {
|
||||||
|
texel.internalFormat = GL_R16I;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case gpu::NUINT16: {
|
||||||
|
texel.internalFormat = GL_R16;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case gpu::NINT16: {
|
||||||
|
texel.internalFormat = GL_R16_SNORM;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case gpu::HALF: {
|
||||||
|
texel.internalFormat = GL_R16F;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case gpu::UINT8: {
|
||||||
|
texel.internalFormat = GL_R8UI;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case gpu::INT8: {
|
||||||
|
texel.internalFormat = GL_R8I;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case gpu::NUINT8: {
|
||||||
|
if ((dstFormat.getSemantic() == gpu::SRGB || dstFormat.getSemantic() == gpu::SRGBA)) {
|
||||||
|
texel.internalFormat = GL_SLUMINANCE8;
|
||||||
|
} else {
|
||||||
|
texel.internalFormat = GL_R8;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case gpu::NINT8: {
|
||||||
|
texel.internalFormat = GL_R8_SNORM;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case gpu::NUM_TYPES: { // quiet compiler
|
||||||
|
Q_UNREACHABLE();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case gpu::R11G11B10:
|
||||||
|
texel.format = GL_RGB;
|
||||||
|
// the type should be float
|
||||||
|
texel.internalFormat = GL_R11F_G11F_B10F;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case gpu::DEPTH:
|
||||||
|
texel.format = GL_DEPTH_COMPONENT; // It's depth component to load it
|
||||||
|
texel.internalFormat = GL_DEPTH_COMPONENT32;
|
||||||
|
switch (dstFormat.getType()) {
|
||||||
|
case gpu::UINT32:
|
||||||
|
case gpu::INT32:
|
||||||
|
case gpu::NUINT32:
|
||||||
|
case gpu::NINT32: {
|
||||||
|
texel.internalFormat = GL_DEPTH_COMPONENT32;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case gpu::FLOAT: {
|
||||||
|
texel.internalFormat = GL_DEPTH_COMPONENT32F;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case gpu::UINT16:
|
||||||
|
case gpu::INT16:
|
||||||
|
case gpu::NUINT16:
|
||||||
|
case gpu::NINT16:
|
||||||
|
case gpu::HALF: {
|
||||||
|
texel.internalFormat = GL_DEPTH_COMPONENT16;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case gpu::UINT8:
|
||||||
|
case gpu::INT8:
|
||||||
|
case gpu::NUINT8:
|
||||||
|
case gpu::NINT8: {
|
||||||
|
texel.internalFormat = GL_DEPTH_COMPONENT24;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case gpu::NUM_TYPES: { // quiet compiler
|
||||||
|
Q_UNREACHABLE();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case gpu::DEPTH_STENCIL:
|
||||||
|
texel.type = GL_UNSIGNED_INT_24_8;
|
||||||
|
texel.format = GL_DEPTH_STENCIL;
|
||||||
|
texel.internalFormat = GL_DEPTH24_STENCIL8;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
qCDebug(gpulogging) << "Unknown combination of texel format";
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case gpu::VEC2: {
|
||||||
|
texel.format = GL_RG;
|
||||||
|
texel.type = _elementTypeToGLType[dstFormat.getType()];
|
||||||
|
|
||||||
|
switch (dstFormat.getSemantic()) {
|
||||||
|
case gpu::RGB:
|
||||||
|
case gpu::RGBA:
|
||||||
|
texel.internalFormat = GL_RG8;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
qCDebug(gpulogging) << "Unknown combination of texel format";
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case gpu::VEC3: {
|
||||||
|
texel.format = GL_RGB;
|
||||||
|
|
||||||
|
texel.type = _elementTypeToGLType[dstFormat.getType()];
|
||||||
|
|
||||||
|
switch (dstFormat.getSemantic()) {
|
||||||
|
case gpu::RGB:
|
||||||
|
case gpu::RGBA:
|
||||||
|
texel.internalFormat = GL_RGB8;
|
||||||
|
break;
|
||||||
|
case gpu::SRGB:
|
||||||
|
case gpu::SRGBA:
|
||||||
|
texel.internalFormat = GL_SRGB8; // standard 2.2 gamma correction color
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
qCDebug(gpulogging) << "Unknown combination of texel format";
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case gpu::VEC4: {
|
||||||
|
texel.format = GL_RGBA;
|
||||||
|
texel.type = _elementTypeToGLType[dstFormat.getType()];
|
||||||
|
|
||||||
|
switch (dstFormat.getSemantic()) {
|
||||||
|
case gpu::RGB:
|
||||||
|
texel.internalFormat = GL_RGB8;
|
||||||
|
break;
|
||||||
|
case gpu::RGBA:
|
||||||
|
texel.internalFormat = GL_RGBA8;
|
||||||
|
switch (dstFormat.getType()) {
|
||||||
|
case gpu::UINT32:
|
||||||
|
texel.format = GL_RGBA_INTEGER;
|
||||||
|
texel.internalFormat = GL_RGBA32UI;
|
||||||
|
break;
|
||||||
|
case gpu::INT32:
|
||||||
|
texel.format = GL_RGBA_INTEGER;
|
||||||
|
texel.internalFormat = GL_RGBA32I;
|
||||||
|
break;
|
||||||
|
case gpu::FLOAT:
|
||||||
|
texel.internalFormat = GL_RGBA32F;
|
||||||
|
break;
|
||||||
|
case gpu::UINT16:
|
||||||
|
texel.format = GL_RGBA_INTEGER;
|
||||||
|
texel.internalFormat = GL_RGBA16UI;
|
||||||
|
break;
|
||||||
|
case gpu::INT16:
|
||||||
|
texel.format = GL_RGBA_INTEGER;
|
||||||
|
texel.internalFormat = GL_RGBA16I;
|
||||||
|
break;
|
||||||
|
case gpu::NUINT16:
|
||||||
|
texel.format = GL_RGBA;
|
||||||
|
texel.internalFormat = GL_RGBA16;
|
||||||
|
break;
|
||||||
|
case gpu::NINT16:
|
||||||
|
texel.format = GL_RGBA;
|
||||||
|
texel.internalFormat = GL_RGBA16_SNORM;
|
||||||
|
break;
|
||||||
|
case gpu::HALF:
|
||||||
|
texel.format = GL_RGBA;
|
||||||
|
texel.internalFormat = GL_RGBA16F;
|
||||||
|
break;
|
||||||
|
case gpu::UINT8:
|
||||||
|
texel.format = GL_RGBA_INTEGER;
|
||||||
|
texel.internalFormat = GL_RGBA8UI;
|
||||||
|
break;
|
||||||
|
case gpu::INT8:
|
||||||
|
texel.format = GL_RGBA_INTEGER;
|
||||||
|
texel.internalFormat = GL_RGBA8I;
|
||||||
|
break;
|
||||||
|
case gpu::NUINT8:
|
||||||
|
texel.format = GL_RGBA;
|
||||||
|
texel.internalFormat = GL_RGBA8;
|
||||||
|
break;
|
||||||
|
case gpu::NINT8:
|
||||||
|
texel.format = GL_RGBA;
|
||||||
|
texel.internalFormat = GL_RGBA8_SNORM;
|
||||||
|
break;
|
||||||
|
case gpu::NUINT32:
|
||||||
|
case gpu::NINT32:
|
||||||
|
case gpu::NUM_TYPES: // quiet compiler
|
||||||
|
Q_UNREACHABLE();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case gpu::SRGB:
|
||||||
|
texel.internalFormat = GL_SRGB8;
|
||||||
|
break;
|
||||||
|
case gpu::SRGBA:
|
||||||
|
texel.internalFormat = GL_SRGB8_ALPHA8; // standard 2.2 gamma correction color
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
qCDebug(gpulogging) << "Unknown combination of texel format";
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
qCDebug(gpulogging) << "Unknown combination of texel format";
|
||||||
|
}
|
||||||
|
return texel;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
|
@ -10,6 +10,7 @@
|
||||||
//
|
//
|
||||||
|
|
||||||
#include "ModelCache.h"
|
#include "ModelCache.h"
|
||||||
|
#include <Finally.h>
|
||||||
#include <FSTReader.h>
|
#include <FSTReader.h>
|
||||||
#include "FBXReader.h"
|
#include "FBXReader.h"
|
||||||
#include "OBJReader.h"
|
#include "OBJReader.h"
|
||||||
|
@ -117,11 +118,12 @@ void GeometryReader::run() {
|
||||||
originalPriority = QThread::NormalPriority;
|
originalPriority = QThread::NormalPriority;
|
||||||
}
|
}
|
||||||
QThread::currentThread()->setPriority(QThread::LowPriority);
|
QThread::currentThread()->setPriority(QThread::LowPriority);
|
||||||
|
Finally setPriorityBackToNormal([originalPriority]() {
|
||||||
|
QThread::currentThread()->setPriority(originalPriority);
|
||||||
|
});
|
||||||
|
|
||||||
// Ensure the resource is still being requested
|
if (!_resource.data()) {
|
||||||
auto resource = _resource.toStrongRef();
|
qCWarning(modelnetworking) << "Abandoning load of" << _url << "; resource was deleted";
|
||||||
if (!resource) {
|
|
||||||
qCWarning(modelnetworking) << "Abandoning load of" << _url << "; could not get strong ref";
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -146,17 +148,29 @@ void GeometryReader::run() {
|
||||||
throw QString("unsupported format");
|
throw QString("unsupported format");
|
||||||
}
|
}
|
||||||
|
|
||||||
QMetaObject::invokeMethod(resource.data(), "setGeometryDefinition",
|
// Ensure the resource has not been deleted, and won't be while invokeMethod is in flight.
|
||||||
Q_ARG(void*, fbxGeometry));
|
auto resource = _resource.toStrongRef();
|
||||||
|
if (!resource) {
|
||||||
|
qCWarning(modelnetworking) << "Abandoning load of" << _url << "; could not get strong ref";
|
||||||
|
delete fbxGeometry;
|
||||||
|
} else {
|
||||||
|
QMetaObject::invokeMethod(resource.data(), "setGeometryDefinition", Qt::BlockingQueuedConnection, Q_ARG(void*, fbxGeometry));
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
throw QString("url is invalid");
|
throw QString("url is invalid");
|
||||||
}
|
}
|
||||||
} catch (const QString& error) {
|
} catch (const QString& error) {
|
||||||
qCDebug(modelnetworking) << "Error reading " << _url << ": " << error;
|
|
||||||
QMetaObject::invokeMethod(resource.data(), "finishedLoading", Q_ARG(bool, false));
|
|
||||||
}
|
|
||||||
|
|
||||||
QThread::currentThread()->setPriority(originalPriority);
|
qCDebug(modelnetworking) << "Error reading " << _url << ": " << error;
|
||||||
|
|
||||||
|
auto resource = _resource.toStrongRef();
|
||||||
|
// Ensure the resoruce has not been deleted, and won't be while invokeMethod is in flight.
|
||||||
|
if (!resource) {
|
||||||
|
qCWarning(modelnetworking) << "Abandoning load of" << _url << "; could not get strong ref";
|
||||||
|
} else {
|
||||||
|
QMetaObject::invokeMethod(resource.data(), "finishedLoading", Qt::BlockingQueuedConnection, Q_ARG(bool, false));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class GeometryDefinitionResource : public GeometryResource {
|
class GeometryDefinitionResource : public GeometryResource {
|
||||||
|
@ -232,7 +246,7 @@ std::shared_ptr<NetworkGeometry> ModelCache::getGeometry(const QUrl& url, const
|
||||||
GeometryExtra geometryExtra = { mapping, textureBaseUrl };
|
GeometryExtra geometryExtra = { mapping, textureBaseUrl };
|
||||||
GeometryResource::Pointer resource = getResource(url, QUrl(), true, &geometryExtra).staticCast<GeometryResource>();
|
GeometryResource::Pointer resource = getResource(url, QUrl(), true, &geometryExtra).staticCast<GeometryResource>();
|
||||||
if (resource) {
|
if (resource) {
|
||||||
if (resource->isLoaded() && !resource->hasTextures()) {
|
if (resource->isLoaded() && resource->shouldSetTextures()) {
|
||||||
resource->setTextures();
|
resource->setTextures();
|
||||||
}
|
}
|
||||||
return std::make_shared<NetworkGeometry>(resource);
|
return std::make_shared<NetworkGeometry>(resource);
|
||||||
|
|
|
@ -107,7 +107,8 @@ protected:
|
||||||
friend class GeometryMappingResource;
|
friend class GeometryMappingResource;
|
||||||
|
|
||||||
// Geometries may not hold onto textures while cached - that is for the texture cache
|
// Geometries may not hold onto textures while cached - that is for the texture cache
|
||||||
bool hasTextures() const { return !_materials.empty(); }
|
// Instead, these methods clear and reset textures from the geometry when caching/loading
|
||||||
|
bool shouldSetTextures() const { return _geometry && _materials.empty(); }
|
||||||
void setTextures();
|
void setTextures();
|
||||||
void resetTextures();
|
void resetTextures();
|
||||||
|
|
||||||
|
|
|
@ -13,18 +13,22 @@
|
||||||
|
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
|
|
||||||
#include <glm/glm.hpp>
|
|
||||||
#include <glm/gtc/random.hpp>
|
|
||||||
|
|
||||||
#include <QNetworkReply>
|
#include <QNetworkReply>
|
||||||
#include <QPainter>
|
#include <QPainter>
|
||||||
#include <QRunnable>
|
#include <QRunnable>
|
||||||
#include <QThreadPool>
|
#include <QThreadPool>
|
||||||
#include <qimagereader.h>
|
#include <QImageReader>
|
||||||
#include <PathUtils.h>
|
|
||||||
|
#include <glm/glm.hpp>
|
||||||
|
#include <glm/gtc/random.hpp>
|
||||||
|
|
||||||
#include <gpu/Batch.h>
|
#include <gpu/Batch.h>
|
||||||
|
|
||||||
|
#include <shared/NsightHelpers.h>
|
||||||
|
|
||||||
|
#include <Finally.h>
|
||||||
|
#include <PathUtils.h>
|
||||||
|
|
||||||
#include "ModelNetworkingLogging.h"
|
#include "ModelNetworkingLogging.h"
|
||||||
|
|
||||||
TextureCache::TextureCache() {
|
TextureCache::TextureCache() {
|
||||||
|
@ -196,7 +200,7 @@ NetworkTexture::NetworkTexture(const QUrl& url, const TextureLoaderFunc& texture
|
||||||
{
|
{
|
||||||
_textureLoader = textureLoader;
|
_textureLoader = textureLoader;
|
||||||
}
|
}
|
||||||
|
|
||||||
NetworkTexture::TextureLoaderFunc NetworkTexture::getTextureLoader() const {
|
NetworkTexture::TextureLoaderFunc NetworkTexture::getTextureLoader() const {
|
||||||
switch (_type) {
|
switch (_type) {
|
||||||
case CUBE_TEXTURE: {
|
case CUBE_TEXTURE: {
|
||||||
|
@ -240,14 +244,14 @@ NetworkTexture::TextureLoaderFunc NetworkTexture::getTextureLoader() const {
|
||||||
class ImageReader : public QRunnable {
|
class ImageReader : public QRunnable {
|
||||||
public:
|
public:
|
||||||
|
|
||||||
ImageReader(const QWeakPointer<Resource>& texture, const QByteArray& data, const QUrl& url = QUrl());
|
ImageReader(const QWeakPointer<Resource>& resource, const QByteArray& data, const QUrl& url = QUrl());
|
||||||
|
|
||||||
virtual void run();
|
virtual void run();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static void listSupportedImageFormats();
|
static void listSupportedImageFormats();
|
||||||
|
|
||||||
QWeakPointer<Resource> _texture;
|
QWeakPointer<Resource> _resource;
|
||||||
QUrl _url;
|
QUrl _url;
|
||||||
QByteArray _content;
|
QByteArray _content;
|
||||||
};
|
};
|
||||||
|
@ -261,9 +265,9 @@ void NetworkTexture::loadContent(const QByteArray& content) {
|
||||||
QThreadPool::globalInstance()->start(new ImageReader(_self, content, _url));
|
QThreadPool::globalInstance()->start(new ImageReader(_self, content, _url));
|
||||||
}
|
}
|
||||||
|
|
||||||
ImageReader::ImageReader(const QWeakPointer<Resource>& texture, const QByteArray& data,
|
ImageReader::ImageReader(const QWeakPointer<Resource>& resource, const QByteArray& data,
|
||||||
const QUrl& url) :
|
const QUrl& url) :
|
||||||
_texture(texture),
|
_resource(resource),
|
||||||
_url(url),
|
_url(url),
|
||||||
_content(data)
|
_content(data)
|
||||||
{
|
{
|
||||||
|
@ -278,31 +282,34 @@ void ImageReader::listSupportedImageFormats() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void ImageReader::run() {
|
void ImageReader::run() {
|
||||||
|
PROFILE_RANGE_EX(__FUNCTION__, 0xffff0000, nullptr);
|
||||||
auto originalPriority = QThread::currentThread()->priority();
|
auto originalPriority = QThread::currentThread()->priority();
|
||||||
if (originalPriority == QThread::InheritPriority) {
|
if (originalPriority == QThread::InheritPriority) {
|
||||||
originalPriority = QThread::NormalPriority;
|
originalPriority = QThread::NormalPriority;
|
||||||
}
|
}
|
||||||
QThread::currentThread()->setPriority(QThread::LowPriority);
|
QThread::currentThread()->setPriority(QThread::LowPriority);
|
||||||
|
Finally restorePriority([originalPriority]{
|
||||||
|
QThread::currentThread()->setPriority(originalPriority);
|
||||||
|
});
|
||||||
|
|
||||||
auto texture = _texture.toStrongRef();
|
if (!_resource.data()) {
|
||||||
if (!texture) {
|
qCWarning(modelnetworking) << "Abandoning load of" << _url << "; could not get strong ref";
|
||||||
qCWarning(modelnetworking) << "Could not get strong ref";
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
listSupportedImageFormats();
|
listSupportedImageFormats();
|
||||||
|
|
||||||
// try to help the QImage loader by extracting the image file format from the url filename ext
|
// Help the QImage loader by extracting the image file format from the url filename ext.
|
||||||
// Some tga are not created properly for example without it
|
// Some tga are not created properly without it.
|
||||||
auto filename = _url.fileName().toStdString();
|
auto filename = _url.fileName().toStdString();
|
||||||
auto filenameExtension = filename.substr(filename.find_last_of('.') + 1);
|
auto filenameExtension = filename.substr(filename.find_last_of('.') + 1);
|
||||||
QImage image = QImage::fromData(_content, filenameExtension.c_str());
|
QImage image = QImage::fromData(_content, filenameExtension.c_str());
|
||||||
|
|
||||||
// Note that QImage.format is the pixel format which is different from the "format" of the image file...
|
// Note that QImage.format is the pixel format which is different from the "format" of the image file...
|
||||||
auto imageFormat = image.format();
|
auto imageFormat = image.format();
|
||||||
int originalWidth = image.width();
|
int originalWidth = image.width();
|
||||||
int originalHeight = image.height();
|
int originalHeight = image.height();
|
||||||
|
|
||||||
if (originalWidth == 0 || originalHeight == 0 || imageFormat == QImage::Format_Invalid) {
|
if (originalWidth == 0 || originalHeight == 0 || imageFormat == QImage::Format_Invalid) {
|
||||||
if (filenameExtension.empty()) {
|
if (filenameExtension.empty()) {
|
||||||
qCDebug(modelnetworking) << "QImage failed to create from content, no file extension:" << _url;
|
qCDebug(modelnetworking) << "QImage failed to create from content, no file extension:" << _url;
|
||||||
|
@ -312,25 +319,40 @@ void ImageReader::run() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
gpu::Texture* theTexture = nullptr;
|
gpu::Texture* texture = nullptr;
|
||||||
auto ntex = texture.dynamicCast<NetworkTexture>();
|
{
|
||||||
if (ntex) {
|
// Double-check the resource still exists between long operations.
|
||||||
theTexture = ntex->getTextureLoader()(image, _url.toString().toStdString());
|
auto resource = _resource.toStrongRef();
|
||||||
|
if (!resource) {
|
||||||
|
qCWarning(modelnetworking) << "Abandoning load of" << _url << "; could not get strong ref";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto url = _url.toString().toStdString();
|
||||||
|
|
||||||
|
PROFILE_RANGE_EX(__FUNCTION__"::textureLoader", 0xffffff00, nullptr);
|
||||||
|
texture = resource.dynamicCast<NetworkTexture>()->getTextureLoader()(image, url);
|
||||||
}
|
}
|
||||||
|
|
||||||
QMetaObject::invokeMethod(texture.data(), "setImage",
|
// Ensure the resource has not been deleted, and won't be while invokeMethod is in flight.
|
||||||
Q_ARG(void*, theTexture),
|
auto resource = _resource.toStrongRef();
|
||||||
Q_ARG(int, originalWidth), Q_ARG(int, originalHeight));
|
if (!resource) {
|
||||||
QThread::currentThread()->setPriority(originalPriority);
|
qCWarning(modelnetworking) << "Abandoning load of" << _url << "; could not get strong ref";
|
||||||
|
delete texture;
|
||||||
|
} else {
|
||||||
|
QMetaObject::invokeMethod(resource.data(), "setImage", Qt::BlockingQueuedConnection,
|
||||||
|
Q_ARG(void*, texture),
|
||||||
|
Q_ARG(int, originalWidth), Q_ARG(int, originalHeight));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void NetworkTexture::setImage(void* voidTexture, int originalWidth,
|
void NetworkTexture::setImage(void* voidTexture, int originalWidth,
|
||||||
int originalHeight) {
|
int originalHeight) {
|
||||||
_originalWidth = originalWidth;
|
_originalWidth = originalWidth;
|
||||||
_originalHeight = originalHeight;
|
_originalHeight = originalHeight;
|
||||||
|
|
||||||
gpu::Texture* texture = static_cast<gpu::Texture*>(voidTexture);
|
gpu::Texture* texture = static_cast<gpu::Texture*>(voidTexture);
|
||||||
|
|
||||||
// Passing ownership
|
// Passing ownership
|
||||||
_textureSource->resetTexture(texture);
|
_textureSource->resetTexture(texture);
|
||||||
auto gpuTexture = _textureSource->getGPUTexture();
|
auto gpuTexture = _textureSource->getGPUTexture();
|
||||||
|
@ -338,7 +360,7 @@ void NetworkTexture::setImage(void* voidTexture, int originalWidth,
|
||||||
if (gpuTexture) {
|
if (gpuTexture) {
|
||||||
_width = gpuTexture->getWidth();
|
_width = gpuTexture->getWidth();
|
||||||
_height = gpuTexture->getHeight();
|
_height = gpuTexture->getHeight();
|
||||||
setBytes(gpuTexture->getStoredSize());
|
setSize(gpuTexture->getStoredSize());
|
||||||
} else {
|
} else {
|
||||||
// FIXME: If !gpuTexture, we failed to load!
|
// FIXME: If !gpuTexture, we failed to load!
|
||||||
_width = _height = 0;
|
_width = _height = 0;
|
||||||
|
|
|
@ -35,6 +35,8 @@ public:
|
||||||
void setCubemap(const gpu::TexturePointer& cubemap);
|
void setCubemap(const gpu::TexturePointer& cubemap);
|
||||||
const gpu::TexturePointer& getCubemap() const { return _cubemap; }
|
const gpu::TexturePointer& getCubemap() const { return _cubemap; }
|
||||||
|
|
||||||
|
virtual void clear() { setCubemap(nullptr); }
|
||||||
|
|
||||||
void prepare(gpu::Batch& batch, int textureSlot = SKYBOX_SKYMAP_SLOT, int bufferSlot = SKYBOX_CONSTANTS_SLOT) const;
|
void prepare(gpu::Batch& batch, int textureSlot = SKYBOX_SKYMAP_SLOT, int bufferSlot = SKYBOX_CONSTANTS_SLOT) const;
|
||||||
virtual void render(gpu::Batch& batch, const ViewFrustum& frustum) const;
|
virtual void render(gpu::Batch& batch, const ViewFrustum& frustum) const;
|
||||||
|
|
||||||
|
|
|
@ -336,6 +336,7 @@ void NodeList::sendDomainServerCheckIn() {
|
||||||
if (_numNoReplyDomainCheckIns >= MAX_SILENT_DOMAIN_SERVER_CHECK_INS) {
|
if (_numNoReplyDomainCheckIns >= MAX_SILENT_DOMAIN_SERVER_CHECK_INS) {
|
||||||
// we haven't heard back from DS in MAX_SILENT_DOMAIN_SERVER_CHECK_INS
|
// we haven't heard back from DS in MAX_SILENT_DOMAIN_SERVER_CHECK_INS
|
||||||
// so emit our signal that says that
|
// so emit our signal that says that
|
||||||
|
qDebug() << "Limit of silent domain checkins reached";
|
||||||
emit limitOfSilentDomainCheckInsReached();
|
emit limitOfSilentDomainCheckInsReached();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -38,6 +38,7 @@ ResourceCache::~ResourceCache() {
|
||||||
void ResourceCache::refreshAll() {
|
void ResourceCache::refreshAll() {
|
||||||
// Clear all unused resources so we don't have to reload them
|
// Clear all unused resources so we don't have to reload them
|
||||||
clearUnusedResource();
|
clearUnusedResource();
|
||||||
|
resetResourceCounters();
|
||||||
|
|
||||||
// Refresh all remaining resources in use
|
// Refresh all remaining resources in use
|
||||||
foreach (auto resource, _resources) {
|
foreach (auto resource, _resources) {
|
||||||
|
@ -53,9 +54,27 @@ void ResourceCache::refresh(const QUrl& url) {
|
||||||
resource->refresh();
|
resource->refresh();
|
||||||
} else {
|
} else {
|
||||||
_resources.remove(url);
|
_resources.remove(url);
|
||||||
|
resetResourceCounters();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QVariantList ResourceCache::getResourceList() {
|
||||||
|
QVariantList list;
|
||||||
|
if (QThread::currentThread() != thread()) {
|
||||||
|
// NOTE: invokeMethod does not allow a const QObject*
|
||||||
|
QMetaObject::invokeMethod(this, "getResourceList", Qt::BlockingQueuedConnection,
|
||||||
|
Q_RETURN_ARG(QVariantList, list));
|
||||||
|
} else {
|
||||||
|
auto resources = _resources.uniqueKeys();
|
||||||
|
list.reserve(resources.size());
|
||||||
|
for (auto& resource : resources) {
|
||||||
|
list << resource;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
void ResourceCache::setRequestLimit(int limit) {
|
void ResourceCache::setRequestLimit(int limit) {
|
||||||
_requestLimit = limit;
|
_requestLimit = limit;
|
||||||
|
|
||||||
|
@ -114,6 +133,7 @@ QSharedPointer<Resource> ResourceCache::getResource(const QUrl& url, const QUrl&
|
||||||
void ResourceCache::setUnusedResourceCacheSize(qint64 unusedResourcesMaxSize) {
|
void ResourceCache::setUnusedResourceCacheSize(qint64 unusedResourcesMaxSize) {
|
||||||
_unusedResourcesMaxSize = clamp(unusedResourcesMaxSize, MIN_UNUSED_MAX_SIZE, MAX_UNUSED_MAX_SIZE);
|
_unusedResourcesMaxSize = clamp(unusedResourcesMaxSize, MIN_UNUSED_MAX_SIZE, MAX_UNUSED_MAX_SIZE);
|
||||||
reserveUnusedResource(0);
|
reserveUnusedResource(0);
|
||||||
|
resetResourceCounters();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ResourceCache::addUnusedResource(const QSharedPointer<Resource>& resource) {
|
void ResourceCache::addUnusedResource(const QSharedPointer<Resource>& resource) {
|
||||||
|
@ -127,6 +147,8 @@ void ResourceCache::addUnusedResource(const QSharedPointer<Resource>& resource)
|
||||||
resource->setLRUKey(++_lastLRUKey);
|
resource->setLRUKey(++_lastLRUKey);
|
||||||
_unusedResources.insert(resource->getLRUKey(), resource);
|
_unusedResources.insert(resource->getLRUKey(), resource);
|
||||||
_unusedResourcesSize += resource->getBytes();
|
_unusedResourcesSize += resource->getBytes();
|
||||||
|
|
||||||
|
resetResourceCounters();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ResourceCache::removeUnusedResource(const QSharedPointer<Resource>& resource) {
|
void ResourceCache::removeUnusedResource(const QSharedPointer<Resource>& resource) {
|
||||||
|
@ -134,6 +156,7 @@ void ResourceCache::removeUnusedResource(const QSharedPointer<Resource>& resourc
|
||||||
_unusedResources.remove(resource->getLRUKey());
|
_unusedResources.remove(resource->getLRUKey());
|
||||||
_unusedResourcesSize -= resource->getBytes();
|
_unusedResourcesSize -= resource->getBytes();
|
||||||
}
|
}
|
||||||
|
resetResourceCounters();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ResourceCache::reserveUnusedResource(qint64 resourceSize) {
|
void ResourceCache::reserveUnusedResource(qint64 resourceSize) {
|
||||||
|
@ -142,8 +165,13 @@ void ResourceCache::reserveUnusedResource(qint64 resourceSize) {
|
||||||
// unload the oldest resource
|
// unload the oldest resource
|
||||||
QMap<int, QSharedPointer<Resource> >::iterator it = _unusedResources.begin();
|
QMap<int, QSharedPointer<Resource> >::iterator it = _unusedResources.begin();
|
||||||
|
|
||||||
_unusedResourcesSize -= it.value()->getBytes();
|
|
||||||
it.value()->setCache(nullptr);
|
it.value()->setCache(nullptr);
|
||||||
|
auto size = it.value()->getBytes();
|
||||||
|
|
||||||
|
_totalResourcesSize -= size;
|
||||||
|
_resources.remove(it.value()->getURL());
|
||||||
|
|
||||||
|
_unusedResourcesSize -= size;
|
||||||
_unusedResources.erase(it);
|
_unusedResources.erase(it);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -159,6 +187,17 @@ void ResourceCache::clearUnusedResource() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ResourceCache::resetResourceCounters() {
|
||||||
|
_numTotalResources = _resources.size();
|
||||||
|
_numUnusedResources = _unusedResources.size();
|
||||||
|
emit dirty();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ResourceCache::updateTotalSize(const qint64& oldSize, const qint64& newSize) {
|
||||||
|
_totalResourcesSize += (newSize - oldSize);
|
||||||
|
emit dirty();
|
||||||
|
}
|
||||||
|
|
||||||
void ResourceCacheSharedItems::appendActiveRequest(Resource* resource) {
|
void ResourceCacheSharedItems::appendActiveRequest(Resource* resource) {
|
||||||
Lock lock(_mutex);
|
Lock lock(_mutex);
|
||||||
_loadingRequests.append(resource);
|
_loadingRequests.append(resource);
|
||||||
|
@ -341,7 +380,7 @@ void Resource::allReferencesCleared() {
|
||||||
_cache->addUnusedResource(self);
|
_cache->addUnusedResource(self);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
delete this;
|
deleteLater();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -377,6 +416,11 @@ void Resource::finishedLoading(bool success) {
|
||||||
emit finished(success);
|
emit finished(success);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Resource::setSize(const qint64& bytes) {
|
||||||
|
QMetaObject::invokeMethod(_cache.data(), "updateTotalSize", Q_ARG(qint64, _bytes), Q_ARG(qint64, bytes));
|
||||||
|
_bytes = bytes;
|
||||||
|
}
|
||||||
|
|
||||||
void Resource::reinsert() {
|
void Resource::reinsert() {
|
||||||
_cache->_resources.insert(_url, _self);
|
_cache->_resources.insert(_url, _self);
|
||||||
}
|
}
|
||||||
|
@ -412,7 +456,7 @@ void Resource::handleDownloadProgress(uint64_t bytesReceived, uint64_t bytesTota
|
||||||
void Resource::handleReplyFinished() {
|
void Resource::handleReplyFinished() {
|
||||||
Q_ASSERT_X(_request, "Resource::handleReplyFinished", "Request should not be null while in handleReplyFinished");
|
Q_ASSERT_X(_request, "Resource::handleReplyFinished", "Request should not be null while in handleReplyFinished");
|
||||||
|
|
||||||
_bytes = _bytesTotal;
|
setSize(_bytesTotal);
|
||||||
|
|
||||||
if (!_request || _request != sender()) {
|
if (!_request || _request != sender()) {
|
||||||
// This can happen in the edge case that a request is timed out, but a `finished` signal is emitted before it is deleted.
|
// This can happen in the edge case that a request is timed out, but a `finished` signal is emitted before it is deleted.
|
||||||
|
|
|
@ -12,7 +12,9 @@
|
||||||
#ifndef hifi_ResourceCache_h
|
#ifndef hifi_ResourceCache_h
|
||||||
#define hifi_ResourceCache_h
|
#define hifi_ResourceCache_h
|
||||||
|
|
||||||
|
#include <atomic>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
|
|
||||||
#include <QtCore/QHash>
|
#include <QtCore/QHash>
|
||||||
#include <QtCore/QList>
|
#include <QtCore/QList>
|
||||||
#include <QtCore/QObject>
|
#include <QtCore/QObject>
|
||||||
|
@ -29,6 +31,8 @@
|
||||||
|
|
||||||
#include "ResourceManager.h"
|
#include "ResourceManager.h"
|
||||||
|
|
||||||
|
Q_DECLARE_METATYPE(size_t)
|
||||||
|
|
||||||
class QNetworkReply;
|
class QNetworkReply;
|
||||||
class QTimer;
|
class QTimer;
|
||||||
|
|
||||||
|
@ -79,8 +83,20 @@ private:
|
||||||
/// Base class for resource caches.
|
/// Base class for resource caches.
|
||||||
class ResourceCache : public QObject {
|
class ResourceCache : public QObject {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
Q_PROPERTY(size_t numTotal READ getNumTotalResources NOTIFY dirty)
|
||||||
|
Q_PROPERTY(size_t numCached READ getNumCachedResources NOTIFY dirty)
|
||||||
|
Q_PROPERTY(size_t sizeTotal READ getSizeTotalResources NOTIFY dirty)
|
||||||
|
Q_PROPERTY(size_t sizeCached READ getSizeCachedResources NOTIFY dirty)
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
size_t getNumTotalResources() const { return _numTotalResources; }
|
||||||
|
size_t getSizeTotalResources() const { return _totalResourcesSize; }
|
||||||
|
|
||||||
|
size_t getNumCachedResources() const { return _numUnusedResources; }
|
||||||
|
size_t getSizeCachedResources() const { return _unusedResourcesSize; }
|
||||||
|
|
||||||
|
Q_INVOKABLE QVariantList getResourceList();
|
||||||
|
|
||||||
static void setRequestLimit(int limit);
|
static void setRequestLimit(int limit);
|
||||||
static int getRequestLimit() { return _requestLimit; }
|
static int getRequestLimit() { return _requestLimit; }
|
||||||
|
|
||||||
|
@ -101,15 +117,21 @@ public:
|
||||||
void refreshAll();
|
void refreshAll();
|
||||||
void refresh(const QUrl& url);
|
void refresh(const QUrl& url);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void dirty();
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void checkAsynchronousGets();
|
void checkAsynchronousGets();
|
||||||
|
|
||||||
|
protected slots:
|
||||||
|
void updateTotalSize(const qint64& oldSize, const qint64& newSize);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
/// Loads a resource from the specified URL.
|
/// Loads a resource from the specified URL.
|
||||||
/// \param fallback a fallback URL to load if the desired one is unavailable
|
/// \param fallback a fallback URL to load if the desired one is unavailable
|
||||||
/// \param delayLoad if true, don't load the resource immediately; wait until load is first requested
|
/// \param delayLoad if true, don't load the resource immediately; wait until load is first requested
|
||||||
/// \param extra extra data to pass to the creator, if appropriate
|
/// \param extra extra data to pass to the creator, if appropriate
|
||||||
Q_INVOKABLE QSharedPointer<Resource> getResource(const QUrl& url, const QUrl& fallback = QUrl(),
|
QSharedPointer<Resource> getResource(const QUrl& url, const QUrl& fallback = QUrl(),
|
||||||
bool delayLoad = false, void* extra = NULL);
|
bool delayLoad = false, void* extra = NULL);
|
||||||
|
|
||||||
/// Creates a new resource.
|
/// Creates a new resource.
|
||||||
|
@ -118,18 +140,20 @@ protected:
|
||||||
|
|
||||||
void addUnusedResource(const QSharedPointer<Resource>& resource);
|
void addUnusedResource(const QSharedPointer<Resource>& resource);
|
||||||
void removeUnusedResource(const QSharedPointer<Resource>& resource);
|
void removeUnusedResource(const QSharedPointer<Resource>& resource);
|
||||||
void reserveUnusedResource(qint64 resourceSize);
|
|
||||||
void clearUnusedResource();
|
|
||||||
|
|
||||||
/// Attempt to load a resource if requests are below the limit, otherwise queue the resource for loading
|
/// Attempt to load a resource if requests are below the limit, otherwise queue the resource for loading
|
||||||
/// \return true if the resource began loading, otherwise false if the resource is in the pending queue
|
/// \return true if the resource began loading, otherwise false if the resource is in the pending queue
|
||||||
Q_INVOKABLE static bool attemptRequest(Resource* resource);
|
static bool attemptRequest(Resource* resource);
|
||||||
static void requestCompleted(Resource* resource);
|
static void requestCompleted(Resource* resource);
|
||||||
static bool attemptHighestPriorityRequest();
|
static bool attemptHighestPriorityRequest();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
friend class Resource;
|
friend class Resource;
|
||||||
|
|
||||||
|
void reserveUnusedResource(qint64 resourceSize);
|
||||||
|
void clearUnusedResource();
|
||||||
|
void resetResourceCounters();
|
||||||
|
|
||||||
QHash<QUrl, QWeakPointer<Resource>> _resources;
|
QHash<QUrl, QWeakPointer<Resource>> _resources;
|
||||||
int _lastLRUKey = 0;
|
int _lastLRUKey = 0;
|
||||||
|
|
||||||
|
@ -140,8 +164,13 @@ private:
|
||||||
QReadWriteLock _resourcesToBeGottenLock;
|
QReadWriteLock _resourcesToBeGottenLock;
|
||||||
QQueue<QUrl> _resourcesToBeGotten;
|
QQueue<QUrl> _resourcesToBeGotten;
|
||||||
|
|
||||||
|
std::atomic<size_t> _numTotalResources { 0 };
|
||||||
|
std::atomic<size_t> _numUnusedResources { 0 };
|
||||||
|
|
||||||
|
std::atomic<qint64> _totalResourcesSize { 0 };
|
||||||
|
std::atomic<qint64> _unusedResourcesSize { 0 };
|
||||||
|
|
||||||
qint64 _unusedResourcesMaxSize = DEFAULT_UNUSED_MAX_SIZE;
|
qint64 _unusedResourcesMaxSize = DEFAULT_UNUSED_MAX_SIZE;
|
||||||
qint64 _unusedResourcesSize = 0;
|
|
||||||
QMap<int, QSharedPointer<Resource>> _unusedResources;
|
QMap<int, QSharedPointer<Resource>> _unusedResources;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -226,7 +255,7 @@ protected:
|
||||||
virtual void downloadFinished(const QByteArray& data) { finishedLoading(true); }
|
virtual void downloadFinished(const QByteArray& data) { finishedLoading(true); }
|
||||||
|
|
||||||
/// Called when the download is finished and processed, sets the number of actual bytes.
|
/// Called when the download is finished and processed, sets the number of actual bytes.
|
||||||
void setBytes(qint64 bytes) { _bytes = bytes; }
|
void setSize(const qint64& bytes);
|
||||||
|
|
||||||
/// Called when the download is finished and processed.
|
/// Called when the download is finished and processed.
|
||||||
/// This should be called by subclasses that override downloadFinished to mark the end of processing.
|
/// This should be called by subclasses that override downloadFinished to mark the end of processing.
|
||||||
|
|
|
@ -384,9 +384,10 @@ int Octree::readElementData(OctreeElementPointer destinationElement, const unsig
|
||||||
// check the exists mask to see if we have a child to traverse into
|
// check the exists mask to see if we have a child to traverse into
|
||||||
|
|
||||||
if (oneAtBit(childInBufferMask, childIndex)) {
|
if (oneAtBit(childInBufferMask, childIndex)) {
|
||||||
if (!destinationElement->getChildAtIndex(childIndex)) {
|
auto childAt = destinationElement->getChildAtIndex(childIndex);
|
||||||
|
if (!childAt) {
|
||||||
// add a child at that index, if it doesn't exist
|
// add a child at that index, if it doesn't exist
|
||||||
destinationElement->addChildAtIndex(childIndex);
|
childAt = destinationElement->addChildAtIndex(childIndex);
|
||||||
bool nodeIsDirty = destinationElement->isDirty();
|
bool nodeIsDirty = destinationElement->isDirty();
|
||||||
if (nodeIsDirty) {
|
if (nodeIsDirty) {
|
||||||
_isDirty = true;
|
_isDirty = true;
|
||||||
|
@ -394,8 +395,7 @@ int Octree::readElementData(OctreeElementPointer destinationElement, const unsig
|
||||||
}
|
}
|
||||||
|
|
||||||
// tell the child to read the subsequent data
|
// tell the child to read the subsequent data
|
||||||
int lowerLevelBytes = readElementData(destinationElement->getChildAtIndex(childIndex),
|
int lowerLevelBytes = readElementData(childAt, nodeData + bytesRead, bytesLeftToRead, args);
|
||||||
nodeData + bytesRead, bytesLeftToRead, args);
|
|
||||||
|
|
||||||
bytesRead += lowerLevelBytes;
|
bytesRead += lowerLevelBytes;
|
||||||
bytesLeftToRead -= lowerLevelBytes;
|
bytesLeftToRead -= lowerLevelBytes;
|
||||||
|
|
|
@ -161,7 +161,12 @@ PhysicsMotionType EntityMotionState::computePhysicsMotionType() const {
|
||||||
}
|
}
|
||||||
return MOTION_TYPE_DYNAMIC;
|
return MOTION_TYPE_DYNAMIC;
|
||||||
}
|
}
|
||||||
return (_entity->isMovingRelativeToParent() || _entity->hasActions()) ? MOTION_TYPE_KINEMATIC : MOTION_TYPE_STATIC;
|
if (_entity->isMovingRelativeToParent() ||
|
||||||
|
_entity->hasActions() ||
|
||||||
|
_entity->hasAncestorOfType(NestableType::Avatar)) {
|
||||||
|
return MOTION_TYPE_KINEMATIC;
|
||||||
|
}
|
||||||
|
return MOTION_TYPE_STATIC;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool EntityMotionState::isMoving() const {
|
bool EntityMotionState::isMoving() const {
|
||||||
|
@ -202,8 +207,16 @@ void EntityMotionState::setWorldTransform(const btTransform& worldTrans) {
|
||||||
|
|
||||||
assert(entityTreeIsLocked());
|
assert(entityTreeIsLocked());
|
||||||
measureBodyAcceleration();
|
measureBodyAcceleration();
|
||||||
_entity->setPosition(bulletToGLM(worldTrans.getOrigin()) + ObjectMotionState::getWorldOffset());
|
bool positionSuccess;
|
||||||
_entity->setRotation(bulletToGLM(worldTrans.getRotation()));
|
_entity->setPosition(bulletToGLM(worldTrans.getOrigin()) + ObjectMotionState::getWorldOffset(), positionSuccess, false);
|
||||||
|
if (!positionSuccess) {
|
||||||
|
qDebug() << "EntityMotionState::setWorldTransform setPosition failed" << _entity->getID();
|
||||||
|
}
|
||||||
|
bool orientationSuccess;
|
||||||
|
_entity->setOrientation(bulletToGLM(worldTrans.getRotation()), orientationSuccess, false);
|
||||||
|
if (!orientationSuccess) {
|
||||||
|
qDebug() << "EntityMotionState::setWorldTransform setOrientation failed" << _entity->getID();
|
||||||
|
}
|
||||||
_entity->setVelocity(getBodyLinearVelocity());
|
_entity->setVelocity(getBodyLinearVelocity());
|
||||||
_entity->setAngularVelocity(getBodyAngularVelocity());
|
_entity->setAngularVelocity(getBodyAngularVelocity());
|
||||||
_entity->setLastSimulated(usecTimestampNow());
|
_entity->setLastSimulated(usecTimestampNow());
|
||||||
|
|
|
@ -96,6 +96,7 @@ bool Procedural::parseVersion(const QJsonValue& version) {
|
||||||
bool Procedural::parseUrl(const QUrl& shaderUrl) {
|
bool Procedural::parseUrl(const QUrl& shaderUrl) {
|
||||||
if (!shaderUrl.isValid()) {
|
if (!shaderUrl.isValid()) {
|
||||||
qWarning() << "Invalid shader URL: " << shaderUrl;
|
qWarning() << "Invalid shader URL: " << shaderUrl;
|
||||||
|
_networkShader.reset();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -110,6 +111,7 @@ bool Procedural::parseUrl(const QUrl& shaderUrl) {
|
||||||
_shaderPath = _shaderUrl.toLocalFile();
|
_shaderPath = _shaderUrl.toLocalFile();
|
||||||
qDebug() << "Shader path: " << _shaderPath;
|
qDebug() << "Shader path: " << _shaderPath;
|
||||||
if (!QFile(_shaderPath).exists()) {
|
if (!QFile(_shaderPath).exists()) {
|
||||||
|
_networkShader.reset();
|
||||||
return false;;
|
return false;;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -135,9 +137,14 @@ bool Procedural::parseTextures(const QJsonArray& channels) {
|
||||||
|
|
||||||
auto textureCache = DependencyManager::get<TextureCache>();
|
auto textureCache = DependencyManager::get<TextureCache>();
|
||||||
size_t channelCount = std::min(MAX_PROCEDURAL_TEXTURE_CHANNELS, (size_t)_parsedChannels.size());
|
size_t channelCount = std::min(MAX_PROCEDURAL_TEXTURE_CHANNELS, (size_t)_parsedChannels.size());
|
||||||
for (size_t i = 0; i < channelCount; ++i) {
|
size_t channel = 0;
|
||||||
QString url = _parsedChannels.at((int)i).toString();
|
for (; channel < channelCount; ++channel) {
|
||||||
_channels[i] = textureCache->getTexture(QUrl(url));
|
QString url = _parsedChannels.at((int)channel).toString();
|
||||||
|
_channels[channel] = textureCache->getTexture(QUrl(url));
|
||||||
|
}
|
||||||
|
for (; channel < MAX_PROCEDURAL_TEXTURE_CHANNELS; ++channel) {
|
||||||
|
// Release those textures no longer in use
|
||||||
|
_channels[channel] = textureCache->getTexture(QUrl());
|
||||||
}
|
}
|
||||||
|
|
||||||
_channelsDirty = true;
|
_channelsDirty = true;
|
||||||
|
@ -149,20 +156,21 @@ bool Procedural::parseTextures(const QJsonArray& channels) {
|
||||||
void Procedural::parse(const QJsonObject& proceduralData) {
|
void Procedural::parse(const QJsonObject& proceduralData) {
|
||||||
_enabled = false;
|
_enabled = false;
|
||||||
|
|
||||||
if (proceduralData.isEmpty()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto version = proceduralData[VERSION_KEY];
|
auto version = proceduralData[VERSION_KEY];
|
||||||
auto shaderUrl = proceduralData[URL_KEY].toString();
|
auto shaderUrl = proceduralData[URL_KEY].toString();
|
||||||
shaderUrl = ResourceManager::normalizeURL(shaderUrl);
|
shaderUrl = ResourceManager::normalizeURL(shaderUrl);
|
||||||
auto uniforms = proceduralData[UNIFORMS_KEY].toObject();
|
auto uniforms = proceduralData[UNIFORMS_KEY].toObject();
|
||||||
auto channels = proceduralData[CHANNELS_KEY].toArray();
|
auto channels = proceduralData[CHANNELS_KEY].toArray();
|
||||||
|
|
||||||
if (parseVersion(version) &&
|
bool isValid = true;
|
||||||
parseUrl(shaderUrl) &&
|
|
||||||
parseUniforms(uniforms) &&
|
// Run through parsing regardless of validity to clear old cached resources
|
||||||
parseTextures(channels)) {
|
isValid = parseVersion(version) && isValid;
|
||||||
|
isValid = parseUrl(shaderUrl) && isValid;
|
||||||
|
isValid = parseUniforms(uniforms) && isValid;
|
||||||
|
isValid = parseTextures(channels) && isValid;
|
||||||
|
|
||||||
|
if (!proceduralData.isEmpty() && isValid) {
|
||||||
_enabled = true;
|
_enabled = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,6 +25,14 @@ ProceduralSkybox::ProceduralSkybox() : model::Skybox() {
|
||||||
_procedural._state->setStencilTest(true, 0xFF, gpu::State::StencilTest(0, 0xFF, gpu::EQUAL, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_KEEP));
|
_procedural._state->setStencilTest(true, 0xFF, gpu::State::StencilTest(0, 0xFF, gpu::EQUAL, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_KEEP));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ProceduralSkybox::clear() {
|
||||||
|
// Parse and prepare a procedural with no shaders to release textures
|
||||||
|
parse(QString());
|
||||||
|
_procedural.ready();
|
||||||
|
|
||||||
|
Skybox::clear();
|
||||||
|
}
|
||||||
|
|
||||||
void ProceduralSkybox::render(gpu::Batch& batch, const ViewFrustum& frustum) const {
|
void ProceduralSkybox::render(gpu::Batch& batch, const ViewFrustum& frustum) const {
|
||||||
if (_procedural.ready()) {
|
if (_procedural.ready()) {
|
||||||
ProceduralSkybox::render(batch, frustum, (*this));
|
ProceduralSkybox::render(batch, frustum, (*this));
|
||||||
|
|
|
@ -24,6 +24,8 @@ public:
|
||||||
|
|
||||||
void parse(const QString& userData) { _procedural.parse(userData); }
|
void parse(const QString& userData) { _procedural.parse(userData); }
|
||||||
|
|
||||||
|
virtual void clear() override;
|
||||||
|
|
||||||
virtual void render(gpu::Batch& batch, const ViewFrustum& frustum) const;
|
virtual void render(gpu::Batch& batch, const ViewFrustum& frustum) const;
|
||||||
static void render(gpu::Batch& batch, const ViewFrustum& frustum, const ProceduralSkybox& skybox);
|
static void render(gpu::Batch& batch, const ViewFrustum& frustum, const ProceduralSkybox& skybox);
|
||||||
|
|
||||||
|
|
|
@ -159,11 +159,13 @@ void Model::updateRenderItems() {
|
||||||
Transform collisionMeshOffset;
|
Transform collisionMeshOffset;
|
||||||
collisionMeshOffset.postTranslate(self->_offset);
|
collisionMeshOffset.postTranslate(self->_offset);
|
||||||
|
|
||||||
|
uint32_t deleteGeometryCounter = self->_deleteGeometryCounter;
|
||||||
|
|
||||||
render::PendingChanges pendingChanges;
|
render::PendingChanges pendingChanges;
|
||||||
foreach (auto itemID, self->_modelMeshRenderItems.keys()) {
|
foreach (auto itemID, self->_modelMeshRenderItems.keys()) {
|
||||||
pendingChanges.updateItem<ModelMeshPartPayload>(itemID, [modelTransform, modelMeshOffset](ModelMeshPartPayload& data) {
|
pendingChanges.updateItem<ModelMeshPartPayload>(itemID, [modelTransform, modelMeshOffset, deleteGeometryCounter](ModelMeshPartPayload& data) {
|
||||||
// Ensure the model geometry was not reset between frames
|
// Ensure the model geometry was not reset between frames
|
||||||
if (data._model->isLoaded()) {
|
if (data._model && data._model->isLoaded() && deleteGeometryCounter == data._model->_deleteGeometryCounter) {
|
||||||
// lazy update of cluster matrices used for rendering. We need to update them here, so we can correctly update the bounding box.
|
// lazy update of cluster matrices used for rendering. We need to update them here, so we can correctly update the bounding box.
|
||||||
data._model->updateClusterMatrices(modelTransform.getTranslation(), modelTransform.getRotation());
|
data._model->updateClusterMatrices(modelTransform.getTranslation(), modelTransform.getRotation());
|
||||||
|
|
||||||
|
@ -1146,6 +1148,7 @@ void Model::setBlendedVertices(int blendNumber, const std::weak_ptr<NetworkGeome
|
||||||
}
|
}
|
||||||
|
|
||||||
void Model::deleteGeometry() {
|
void Model::deleteGeometry() {
|
||||||
|
_deleteGeometryCounter++;
|
||||||
_blendedVertexBuffers.clear();
|
_blendedVertexBuffers.clear();
|
||||||
_meshStates.clear();
|
_meshStates.clear();
|
||||||
_rig->destroyAnimGraph();
|
_rig->destroyAnimGraph();
|
||||||
|
|
|
@ -394,6 +394,8 @@ protected:
|
||||||
|
|
||||||
friend class ModelMeshPartPayload;
|
friend class ModelMeshPartPayload;
|
||||||
RigPointer _rig;
|
RigPointer _rig;
|
||||||
|
|
||||||
|
uint32_t _deleteGeometryCounter { 0 };
|
||||||
};
|
};
|
||||||
|
|
||||||
Q_DECLARE_METATYPE(ModelPointer)
|
Q_DECLARE_METATYPE(ModelPointer)
|
||||||
|
|
52
libraries/render-utils/src/sdf_text3D_overlay.slf
Normal file
52
libraries/render-utils/src/sdf_text3D_overlay.slf
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
<@include gpu/Config.slh@>
|
||||||
|
<$VERSION_HEADER$>
|
||||||
|
// Generated on <$_SCRIBE_DATE$>
|
||||||
|
// sdf_text.frag
|
||||||
|
// fragment shader
|
||||||
|
//
|
||||||
|
// Created by Bradley Austin Davis on 2015-02-04
|
||||||
|
// Based on fragment shader code from
|
||||||
|
// https://github.com/paulhoux/Cinder-Samples/blob/master/TextRendering/include/text/Text.cpp
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
|
||||||
|
uniform sampler2D Font;
|
||||||
|
uniform bool Outline;
|
||||||
|
uniform vec4 Color;
|
||||||
|
|
||||||
|
// the interpolated normal
|
||||||
|
in vec3 _normal;
|
||||||
|
in vec2 _texCoord0;
|
||||||
|
|
||||||
|
layout(location = 0) out vec4 _fragColor0;
|
||||||
|
|
||||||
|
const float gamma = 2.2;
|
||||||
|
const float smoothing = 32.0;
|
||||||
|
const float interiorCutoff = 0.8;
|
||||||
|
const float outlineExpansion = 0.2;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
// retrieve signed distance
|
||||||
|
float sdf = texture(Font, _texCoord0).g;
|
||||||
|
if (Outline) {
|
||||||
|
if (sdf > interiorCutoff) {
|
||||||
|
sdf = 1.0 - sdf;
|
||||||
|
} else {
|
||||||
|
sdf += outlineExpansion;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// perform adaptive anti-aliasing of the edges
|
||||||
|
// The larger we're rendering, the less anti-aliasing we need
|
||||||
|
float s = smoothing * length(fwidth(_texCoord0));
|
||||||
|
float w = clamp( s, 0.0, 0.5);
|
||||||
|
float a = smoothstep(0.5 - w, 0.5 + w, sdf);
|
||||||
|
|
||||||
|
// gamma correction for linear attenuation
|
||||||
|
a = pow(a, 1.0 / gamma);
|
||||||
|
|
||||||
|
// discard if unvisible
|
||||||
|
if (a < 0.01) {
|
||||||
|
discard;
|
||||||
|
}
|
||||||
|
_fragColor0 = vec4(Color.rgb, a);
|
||||||
|
}
|
|
@ -9,6 +9,7 @@
|
||||||
|
|
||||||
#include "sdf_text3D_vert.h"
|
#include "sdf_text3D_vert.h"
|
||||||
#include "sdf_text3D_frag.h"
|
#include "sdf_text3D_frag.h"
|
||||||
|
#include "sdf_text3D_overlay_frag.h"
|
||||||
|
|
||||||
#include "../RenderUtilsLogging.h"
|
#include "../RenderUtilsLogging.h"
|
||||||
#include "FontFamilies.h"
|
#include "FontFamilies.h"
|
||||||
|
@ -220,10 +221,13 @@ void Font::setupGPU() {
|
||||||
{
|
{
|
||||||
auto vertexShader = gpu::Shader::createVertex(std::string(sdf_text3D_vert));
|
auto vertexShader = gpu::Shader::createVertex(std::string(sdf_text3D_vert));
|
||||||
auto pixelShader = gpu::Shader::createPixel(std::string(sdf_text3D_frag));
|
auto pixelShader = gpu::Shader::createPixel(std::string(sdf_text3D_frag));
|
||||||
|
auto pixelShaderOverlay = gpu::Shader::createPixel(std::string(sdf_text3D_overlay_frag));
|
||||||
gpu::ShaderPointer program = gpu::Shader::createProgram(vertexShader, pixelShader);
|
gpu::ShaderPointer program = gpu::Shader::createProgram(vertexShader, pixelShader);
|
||||||
|
gpu::ShaderPointer programOverlay = gpu::Shader::createProgram(vertexShader, pixelShaderOverlay);
|
||||||
|
|
||||||
gpu::Shader::BindingSet slotBindings;
|
gpu::Shader::BindingSet slotBindings;
|
||||||
gpu::Shader::makeProgram(*program, slotBindings);
|
gpu::Shader::makeProgram(*program, slotBindings);
|
||||||
|
gpu::Shader::makeProgram(*programOverlay, slotBindings);
|
||||||
|
|
||||||
_fontLoc = program->getTextures().findLocation("Font");
|
_fontLoc = program->getTextures().findLocation("Font");
|
||||||
_outlineLoc = program->getUniforms().findLocation("Outline");
|
_outlineLoc = program->getUniforms().findLocation("Outline");
|
||||||
|
@ -237,9 +241,10 @@ void Font::setupGPU() {
|
||||||
gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE);
|
gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE);
|
||||||
_pipeline = gpu::Pipeline::create(program, state);
|
_pipeline = gpu::Pipeline::create(program, state);
|
||||||
|
|
||||||
auto layeredState = std::make_shared<gpu::State>(state->getValues());
|
auto layeredState = std::make_shared<gpu::State>();
|
||||||
layeredState->setDepthTest(false);
|
layeredState->setCullMode(gpu::State::CULL_BACK);
|
||||||
_layeredPipeline = gpu::Pipeline::create(program, layeredState);
|
layeredState->setDepthTest(true, true, gpu::LESS_EQUAL);
|
||||||
|
_layeredPipeline = gpu::Pipeline::create(programOverlay, layeredState);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sanity checks
|
// Sanity checks
|
||||||
|
|
|
@ -17,7 +17,6 @@
|
||||||
void registerAudioMetaTypes(QScriptEngine* engine) {
|
void registerAudioMetaTypes(QScriptEngine* engine) {
|
||||||
qScriptRegisterMetaType(engine, injectorOptionsToScriptValue, injectorOptionsFromScriptValue);
|
qScriptRegisterMetaType(engine, injectorOptionsToScriptValue, injectorOptionsFromScriptValue);
|
||||||
qScriptRegisterMetaType(engine, soundSharedPointerToScriptValue, soundSharedPointerFromScriptValue);
|
qScriptRegisterMetaType(engine, soundSharedPointerToScriptValue, soundSharedPointerFromScriptValue);
|
||||||
qScriptRegisterMetaType(engine, soundPointerToScriptValue, soundPointerFromScriptValue);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
AudioScriptingInterface& AudioScriptingInterface::getInstance() {
|
AudioScriptingInterface& AudioScriptingInterface::getInstance() {
|
||||||
|
@ -31,13 +30,14 @@ AudioScriptingInterface::AudioScriptingInterface() :
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ScriptAudioInjector* AudioScriptingInterface::playSound(Sound* sound, const AudioInjectorOptions& injectorOptions) {
|
ScriptAudioInjector* AudioScriptingInterface::playSound(SharedSoundPointer sound, const AudioInjectorOptions& injectorOptions) {
|
||||||
if (QThread::currentThread() != thread()) {
|
if (QThread::currentThread() != thread()) {
|
||||||
ScriptAudioInjector* injector = NULL;
|
ScriptAudioInjector* injector = NULL;
|
||||||
|
|
||||||
QMetaObject::invokeMethod(this, "playSound", Qt::BlockingQueuedConnection,
|
QMetaObject::invokeMethod(this, "playSound", Qt::BlockingQueuedConnection,
|
||||||
Q_RETURN_ARG(ScriptAudioInjector*, injector),
|
Q_RETURN_ARG(ScriptAudioInjector*, injector),
|
||||||
Q_ARG(Sound*, sound), Q_ARG(const AudioInjectorOptions&, injectorOptions));
|
Q_ARG(SharedSoundPointer, sound),
|
||||||
|
Q_ARG(const AudioInjectorOptions&, injectorOptions));
|
||||||
return injector;
|
return injector;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -27,7 +27,7 @@ public:
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
// this method is protected to stop C++ callers from calling, but invokable from script
|
// this method is protected to stop C++ callers from calling, but invokable from script
|
||||||
Q_INVOKABLE ScriptAudioInjector* playSound(Sound* sound, const AudioInjectorOptions& injectorOptions = AudioInjectorOptions());
|
Q_INVOKABLE ScriptAudioInjector* playSound(SharedSoundPointer sound, const AudioInjectorOptions& injectorOptions = AudioInjectorOptions());
|
||||||
|
|
||||||
Q_INVOKABLE void setStereoInput(bool stereo);
|
Q_INVOKABLE void setStereoInput(bool stereo);
|
||||||
|
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue