mirror of
https://github.com/lubosz/overte.git
synced 2025-04-27 06:35:32 +02:00
Merge branch 'master' of https://github.com/highfidelity/hifi into orange
This commit is contained in:
commit
ef70d41cf4
57 changed files with 1097 additions and 206 deletions
assignment-client/src
domain-server
resources
src
examples
interface
resources
src
libraries
audio/src
controllers/src/controllers
display-plugins/src/display-plugins
entities-renderer/src
entities/src
gpu/src/gpu
networking/src
physics/src
render-utils/src
script-engine/src
shared/src
ui/src
plugins/oculus/src
|
@ -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
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
// 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 */
|
||||
|
||||
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 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 CHECK_TOO_FAR_UNEQUIP_TIME = 1.0; // seconds
|
||||
|
||||
//
|
||||
// equip
|
||||
|
@ -290,11 +291,13 @@ function MyController(hand) {
|
|||
this.intersectionDistance = 0.0;
|
||||
this.searchSphereDistance = DEFAULT_SEARCH_SPHERE_DISTANCE;
|
||||
|
||||
|
||||
this.ignoreIK = false;
|
||||
this.offsetPosition = Vec3.ZERO;
|
||||
this.offsetRotation = Quat.IDENTITY;
|
||||
|
||||
this.lastPickTime = 0;
|
||||
this.lastUnequipCheckTime = 0;
|
||||
|
||||
var _this = this;
|
||||
|
||||
this.update = function() {
|
||||
|
@ -1523,18 +1526,30 @@ function MyController(hand) {
|
|||
return;
|
||||
}
|
||||
|
||||
if (props.parentID == MyAvatar.sessionUUID &&
|
||||
Vec3.length(props.localPosition) > NEAR_PICK_MAX_DISTANCE * 2.0) {
|
||||
// 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");
|
||||
|
||||
var now = Date.now();
|
||||
if (now - this.lastUnequipCheckTime > MSECS_PER_SEC * CHECK_TOO_FAR_UNEQUIP_TIME) {
|
||||
this.lastUnequipCheckTime = now;
|
||||
|
||||
if (props.parentID == MyAvatar.sessionUUID &&
|
||||
Vec3.length(props.localPosition) > NEAR_PICK_MAX_DISTANCE * 2.0) {
|
||||
var handPosition = this.getHandPosition();
|
||||
// the center of the equipped object being far from the hand isn't enough to autoequip -- we also
|
||||
// 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.
|
||||
|
|
|
@ -98,10 +98,21 @@ function seekToLookAt() {
|
|||
}
|
||||
|
||||
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
|
||||
// system overlay (like a window), then hide the reticle
|
||||
if (Reticle.visible && !Reticle.pointingAtSystemOverlay) {
|
||||
var now = Date.now();
|
||||
var timeSinceLastMouseMove = now - lastMouseMoveOrClick;
|
||||
if (timeSinceLastMouseMove > HIDE_STATIC_MOUSE_AFTER) {
|
||||
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.");
|
||||
};
|
||||
});
|
|
@ -10,43 +10,50 @@
|
|||
|
||||
@font-face {
|
||||
font-family: Raleway-Regular;
|
||||
src: url(../../resources/fonts/Raleway-Regular.ttf), /* Production */
|
||||
url(../../interface/resources/fonts/Raleway-Regular.ttf); /* Development */
|
||||
src: url(../../resources/fonts/Raleway-Regular.ttf), /* Windows production */
|
||||
url(../../fonts/Raleway-Regular.ttf), /* OSX production */
|
||||
url(../../interface/resources/fonts/Raleway-Regular.ttf); /* Development, running script in /HiFi/examples */
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: Raleway-Light;
|
||||
src: url(../../resources/fonts/Raleway-Light.ttf),
|
||||
url(../../fonts/Raleway-Light.ttf),
|
||||
url(../../interface/resources/fonts/Raleway-Light.ttf);
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: Raleway-Bold;
|
||||
src: url(../../resources/fonts/Raleway-Bold.ttf),
|
||||
url(../../fonts/Raleway-Bold.ttf),
|
||||
url(../../interface/resources/fonts/Raleway-Bold.ttf);
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: Raleway-SemiBold;
|
||||
src: url(../../resources/fonts/Raleway-SemiBold.ttf),
|
||||
url(../../fonts/Raleway-SemiBold.ttf),
|
||||
url(../../interface/resources/fonts/Raleway-SemiBold.ttf);
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: FiraSans-SemiBold;
|
||||
src: url(../../resources/fonts/FiraSans-SemiBold.ttf),
|
||||
url(../../fonts/FiraSans-SemiBold.ttf),
|
||||
url(../../interface/resources/fonts/FiraSans-SemiBold.ttf);
|
||||
}
|
||||
|
||||
@font-face {
|
||||
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);
|
||||
}
|
||||
|
||||
@font-face {
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -69,8 +76,10 @@ body {
|
|||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
table {
|
||||
font-family: FiraSans-SemiBold;
|
||||
|
@ -231,11 +240,11 @@ input[type="text"] {
|
|||
input[type="number"] {
|
||||
position: relative;
|
||||
height: 28px;
|
||||
width: 120px;
|
||||
width: 124px;
|
||||
}
|
||||
|
||||
input[type=number] {
|
||||
padding-right: 6px;
|
||||
padding-right: 3px;
|
||||
}
|
||||
input[type=number]::-webkit-inner-spin-button {
|
||||
-webkit-appearance: none;
|
||||
|
@ -243,6 +252,7 @@ input[type=number]::-webkit-inner-spin-button {
|
|||
display: block;
|
||||
position: relative;
|
||||
width: 10px;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
font-family: hifi-glyphs;
|
||||
font-size: 50px;
|
||||
|
@ -452,6 +462,10 @@ input[type=checkbox]:checked + label:hover {
|
|||
min-height: 29px;
|
||||
}
|
||||
|
||||
.property.checkbox {
|
||||
width: auto;
|
||||
}
|
||||
|
||||
.property label {
|
||||
display: table-cell;
|
||||
vertical-align: middle;
|
||||
|
@ -657,7 +671,7 @@ div.refresh input[type="button"] {
|
|||
padding-left: 25px;
|
||||
}
|
||||
.pyr .tuple input {
|
||||
padding-left: 45px;
|
||||
padding-left: 40px;
|
||||
}
|
||||
|
||||
.tuple div > label:first-child {
|
||||
|
@ -779,6 +793,7 @@ textarea:enabled[scrolling="true"]::-webkit-resizer {
|
|||
width: 100%;
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
box-sizing: border-box;
|
||||
padding-top: 28px; /* Space for header and footer outside of scroll region. */
|
||||
margin-top: 28px;
|
||||
border-left: 2px solid #575757;
|
||||
|
@ -797,6 +812,7 @@ textarea:enabled[scrolling="true"]::-webkit-resizer {
|
|||
}
|
||||
|
||||
#entity-table thead {
|
||||
box-sizing: border-box;
|
||||
border: 2px solid #575757;
|
||||
border-top-left-radius: 7px;
|
||||
border-top-right-radius: 7px;
|
||||
|
@ -804,6 +820,7 @@ textarea:enabled[scrolling="true"]::-webkit-resizer {
|
|||
}
|
||||
|
||||
#entity-table tfoot {
|
||||
box-sizing: border-box;
|
||||
border: 2px solid #575757;
|
||||
border-bottom-left-radius: 7px;
|
||||
border-bottom-right-radius: 7px;
|
||||
|
|
|
@ -254,7 +254,7 @@
|
|||
|
||||
function resize() {
|
||||
// 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
|
||||
var tds = document.querySelectorAll("#entity-table-body tr:first-child td");
|
||||
|
|
|
@ -7,7 +7,8 @@ Script.include([
|
|||
]);
|
||||
|
||||
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 toolWidth = 50;
|
||||
|
||||
|
|
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",
|
||||
"when": "Application.NavigationFocused",
|
||||
"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.DD", "to": "Actions.UiNavVertical", "filters": "invert" },
|
||||
{ "from": "Standard.DL", "to": "Actions.UiNavLateral", "filters": "invert" },
|
||||
|
|
|
@ -30,7 +30,7 @@ Window {
|
|||
title: "Edit"
|
||||
property alias tabView: tabView
|
||||
implicitWidth: 520; implicitHeight: 695
|
||||
minSize: Qt.vector2d(400, 500)
|
||||
minSize: Qt.vector2d(412, 500)
|
||||
|
||||
HifiConstants { id: hifi }
|
||||
|
||||
|
|
|
@ -241,6 +241,8 @@ public:
|
|||
*crashTrigger = 0xDEAD10CC;
|
||||
}
|
||||
|
||||
static void setSuppressStatus(bool suppress) { _suppressStatus = suppress; }
|
||||
|
||||
void run() override {
|
||||
while (!_quit) {
|
||||
QThread::sleep(HEARTBEAT_UPDATE_INTERVAL_SECS);
|
||||
|
@ -252,35 +254,45 @@ public:
|
|||
auto elapsedMovingAverage = _movingAverage.getAverage();
|
||||
|
||||
if (elapsedMovingAverage > _maxElapsedAverage) {
|
||||
qDebug() << "DEADLOCK WATCHDOG NEW maxElapsedAverage:"
|
||||
qDebug() << "DEADLOCK WATCHDOG WARNING:"
|
||||
<< "lastHeartbeatAge:" << lastHeartbeatAge
|
||||
<< "elapsedMovingAverage:" << elapsedMovingAverage
|
||||
<< "maxElapsed:" << _maxElapsed
|
||||
<< "PREVIOUS maxElapsedAverage:" << _maxElapsedAverage
|
||||
<< "NEW maxElapsedAverage:" << elapsedMovingAverage
|
||||
<< "NEW maxElapsedAverage:" << elapsedMovingAverage << "** NEW MAX ELAPSED AVERAGE **"
|
||||
<< "samples:" << _movingAverage.getSamples();
|
||||
_maxElapsedAverage = elapsedMovingAverage;
|
||||
}
|
||||
if (lastHeartbeatAge > _maxElapsed) {
|
||||
qDebug() << "DEADLOCK WATCHDOG NEW maxElapsed:"
|
||||
qDebug() << "DEADLOCK WATCHDOG WARNING:"
|
||||
<< "lastHeartbeatAge:" << lastHeartbeatAge
|
||||
<< "elapsedMovingAverage:" << elapsedMovingAverage
|
||||
<< "PREVIOUS maxElapsed:" << _maxElapsed
|
||||
<< "NEW maxElapsed:" << lastHeartbeatAge
|
||||
<< "NEW maxElapsed:" << lastHeartbeatAge << "** NEW MAX ELAPSED **"
|
||||
<< "maxElapsedAverage:" << _maxElapsedAverage
|
||||
<< "samples:" << _movingAverage.getSamples();
|
||||
_maxElapsed = lastHeartbeatAge;
|
||||
}
|
||||
if ((sinceLastReport > HEARTBEAT_REPORT_INTERVAL_USECS) || (elapsedMovingAverage > WARNING_ELAPSED_HEARTBEAT)) {
|
||||
qDebug() << "DEADLOCK WATCHDOG STATUS -- lastHeartbeatAge:" << lastHeartbeatAge
|
||||
<< "elapsedMovingAverage:" << elapsedMovingAverage
|
||||
<< "maxElapsed:" << _maxElapsed
|
||||
<< "maxElapsedAverage:" << _maxElapsedAverage
|
||||
<< "samples:" << _movingAverage.getSamples();
|
||||
if (elapsedMovingAverage > WARNING_ELAPSED_HEARTBEAT) {
|
||||
qDebug() << "DEADLOCK WATCHDOG WARNING:"
|
||||
<< "lastHeartbeatAge:" << lastHeartbeatAge
|
||||
<< "elapsedMovingAverage:" << elapsedMovingAverage << "** OVER EXPECTED VALUE**"
|
||||
<< "maxElapsed:" << _maxElapsed
|
||||
<< "maxElapsedAverage:" << _maxElapsedAverage
|
||||
<< "samples:" << _movingAverage.getSamples();
|
||||
_lastReport = now;
|
||||
}
|
||||
|
||||
if (!_suppressStatus && sinceLastReport > HEARTBEAT_REPORT_INTERVAL_USECS) {
|
||||
qDebug() << "DEADLOCK WATCHDOG STATUS:"
|
||||
<< "lastHeartbeatAge:" << lastHeartbeatAge
|
||||
<< "elapsedMovingAverage:" << elapsedMovingAverage
|
||||
<< "maxElapsed:" << _maxElapsed
|
||||
<< "maxElapsedAverage:" << _maxElapsedAverage
|
||||
<< "samples:" << _movingAverage.getSamples();
|
||||
_lastReport = now;
|
||||
}
|
||||
|
||||
#ifdef NDEBUG
|
||||
if (lastHeartbeatAge > MAX_HEARTBEAT_AGE_USECS) {
|
||||
qDebug() << "DEADLOCK DETECTED -- "
|
||||
<< "lastHeartbeatAge:" << lastHeartbeatAge
|
||||
|
@ -290,12 +302,17 @@ public:
|
|||
<< "maxElapsed:" << _maxElapsed
|
||||
<< "maxElapsedAverage:" << _maxElapsedAverage
|
||||
<< "samples:" << _movingAverage.getSamples();
|
||||
deadlockDetectionCrash();
|
||||
|
||||
// Don't actually crash in debug builds, in case this apparent deadlock is simply from
|
||||
// the developer actively debugging code
|
||||
#ifdef NDEBUG
|
||||
deadlockDetectionCrash();
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
static std::atomic<bool> _suppressStatus;
|
||||
static std::atomic<uint64_t> _heartbeat;
|
||||
static std::atomic<uint64_t> _lastReport;
|
||||
static std::atomic<uint64_t> _maxElapsed;
|
||||
|
@ -305,12 +322,17 @@ public:
|
|||
bool _quit { false };
|
||||
};
|
||||
|
||||
std::atomic<bool> DeadlockWatchdogThread::_suppressStatus;
|
||||
std::atomic<uint64_t> DeadlockWatchdogThread::_heartbeat;
|
||||
std::atomic<uint64_t> DeadlockWatchdogThread::_lastReport;
|
||||
std::atomic<uint64_t> DeadlockWatchdogThread::_maxElapsed;
|
||||
std::atomic<int> DeadlockWatchdogThread::_maxElapsedAverage;
|
||||
ThreadSafeMovingAverage<int, DeadlockWatchdogThread::HEARTBEAT_SAMPLES> DeadlockWatchdogThread::_movingAverage;
|
||||
|
||||
void Application::toggleSuppressDeadlockWatchdogStatus(bool checked) {
|
||||
DeadlockWatchdogThread::setSuppressStatus(checked);
|
||||
}
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
class MyNativeEventFilter : public QAbstractNativeEventFilter {
|
||||
public:
|
||||
|
@ -380,6 +402,11 @@ void messageHandler(QtMsgType type, const QMessageLogContext& context, const QSt
|
|||
}
|
||||
}
|
||||
|
||||
static const QString STATE_IN_HMD = "InHMD";
|
||||
static const QString STATE_SNAP_TURN = "SnapTurn";
|
||||
static const QString STATE_GROUNDED = "Grounded";
|
||||
static const QString STATE_NAV_FOCUSED = "NavigationFocused";
|
||||
|
||||
bool setupEssentials(int& argc, char** argv) {
|
||||
unsigned int listenPort = 0; // bind to an ephemeral port by default
|
||||
const char** constArgv = const_cast<const char**>(argv);
|
||||
|
@ -449,6 +476,7 @@ bool setupEssentials(int& argc, char** argv) {
|
|||
DependencyManager::set<InterfaceActionFactory>();
|
||||
DependencyManager::set<AudioInjectorManager>();
|
||||
DependencyManager::set<MessagesClient>();
|
||||
controller::StateController::setStateVariables({ { STATE_IN_HMD, STATE_SNAP_TURN, STATE_GROUNDED, STATE_NAV_FOCUSED } });
|
||||
DependencyManager::set<UserInputMapper>();
|
||||
DependencyManager::set<controller::ScriptingInterface, ControllerScriptingInterface>();
|
||||
DependencyManager::set<InterfaceParentFinder>();
|
||||
|
@ -895,9 +923,19 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) :
|
|||
DependencyManager::get<AudioClient>()->toggleMute();
|
||||
} else if (action == controller::toInt(controller::Action::CYCLE_CAMERA)) {
|
||||
cycleCamera();
|
||||
} else if (action == controller::toInt(controller::Action::UI_NAV_SELECT)) {
|
||||
if (!offscreenUi->navigationFocused()) {
|
||||
auto reticlePosition = getApplicationCompositor().getReticlePosition();
|
||||
offscreenUi->toggleMenu(_glWidget->mapFromGlobal(QPoint(reticlePosition.x, reticlePosition.y)));
|
||||
}
|
||||
} else if (action == controller::toInt(controller::Action::CONTEXT_MENU)) {
|
||||
auto reticlePosition = getApplicationCompositor().getReticlePosition();
|
||||
offscreenUi->toggleMenu(_glWidget->mapFromGlobal(QPoint(reticlePosition.x, reticlePosition.y)));
|
||||
} else if (action == controller::toInt(controller::Action::UI_NAV_SELECT)) {
|
||||
if (!offscreenUi->navigationFocused()) {
|
||||
auto reticlePosition = getApplicationCompositor().getReticlePosition();
|
||||
offscreenUi->toggleMenu(_glWidget->mapFromGlobal(QPoint(reticlePosition.x, reticlePosition.y)));
|
||||
}
|
||||
} else if (action == controller::toInt(controller::Action::RETICLE_X)) {
|
||||
auto oldPos = getApplicationCompositor().getReticlePosition();
|
||||
getApplicationCompositor().setReticlePosition({ oldPos.x + state, oldPos.y });
|
||||
|
@ -910,28 +948,23 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) :
|
|||
}
|
||||
});
|
||||
|
||||
// A new controllerInput device used to reflect current values from the application state
|
||||
_applicationStateDevice = std::make_shared<controller::StateController>();
|
||||
_applicationStateDevice = userInputMapper->getStateDevice();
|
||||
|
||||
_applicationStateDevice->addInputVariant(QString("InHMD"), controller::StateController::ReadLambda([]() -> float {
|
||||
return (float)qApp->isHMDMode();
|
||||
}));
|
||||
_applicationStateDevice->addInputVariant(QString("SnapTurn"), controller::StateController::ReadLambda([]() -> float {
|
||||
return (float)qApp->getMyAvatar()->getSnapTurn();
|
||||
}));
|
||||
_applicationStateDevice->addInputVariant(QString("Grounded"), controller::StateController::ReadLambda([]() -> float {
|
||||
return (float)qApp->getMyAvatar()->getCharacterController()->onGround();
|
||||
}));
|
||||
_applicationStateDevice->addInputVariant(QString("NavigationFocused"), controller::StateController::ReadLambda([]() -> float {
|
||||
auto offscreenUi = DependencyManager::get<OffscreenUi>();
|
||||
return offscreenUi->navigationFocused() ? 1.0 : 0.0;
|
||||
}));
|
||||
|
||||
userInputMapper->registerDevice(_applicationStateDevice);
|
||||
_applicationStateDevice->setInputVariant(STATE_IN_HMD, []() -> float {
|
||||
return qApp->isHMDMode() ? 1 : 0;
|
||||
});
|
||||
_applicationStateDevice->setInputVariant(STATE_SNAP_TURN, []() -> float {
|
||||
return qApp->getMyAvatar()->getSnapTurn() ? 1 : 0;
|
||||
});
|
||||
_applicationStateDevice->setInputVariant(STATE_GROUNDED, []() -> float {
|
||||
return qApp->getMyAvatar()->getCharacterController()->onGround() ? 1 : 0;
|
||||
});
|
||||
_applicationStateDevice->setInputVariant(STATE_NAV_FOCUSED, []() -> float {
|
||||
return DependencyManager::get<OffscreenUi>()->navigationFocused() ? 1 : 0;
|
||||
});
|
||||
|
||||
// Setup the keyboardMouseDevice and the user input mapper with the default bindings
|
||||
userInputMapper->registerDevice(_keyboardMouseDevice->getInputDevice());
|
||||
userInputMapper->loadDefaultMapping(userInputMapper->getStandardDeviceID());
|
||||
|
||||
// force the model the look at the correct directory (weird order of operations issue)
|
||||
scriptEngines->setScriptsLocation(scriptEngines->getScriptsLocation());
|
||||
|
@ -2152,10 +2185,43 @@ void Application::keyPressEvent(QKeyEvent* event) {
|
|||
if (isShifted) {
|
||||
Menu::getInstance()->triggerOption(MenuOption::MiniMirror);
|
||||
} else {
|
||||
// whenever switching to/from full screen mirror from the keyboard, remember
|
||||
// the state you were in before full screen mirror, and return to that.
|
||||
auto previousMode = _myCamera.getMode();
|
||||
if (previousMode != CAMERA_MODE_MIRROR) {
|
||||
switch (previousMode) {
|
||||
case CAMERA_MODE_FIRST_PERSON:
|
||||
_returnFromFullScreenMirrorTo = MenuOption::FirstPerson;
|
||||
break;
|
||||
case CAMERA_MODE_THIRD_PERSON:
|
||||
_returnFromFullScreenMirrorTo = MenuOption::ThirdPerson;
|
||||
break;
|
||||
|
||||
// FIXME - it's not clear that these modes make sense to return to...
|
||||
case CAMERA_MODE_INDEPENDENT:
|
||||
_returnFromFullScreenMirrorTo = MenuOption::IndependentMode;
|
||||
break;
|
||||
case CAMERA_MODE_ENTITY:
|
||||
_returnFromFullScreenMirrorTo = MenuOption::CameraEntityMode;
|
||||
break;
|
||||
|
||||
default:
|
||||
_returnFromFullScreenMirrorTo = MenuOption::ThirdPerson;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool isMirrorChecked = Menu::getInstance()->isOptionChecked(MenuOption::FullscreenMirror);
|
||||
Menu::getInstance()->setIsOptionChecked(MenuOption::FullscreenMirror, !isMirrorChecked);
|
||||
if (isMirrorChecked) {
|
||||
Menu::getInstance()->setIsOptionChecked(MenuOption::ThirdPerson, true);
|
||||
|
||||
// if we got here without coming in from a non-Full Screen mirror case, then our
|
||||
// _returnFromFullScreenMirrorTo is unknown. In that case we'll go to the old
|
||||
// behavior of returning to ThirdPerson
|
||||
if (_returnFromFullScreenMirrorTo.isEmpty()) {
|
||||
_returnFromFullScreenMirrorTo = MenuOption::ThirdPerson;
|
||||
}
|
||||
Menu::getInstance()->setIsOptionChecked(_returnFromFullScreenMirrorTo, true);
|
||||
}
|
||||
cameraMenuChanged();
|
||||
}
|
||||
|
@ -3462,11 +3528,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 +3625,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 +3712,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 +3781,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 +4207,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 +4229,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 +4248,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
|
||||
|
|
|
@ -255,6 +255,7 @@ public slots:
|
|||
|
||||
void resetSensors(bool andReload = false);
|
||||
void setActiveFaceTracker() const;
|
||||
void toggleSuppressDeadlockWatchdogStatus(bool checked);
|
||||
|
||||
#ifdef HAVE_IVIEWHMD
|
||||
void setActiveEyeTracker();
|
||||
|
@ -312,7 +313,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 +332,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 +521,10 @@ private:
|
|||
std::atomic<uint32_t> _processOctreeStatsCounter { 0 };
|
||||
|
||||
bool _keyboardDeviceHasFocus { true };
|
||||
|
||||
bool _recentlyClearedDomain { false };
|
||||
|
||||
QString _returnFromFullScreenMirrorTo;
|
||||
};
|
||||
|
||||
#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,
|
||||
|
@ -530,6 +530,9 @@ Menu::Menu() {
|
|||
addCheckableActionToQMenuAndActionHash(timingMenu, MenuOption::PipelineWarnings);
|
||||
addCheckableActionToQMenuAndActionHash(timingMenu, MenuOption::LogExtraTimings);
|
||||
addCheckableActionToQMenuAndActionHash(timingMenu, MenuOption::SuppressShortTimings);
|
||||
addCheckableActionToQMenuAndActionHash(timingMenu, MenuOption::SupressDeadlockWatchdogStatus, 0, false,
|
||||
qApp, SLOT(toggleSuppressDeadlockWatchdogStatus(bool)));
|
||||
|
||||
|
||||
// Developer > Audio >>>
|
||||
MenuWrapper* audioDebugMenu = developerMenu->addMenu("Audio");
|
||||
|
|
|
@ -162,6 +162,7 @@ namespace MenuOption {
|
|||
const QString Stats = "Stats";
|
||||
const QString StopAllScripts = "Stop All Scripts";
|
||||
const QString SuppressShortTimings = "Suppress Timings Less than 10ms";
|
||||
const QString SupressDeadlockWatchdogStatus = "Supress Deadlock Watchdog Status";
|
||||
const QString ThirdPerson = "Third Person";
|
||||
const QString ThreePointCalibration = "3 Point Calibration";
|
||||
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) :
|
||||
_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
|
||||
|
|
|
@ -33,8 +33,10 @@ static QVariantMap createDeviceMap(const controller::InputDevice::Pointer device
|
|||
for (const auto& inputMapping : userInputMapper->getAvailableInputs(device->getDeviceID())) {
|
||||
const auto& input = inputMapping.first;
|
||||
const auto inputName = QString(inputMapping.second).remove(SANITIZE_NAME_EXPRESSION);
|
||||
#ifdef DEBUG
|
||||
qCDebug(controllers) << "\tInput " << input.getChannel() << (int)input.getType()
|
||||
<< QString::number(input.getID(), 16) << ": " << inputName;
|
||||
#endif
|
||||
deviceMap.insert(inputName, input.getID());
|
||||
}
|
||||
return deviceMap;
|
||||
|
|
|
@ -19,32 +19,39 @@
|
|||
|
||||
namespace controller {
|
||||
|
||||
static QStringList stateVariables;
|
||||
|
||||
void StateController::setStateVariables(const QStringList& newStateVariables) {
|
||||
stateVariables = newStateVariables;
|
||||
}
|
||||
|
||||
StateController::StateController() : InputDevice("Application") {
|
||||
_deviceID = UserInputMapper::STATE_DEVICE;
|
||||
for (const auto& variable : stateVariables) {
|
||||
_namedReadLambdas[variable] = []()->float{ return 0; };
|
||||
}
|
||||
}
|
||||
|
||||
StateController::~StateController() {
|
||||
}
|
||||
|
||||
void StateController::update(float deltaTime, const InputCalibrationData& inputCalibrationData, bool jointsCaptured) {}
|
||||
|
||||
void StateController::focusOutEvent() {}
|
||||
|
||||
void StateController::addInputVariant(QString name, ReadLambda lambda) {
|
||||
_namedReadLambdas.push_back(NamedReadLambda(name, lambda));
|
||||
void StateController::setInputVariant(const QString& name, ReadLambda lambda) {
|
||||
// All state variables must be predeclared;
|
||||
Q_ASSERT(_namedReadLambdas.contains(name));
|
||||
_namedReadLambdas[name] = lambda;
|
||||
}
|
||||
|
||||
Input::NamedVector StateController::getAvailableInputs() const {
|
||||
Input::NamedVector availableInputs;
|
||||
int i = 0;
|
||||
for (auto& pair : _namedReadLambdas) {
|
||||
availableInputs.push_back(Input::NamedPair(Input(_deviceID, i, ChannelType::BUTTON), pair.first));
|
||||
for (const auto& name : stateVariables) {
|
||||
availableInputs.push_back(Input::NamedPair(Input(_deviceID, i, ChannelType::BUTTON), name));
|
||||
i++;
|
||||
}
|
||||
return availableInputs;
|
||||
}
|
||||
|
||||
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)
|
||||
|
||||
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; }
|
||||
|
||||
// Device functions
|
||||
virtual Input::NamedVector getAvailableInputs() const override;
|
||||
virtual void update(float deltaTime, const InputCalibrationData& inputCalibrationData, bool jointsCaptured) override;
|
||||
virtual void focusOutEvent() override;
|
||||
|
||||
StateController();
|
||||
virtual ~StateController();
|
||||
void update(float deltaTime, const InputCalibrationData& inputCalibrationData, bool jointsCaptured) override {}
|
||||
void focusOutEvent() override {}
|
||||
|
||||
using ReadLambda = std::function<float()>;
|
||||
using NamedReadLambda = QPair<QString, ReadLambda>;
|
||||
|
||||
void addInputVariant(QString name, ReadLambda lambda);
|
||||
|
||||
virtual EndpointPointer createEndpoint(const Input& input) const override;
|
||||
void setInputVariant(const QString& name, ReadLambda lambda);
|
||||
|
||||
EndpointPointer createEndpoint(const Input& input) const override;
|
||||
|
||||
protected:
|
||||
QVector<NamedReadLambda> _namedReadLambdas;
|
||||
QHash<QString, ReadLambda> _namedReadLambdas;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -44,13 +44,15 @@
|
|||
|
||||
|
||||
namespace controller {
|
||||
const uint16_t UserInputMapper::ACTIONS_DEVICE = Input::INVALID_DEVICE - 0xFF;
|
||||
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
|
||||
controller::UserInputMapper::UserInputMapper() {
|
||||
registerDevice(std::make_shared<ActionsDevice>());
|
||||
registerDevice(_stateDevice = std::make_shared<StateController>());
|
||||
registerDevice(std::make_shared<StandardController>());
|
||||
}
|
||||
|
||||
|
@ -138,7 +140,6 @@ void UserInputMapper::loadDefaultMapping(uint16 deviceID) {
|
|||
return;
|
||||
}
|
||||
|
||||
|
||||
auto mapping = loadMappings(proxyEntry->second->getDefaultMappingConfigs());
|
||||
if (mapping) {
|
||||
auto prevMapping = _mappingsByDevice[deviceID];
|
||||
|
@ -235,6 +236,10 @@ void fixBisectedAxis(float& full, float& negative, float& positive) {
|
|||
|
||||
void UserInputMapper::update(float deltaTime) {
|
||||
Locker locker(_lock);
|
||||
|
||||
static uint64_t updateCount = 0;
|
||||
++updateCount;
|
||||
|
||||
// Reset the axis state for next loop
|
||||
for (auto& channel : _actionStates) {
|
||||
channel = 0.0f;
|
||||
|
@ -694,11 +699,17 @@ Pose UserInputMapper::getPose(const Input& input) const {
|
|||
return getPose(endpoint);
|
||||
}
|
||||
|
||||
Mapping::Pointer UserInputMapper::loadMapping(const QString& jsonFile) {
|
||||
Mapping::Pointer UserInputMapper::loadMapping(const QString& jsonFile, bool enable) {
|
||||
Locker locker(_lock);
|
||||
if (jsonFile.isEmpty()) {
|
||||
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;
|
||||
{
|
||||
QFile file(jsonFile);
|
||||
|
@ -707,7 +718,11 @@ Mapping::Pointer UserInputMapper::loadMapping(const QString& jsonFile) {
|
|||
}
|
||||
file.close();
|
||||
}
|
||||
return parseMapping(json);
|
||||
auto result = parseMapping(json);
|
||||
if (enable) {
|
||||
enableMapping(result->name);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
MappingPointer UserInputMapper::loadMappings(const QStringList& jsonFiles) {
|
||||
|
@ -961,7 +976,7 @@ Route::Pointer UserInputMapper::parseRoute(const QJsonValue& value) {
|
|||
result->json = QString(QJsonDocument(obj).toJson());
|
||||
result->source = parseSource(obj[JSON_CHANNEL_FROM]);
|
||||
result->debug = obj[JSON_CHANNEL_DEBUG].toBool();
|
||||
result->debug = obj[JSON_CHANNEL_PEEK].toBool();
|
||||
result->peek = obj[JSON_CHANNEL_PEEK].toBool();
|
||||
if (!result->source) {
|
||||
qWarning() << "Invalid route source " << obj[JSON_CHANNEL_FROM];
|
||||
return Route::Pointer();
|
||||
|
@ -1033,7 +1048,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 << QString(QJsonDocument(channelIt.toObject()).toJson());
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
|
@ -30,6 +30,7 @@
|
|||
#include "DeviceProxy.h"
|
||||
#include "StandardControls.h"
|
||||
#include "Actions.h"
|
||||
#include "StateController.h"
|
||||
|
||||
namespace controller {
|
||||
|
||||
|
@ -55,8 +56,9 @@ namespace controller {
|
|||
using uint16 = uint16_t;
|
||||
using uint32 = uint32_t;
|
||||
|
||||
static const uint16_t ACTIONS_DEVICE;
|
||||
static const uint16_t STANDARD_DEVICE;
|
||||
static const uint16_t ACTIONS_DEVICE;
|
||||
static const uint16_t STATE_DEVICE;
|
||||
|
||||
UserInputMapper();
|
||||
virtual ~UserInputMapper();
|
||||
|
@ -100,10 +102,11 @@ namespace controller {
|
|||
const DevicesMap& getDevices() { return _registeredDevices; }
|
||||
uint16 getStandardDeviceID() const { return STANDARD_DEVICE; }
|
||||
InputDevice::Pointer getStandardDevice() { return _registeredDevices[getStandardDeviceID()]; }
|
||||
StateController::Pointer getStateDevice() { return _stateDevice; }
|
||||
|
||||
MappingPointer newMapping(const QString& mappingName);
|
||||
MappingPointer parseMapping(const QString& json);
|
||||
MappingPointer loadMapping(const QString& jsonFile);
|
||||
MappingPointer loadMapping(const QString& jsonFile, bool enable = false);
|
||||
MappingPointer loadMappings(const QStringList& jsonFiles);
|
||||
|
||||
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.
|
||||
uint16 getFreeDeviceID() { return _nextFreeDeviceID++; }
|
||||
DevicesMap _registeredDevices;
|
||||
StateController::Pointer _stateDevice;
|
||||
uint16 _nextFreeDeviceID = STANDARD_DEVICE + 1;
|
||||
|
||||
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,
|
||||
// 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;
|
||||
};
|
||||
|
||||
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 {
|
||||
public:
|
||||
|
|
|
@ -467,8 +467,6 @@ void CompositorHelper::toggle() {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
glm::mat4 CompositorHelper::getReticleTransform(const glm::mat4& eyePose, const glm::vec3& headPosition) const {
|
||||
glm::mat4 result;
|
||||
if (isHMD()) {
|
||||
|
@ -487,7 +485,7 @@ glm::mat4 CompositorHelper::getReticleTransform(const glm::mat4& eyePose, const
|
|||
pointerTransform[3] = vec4(cursorRay + headPosition, 1);
|
||||
// Scale up the cursor because of distance
|
||||
reticleScale *= reticleDepth;
|
||||
}
|
||||
}
|
||||
glm::mat4 overlayXfm;
|
||||
_modelTransform.getMatrix(overlayXfm);
|
||||
pointerTransform = overlayXfm * pointerTransform;
|
||||
|
@ -503,7 +501,7 @@ glm::mat4 CompositorHelper::getReticleTransform(const glm::mat4& eyePose, const
|
|||
mousePosition.y *= -1.0f;
|
||||
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -176,7 +176,7 @@ private:
|
|||
|
||||
bool _reticleOverQml { false };
|
||||
|
||||
bool _allowMouseCapture { true };
|
||||
std::atomic<bool> _allowMouseCapture { true };
|
||||
|
||||
bool _fakeMouseEvent { false };
|
||||
|
||||
|
|
|
@ -245,8 +245,10 @@ bool OpenGLDisplayPlugin::activate() {
|
|||
|
||||
#if THREADED_PRESENT
|
||||
// Start the present thread if necessary
|
||||
auto presentThread = DependencyManager::get<PresentThread>();
|
||||
if (!presentThread) {
|
||||
QSharedPointer<PresentThread> presentThread;
|
||||
if (DependencyManager::isSet<PresentThread>()) {
|
||||
presentThread = DependencyManager::get<PresentThread>();
|
||||
} else {
|
||||
auto widget = _container->getPrimaryWidget();
|
||||
DependencyManager::set<PresentThread>();
|
||||
presentThread = DependencyManager::get<PresentThread>();
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -888,6 +888,9 @@ void EntityItem::simulateKinematicMotion(float timeElapsed, bool setFlags) {
|
|||
if (hasActions()) {
|
||||
return;
|
||||
}
|
||||
if (!_parentID.isNull()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (hasLocalAngularVelocity()) {
|
||||
glm::vec3 localAngularVelocity = getLocalAngularVelocity();
|
||||
|
@ -1975,6 +1978,11 @@ QList<EntityActionPointer> EntityItem::getActionsOfType(EntityActionType typeToG
|
|||
|
||||
void EntityItem::locationChanged() {
|
||||
requiresRecalcBoxes();
|
||||
_dirtyFlags |= Simulation::DIRTY_TRANSFORM;
|
||||
EntityTreePointer tree = getTree();
|
||||
if (tree) {
|
||||
tree->entityChanged(getThisPointer());
|
||||
}
|
||||
SpatiallyNestable::locationChanged(); // tell all the children, also
|
||||
}
|
||||
|
||||
|
@ -1984,6 +1992,7 @@ void EntityItem::dimensionsChanged() {
|
|||
}
|
||||
|
||||
void EntityItem::globalizeProperties(EntityItemProperties& properties, const QString& messageTemplate, const glm::vec3& offset) const {
|
||||
// TODO -- combine this with convertLocationToScriptSemantics
|
||||
bool success;
|
||||
auto globalPosition = getPosition(success);
|
||||
if (success) {
|
||||
|
|
|
@ -475,13 +475,20 @@ QVector<QUuid> EntityScriptingInterface::findEntitiesInBox(const glm::vec3& corn
|
|||
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> 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> entitiesToDiscard = qVectorEntityItemIDFromScriptValue(entityIdsToDiscard);
|
||||
return findRayIntersectionWorker(ray, Octree::Lock, precisionPicking, entitiesToInclude, entitiesToDiscard);
|
||||
|
|
|
@ -51,6 +51,12 @@ void GLTextureTransferHelper::setup() {
|
|||
#endif
|
||||
}
|
||||
|
||||
void GLTextureTransferHelper::shutdown() {
|
||||
_canvas->doneCurrent();
|
||||
_canvas->moveToThreadWithContext(qApp->thread());
|
||||
}
|
||||
|
||||
|
||||
bool GLTextureTransferHelper::processQueueItems(const Queue& messages) {
|
||||
for (auto package : messages) {
|
||||
TexturePointer texturePointer = package.texture.lock();
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -161,7 +161,12 @@ PhysicsMotionType EntityMotionState::computePhysicsMotionType() const {
|
|||
}
|
||||
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 {
|
||||
|
|
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;
|
||||
|
|
|
@ -49,6 +49,9 @@ public:
|
|||
template<typename T>
|
||||
static QSharedPointer<T> get();
|
||||
|
||||
template<typename T>
|
||||
static bool isSet();
|
||||
|
||||
template<typename T, typename ...Args>
|
||||
static QSharedPointer<T> set(Args&&... args);
|
||||
|
||||
|
@ -89,6 +92,14 @@ QSharedPointer<T> DependencyManager::get() {
|
|||
return instance.toStrongRef();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool DependencyManager::isSet() {
|
||||
static size_t hashCode = manager().getHashCode<T>();
|
||||
|
||||
QSharedPointer<Dependency>& instance = manager().safeGet(hashCode);
|
||||
return !instance.isNull();
|
||||
}
|
||||
|
||||
template <typename T, typename ...Args>
|
||||
QSharedPointer<T> DependencyManager::set(Args&&... args) {
|
||||
static size_t hashCode = manager().getHashCode<T>();
|
||||
|
|
|
@ -849,3 +849,17 @@ AACube SpatiallyNestable::getQueryAACube() const {
|
|||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bool SpatiallyNestable::hasAncestorOfType(NestableType nestableType) {
|
||||
bool success;
|
||||
SpatiallyNestablePointer parent = getParentPointer(success);
|
||||
if (!success || !parent) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (parent->_nestableType == nestableType) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return parent->hasAncestorOfType(nestableType);
|
||||
}
|
||||
|
|
|
@ -142,6 +142,8 @@ public:
|
|||
bool isParentIDValid() const { bool success = false; getParentPointer(success); return success; }
|
||||
virtual SpatialParentTree* getParentTree() const { return nullptr; }
|
||||
|
||||
bool hasAncestorOfType(NestableType nestableType);
|
||||
|
||||
protected:
|
||||
const NestableType _nestableType; // EntityItem or an AvatarData
|
||||
QUuid _id;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -44,12 +44,17 @@ bool OculusControllerManager::activate() {
|
|||
|
||||
// register with UserInputMapper
|
||||
auto userInputMapper = DependencyManager::get<controller::UserInputMapper>();
|
||||
if (_remote) {
|
||||
|
||||
if (OVR_SUCCESS(ovr_GetInputState(_session, ovrControllerType_Remote, &_inputState))) {
|
||||
_remote = std::make_shared<RemoteDevice>(*this);
|
||||
userInputMapper->registerDevice(_remote);
|
||||
}
|
||||
if (_touch) {
|
||||
|
||||
if (OVR_SUCCESS(ovr_GetInputState(_session, ovrControllerType_Touch, &_inputState))) {
|
||||
_touch = std::make_shared<TouchDevice>(*this);
|
||||
userInputMapper->registerDevice(_touch);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -74,16 +79,20 @@ void OculusControllerManager::deactivate() {
|
|||
void OculusControllerManager::pluginUpdate(float deltaTime, const controller::InputCalibrationData& inputCalibrationData, bool jointsCaptured) {
|
||||
PerformanceTimer perfTimer("OculusControllerManager::TouchDevice::update");
|
||||
|
||||
if (!OVR_SUCCESS(ovr_GetInputState(_session, ovrControllerType_Touch, &_inputState))) {
|
||||
qCWarning(oculus) << "Unable to read oculus input state";
|
||||
return;
|
||||
if (_touch) {
|
||||
if (OVR_SUCCESS(ovr_GetInputState(_session, ovrControllerType_Touch, &_inputState))) {
|
||||
_touch->update(deltaTime, inputCalibrationData, jointsCaptured);
|
||||
} else {
|
||||
qCWarning(oculus) << "Unable to read Oculus touch input state";
|
||||
}
|
||||
}
|
||||
|
||||
if (_touch) {
|
||||
_touch->update(deltaTime, inputCalibrationData, jointsCaptured);
|
||||
}
|
||||
if (_remote) {
|
||||
_remote->update(deltaTime, inputCalibrationData, jointsCaptured);
|
||||
if (OVR_SUCCESS(ovr_GetInputState(_session, ovrControllerType_Remote, &_inputState))) {
|
||||
_remote->update(deltaTime, inputCalibrationData, jointsCaptured);
|
||||
} else {
|
||||
qCWarning(oculus) << "Unable to read Oculus remote input state";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -99,6 +108,12 @@ void OculusControllerManager::pluginFocusOutEvent() {
|
|||
using namespace controller;
|
||||
|
||||
static const std::vector<std::pair<ovrButton, StandardButtonChannel>> BUTTON_MAP { {
|
||||
{ ovrButton_Up, DU },
|
||||
{ ovrButton_Down, DD },
|
||||
{ ovrButton_Left, DL },
|
||||
{ ovrButton_Right, DR },
|
||||
{ ovrButton_Enter, START },
|
||||
{ ovrButton_Back, BACK },
|
||||
{ ovrButton_X, X },
|
||||
{ ovrButton_Y, Y },
|
||||
{ ovrButton_A, A },
|
||||
|
@ -124,6 +139,39 @@ static const std::vector<std::pair<ovrTouch, StandardButtonChannel>> TOUCH_MAP {
|
|||
{ ovrTouch_RIndexPointing, RIGHT_INDEX_POINT },
|
||||
} };
|
||||
|
||||
|
||||
controller::Input::NamedVector OculusControllerManager::RemoteDevice::getAvailableInputs() const {
|
||||
using namespace controller;
|
||||
QVector<Input::NamedPair> availableInputs {
|
||||
makePair(DU, "DU"),
|
||||
makePair(DD, "DD"),
|
||||
makePair(DL, "DL"),
|
||||
makePair(DR, "DR"),
|
||||
makePair(START, "Start"),
|
||||
makePair(BACK, "Back"),
|
||||
};
|
||||
return availableInputs;
|
||||
}
|
||||
|
||||
QString OculusControllerManager::RemoteDevice::getDefaultMappingConfig() const {
|
||||
static const QString MAPPING_JSON = PathUtils::resourcesPath() + "/controllers/oculus_remote.json";
|
||||
return MAPPING_JSON;
|
||||
}
|
||||
|
||||
void OculusControllerManager::RemoteDevice::update(float deltaTime, const controller::InputCalibrationData& inputCalibrationData, bool jointsCaptured) {
|
||||
_buttonPressedMap.clear();
|
||||
const auto& inputState = _parent._inputState;
|
||||
for (const auto& pair : BUTTON_MAP) {
|
||||
if (inputState.Buttons & pair.first) {
|
||||
_buttonPressedMap.insert(pair.second);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void OculusControllerManager::RemoteDevice::focusOutEvent() {
|
||||
_buttonPressedMap.clear();
|
||||
}
|
||||
|
||||
void OculusControllerManager::TouchDevice::update(float deltaTime, const controller::InputCalibrationData& inputCalibrationData, bool jointsCaptured) {
|
||||
_poseStateMap.clear();
|
||||
_buttonPressedMap.clear();
|
||||
|
@ -211,7 +259,7 @@ controller::Input::NamedVector OculusControllerManager::TouchDevice::getAvailabl
|
|||
}
|
||||
|
||||
QString OculusControllerManager::TouchDevice::getDefaultMappingConfig() const {
|
||||
static const QString MAPPING_JSON = PathUtils::resourcesPath() + "/controllers/touch.json";
|
||||
static const QString MAPPING_JSON = PathUtils::resourcesPath() + "/controllers/oculus_touch.json";
|
||||
return MAPPING_JSON;
|
||||
}
|
||||
|
||||
|
|
|
@ -45,7 +45,7 @@ private:
|
|||
class RemoteDevice : public OculusInputDevice {
|
||||
public:
|
||||
using Pointer = std::shared_ptr<RemoteDevice>;
|
||||
RemoteDevice(OculusControllerManager& parent) : OculusInputDevice(parent, "Oculus Remote") {}
|
||||
RemoteDevice(OculusControllerManager& parent) : OculusInputDevice(parent, "OculusRemote") {}
|
||||
|
||||
controller::Input::NamedVector getAvailableInputs() const override;
|
||||
QString getDefaultMappingConfig() const override;
|
||||
|
@ -58,7 +58,7 @@ private:
|
|||
class TouchDevice : public OculusInputDevice {
|
||||
public:
|
||||
using Pointer = std::shared_ptr<TouchDevice>;
|
||||
TouchDevice(OculusControllerManager& parent) : OculusInputDevice(parent, "Oculus Touch") {}
|
||||
TouchDevice(OculusControllerManager& parent) : OculusInputDevice(parent, "OculusTouch") {}
|
||||
|
||||
controller::Input::NamedVector getAvailableInputs() const override;
|
||||
QString getDefaultMappingConfig() const override;
|
||||
|
|
Loading…
Reference in a new issue