mirror of
https://github.com/lubosz/overte.git
synced 2025-04-24 16:43:33 +02:00
Merge branch 'master' of github.com:highfidelity/hifi into update-collision-hulls-of-avatar-children
This commit is contained in:
commit
8158d34b20
31 changed files with 691 additions and 92 deletions
|
@ -397,7 +397,7 @@ void Agent::processAgentAvatarAndAudio(float deltaTime) {
|
|||
if (_numAvatarSoundSentBytes == soundByteArray.size()) {
|
||||
// we're done with this sound object - so set our pointer back to NULL
|
||||
// and our sent bytes back to zero
|
||||
_avatarSound = NULL;
|
||||
_avatarSound.clear();
|
||||
_numAvatarSoundSentBytes = 0;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -56,7 +56,7 @@ public:
|
|||
|
||||
public slots:
|
||||
void run();
|
||||
void playAvatarSound(Sound* avatarSound) { setAvatarSound(avatarSound); }
|
||||
void playAvatarSound(SharedSoundPointer avatarSound) { setAvatarSound(avatarSound); }
|
||||
|
||||
private slots:
|
||||
void requestScript();
|
||||
|
@ -77,7 +77,7 @@ private:
|
|||
MixedAudioStream _receivedAudioStream;
|
||||
float _lastReceivedAudioLoudness;
|
||||
|
||||
void setAvatarSound(Sound* avatarSound) { _avatarSound = avatarSound; }
|
||||
void setAvatarSound(SharedSoundPointer avatarSound) { _avatarSound = avatarSound; }
|
||||
|
||||
void sendAvatarIdentityPacket();
|
||||
void sendAvatarBillboardPacket();
|
||||
|
@ -85,7 +85,7 @@ private:
|
|||
QString _scriptContents;
|
||||
QTimer* _scriptRequestTimeout { nullptr };
|
||||
bool _isListeningToAudioStream = false;
|
||||
Sound* _avatarSound = nullptr;
|
||||
SharedSoundPointer _avatarSound;
|
||||
int _numAvatarSoundSentBytes = 0;
|
||||
bool _isAvatar = false;
|
||||
QTimer* _avatarIdentityTimer = nullptr;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"version": 1.1,
|
||||
"version": 1.2,
|
||||
"settings": [
|
||||
{
|
||||
"name": "metaverse",
|
||||
|
@ -249,7 +249,7 @@
|
|||
"label": "X end",
|
||||
"can_set": true,
|
||||
"placeholder": "16384.0"
|
||||
},
|
||||
},
|
||||
{
|
||||
"name": "y_min",
|
||||
"label": "Y start",
|
||||
|
|
|
@ -103,4 +103,5 @@
|
|||
<script src='js/sweetalert.min.js'></script>
|
||||
<script src='js/settings.js'></script>
|
||||
<script src='js/form2js.min.js'></script>
|
||||
<script src='js/sha256.js'></script>
|
||||
<!--#include virtual="page-end.html"-->
|
||||
|
|
|
@ -867,6 +867,14 @@ function saveSettings() {
|
|||
// grab a JSON representation of the form via form2js
|
||||
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);
|
||||
|
||||
// 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();
|
||||
const QVariant* settingsPasswordVariant = valueForKeyPath(settingsMap, BASIC_AUTH_PASSWORD_KEY_PATH);
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -129,7 +129,9 @@ void DomainServerSettingsManager::setupConfigMap(const QStringList& argumentList
|
|||
// reload the master and user config so that the merged config is right
|
||||
_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_FILE_NAME_KEY = "persistFilename";
|
||||
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
|
||||
|
|
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.");
|
||||
};
|
||||
});
|
|
@ -452,6 +452,10 @@ input[type=checkbox]:checked + label:hover {
|
|||
min-height: 29px;
|
||||
}
|
||||
|
||||
.property.checkbox {
|
||||
width: auto;
|
||||
}
|
||||
|
||||
.property label {
|
||||
display: table-cell;
|
||||
vertical-align: middle;
|
||||
|
|
|
@ -3462,11 +3462,9 @@ void Application::update(float deltaTime) {
|
|||
const quint64 TOO_LONG_SINCE_LAST_QUERY = 3 * USECS_PER_SECOND;
|
||||
bool queryIsDue = sinceLastQuery > TOO_LONG_SINCE_LAST_QUERY;
|
||||
bool viewIsDifferentEnough = !_lastQueriedViewFrustum.isVerySimilar(_viewFrustum);
|
||||
|
||||
// if it's been a while since our last query or the view has significantly changed then send a query, otherwise suppress it
|
||||
if (queryIsDue || viewIsDifferentEnough) {
|
||||
_lastQueriedTime = now;
|
||||
|
||||
if (DependencyManager::get<SceneScriptingInterface>()->shouldRenderEntities()) {
|
||||
queryOctree(NodeType::EntityServer, PacketType::EntityQuery, _entityServerJurisdictions);
|
||||
}
|
||||
|
@ -3561,7 +3559,7 @@ int Application::sendNackPackets() {
|
|||
return packetsSent;
|
||||
}
|
||||
|
||||
void Application::queryOctree(NodeType_t serverType, PacketType packetType, NodeToJurisdictionMap& jurisdictions) {
|
||||
void Application::queryOctree(NodeType_t serverType, PacketType packetType, NodeToJurisdictionMap& jurisdictions, bool forceResend) {
|
||||
|
||||
if (!_settingsLoaded) {
|
||||
return; // bail early if settings are not loaded
|
||||
|
@ -3648,7 +3646,7 @@ void Application::queryOctree(NodeType_t serverType, PacketType packetType, Node
|
|||
|
||||
auto queryPacket = NLPacket::create(packetType);
|
||||
|
||||
nodeList->eachNode([&](const SharedNodePointer& node){
|
||||
nodeList->eachNode([&](const SharedNodePointer& node) {
|
||||
// only send to the NodeTypes that are serverType
|
||||
if (node->getActiveSocket() && node->getType() == serverType) {
|
||||
|
||||
|
@ -3717,6 +3715,16 @@ void Application::queryOctree(NodeType_t serverType, PacketType packetType, Node
|
|||
_octreeQuery.setMaxQueryPacketsPerSecond(0);
|
||||
}
|
||||
|
||||
// if asked to forceResend, then set the query's position/orientation to be degenerate in a manner
|
||||
// that will cause our next query to be guarenteed to be different and the server will resend to us
|
||||
if (forceResend) {
|
||||
_octreeQuery.setCameraPosition(glm::vec3(-0.1, -0.1, -0.1));
|
||||
const glm::quat OFF_IN_NEGATIVE_SPACE = glm::quat(-0.5, 0, -0.5, 1.0);
|
||||
_octreeQuery.setCameraOrientation(OFF_IN_NEGATIVE_SPACE);
|
||||
_octreeQuery.setCameraNearClip(0.1f);
|
||||
_octreeQuery.setCameraFarClip(0.1f);
|
||||
}
|
||||
|
||||
// encode the query data
|
||||
int packetSize = _octreeQuery.getBroadcastData(reinterpret_cast<unsigned char*>(queryPacket->getPayload()));
|
||||
queryPacket->setPayloadSize(packetSize);
|
||||
|
@ -4133,6 +4141,7 @@ void Application::clearDomainOctreeDetails() {
|
|||
auto skyStage = DependencyManager::get<SceneScriptingInterface>()->getSkyStage();
|
||||
skyStage->setBackgroundMode(model::SunSkyStage::SKY_DOME);
|
||||
|
||||
_recentlyClearedDomain = true;
|
||||
}
|
||||
|
||||
void Application::domainChanged(const QString& domainHostname) {
|
||||
|
@ -4154,7 +4163,7 @@ void Application::nodeAdded(SharedNodePointer node) const {
|
|||
}
|
||||
}
|
||||
|
||||
void Application::nodeActivated(SharedNodePointer node) const {
|
||||
void Application::nodeActivated(SharedNodePointer node) {
|
||||
if (node->getType() == NodeType::AssetServer) {
|
||||
// asset server just connected - check if we have the asset browser showing
|
||||
|
||||
|
@ -4173,10 +4182,20 @@ void Application::nodeActivated(SharedNodePointer node) const {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If we get a new EntityServer activated, do a "forceRedraw" query. This will send a degenerate
|
||||
// query so that the server will think our next non-degenerate query is "different enough" to send
|
||||
// us a full scene
|
||||
if (_recentlyClearedDomain && node->getType() == NodeType::EntityServer) {
|
||||
_recentlyClearedDomain = false;
|
||||
if (DependencyManager::get<SceneScriptingInterface>()->shouldRenderEntities()) {
|
||||
queryOctree(NodeType::EntityServer, PacketType::EntityQuery, _entityServerJurisdictions, true);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void Application::nodeKilled(SharedNodePointer node) {
|
||||
|
||||
// These are here because connecting NodeList::nodeKilled to OctreePacketProcessor::nodeKilled doesn't work:
|
||||
// OctreePacketProcessor::nodeKilled is not being called when NodeList::nodeKilled is emitted.
|
||||
// This may have to do with GenericThread::threadRoutine() blocking the QThread event loop
|
||||
|
|
|
@ -312,7 +312,7 @@ private slots:
|
|||
void domainChanged(const QString& domainHostname);
|
||||
void updateWindowTitle() const;
|
||||
void nodeAdded(SharedNodePointer node) const;
|
||||
void nodeActivated(SharedNodePointer node) const;
|
||||
void nodeActivated(SharedNodePointer node);
|
||||
void nodeKilled(SharedNodePointer node);
|
||||
static void packetSent(quint64 length);
|
||||
void updateDisplayMode();
|
||||
|
@ -331,7 +331,7 @@ private:
|
|||
void updateThreads(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);
|
||||
static void loadViewFrustum(Camera& camera, ViewFrustum& viewFrustum);
|
||||
|
||||
glm::vec3 getSunDirection() const;
|
||||
|
@ -520,6 +520,8 @@ private:
|
|||
std::atomic<uint32_t> _processOctreeStatsCounter { 0 };
|
||||
|
||||
bool _keyboardDeviceHasFocus { true };
|
||||
|
||||
bool _recentlyClearedDomain { false };
|
||||
};
|
||||
|
||||
#endif // hifi_Application_h
|
||||
|
|
|
@ -219,12 +219,12 @@ Menu::Menu() {
|
|||
// View > First Person
|
||||
cameraModeGroup->addAction(addCheckableActionToQMenuAndActionHash(viewMenu,
|
||||
MenuOption::FirstPerson, 0, // QML Qt:: Key_P
|
||||
false, qApp, SLOT(cameraMenuChanged())));
|
||||
true, qApp, SLOT(cameraMenuChanged())));
|
||||
|
||||
// View > Third Person
|
||||
cameraModeGroup->addAction(addCheckableActionToQMenuAndActionHash(viewMenu,
|
||||
MenuOption::ThirdPerson, 0,
|
||||
true, qApp, SLOT(cameraMenuChanged())));
|
||||
false, qApp, SLOT(cameraMenuChanged())));
|
||||
|
||||
// View > Mirror
|
||||
cameraModeGroup->addAction(addCheckableActionToQMenuAndActionHash(viewMenu,
|
||||
|
|
|
@ -32,8 +32,8 @@ AudioInjector::AudioInjector(QObject* parent) :
|
|||
|
||||
}
|
||||
|
||||
AudioInjector::AudioInjector(Sound* sound, const AudioInjectorOptions& injectorOptions) :
|
||||
_audioData(sound->getByteArray()),
|
||||
AudioInjector::AudioInjector(const Sound& sound, const AudioInjectorOptions& injectorOptions) :
|
||||
_audioData(sound.getByteArray()),
|
||||
_options(injectorOptions)
|
||||
{
|
||||
|
||||
|
|
|
@ -45,7 +45,7 @@ public:
|
|||
};
|
||||
|
||||
AudioInjector(QObject* parent);
|
||||
AudioInjector(Sound* sound, const AudioInjectorOptions& injectorOptions);
|
||||
AudioInjector(const Sound& sound, const AudioInjectorOptions& injectorOptions);
|
||||
AudioInjector(const QByteArray& audioData, const AudioInjectorOptions& injectorOptions);
|
||||
|
||||
bool isFinished() const { return _state == State::Finished; }
|
||||
|
|
|
@ -23,13 +23,15 @@
|
|||
|
||||
#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) :
|
||||
_frameCapacity(numFramesCapacity),
|
||||
_sampleCapacity(numFrameSamples * numFramesCapacity),
|
||||
_bufferLength(numFrameSamples * (numFramesCapacity + 1)),
|
||||
_numFrameSamples(numFrameSamples),
|
||||
_randomAccessMode(randomAccessMode),
|
||||
_overflowCount(0)
|
||||
_frameCapacity(numFramesCapacity),
|
||||
_sampleCapacity(numFrameSamples * numFramesCapacity),
|
||||
_bufferLength(numFrameSamples * (numFramesCapacity + 1)),
|
||||
_numFrameSamples(numFrameSamples),
|
||||
_randomAccessMode(randomAccessMode),
|
||||
_overflowCount(0)
|
||||
{
|
||||
if (numFrameSamples) {
|
||||
_buffer = new int16_t[_bufferLength];
|
||||
|
@ -41,6 +43,8 @@ _overflowCount(0)
|
|||
_nextOutput = NULL;
|
||||
_endOfLastWrite = NULL;
|
||||
}
|
||||
|
||||
static QString repeatedMessage = LogHandler::getInstance().addRepeatedMessageRegex(RING_BUFFER_OVERFLOW_DEBUG);
|
||||
};
|
||||
|
||||
AudioRingBuffer::~AudioRingBuffer() {
|
||||
|
@ -131,8 +135,6 @@ int AudioRingBuffer::writeData(const char* data, int maxSize) {
|
|||
_nextOutput = shiftedPositionAccomodatingWrap(_nextOutput, samplesToDelete);
|
||||
_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);
|
||||
}
|
||||
|
||||
|
@ -179,7 +181,12 @@ int AudioRingBuffer::addSilentSamples(int silentSamples) {
|
|||
if (silentSamples > samplesRoomFor) {
|
||||
// there's not enough room for this write. write as many silent samples as we have room for
|
||||
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
|
||||
|
@ -243,7 +250,7 @@ int AudioRingBuffer::writeSamples(ConstIterator source, int maxSamples) {
|
|||
int samplesToDelete = samplesToCopy - samplesRoomFor;
|
||||
_nextOutput = shiftedPositionAccomodatingWrap(_nextOutput, samplesToDelete);
|
||||
_overflowCount++;
|
||||
qCDebug(audio) << "Overflowed ring buffer! Overwriting old data";
|
||||
qCDebug(audio) << qPrintable(RING_BUFFER_OVERFLOW_DEBUG);
|
||||
}
|
||||
|
||||
int16_t* bufferLast = _buffer + _bufferLength - 1;
|
||||
|
@ -264,7 +271,7 @@ int AudioRingBuffer::writeSamplesWithFade(ConstIterator source, int maxSamples,
|
|||
int samplesToDelete = samplesToCopy - samplesRoomFor;
|
||||
_nextOutput = shiftedPositionAccomodatingWrap(_nextOutput, samplesToDelete);
|
||||
_overflowCount++;
|
||||
qCDebug(audio) << "Overflowed ring buffer! Overwriting old data";
|
||||
qCDebug(audio) << qPrintable(RING_BUFFER_OVERFLOW_DEBUG);
|
||||
}
|
||||
|
||||
int16_t* bufferLast = _buffer + _bufferLength - 1;
|
||||
|
|
|
@ -27,22 +27,18 @@
|
|||
#include "AudioLogging.h"
|
||||
#include "Sound.h"
|
||||
|
||||
static int soundMetaTypeId = qRegisterMetaType<Sound*>();
|
||||
|
||||
QScriptValue soundSharedPointerToScriptValue(QScriptEngine* engine, SharedSoundPointer const& in) {
|
||||
return engine->newQObject(in.data());
|
||||
QScriptValue soundSharedPointerToScriptValue(QScriptEngine* engine, const SharedSoundPointer& in) {
|
||||
return engine->newQObject(new SoundScriptingInterface(in), QScriptEngine::ScriptOwnership);
|
||||
}
|
||||
|
||||
void soundSharedPointerFromScriptValue(const QScriptValue& object, SharedSoundPointer &out) {
|
||||
out = SharedSoundPointer(qobject_cast<Sound*>(object.toQObject()));
|
||||
void soundSharedPointerFromScriptValue(const QScriptValue& object, SharedSoundPointer& out) {
|
||||
if (auto soundInterface = qobject_cast<SoundScriptingInterface*>(object.toQObject())) {
|
||||
out = soundInterface->getSound();
|
||||
}
|
||||
}
|
||||
|
||||
QScriptValue soundPointerToScriptValue(QScriptEngine* engine, Sound* const& in) {
|
||||
return engine->newQObject(in);
|
||||
}
|
||||
|
||||
void soundPointerFromScriptValue(const QScriptValue &object, Sound* &out) {
|
||||
out = qobject_cast<Sound*>(object.toQObject());
|
||||
SoundScriptingInterface::SoundScriptingInterface(SharedSoundPointer sound) : _sound(sound) {
|
||||
QObject::connect(sound.data(), &Sound::ready, this, &SoundScriptingInterface::ready);
|
||||
}
|
||||
|
||||
Sound::Sound(const QUrl& url, bool isStereo) :
|
||||
|
|
|
@ -20,18 +20,16 @@
|
|||
|
||||
class Sound : public Resource {
|
||||
Q_OBJECT
|
||||
|
||||
Q_PROPERTY(bool downloaded READ isReady)
|
||||
Q_PROPERTY(float duration READ getDuration)
|
||||
|
||||
public:
|
||||
Sound(const QUrl& url, bool isStereo = false);
|
||||
|
||||
bool isStereo() const { return _isStereo; }
|
||||
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:
|
||||
void ready();
|
||||
|
@ -50,13 +48,28 @@ private:
|
|||
|
||||
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)
|
||||
QScriptValue soundSharedPointerToScriptValue(QScriptEngine* engine, SharedSoundPointer const& in);
|
||||
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);
|
||||
|
||||
QScriptValue soundSharedPointerToScriptValue(QScriptEngine* engine, const SharedSoundPointer& in);
|
||||
void soundSharedPointerFromScriptValue(const QScriptValue& object, SharedSoundPointer& out);
|
||||
|
||||
#endif // hifi_Sound_h
|
||||
|
|
|
@ -1033,7 +1033,7 @@ Mapping::Pointer UserInputMapper::parseMapping(const QJsonValue& json) {
|
|||
Route::Pointer route = parseRoute(channelIt);
|
||||
|
||||
if (!route) {
|
||||
qWarning() << "Couldn't parse route";
|
||||
qWarning() << "Couldn't parse route:" << mapping->name << channelIt;
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
|
@ -71,8 +71,8 @@ EntityTreeRenderer::EntityTreeRenderer(bool wantScripts, AbstractViewStateInterf
|
|||
}
|
||||
|
||||
EntityTreeRenderer::~EntityTreeRenderer() {
|
||||
// NOTE: we don't need to delete _entitiesScriptEngine because it is registered with the application and has a
|
||||
// signal tied to call it's deleteLater on doneRunning
|
||||
// NOTE: We don't need to delete _entitiesScriptEngine because
|
||||
// it is registered with ScriptEngines, which will call deleteLater for us.
|
||||
}
|
||||
|
||||
void EntityTreeRenderer::clear() {
|
||||
|
|
|
@ -77,6 +77,12 @@ void GLTextureTransferHelper::setup() {
|
|||
#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);
|
||||
|
|
|
@ -28,6 +28,7 @@ public:
|
|||
|
||||
protected:
|
||||
void setup() override;
|
||||
void shutdown() override;
|
||||
bool processQueueItems(const Queue& messages) override;
|
||||
void transferTextureSynchronous(const gpu::Texture& texture);
|
||||
|
||||
|
|
|
@ -380,7 +380,7 @@ void Resource::allReferencesCleared() {
|
|||
_cache->addUnusedResource(self);
|
||||
|
||||
} else {
|
||||
delete this;
|
||||
deleteLater();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
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_frag.h"
|
||||
#include "sdf_text3D_overlay_frag.h"
|
||||
|
||||
#include "../RenderUtilsLogging.h"
|
||||
#include "FontFamilies.h"
|
||||
|
@ -220,10 +221,13 @@ void Font::setupGPU() {
|
|||
{
|
||||
auto vertexShader = gpu::Shader::createVertex(std::string(sdf_text3D_vert));
|
||||
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 programOverlay = gpu::Shader::createProgram(vertexShader, pixelShaderOverlay);
|
||||
|
||||
gpu::Shader::BindingSet slotBindings;
|
||||
gpu::Shader::makeProgram(*program, slotBindings);
|
||||
gpu::Shader::makeProgram(*programOverlay, slotBindings);
|
||||
|
||||
_fontLoc = program->getTextures().findLocation("Font");
|
||||
_outlineLoc = program->getUniforms().findLocation("Outline");
|
||||
|
@ -237,9 +241,10 @@ void Font::setupGPU() {
|
|||
gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE);
|
||||
_pipeline = gpu::Pipeline::create(program, state);
|
||||
|
||||
auto layeredState = std::make_shared<gpu::State>(state->getValues());
|
||||
layeredState->setDepthTest(false);
|
||||
_layeredPipeline = gpu::Pipeline::create(program, layeredState);
|
||||
auto layeredState = std::make_shared<gpu::State>();
|
||||
layeredState->setCullMode(gpu::State::CULL_BACK);
|
||||
layeredState->setDepthTest(true, true, gpu::LESS_EQUAL);
|
||||
_layeredPipeline = gpu::Pipeline::create(programOverlay, layeredState);
|
||||
}
|
||||
|
||||
// Sanity checks
|
||||
|
|
|
@ -17,7 +17,6 @@
|
|||
void registerAudioMetaTypes(QScriptEngine* engine) {
|
||||
qScriptRegisterMetaType(engine, injectorOptionsToScriptValue, injectorOptionsFromScriptValue);
|
||||
qScriptRegisterMetaType(engine, soundSharedPointerToScriptValue, soundSharedPointerFromScriptValue);
|
||||
qScriptRegisterMetaType(engine, soundPointerToScriptValue, soundPointerFromScriptValue);
|
||||
}
|
||||
|
||||
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()) {
|
||||
ScriptAudioInjector* injector = NULL;
|
||||
|
||||
QMetaObject::invokeMethod(this, "playSound", Qt::BlockingQueuedConnection,
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
|
@ -27,7 +27,7 @@ public:
|
|||
|
||||
protected:
|
||||
// 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);
|
||||
|
||||
|
|
|
@ -154,7 +154,11 @@ ScriptEngine::~ScriptEngine() {
|
|||
|
||||
void ScriptEngine::disconnectNonEssentialSignals() {
|
||||
disconnect();
|
||||
connect(this, &ScriptEngine::doneRunning, thread(), &QThread::quit);
|
||||
QThread* receiver;
|
||||
// Ensure the thread should be running, and does exist
|
||||
if (_isRunning && _isThreaded && (receiver = thread())) {
|
||||
connect(this, &ScriptEngine::doneRunning, receiver, &QThread::quit);
|
||||
}
|
||||
}
|
||||
|
||||
void ScriptEngine::runInThread() {
|
||||
|
@ -578,7 +582,7 @@ void ScriptEngine::addEventHandler(const EntityItemID& entityID, const QString&
|
|||
_registeredHandlers[entityID] = RegisteredEventHandlers();
|
||||
}
|
||||
CallbackList& handlersForEvent = _registeredHandlers[entityID][eventName];
|
||||
CallbackData handlerData = {handler, currentEntityIdentifier};
|
||||
CallbackData handlerData = {handler, currentEntityIdentifier, currentSandboxURL};
|
||||
handlersForEvent << handlerData; // Note that the same handler can be added many times. See removeEntityEventHandler().
|
||||
}
|
||||
|
||||
|
@ -795,7 +799,7 @@ void ScriptEngine::timerFired() {
|
|||
|
||||
// call the associated JS function, if it exists
|
||||
if (timerData.function.isValid()) {
|
||||
callWithEnvironment(timerData.definingEntityIdentifier, timerData.function, timerData.function, QScriptValueList());
|
||||
callWithEnvironment(timerData.definingEntityIdentifier, timerData.definingSandboxURL, timerData.function, timerData.function, QScriptValueList());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -810,7 +814,7 @@ QObject* ScriptEngine::setupTimerWithInterval(const QScriptValue& function, int
|
|||
// make sure the timer stops when the script does
|
||||
connect(this, &ScriptEngine::scriptEnding, newTimer, &QTimer::stop);
|
||||
|
||||
CallbackData timerData = {function, currentEntityIdentifier};
|
||||
CallbackData timerData = {function, currentEntityIdentifier, currentSandboxURL};
|
||||
_timerFunctionMap.insert(newTimer, timerData);
|
||||
|
||||
newTimer->start(intervalMS);
|
||||
|
@ -885,19 +889,49 @@ void ScriptEngine::include(const QStringList& includeFiles, QScriptValue callbac
|
|||
return; // bail early
|
||||
}
|
||||
QList<QUrl> urls;
|
||||
bool knowsSensitivity = false;
|
||||
Qt::CaseSensitivity sensitivity;
|
||||
auto getSensitivity = [&]() {
|
||||
if (!knowsSensitivity) {
|
||||
QString path = currentSandboxURL.path();
|
||||
QFileInfo upperFI(path.toUpper());
|
||||
QFileInfo lowerFI(path.toLower());
|
||||
sensitivity = (upperFI == lowerFI) ? Qt::CaseInsensitive : Qt::CaseSensitive;
|
||||
knowsSensitivity = true;
|
||||
}
|
||||
return sensitivity;
|
||||
};
|
||||
|
||||
// Guard against meaningless query and fragment parts.
|
||||
// Do NOT use PreferLocalFile as its behavior is unpredictable (e.g., on defaultScriptsLocation())
|
||||
const auto strippingFlags = QUrl::RemoveFilename | QUrl::RemoveQuery | QUrl::RemoveFragment;
|
||||
for (QString file : includeFiles) {
|
||||
QUrl thisURL { resolvePath(file) };
|
||||
if (!_includedURLs.contains(thisURL)) {
|
||||
urls.append(thisURL);
|
||||
_includedURLs << thisURL;
|
||||
}
|
||||
else {
|
||||
if (!currentSandboxURL.isEmpty() && (thisURL.scheme() == "file") &&
|
||||
(
|
||||
(currentSandboxURL.scheme() != "file") ||
|
||||
(
|
||||
!thisURL.toString(strippingFlags).startsWith(defaultScriptsLocation().toString(), getSensitivity()) &&
|
||||
!thisURL.toString(strippingFlags).startsWith(currentSandboxURL.toString(strippingFlags), getSensitivity())
|
||||
)
|
||||
)
|
||||
) {
|
||||
qCWarning(scriptengine) << "Script.include() ignoring file path" << thisURL << "outside of original entity script" << currentSandboxURL;
|
||||
} else {
|
||||
// We could also check here for CORS, but we don't yet.
|
||||
// It turns out that QUrl.resolve will not change hosts and copy authority, so we don't need to check that here.
|
||||
urls.append(thisURL);
|
||||
_includedURLs << thisURL;
|
||||
}
|
||||
} else {
|
||||
qCDebug(scriptengine) << "Script.include() ignoring previously included url:" << thisURL;
|
||||
}
|
||||
}
|
||||
|
||||
BatchLoader* loader = new BatchLoader(urls);
|
||||
EntityItemID capturedEntityIdentifier = currentEntityIdentifier;
|
||||
QUrl capturedSandboxURL = currentSandboxURL;
|
||||
|
||||
auto evaluateScripts = [=](const QMap<QUrl, QString>& data) {
|
||||
auto parentURL = _parentURL;
|
||||
|
@ -912,13 +946,13 @@ void ScriptEngine::include(const QStringList& includeFiles, QScriptValue callbac
|
|||
auto operation = [&]() {
|
||||
evaluate(contents, url.toString());
|
||||
};
|
||||
doWithEnvironment(capturedEntityIdentifier, operation);
|
||||
doWithEnvironment(capturedEntityIdentifier, capturedSandboxURL, operation);
|
||||
}
|
||||
}
|
||||
_parentURL = parentURL;
|
||||
|
||||
if (callback.isFunction()) {
|
||||
callWithEnvironment(capturedEntityIdentifier, QScriptValue(callback), QScriptValue(), QScriptValueList());
|
||||
callWithEnvironment(capturedEntityIdentifier, capturedSandboxURL, QScriptValue(callback), QScriptValue(), QScriptValueList());
|
||||
}
|
||||
|
||||
loader->deleteLater();
|
||||
|
@ -996,10 +1030,11 @@ void ScriptEngine::forwardHandlerCall(const EntityItemID& entityID, const QStrin
|
|||
CallbackList handlersForEvent = handlersOnEntity[eventName];
|
||||
if (!handlersForEvent.isEmpty()) {
|
||||
for (int i = 0; i < handlersForEvent.count(); ++i) {
|
||||
// handlersForEvent[i] can tonain many handlers that may have each been added by different interface or entity scripts,
|
||||
// handlersForEvent[i] can contain many handlers that may have each been added by different interface or entity scripts,
|
||||
// and the entity scripts may be for entities other than the one this is a handler for.
|
||||
// Fortunately, the definingEntityIdentifier captured the entity script id (if any) when the handler was added.
|
||||
callWithEnvironment(handlersForEvent[i].definingEntityIdentifier, handlersForEvent[i].function, QScriptValue(), eventHandlerArgs);
|
||||
CallbackData& handler = handlersForEvent[i];
|
||||
callWithEnvironment(handler.definingEntityIdentifier, handler.definingSandboxURL, handler.function, QScriptValue(), eventHandlerArgs);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1089,9 +1124,19 @@ void ScriptEngine::entityScriptContentAvailable(const EntityItemID& entityID, co
|
|||
}
|
||||
|
||||
if (!testConstructor.isFunction()) {
|
||||
qCDebug(scriptengine) << "ScriptEngine::loadEntityScript() entity:" << entityID << "\n"
|
||||
" NOT CONSTRUCTOR\n"
|
||||
" SCRIPT:" << scriptOrURL;
|
||||
QString testConstructorType = QString(testConstructor.toVariant().typeName());
|
||||
if (testConstructorType == "") {
|
||||
testConstructorType = "empty";
|
||||
}
|
||||
QString testConstructorValue = testConstructor.toString();
|
||||
const int maxTestConstructorValueSize = 80;
|
||||
if (testConstructorValue.size() > maxTestConstructorValueSize) {
|
||||
testConstructorValue = testConstructorValue.mid(0, maxTestConstructorValueSize) + "...";
|
||||
}
|
||||
qCDebug(scriptengine) << "Error -- ScriptEngine::loadEntityScript() entity:" << entityID
|
||||
<< "failed to load entity script -- expected a function, got " + testConstructorType
|
||||
<< "," << testConstructorValue
|
||||
<< "," << scriptOrURL;
|
||||
|
||||
if (!isFileUrl) {
|
||||
scriptCache->addScriptToBadScriptList(scriptOrURL);
|
||||
|
@ -1106,13 +1151,14 @@ void ScriptEngine::entityScriptContentAvailable(const EntityItemID& entityID, co
|
|||
lastModified = (quint64)QFileInfo(file).lastModified().toMSecsSinceEpoch();
|
||||
}
|
||||
QScriptValue entityScriptConstructor, entityScriptObject;
|
||||
QUrl sandboxURL = currentSandboxURL.isEmpty() ? scriptOrURL : currentSandboxURL;
|
||||
auto initialization = [&]{
|
||||
entityScriptConstructor = evaluate(contents, fileName);
|
||||
entityScriptObject = entityScriptConstructor.construct();
|
||||
};
|
||||
doWithEnvironment(entityID, initialization);
|
||||
doWithEnvironment(entityID, sandboxURL, initialization);
|
||||
|
||||
EntityScriptDetails newDetails = { scriptOrURL, entityScriptObject, lastModified };
|
||||
EntityScriptDetails newDetails = { scriptOrURL, entityScriptObject, lastModified, sandboxURL };
|
||||
_entityScripts[entityID] = newDetails;
|
||||
if (isURL) {
|
||||
setParentURL("");
|
||||
|
@ -1201,9 +1247,11 @@ void ScriptEngine::refreshFileScript(const EntityItemID& entityID) {
|
|||
// Even if entityID is supplied as currentEntityIdentifier, this still documents the source
|
||||
// of the code being executed (e.g., if we ever sandbox different entity scripts, or provide different
|
||||
// global values for different entity scripts).
|
||||
void ScriptEngine::doWithEnvironment(const EntityItemID& entityID, std::function<void()> operation) {
|
||||
void ScriptEngine::doWithEnvironment(const EntityItemID& entityID, const QUrl& sandboxURL, std::function<void()> operation) {
|
||||
EntityItemID oldIdentifier = currentEntityIdentifier;
|
||||
QUrl oldSandboxURL = currentSandboxURL;
|
||||
currentEntityIdentifier = entityID;
|
||||
currentSandboxURL = sandboxURL;
|
||||
|
||||
#if DEBUG_CURRENT_ENTITY
|
||||
QScriptValue oldData = this->globalObject().property("debugEntityID");
|
||||
|
@ -1215,12 +1263,13 @@ void ScriptEngine::doWithEnvironment(const EntityItemID& entityID, std::function
|
|||
#endif
|
||||
|
||||
currentEntityIdentifier = oldIdentifier;
|
||||
currentSandboxURL = oldSandboxURL;
|
||||
}
|
||||
void ScriptEngine::callWithEnvironment(const EntityItemID& entityID, QScriptValue function, QScriptValue thisObject, QScriptValueList args) {
|
||||
void ScriptEngine::callWithEnvironment(const EntityItemID& entityID, const QUrl& sandboxURL, QScriptValue function, QScriptValue thisObject, QScriptValueList args) {
|
||||
auto operation = [&]() {
|
||||
function.call(thisObject, args);
|
||||
};
|
||||
doWithEnvironment(entityID, operation);
|
||||
doWithEnvironment(entityID, sandboxURL, operation);
|
||||
}
|
||||
|
||||
void ScriptEngine::callEntityScriptMethod(const EntityItemID& entityID, const QString& methodName, const QStringList& params) {
|
||||
|
@ -1249,7 +1298,7 @@ void ScriptEngine::callEntityScriptMethod(const EntityItemID& entityID, const QS
|
|||
QScriptValueList args;
|
||||
args << entityID.toScriptValue(this);
|
||||
args << qScriptValueFromSequence(this, params);
|
||||
callWithEnvironment(entityID, entityScript.property(methodName), entityScript, args);
|
||||
callWithEnvironment(entityID, details.definingSandboxURL, entityScript.property(methodName), entityScript, args);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1281,7 +1330,7 @@ void ScriptEngine::callEntityScriptMethod(const EntityItemID& entityID, const QS
|
|||
QScriptValueList args;
|
||||
args << entityID.toScriptValue(this);
|
||||
args << event.toScriptValue(this);
|
||||
callWithEnvironment(entityID, entityScript.property(methodName), entityScript, args);
|
||||
callWithEnvironment(entityID, details.definingSandboxURL, entityScript.property(methodName), entityScript, args);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1315,7 +1364,7 @@ void ScriptEngine::callEntityScriptMethod(const EntityItemID& entityID, const QS
|
|||
args << entityID.toScriptValue(this);
|
||||
args << otherID.toScriptValue(this);
|
||||
args << collisionToScriptValue(this, collision);
|
||||
callWithEnvironment(entityID, entityScript.property(methodName), entityScript, args);
|
||||
callWithEnvironment(entityID, details.definingSandboxURL, entityScript.property(methodName), entityScript, args);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -47,6 +47,7 @@ class CallbackData {
|
|||
public:
|
||||
QScriptValue function;
|
||||
EntityItemID definingEntityIdentifier;
|
||||
QUrl definingSandboxURL;
|
||||
};
|
||||
|
||||
typedef QList<CallbackData> CallbackList;
|
||||
|
@ -57,6 +58,7 @@ public:
|
|||
QString scriptText;
|
||||
QScriptValue scriptObject;
|
||||
int64_t lastModified;
|
||||
QUrl definingSandboxURL;
|
||||
};
|
||||
|
||||
class ScriptEngine : public QScriptEngine, public ScriptUser, public EntitiesScriptEngineProvider {
|
||||
|
@ -214,8 +216,9 @@ protected:
|
|||
Q_INVOKABLE void entityScriptContentAvailable(const EntityItemID& entityID, const QString& scriptOrURL, const QString& contents, bool isURL, bool success);
|
||||
|
||||
EntityItemID currentEntityIdentifier {}; // Contains the defining entity script entity id during execution, if any. Empty for interface script execution.
|
||||
void doWithEnvironment(const EntityItemID& entityID, std::function<void()> operation);
|
||||
void callWithEnvironment(const EntityItemID& entityID, QScriptValue function, QScriptValue thisObject, QScriptValueList args);
|
||||
QUrl currentSandboxURL {}; // The toplevel url string for the entity script that loaded the code being executed, else empty.
|
||||
void doWithEnvironment(const EntityItemID& entityID, const QUrl& sandboxURL, std::function<void()> operation);
|
||||
void callWithEnvironment(const EntityItemID& entityID, const QUrl& sandboxURL, QScriptValue function, QScriptValue thisObject, QScriptValueList args);
|
||||
|
||||
friend class ScriptEngines;
|
||||
static std::atomic<bool> _stoppingAllScripts;
|
||||
|
|
|
@ -43,6 +43,11 @@ public:
|
|||
auto result = static_cast<MenuUserData*>(object->userData(USER_DATA_ID));
|
||||
if (!result) {
|
||||
qWarning() << "Unable to find MenuUserData for object " << object;
|
||||
if (auto action = dynamic_cast<QAction*>(object)) {
|
||||
qWarning() << action->text();
|
||||
} else if (auto menu = dynamic_cast<QMenu*>(object)) {
|
||||
qWarning() << menu->title();
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
return result;
|
||||
|
|
Loading…
Reference in a new issue