Merge branch 'master' of https://github.com/highfidelity/hifi into orange

This commit is contained in:
samcake 2017-06-21 11:25:07 -07:00
commit 56d5f4ec5c
77 changed files with 1395 additions and 801 deletions

View file

@ -23,6 +23,7 @@
#include <AvatarHashMap.h>
#include <AudioInjectorManager.h>
#include <AssetClient.h>
#include <LocationScriptingInterface.h>
#include <MessagesClient.h>
#include <NetworkAccessManager.h>
#include <NodeList.h>
@ -453,6 +454,9 @@ void Agent::executeScript() {
_scriptEngine->registerGlobalObject("EntityViewer", &_entityViewer);
_scriptEngine->registerGetterSetter("location", LocationScriptingInterface::locationGetter,
LocationScriptingInterface::locationSetter);
auto recordingInterface = DependencyManager::get<RecordingScriptingInterface>();
_scriptEngine->registerGlobalObject("Recording", recordingInterface.data());

View file

@ -10,7 +10,6 @@
<link href="/css/sweetalert.css" rel="stylesheet" media="screen">
<link href="/css/bootstrap-switch.min.css" rel="stylesheet" media="screen">
</head>
<body>
<nav class="navbar navbar-default" role="navigation">
@ -38,8 +37,23 @@
</li>
<li><a href="/content/">Content</a></li>
<li><a href="/settings/">Settings</a></li>
<li><a href="#" id="restart-server"><span class="glyphicon glyphicon-refresh"></span> Restart</a></li>
</ul>
</div>
</div><!-- /.container-fluid -->
</nav>
<div class="modal fade" id="restart-modal">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title">domain-server is restarting</h4>
</div>
<div class="modal-body">
<h5>This page will automatically refresh in <span id="refresh-time">3 seconds</span>.</h5>
</div>
</div><!-- /.modal-content -->
</div><!-- /.modal-dialog -->
</div><!-- /.modal -->
<div class="container-fluid">

View file

@ -1,3 +1,28 @@
function showRestartModal() {
$('#restart-modal').modal({
backdrop: 'static',
keyboard: false
});
var secondsElapsed = 0;
var numberOfSecondsToWait = 3;
var refreshSpan = $('span#refresh-time')
refreshSpan.html(numberOfSecondsToWait + " seconds");
// call ourselves every 1 second to countdown
var refreshCountdown = setInterval(function(){
secondsElapsed++;
secondsLeft = numberOfSecondsToWait - secondsElapsed
refreshSpan.html(secondsLeft + (secondsLeft == 1 ? " second" : " seconds"))
if (secondsElapsed == numberOfSecondsToWait) {
location.reload(true);
clearInterval(refreshCountdown);
}
}, 1000);
}
$(document).ready(function(){
var url = window.location;
// Will only work if string in href matches with location
@ -7,4 +32,10 @@ $(document).ready(function(){
$('ul.nav a').filter(function() {
return this.href == url;
}).parent().addClass('active');
$('body').on('click', '#restart-server', function(e){
$.get("/restart");
showRestartModal();
return false;
});
});

View file

@ -81,19 +81,6 @@
</div>
</div>
<div class="modal fade" id="restart-modal">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title">domain-server is restarting</h4>
</div>
<div class="modal-body">
<h5>This page will automatically refresh in <span id="refresh-time">3 seconds</span>.</h5>
</div>
</div><!-- /.modal-content -->
</div><!-- /.modal-dialog -->
</div><!-- /.modal -->
<!--#include virtual="footer.html"-->
<script src='/js/underscore-min.js'></script>
<script src='/js/underscore-keypath.min.js'></script>

View file

@ -1680,31 +1680,6 @@ function updateDataChangedForSiblingRows(row, forceTrue) {
})
}
function showRestartModal() {
$('#restart-modal').modal({
backdrop: 'static',
keyboard: false
});
var secondsElapsed = 0;
var numberOfSecondsToWait = 3;
var refreshSpan = $('span#refresh-time')
refreshSpan.html(numberOfSecondsToWait + " seconds");
// call ourselves every 1 second to countdown
var refreshCountdown = setInterval(function(){
secondsElapsed++;
secondsLeft = numberOfSecondsToWait - secondsElapsed
refreshSpan.html(secondsLeft + (secondsLeft == 1 ? " second" : " seconds"))
if (secondsElapsed == numberOfSecondsToWait) {
location.reload(true);
clearInterval(refreshCountdown);
}
}, 1000);
}
function cleanupFormValues(node) {
if (node.type && node.type === 'checkbox') {
return { name: node.name, value: node.checked ? true : false };

View file

@ -1650,6 +1650,7 @@ bool DomainServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url
const QString URI_NODES = "/nodes";
const QString URI_SETTINGS = "/settings";
const QString URI_ENTITY_FILE_UPLOAD = "/content/upload";
const QString URI_RESTART = "/restart";
const QString UUID_REGEX_STRING = "[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}";
@ -1804,6 +1805,10 @@ bool DomainServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url
// send the response
connection->respond(HTTPConnection::StatusCode200, nodesDocument.toJson(), qPrintable(JSON_MIME_TYPE));
return true;
} else if (url.path() == URI_RESTART) {
connection->respond(HTTPConnection::StatusCode200);
restart();
return true;
} else {
// check if this is for json stats for a node

View file

@ -1,70 +1,25 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 19.2.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
version="1.1"
x="0px"
y="0px"
viewBox="0 0 50 50"
style="enable-background:new 0 0 50 50;"
xml:space="preserve"
id="svg2"
inkscape:version="0.91 r13725"
sodipodi:docname="mic-mute-a.svg"><metadata
id="metadata22"><rdf:RDF><cc:Work
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
id="defs20" /><sodipodi:namedview
pagecolor="#ff0000"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="852"
inkscape:window-height="480"
id="namedview18"
showgrid="false"
inkscape:zoom="4.72"
inkscape:cx="25"
inkscape:cy="25"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="0"
inkscape:current-layer="svg2" /><style
type="text/css"
id="style4">
.st0{fill:#FFFFFF;}
</style><g
id="Layer_2" /><g
id="Layer_1"
style="fill:#000000;fill-opacity:1"><path
class="st0"
d="M28.9,17.1v-0.5c0-2-1.7-3.6-3.7-3.6h0c-2,0-3.7,1.6-3.7,3.6v6.9L28.9,17.1z"
id="path8"
style="fill:#000000;fill-opacity:1" /><path
class="st0"
d="M21.5,29.2v0.2c0,2,1.6,3.6,3.7,3.6h0c2,0,3.7-1.6,3.7-3.6v-6.6L21.5,29.2z"
id="path10"
style="fill:#000000;fill-opacity:1" /><path
class="st0"
d="M39.1,16.8L13.6,39.1c-0.7,0.6-1.8,0.5-2.4-0.2l-0.2-0.2c-0.6-0.7-0.5-1.8,0.2-2.4l25.4-22.4 c0.7-0.6,1.8-0.5,2.4,0.2l0.2,0.2C39.8,15.1,39.7,16.1,39.1,16.8z"
id="path12"
style="fill:#000000;fill-opacity:1" /><path
class="st0"
d="M23.4,40.2l0,3.4h-4.3c-1,0-1.8,0.8-1.8,1.8c0,1,0.8,1.8,1.8,1.8h12.3c1,0,1.8-0.8,1.8-1.8 c0-1-0.8-1.8-1.8-1.8h-4.4l0-3.4c5.2-0.8,9.2-5,9.2-10.1c0-0.1,0-5.1,0-5.3c0-1-0.9-1.7-1.8-1.7c-1,0-1.7,0.8-1.7,1.8 c0,0.3,0,4.8,0,5.2c0,3.7-3.4,6.7-7.5,6.7c-3.6,0-6.7-2.3-7.3-5.4L15,34C16.4,37.2,19.6,39.7,23.4,40.2z"
id="path14"
style="fill:#000000;fill-opacity:1" /><path
class="st0"
d="M17.7,24.9c0-1-0.7-1.8-1.6-1.8c-1-0.1-1.8,0.7-1.9,1.6c0,0.2,0,4.2,0,5.3l3.5-3.1 C17.7,25.9,17.7,25,17.7,24.9z"
id="path16"
style="fill:#000000;fill-opacity:1" /></g></svg>
<svg version="1.1"
id="svg2" inkscape:version="0.91 r13725" sodipodi:docname="mic-mute-a.svg" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 50 50"
style="enable-background:new 0 0 50 50;" xml:space="preserve">
<style type="text/css">
.st0{fill:#EA4C5F;}
</style>
<sodipodi:namedview bordercolor="#666666" borderopacity="1" gridtolerance="10" guidetolerance="10" id="namedview18" inkscape:current-layer="svg2" inkscape:cx="25" inkscape:cy="25" inkscape:pageopacity="0" inkscape:pageshadow="2" inkscape:window-height="480" inkscape:window-maximized="0" inkscape:window-width="852" inkscape:window-x="0" inkscape:window-y="0" inkscape:zoom="4.72" objecttolerance="10" pagecolor="#ff0000" showgrid="false">
</sodipodi:namedview>
<g id="Layer_2">
</g>
<g id="Layer_1">
<path id="path8" class="st0" d="M28.9,17.1v-0.5c0-2-1.7-3.6-3.7-3.6l0,0c-2,0-3.7,1.6-3.7,3.6v6.9L28.9,17.1z"/>
<path id="path10" class="st0" d="M21.5,29.2v0.2c0,2,1.6,3.6,3.7,3.6l0,0c2,0,3.7-1.6,3.7-3.6v-6.6L21.5,29.2z"/>
<path id="path12" class="st0" d="M39.1,16.8L13.6,39.1c-0.7,0.6-1.8,0.5-2.4-0.2L11,38.7c-0.6-0.7-0.5-1.8,0.2-2.4l25.4-22.4
c0.7-0.6,1.8-0.5,2.4,0.2l0.2,0.2C39.8,15.1,39.7,16.1,39.1,16.8z"/>
<path id="path14" class="st0" d="M23.4,40.2v3.4h-4.3c-1,0-1.8,0.8-1.8,1.8s0.8,1.8,1.8,1.8h12.3c1,0,1.8-0.8,1.8-1.8
s-0.8-1.8-1.8-1.8H27v-3.4c5.2-0.8,9.2-5,9.2-10.1c0-0.1,0-5.1,0-5.3c0-1-0.9-1.7-1.8-1.7c-1,0-1.7,0.8-1.7,1.8c0,0.3,0,4.8,0,5.2
c0,3.7-3.4,6.7-7.5,6.7c-3.6,0-6.7-2.3-7.3-5.4L15,34C16.4,37.2,19.6,39.7,23.4,40.2z"/>
<path id="path16" class="st0" d="M17.7,24.9c0-1-0.7-1.8-1.6-1.8c-1-0.1-1.8,0.7-1.9,1.6c0,0.2,0,4.2,0,5.3l3.5-3.1
C17.7,25.9,17.7,25,17.7,24.9z"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 2.9 KiB

After

Width:  |  Height:  |  Size: 2.1 KiB

View file

@ -1,21 +1,25 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 19.2.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 50 50" style="enable-background:new 0 0 50 50;" xml:space="preserve">
<svg version="1.1"
id="svg2" inkscape:version="0.91 r13725" sodipodi:docname="mic-mute-a.svg" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 50 50"
style="enable-background:new 0 0 50 50;" xml:space="preserve">
<style type="text/css">
.st0{fill:#FFFFFF;}
.st0{fill:#EA4C5F;}
</style>
<sodipodi:namedview bordercolor="#666666" borderopacity="1" gridtolerance="10" guidetolerance="10" id="namedview18" inkscape:current-layer="svg2" inkscape:cx="25" inkscape:cy="25" inkscape:pageopacity="0" inkscape:pageshadow="2" inkscape:window-height="480" inkscape:window-maximized="0" inkscape:window-width="852" inkscape:window-x="0" inkscape:window-y="0" inkscape:zoom="4.72" objecttolerance="10" pagecolor="#ff0000" showgrid="false">
</sodipodi:namedview>
<g id="Layer_2">
</g>
<g id="Layer_1">
<path class="st0" d="M28.9,17.1v-0.5c0-2-1.7-3.6-3.7-3.6h0c-2,0-3.7,1.6-3.7,3.6v6.9L28.9,17.1z"/>
<path class="st0" d="M21.5,29.2v0.2c0,2,1.6,3.6,3.7,3.6h0c2,0,3.7-1.6,3.7-3.6v-6.6L21.5,29.2z"/>
<path class="st0" d="M39.1,16.8L13.6,39.1c-0.7,0.6-1.8,0.5-2.4-0.2l-0.2-0.2c-0.6-0.7-0.5-1.8,0.2-2.4l25.4-22.4
<path id="path8" class="st0" d="M28.9,17.1v-0.5c0-2-1.7-3.6-3.7-3.6l0,0c-2,0-3.7,1.6-3.7,3.6v6.9L28.9,17.1z"/>
<path id="path10" class="st0" d="M21.5,29.2v0.2c0,2,1.6,3.6,3.7,3.6l0,0c2,0,3.7-1.6,3.7-3.6v-6.6L21.5,29.2z"/>
<path id="path12" class="st0" d="M39.1,16.8L13.6,39.1c-0.7,0.6-1.8,0.5-2.4-0.2L11,38.7c-0.6-0.7-0.5-1.8,0.2-2.4l25.4-22.4
c0.7-0.6,1.8-0.5,2.4,0.2l0.2,0.2C39.8,15.1,39.7,16.1,39.1,16.8z"/>
<path class="st0" d="M23.4,40.2l0,3.4h-4.3c-1,0-1.8,0.8-1.8,1.8c0,1,0.8,1.8,1.8,1.8h12.3c1,0,1.8-0.8,1.8-1.8
c0-1-0.8-1.8-1.8-1.8h-4.4l0-3.4c5.2-0.8,9.2-5,9.2-10.1c0-0.1,0-5.1,0-5.3c0-1-0.9-1.7-1.8-1.7c-1,0-1.7,0.8-1.7,1.8
c0,0.3,0,4.8,0,5.2c0,3.7-3.4,6.7-7.5,6.7c-3.6,0-6.7-2.3-7.3-5.4L15,34C16.4,37.2,19.6,39.7,23.4,40.2z"/>
<path class="st0" d="M17.7,24.9c0-1-0.7-1.8-1.6-1.8c-1-0.1-1.8,0.7-1.9,1.6c0,0.2,0,4.2,0,5.3l3.5-3.1
<path id="path14" class="st0" d="M23.4,40.2v3.4h-4.3c-1,0-1.8,0.8-1.8,1.8s0.8,1.8,1.8,1.8h12.3c1,0,1.8-0.8,1.8-1.8
s-0.8-1.8-1.8-1.8H27v-3.4c5.2-0.8,9.2-5,9.2-10.1c0-0.1,0-5.1,0-5.3c0-1-0.9-1.7-1.8-1.7c-1,0-1.7,0.8-1.7,1.8c0,0.3,0,4.8,0,5.2
c0,3.7-3.4,6.7-7.5,6.7c-3.6,0-6.7-2.3-7.3-5.4L15,34C16.4,37.2,19.6,39.7,23.4,40.2z"/>
<path id="path16" class="st0" d="M17.7,24.9c0-1-0.7-1.8-1.6-1.8c-1-0.1-1.8,0.7-1.9,1.6c0,0.2,0,4.2,0,5.3l3.5-3.1
C17.7,25.9,17.7,25,17.7,24.9z"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 2.1 KiB

View file

@ -12,84 +12,21 @@ import QtQuick.Controls 1.3
import QtGraphicalEffects 1.0
import Qt.labs.settings 1.0
import "./hifi/audio" as HifiAudio
Hifi.AvatarInputs {
id: root
id: root;
objectName: "AvatarInputs"
width: rootWidth
height: controls.height
x: 10; y: 5
width: audio.width;
height: audio.height;
x: 10; y: 5;
readonly property int rootWidth: 265
readonly property int iconSize: 24
readonly property int iconPadding: 5
readonly property bool shouldReposition: true;
readonly property bool shouldReposition: true
Settings {
category: "Overlay.AvatarInputs"
property alias x: root.x
property alias y: root.y
}
MouseArea {
id: hover
hoverEnabled: true
drag.target: parent
anchors.fill: parent
}
Item {
id: controls
width: root.rootWidth
height: 44
visible: root.showAudioTools
Rectangle {
anchors.fill: parent
color: "#00000000"
Item {
id: audioMeter
anchors.verticalCenter: parent.verticalCenter
anchors.left: parent.left
anchors.leftMargin: root.iconPadding
anchors.right: parent.right
anchors.rightMargin: root.iconPadding
height: 8
Rectangle {
id: blueRect
color: "blue"
anchors.top: parent.top
anchors.bottom: parent.bottom
anchors.left: parent.left
width: parent.width / 4
}
Rectangle {
id: greenRect
color: "green"
anchors.top: parent.top
anchors.bottom: parent.bottom
anchors.left: blueRect.right
anchors.right: redRect.left
}
Rectangle {
id: redRect
color: "red"
anchors.top: parent.top
anchors.bottom: parent.bottom
anchors.right: parent.right
width: parent.width / 5
}
Rectangle {
z: 100
anchors.top: parent.top
anchors.bottom: parent.bottom
anchors.right: parent.right
width: (1.0 - root.audioLevel) * parent.width
color: "black"
}
}
}
HifiAudio.MicBar {
id: audio;
visible: root.showAudioTools;
standalone: true;
dragTarget: parent;
}
}

View file

@ -65,6 +65,12 @@ Windows.Window {
root.dynamicContent.fromScript(message);
}
}
function clearDebugWindow() {
if (root.dynamicContent && root.dynamicContent.clearWindow) {
root.dynamicContent.clearWindow();
}
}
// Handle message traffic from our loaded QML to the script that launched us
signal sendToScript(var message);

View file

@ -22,6 +22,7 @@ Original.CheckBox {
readonly property bool isLightColorScheme: colorScheme == hifi.colorSchemes.light
property bool isRedCheck: false
property int boxSize: 14
property bool wrap: true;
readonly property int boxRadius: 3
readonly property int checkSize: Math.max(boxSize - 8, 10)
readonly property int checkRadius: 2
@ -92,7 +93,8 @@ Original.CheckBox {
text: control.text
color: control.color
x: 2
wrapMode: Text.Wrap
wrapMode: checkBox.wrap ? Text.Wrap : Text.NoWrap
elide: checkBox.wrap ? Text.ElideNone : Text.ElideRight
enabled: checkBox.enabled
}
}

View file

@ -18,7 +18,7 @@ import QtQuick.Layouts 1.3
import "../../styles-uit"
import "../../controls-uit" as HifiControls
import "../../windows"
import "./" as Audio
import "./" as AudioControls
Rectangle {
id: root;
@ -52,33 +52,40 @@ Rectangle {
Separator { visible: root.showTitle() }
Grid {
columns: 2;
ColumnLayout {
x: 16; // padding does not work
spacing: 16;
Audio.CheckBox {
text: qsTr("Mute microphone");
checked: Audio.muted;
onClicked: {
Audio.muted = checked;
checked = Qt.binding(function() { return Audio.muted; }); // restore binding
// mute is in its own row
RowLayout {
AudioControls.CheckBox {
text: qsTr("Mute microphone");
isRedCheck: true;
checked: Audio.muted;
onClicked: {
Audio.muted = checked;
checked = Qt.binding(function() { return Audio.muted; }); // restore binding
}
}
}
Audio.CheckBox {
text: qsTr("Enable noise reduction");
checked: Audio.noiseReduction;
onClicked: {
Audio.noiseReduction = checked;
checked = Qt.binding(function() { return Audio.noiseReduction; }); // restore binding
RowLayout {
spacing: 16;
AudioControls.CheckBox {
text: qsTr("Enable noise reduction");
checked: Audio.noiseReduction;
onClicked: {
Audio.noiseReduction = checked;
checked = Qt.binding(function() { return Audio.noiseReduction; }); // restore binding
}
}
}
Audio.CheckBox {
text: qsTr("Show audio level meter");
checked: AvatarInputs.showAudioTools;
onClicked: {
AvatarInputs.showAudioTools = checked;
checked = Qt.binding(function() { return AvatarInputs.showAudioTools; }); // restore binding
AudioControls.CheckBox {
text: qsTr("Show audio level meter");
checked: AvatarInputs.showAudioTools;
onClicked: {
AvatarInputs.showAudioTools = checked;
checked = Qt.binding(function() { return AvatarInputs.showAudioTools; }); // restore binding
}
}
}
}
@ -110,12 +117,25 @@ Rectangle {
delegate: Item {
width: parent.width;
height: 36;
Audio.CheckBox {
text: display;
checked: selected;
onClicked: {
selected = checked;
checked = Qt.binding(function() { return selected; }); // restore binding
RowLayout {
width: parent.width;
AudioControls.CheckBox {
Layout.maximumWidth: parent.width - level.width - 40;
text: display;
wrap: false;
checked: selected;
onClicked: {
selected = checked;
checked = Qt.binding(function() { return selected; }); // restore binding
}
}
InputLevel {
id: level;
Layout.alignment: Qt.AlignRight;
Layout.rightMargin: 30;
visible: selected;
}
}
}
@ -124,17 +144,23 @@ Rectangle {
Separator {}
RowLayout {
HiFiGlyphs {
text: hifi.glyphs.unmuted;
color: hifi.colors.primaryHighlight;
anchors.verticalCenter: parent.verticalCenter;
size: 36;
}
RalewayRegular {
anchors.verticalCenter: parent.verticalCenter;
size: 16;
color: hifi.colors.lightGrayText;
text: qsTr("CHOOSE OUTPUT DEVICE");
Column {
RowLayout {
HiFiGlyphs {
text: hifi.glyphs.unmuted;
color: hifi.colors.primaryHighlight;
anchors.verticalCenter: parent.verticalCenter;
size: 36;
}
RalewayRegular {
anchors.verticalCenter: parent.verticalCenter;
size: 16;
color: hifi.colors.lightGrayText;
text: qsTr("CHOOSE OUTPUT DEVICE");
}
}
PlaySampleSound { anchors { left: parent.left; leftMargin: 60 }}
}
}
@ -148,7 +174,7 @@ Rectangle {
delegate: Item {
width: parent.width;
height: 36;
Audio.CheckBox {
AudioControls.CheckBox {
text: display;
checked: selected;
onClicked: {

View file

@ -0,0 +1,105 @@
//
// InputLevel.qml
// qml/hifi/audio
//
// Created by Zach Pomerantz on 6/20/2017
// Copyright 2017 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
import QtQuick 2.5
import QtQuick.Controls 1.4
import QtQuick.Layouts 1.3
import QtGraphicalEffects 1.0
Rectangle {
readonly property var level: Audio.inputLevel;
width: 70;
height: 8;
color: "transparent";
Item {
id: colors;
readonly property string muted: "#E2334D";
readonly property string gutter: "#575757";
readonly property string greenStart: "#39A38F";
readonly property string greenEnd: "#1FC6A6";
readonly property string red: colors.muted;
}
Text {
id: status;
anchors {
horizontalCenter: parent.horizontalCenter;
verticalCenter: parent.verticalCenter;
}
visible: Audio.muted;
color: colors.muted;
text: "MUTED";
font.pointSize: 10;
}
Item {
id: bar;
width: parent.width;
height: parent.height;
anchors { fill: parent }
visible: !status.visible;
Rectangle { // base
radius: 4;
anchors { fill: parent }
color: colors.gutter;
}
Rectangle { // mask
id: mask;
width: parent.width * level;
radius: 5;
anchors {
bottom: parent.bottom;
bottomMargin: 0;
top: parent.top;
topMargin: 0;
left: parent.left;
leftMargin: 0;
}
}
LinearGradient {
anchors { fill: mask }
source: mask
start: Qt.point(0, 0);
end: Qt.point(70, 0);
gradient: Gradient {
GradientStop {
position: 0;
color: colors.greenStart;
}
GradientStop {
position: 0.8;
color: colors.greenEnd;
}
GradientStop {
position: 0.801;
color: colors.red;
}
GradientStop {
position: 1;
color: colors.red;
}
}
}
}
}

View file

@ -0,0 +1,223 @@
//
// MicBar.qml
// qml/hifi/audio
//
// Created by Zach Pomerantz on 6/14/2017
// Copyright 2017 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
import QtQuick 2.5
import QtQuick.Controls 1.4
import QtQuick.Layouts 1.3
import QtGraphicalEffects 1.0
Rectangle {
readonly property var level: Audio.inputLevel;
property bool standalone: false;
property var dragTarget: null;
width: 240;
height: 50;
radius: 5;
color: "#00000000";
border {
width: (standalone || Audio.muted || mouseArea.containsMouse) ? 2 : 0;
color: colors.border;
}
// borders are painted over fill, so reduce the fill to fit inside the border
Rectangle {
color: standalone ? colors.fill : "#00000000";
width: 236;
height: 46;
radius: 5;
anchors {
verticalCenter: parent.verticalCenter;
horizontalCenter: parent.horizontalCenter;
}
}
MouseArea {
id: mouseArea;
anchors {
left: icon.left;
right: bar.right;
top: icon.top;
bottom: icon.bottom;
}
hoverEnabled: true;
scrollGestureEnabled: false;
onClicked: { Audio.muted = !Audio.muted; }
drag.target: dragTarget;
}
Item {
id: colors;
readonly property string unmuted: "#FFF";
readonly property string muted: "#E2334D";
readonly property string gutter: "#575757";
readonly property string greenStart: "#39A38F";
readonly property string greenEnd: "#1FC6A6";
readonly property string red: colors.muted;
readonly property string fill: "#55000000";
readonly property string border: standalone ? "#80FFFFFF" : "#55FFFFFF";
readonly property string icon: (Audio.muted && !mouseArea.containsMouse) ? muted : unmuted;
}
Item {
id: icon;
anchors {
left: parent.left;
leftMargin: 5;
verticalCenter: parent.verticalCenter;
}
width: 40;
height: 40;
Item {
Image {
readonly property string unmutedIcon: "../../../icons/tablet-icons/mic-unmute-i.svg";
readonly property string mutedIcon: "../../../icons/tablet-icons/mic-mute-i.svg";
function exclusiveOr(a, b) { return (a || b) && !(a && b); }
id: image;
source: exclusiveOr(Audio.muted, mouseArea.containsMouse) ? mutedIcon : unmutedIcon;
width: 30;
height: 30;
anchors {
left: parent.left;
leftMargin: 5;
top: parent.top;
topMargin: 5;
}
}
ColorOverlay {
anchors { fill: image }
source: image;
color: colors.icon;
}
}
}
Item {
id: status;
readonly property string color: (Audio.muted && !mouseArea.containsMouse) ? colors.muted : colors.unmuted;
visible: Audio.muted || mouseArea.containsMouse;
anchors {
left: parent.left;
leftMargin: 50;
verticalCenter: parent.verticalCenter;
}
width: 170;
height: 8
Text {
anchors {
horizontalCenter: parent.horizontalCenter;
verticalCenter: parent.verticalCenter;
}
color: parent.color;
text: Audio.muted ? (mouseArea.containsMouse ? "UNMUTE" : "MUTED") : "MUTE";
font.pointSize: 12;
}
Rectangle {
anchors {
left: parent.left;
verticalCenter: parent.verticalCenter;
}
width: 50;
height: 4;
color: parent.color;
}
Rectangle {
anchors {
right: parent.right;
verticalCenter: parent.verticalCenter;
}
width: 50;
height: 4;
color: parent.color;
}
}
Item {
id: bar;
visible: !status.visible;
anchors.fill: status;
width: status.width;
Rectangle { // base
radius: 4;
anchors { fill: parent }
color: colors.gutter;
}
Rectangle { // mask
id: mask;
width: parent.width * level;
radius: 5;
anchors {
bottom: parent.bottom;
bottomMargin: 0;
top: parent.top;
topMargin: 0;
left: parent.left;
leftMargin: 0;
}
}
LinearGradient {
anchors { fill: mask }
source: mask
start: Qt.point(0, 0);
end: Qt.point(170, 0);
gradient: Gradient {
GradientStop {
position: 0;
color: colors.greenStart;
}
GradientStop {
position: 0.8;
color: colors.greenEnd;
}
GradientStop {
position: 0.81;
color: colors.red;
}
GradientStop {
position: 1;
color: colors.red;
}
}
}
}
}

View file

@ -0,0 +1,86 @@
//
// PlaySampleSound.qml
// qml/hifi/audio
//
// Created by Zach Pomerantz on 6/13/2017
// Copyright 2017 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
import QtQuick 2.5
import QtQuick.Controls 1.4
import QtQuick.Controls.Styles 1.4
import QtQuick.Layouts 1.3
import "../../styles-uit"
import "../../controls-uit" as HifiControls
RowLayout {
property var sound: null;
property var sample: null;
property bool isPlaying: false;
function createSampleSound() {
var SOUND = Qt.resolvedUrl("../../../sounds/sample.wav");
sound = SoundCache.getSound(SOUND);
sample = null;
}
function playSound() {
// FIXME: MyAvatar is not properly exposed to QML; MyAvatar.qmlPosition is a stopgap
// FIXME: Audio.playSystemSound should not require position
sample = Audio.playSystemSound(sound, MyAvatar.qmlPosition);
isPlaying = true;
sample.finished.connect(function() { isPlaying = false; sample = null; });
}
function stopSound() {
sample && sample.stop();
}
Component.onCompleted: createSampleSound();
Component.onDestruction: stopSound();
onVisibleChanged: {
if (!visible) {
stopSound();
}
}
HifiConstants { id: hifi; }
Button {
style: ButtonStyle {
background: Rectangle {
implicitWidth: 20;
implicitHeight: 20;
radius: hifi.buttons.radius;
gradient: Gradient {
GradientStop {
position: 0.2;
color: isPlaying ? hifi.buttons.colorStart[hifi.buttons.blue] : hifi.buttons.colorStart[hifi.buttons.black];
}
GradientStop {
position: 1.0;
color: isPlaying ? hifi.buttons.colorFinish[hifi.buttons.blue] : hifi.buttons.colorFinish[hifi.buttons.black];
}
}
}
label: HiFiGlyphs {
// absolutely position due to asymmetry in glyph
x: isPlaying ? 0 : 1;
y: 1;
size: 14;
color: (control.pressed || control.hovered) ? (isPlaying ? "black" : hifi.colors.primaryHighlight) : "white";
text: isPlaying ? hifi.glyphs.stop_square : hifi.glyphs.playback_play;
}
}
onClicked: isPlaying ? stopSound() : playSound();
}
RalewayRegular {
Layout.leftMargin: 2;
size: 14;
color: "white";
text: isPlaying ? qsTr("Stop sample sound") : qsTr("Play sample sound");
}
}

View file

@ -1,20 +1,16 @@
import QtQuick 2.5
import QtGraphicalEffects 1.0
import "../../styles-uit"
import "../audio" as HifiAudio
Item {
id: tablet
objectName: "tablet"
property double micLevel: 0.8
property int rowIndex: 0
property int columnIndex: 0
property int count: (flowMain.children.length - 1)
// called by C++ code to keep audio bar updated
function setMicLevel(newMicLevel) {
tablet.micLevel = newMicLevel;
}
// used to look up a button by its uuid
function findButtonIndex(uuid) {
if (!uuid) {
@ -83,6 +79,16 @@ Item {
Rectangle {
id: bgTopBar
height: 90
anchors {
top: parent.top
topMargin: 0
left: parent.left
leftMargin: 0
right: parent.right
rightMargin: 0
}
gradient: Gradient {
GradientStop {
position: 0
@ -94,108 +100,13 @@ Item {
color: "#1e1e1e"
}
}
anchors.right: parent.right
anchors.rightMargin: 0
anchors.left: parent.left
anchors.leftMargin: 0
anchors.topMargin: 0
anchors.top: parent.top
Item {
id: audioIcon
anchors.verticalCenter: parent.verticalCenter
width: 40
height: 40
anchors.left: parent.left
anchors.leftMargin: 5
Image {
id: micIcon
source: "../../../icons/tablet-icons/mic.svg"
}
Item {
visible: (Audio.muted && !toggleMuteMouseArea.containsMouse)
|| (!Audio.muted && toggleMuteMouseArea.containsMouse)
Image {
id: muteIcon
source: "../../../icons/tablet-icons/mic-mute.svg"
}
ColorOverlay {
anchors.fill: muteIcon
source: muteIcon
color: toggleMuteMouseArea.containsMouse ? "#a0a0a0" : "#ff0000"
}
}
}
Item {
id: audioBar
width: 170
height: 10
anchors.left: parent.left
anchors.leftMargin: 50
anchors.verticalCenter: parent.verticalCenter
Rectangle {
id: audioBarBase
color: "#333333"
radius: 5
anchors.fill: parent
}
Rectangle {
id: audioBarMask
width: parent.width * tablet.micLevel
color: "#333333"
radius: 5
anchors.bottom: parent.bottom
anchors.bottomMargin: 0
anchors.top: parent.top
anchors.topMargin: 0
anchors.left: parent.left
anchors.leftMargin: 0
}
LinearGradient {
anchors.fill: audioBarMask
source: audioBarMask
start: Qt.point(0, 0)
end: Qt.point(170, 0)
gradient: Gradient {
GradientStop {
position: 0
color: "#2c8e72"
}
GradientStop {
position: 0.8
color: "#1fc6a6"
}
GradientStop {
position: 0.81
color: "#ea4c5f"
}
GradientStop {
position: 1
color: "#ea4c5f"
}
}
}
}
MouseArea {
id: toggleMuteMouseArea
HifiAudio.MicBar {
anchors {
left: audioIcon.left
right: audioBar.right
top: audioIcon.top
bottom: audioIcon.bottom
left: parent.left
leftMargin: 30
verticalCenter: parent.verticalCenter
}
hoverEnabled: true
preventStealing: true
propagateComposedEvents: false
scrollGestureEnabled: false
onClicked: { Audio.muted = !Audio.muted }
}
RalewaySemiBold {
@ -254,27 +165,6 @@ Item {
}
}
states: [
State {
name: "muted state"
PropertyChanges {
target: muteText
text: "UNMUTE"
}
PropertyChanges {
target: muteIcon
visible: !Audio.muted
}
PropertyChanges {
target: tablet
micLevel: 0
}
}
]
function setCurrentItemState(state) {
var index = rowIndex + columnIndex;

View file

@ -333,5 +333,7 @@ Item {
readonly property string vol_x_2: "\ue015"
readonly property string vol_x_3: "\ue016"
readonly property string vol_x_4: "\ue017"
readonly property string playback_play: "\ue01d"
readonly property string stop_square: "\ue01e"
}
}

Binary file not shown.

View file

@ -88,6 +88,7 @@
#include <UserActivityLoggerScriptingInterface.h>
#include <LogHandler.h>
#include "LocationBookmarks.h"
#include <LocationScriptingInterface.h>
#include <MainWindow.h>
#include <MappingRequest.h>
#include <MessagesClient.h>
@ -160,7 +161,6 @@
#include "scripting/DesktopScriptingInterface.h"
#include "scripting/GlobalServicesScriptingInterface.h"
#include "scripting/HMDScriptingInterface.h"
#include "scripting/LocationScriptingInterface.h"
#include "scripting/MenuScriptingInterface.h"
#include "scripting/SettingsScriptingInterface.h"
#include "scripting/WindowScriptingInterface.h"
@ -5566,6 +5566,7 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEngine* scri
connect(scriptEngine, &ScriptEngine::errorMessage, DependencyManager::get<ScriptEngines>().data(), &ScriptEngines::onErrorMessage);
connect(scriptEngine, &ScriptEngine::warningMessage, DependencyManager::get<ScriptEngines>().data(), &ScriptEngines::onWarningMessage);
connect(scriptEngine, &ScriptEngine::infoMessage, DependencyManager::get<ScriptEngines>().data(), &ScriptEngines::onInfoMessage);
connect(scriptEngine, &ScriptEngine::clearDebugWindow, DependencyManager::get<ScriptEngines>().data(), &ScriptEngines::onClearDebugWindow);
}
bool Application::canAcceptURL(const QString& urlString) const {

View file

@ -87,9 +87,10 @@ const float MyAvatar::ZOOM_MAX = 25.0f;
const float MyAvatar::ZOOM_DEFAULT = 1.5f;
// default values, used when avatar is missing joints... (avatar space)
// static const glm::quat DEFAULT_AVATAR_MIDDLE_EYE_ROT { Quaternions::Y_180 };
static const glm::quat DEFAULT_AVATAR_MIDDLE_EYE_ROT { Quaternions::Y_180 };
static const glm::vec3 DEFAULT_AVATAR_MIDDLE_EYE_POS { 0.0f, 0.6f, 0.0f };
static const glm::vec3 DEFAULT_AVATAR_HEAD_POS { 0.0f, 0.53f, 0.0f };
static const glm::quat DEFAULT_AVATAR_HEAD_ROT { Quaternions::Y_180 };
static const glm::vec3 DEFAULT_AVATAR_RIGHTARM_POS { -0.134824f, 0.396348f, -0.0515777f };
static const glm::quat DEFAULT_AVATAR_RIGHTARM_ROT { -0.536241f, 0.536241f, -0.460918f, -0.460918f };
static const glm::vec3 DEFAULT_AVATAR_LEFTARM_POS { 0.134795f, 0.396349f, -0.0515881f };
@ -100,7 +101,9 @@ static const glm::vec3 DEFAULT_AVATAR_LEFTHAND_POS { 0.727588f, 0.39635f, -0.051
static const glm::quat DEFAULT_AVATAR_LEFTHAND_ROT { -0.479181f, -0.52001f, 0.52254f, -0.476369f };
static const glm::vec3 DEFAULT_AVATAR_NECK_POS { 0.0f, 0.445f, 0.025f };
static const glm::vec3 DEFAULT_AVATAR_SPINE2_POS { 0.0f, 0.32f, 0.02f };
static const glm::quat DEFAULT_AVATAR_SPINE2_ROT { Quaternions::Y_180 };
static const glm::vec3 DEFAULT_AVATAR_HIPS_POS { 0.0f, 0.0f, 0.0f };
static const glm::quat DEFAULT_AVATAR_HIPS_ROT { Quaternions::Y_180 };
static const glm::vec3 DEFAULT_AVATAR_LEFTFOOT_POS { -0.08f, -0.96f, 0.029f};
static const glm::quat DEFAULT_AVATAR_LEFTFOOT_ROT { -0.40167322754859924f, 0.9154590368270874f, -0.005437685176730156f, -0.023744143545627594f };
static const glm::vec3 DEFAULT_AVATAR_RIGHTFOOT_POS { 0.08f, -0.96f, 0.029f };
@ -2764,7 +2767,7 @@ void MyAvatar::FollowHelper::prePhysicsUpdate(MyAvatar& myAvatar, const glm::mat
const glm::mat4& currentBodyMatrix, bool hasDriveInput) {
if (myAvatar.getHMDLeanRecenterEnabled()) {
if (!isActive(Rotation) && shouldActivateRotation(myAvatar, desiredBodyMatrix, currentBodyMatrix)) {
if (!isActive(Rotation) && (shouldActivateRotation(myAvatar, desiredBodyMatrix, currentBodyMatrix) || hasDriveInput)) {
activate(Rotation);
}
if (!isActive(Horizontal) && shouldActivateHorizontal(myAvatar, desiredBodyMatrix, currentBodyMatrix)) {
@ -2966,7 +2969,7 @@ glm::mat4 MyAvatar::getCenterEyeCalibrationMat() const {
auto centerEyeRot = Quaternions::Y_180;
return createMatFromQuatAndPos(centerEyeRot, centerEyePos);
} else {
return createMatFromQuatAndPos(DEFAULT_AVATAR_MIDDLE_EYE_POS, DEFAULT_AVATAR_MIDDLE_EYE_POS);
return createMatFromQuatAndPos(DEFAULT_AVATAR_MIDDLE_EYE_ROT, DEFAULT_AVATAR_MIDDLE_EYE_POS);
}
}
@ -2978,7 +2981,7 @@ glm::mat4 MyAvatar::getHeadCalibrationMat() const {
auto headRot = getAbsoluteDefaultJointRotationInObjectFrame(headIndex);
return createMatFromQuatAndPos(headRot, headPos);
} else {
return createMatFromQuatAndPos(DEFAULT_AVATAR_HEAD_POS, DEFAULT_AVATAR_HEAD_POS);
return createMatFromQuatAndPos(DEFAULT_AVATAR_HEAD_ROT, DEFAULT_AVATAR_HEAD_POS);
}
}
@ -2990,7 +2993,7 @@ glm::mat4 MyAvatar::getSpine2CalibrationMat() const {
auto spine2Rot = getAbsoluteDefaultJointRotationInObjectFrame(spine2Index);
return createMatFromQuatAndPos(spine2Rot, spine2Pos);
} else {
return createMatFromQuatAndPos(DEFAULT_AVATAR_SPINE2_POS, DEFAULT_AVATAR_SPINE2_POS);
return createMatFromQuatAndPos(DEFAULT_AVATAR_SPINE2_ROT, DEFAULT_AVATAR_SPINE2_POS);
}
}
@ -3002,7 +3005,7 @@ glm::mat4 MyAvatar::getHipsCalibrationMat() const {
auto hipsRot = getAbsoluteDefaultJointRotationInObjectFrame(hipsIndex);
return createMatFromQuatAndPos(hipsRot, hipsPos);
} else {
return createMatFromQuatAndPos(DEFAULT_AVATAR_HIPS_POS, DEFAULT_AVATAR_HIPS_POS);
return createMatFromQuatAndPos(DEFAULT_AVATAR_HIPS_ROT, DEFAULT_AVATAR_HIPS_POS);
}
}

View file

@ -56,6 +56,7 @@ class MyAvatar : public Avatar {
*
* @namespace MyAvatar
* @augments Avatar
* @property qmlPosition {Vec3} Used as a stopgap for position access by QML, as glm::vec3 is unavailable outside of scripts
* @property shouldRenderLocally {bool} Set it to true if you would like to see MyAvatar in your local interface,
* and false if you would not like to see MyAvatar in your local interface.
* @property motorVelocity {Vec3} Can be used to move the avatar with this velocity.
@ -101,6 +102,10 @@ class MyAvatar : public Avatar {
* "scripts/system/controllers/toggleAdvancedMovementForHandControllers.js".
*/
// FIXME: `glm::vec3 position` is not accessible from QML, so this exposes position in a QML-native type
Q_PROPERTY(QVector3D qmlPosition READ getQmlPosition)
QVector3D getQmlPosition() { auto p = getPosition(); return QVector3D(p.x, p.y, p.z); }
Q_PROPERTY(bool shouldRenderLocally READ getShouldRenderLocally WRITE setShouldRenderLocally)
Q_PROPERTY(glm::vec3 motorVelocity READ getScriptedMotorVelocity WRITE setScriptedMotorVelocity)
Q_PROPERTY(float motorTimescale READ getScriptedMotorTimescale WRITE setScriptedMotorTimescale)

View file

@ -23,9 +23,33 @@ QString Audio::HMD { "VR" };
Setting::Handle<bool> enableNoiseReductionSetting { QStringList { Audio::AUDIO, "NoiseReduction" }, true };
float Audio::loudnessToLevel(float loudness) {
const float LOG2 = log(2.0f);
const float METER_LOUDNESS_SCALE = 2.8f / 5.0f;
const float LOG2_LOUDNESS_FLOOR = 11.0f;
float level = 0.0f;
loudness += 1.0f;
float log2loudness = logf(loudness) / LOG2;
if (log2loudness <= LOG2_LOUDNESS_FLOOR) {
level = (log2loudness / LOG2_LOUDNESS_FLOOR) * METER_LOUDNESS_SCALE;
} else {
level = (log2loudness - (LOG2_LOUDNESS_FLOOR - 1.0f)) * METER_LOUDNESS_SCALE;
}
if (level > 1.0f) {
level = 1.0;
}
return level;
}
Audio::Audio() : _devices(_contextIsHMD) {
auto client = DependencyManager::get<AudioClient>();
connect(client.data(), &AudioClient::muteToggled, this, &Audio::onMutedChanged);
auto client = DependencyManager::get<AudioClient>().data();
connect(client, &AudioClient::muteToggled, this, &Audio::onMutedChanged);
connect(client, &AudioClient::inputLoudnessChanged, this, &Audio::onInputLoudnessChanged);
connect(this, &Audio::contextChanged, &_devices, &AudioDevices::onContextChanged);
connect(&_devices._inputs, &AudioDeviceList::deviceChanged, this, &Audio::onInputChanged);
enableNoiseReduction(enableNoiseReductionSetting.get());
@ -88,6 +112,15 @@ void Audio::onInputChanged() {
}
}
void Audio::onInputLoudnessChanged(float loudness) {
float level = loudnessToLevel(loudness);
if (_inputLevel != level) {
_inputLevel = level;
emit inputLevelChanged(_inputLevel);
}
}
QString Audio::getContext() const {
return _contextIsHMD ? Audio::HMD : Audio::DESKTOP;
}

View file

@ -26,6 +26,7 @@ class Audio : public AudioScriptingInterface {
Q_PROPERTY(bool muted READ isMuted WRITE setMuted NOTIFY mutedChanged)
Q_PROPERTY(bool noiseReduction READ noiseReductionEnabled WRITE enableNoiseReduction NOTIFY noiseReductionChanged)
Q_PROPERTY(float inputVolume READ getInputVolume WRITE setInputVolume NOTIFY inputVolumeChanged)
Q_PROPERTY(float inputLevel READ getInputLevel NOTIFY inputLevelChanged)
Q_PROPERTY(QString context READ getContext NOTIFY contextChanged)
Q_PROPERTY(AudioDevices* devices READ getDevices NOTIFY nop)
@ -34,11 +35,14 @@ public:
static QString HMD;
static QString DESKTOP;
static float loudnessToLevel(float loudness);
virtual ~Audio() {}
bool isMuted() const { return _isMuted; }
bool noiseReductionEnabled() const { return _enableNoiseReduction; }
float getInputVolume() const { return _inputVolume; }
float getInputLevel() const { return _inputLevel; }
QString getContext() const;
void setMuted(bool muted);
@ -54,12 +58,14 @@ signals:
void mutedChanged(bool isMuted);
void noiseReductionChanged(bool isEnabled);
void inputVolumeChanged(float volume);
void inputLevelChanged(float level);
void contextChanged(const QString& context);
public slots:
void onMutedChanged();
void onContextChanged();
void onInputChanged();
void onInputLoudnessChanged(float loudness);
protected:
// Audio must live on a separate thread from AudioClient to avoid deadlocks
@ -68,6 +74,7 @@ protected:
private:
float _inputVolume { 1.0f };
float _inputLevel { 0.0f };
bool _isMuted { false };
bool _enableNoiseReduction;
bool _contextIsHMD { false };

View file

@ -45,15 +45,6 @@ AvatarInputs::AvatarInputs(QQuickItem* parent) : QQuickItem(parent) {
} \
}
#define AI_UPDATE_FLOAT(name, src, epsilon) \
{ \
float val = src; \
if (fabsf(_##name - val) >= epsilon) { \
_##name = val; \
emit name##Changed(); \
} \
}
float AvatarInputs::loudnessToAudioLevel(float loudness) {
const float AUDIO_METER_AVERAGING = 0.5;
const float LOG2 = log(2.0f);
@ -85,27 +76,6 @@ void AvatarInputs::update() {
AI_UPDATE(cameraEnabled, !Menu::getInstance()->isOptionChecked(MenuOption::NoFaceTracking));
AI_UPDATE(cameraMuted, Menu::getInstance()->isOptionChecked(MenuOption::MuteFaceTracking));
AI_UPDATE(isHMD, qApp->isHMDMode());
auto audioIO = DependencyManager::get<AudioClient>();
const float audioLevel = loudnessToAudioLevel(DependencyManager::get<AudioClient>()->getLastInputLoudness());
AI_UPDATE_FLOAT(audioLevel, audioLevel, 0.01f);
AI_UPDATE(audioClipping, ((audioIO->getTimeSinceLastClip() > 0.0f) && (audioIO->getTimeSinceLastClip() < 1.0f)));
AI_UPDATE(audioMuted, audioIO->isMuted());
//// Make muted icon pulsate
//static const float PULSE_MIN = 0.4f;
//static const float PULSE_MAX = 1.0f;
//static const float PULSE_FREQUENCY = 1.0f; // in Hz
//qint64 now = usecTimestampNow();
//if (now - _iconPulseTimeReference > (qint64)USECS_PER_SECOND) {
// // Prevents t from getting too big, which would diminish glm::cos precision
// _iconPulseTimeReference = now - ((now - _iconPulseTimeReference) % USECS_PER_SECOND);
//}
//float t = (float)(now - _iconPulseTimeReference) / (float)USECS_PER_SECOND;
//float pulseFactor = (glm::cos(t * PULSE_FREQUENCY * 2.0f * PI) + 1.0f) / 2.0f;
//iconColor = PULSE_MIN + (PULSE_MAX - PULSE_MIN) * pulseFactor;
}
void AvatarInputs::setShowAudioTools(bool showAudioTools) {
@ -124,10 +94,6 @@ void AvatarInputs::toggleCameraMute() {
}
}
void AvatarInputs::toggleAudioMute() {
DependencyManager::get<AudioClient>()->toggleMute();
}
void AvatarInputs::resetSensors() {
qApp->resetSensors();
}

View file

@ -25,9 +25,6 @@ class AvatarInputs : public QQuickItem {
AI_PROPERTY(bool, cameraEnabled, false)
AI_PROPERTY(bool, cameraMuted, false)
AI_PROPERTY(bool, audioMuted, false)
AI_PROPERTY(bool, audioClipping, false)
AI_PROPERTY(float, audioLevel, 0)
AI_PROPERTY(bool, isHMD, false)
Q_PROPERTY(bool showAudioTools READ showAudioTools WRITE setShowAudioTools NOTIFY showAudioToolsChanged)
@ -45,16 +42,12 @@ public slots:
signals:
void cameraEnabledChanged();
void cameraMutedChanged();
void audioMutedChanged();
void audioClippingChanged();
void audioLevelChanged();
void isHMDChanged();
void showAudioToolsChanged(bool show);
protected:
Q_INVOKABLE void resetSensors();
Q_INVOKABLE void toggleCameraMute();
Q_INVOKABLE void toggleAudioMute();
private:
float _trailingAudioLoudness{ 0 };

View file

@ -51,6 +51,7 @@
#include "avatar/AvatarManager.h"
#include "scripting/GlobalServicesScriptingInterface.h"
#include "ui/Snapshot.h"
#include "SoundCache.h"
static const float DPI = 30.47f;
static const float INCHES_TO_METERS = 1.0f / 39.3701f;
@ -199,6 +200,7 @@ void Web3DOverlay::loadSourceURL() {
_webSurface->getSurfaceContext()->setContextProperty("GlobalServices", GlobalServicesScriptingInterface::getInstance());
_webSurface->getSurfaceContext()->setContextProperty("AvatarList", DependencyManager::get<AvatarManager>().data());
_webSurface->getSurfaceContext()->setContextProperty("DialogsManager", DialogsManagerScriptingInterface::getInstance());
_webSurface->getSurfaceContext()->setContextProperty("SoundCache", DependencyManager::get<SoundCache>().data());
_webSurface->getSurfaceContext()->setContextProperty("pathToFonts", "../../");
tabletScriptingInterface->setQmlTabletRoot("com.highfidelity.interface.tablet.system", _webSurface->getRootItem(), _webSurface.data());

View file

@ -1023,6 +1023,8 @@ void AudioClient::handleAudioInput(QByteArray& audioBuffer) {
emit inputReceived(audioBuffer);
}
emit inputLoudnessChanged(_lastInputLoudness);
// state machine to detect gate opening and closing
bool audioGateOpen = (_lastInputLoudness != 0.0f);
bool openedInLastBlock = !_audioGateOpen && audioGateOpen; // the gate just opened

View file

@ -210,6 +210,7 @@ signals:
bool muteToggled();
void mutedByMixer();
void inputReceived(const QByteArray& inputSamples);
void inputLoudnessChanged(float loudness);
void outputBytesToNetwork(int numBytes);
void inputBytesFromNetwork(int numBytes);
void noiseGateOpened();

View file

@ -386,7 +386,13 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef
return 0;
}
qint64 clockSkew = args.sourceNode ? args.sourceNode->getClockSkewUsec() : 0;
int64_t clockSkew = 0;
uint64_t maxPingRoundTrip = 33333; // two frames periods at 60 fps
if (args.sourceNode) {
clockSkew = args.sourceNode->getClockSkewUsec();
const float MSECS_PER_USEC = 1000;
maxPingRoundTrip += args.sourceNode->getPingMs() * MSECS_PER_USEC;
}
BufferParser parser(data, bytesLeftToRead);
@ -653,7 +659,6 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef
const QUuid& myNodeID = nodeList->getSessionUUID();
bool weOwnSimulation = _simulationOwner.matchesValidID(myNodeID);
// pack SimulationOwner and terse update properties near each other
// NOTE: the server is authoritative for changes to simOwnerID so we always unpack ownership data
// even when we would otherwise ignore the rest of the packet.
@ -678,7 +683,7 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef
if (newSimOwner.getID().isNull() && !_simulationOwner.pendingRelease(lastEditedFromBufferAdjusted)) {
// entity-server is trying to clear our ownership (probably at our own request)
// but we actually want to own it, therefore we ignore this clear event
// and pretend that we own it (we assume we'll recover it soon)
// and pretend that we own it (e.g. we assume we'll receive ownership soon)
// However, for now, when the server uses a newer time than what we sent, listen to what we're told.
if (overwriteLocalData) {
@ -690,16 +695,19 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef
// recompute weOwnSimulation for later
weOwnSimulation = _simulationOwner.matchesValidID(myNodeID);
}
} else if (newSimOwner.getID().isNull() && _simulationOwner.pendingTake(lastEditedFromBufferAdjusted)) {
// entity-server is trying to clear someone else's ownership
// but we want to own it, therefore we ignore this clear event
// and pretend that we own it (we assume we'll get it soon)
} else if (_simulationOwner.pendingTake(now - maxPingRoundTrip)) {
// we sent a bid before this packet could have been sent from the server
// so we ignore it and pretend we own the object's simulation
weOwnSimulation = true;
if (!_simulationOwner.isNull()) {
// someone else really did own it
markDirtyFlags(Simulation::DIRTY_SIMULATOR_ID);
somethingChanged = true;
_simulationOwner.clearCurrentOwner();
if (newSimOwner.getID().isNull()) {
// entity-server is trying to clear someone else's ownership
// but we want to own it, therefore we ignore this clear event
if (!_simulationOwner.isNull()) {
// someone else really did own it
markDirtyFlags(Simulation::DIRTY_SIMULATOR_ID);
somethingChanged = true;
_simulationOwner.clearCurrentOwner();
}
}
} else if (newSimOwner.matchesValidID(myNodeID) && !_hasBidOnSimulation) {
// entity-server tells us that we have simulation ownership while we never requested this for this EntityItem,

View file

@ -24,9 +24,10 @@ const int KTXCache::INVALID_VERSION = 0x00;
const char* KTXCache::SETTING_VERSION_NAME = "hifi.ktx.cache_version";
KTXCache::KTXCache(const std::string& dir, const std::string& ext) :
FileCache(dir, ext) {
initialize();
FileCache(dir, ext) { }
void KTXCache::initialize() {
FileCache::initialize();
Setting::Handle<int> cacheVersionHandle(SETTING_VERSION_NAME, INVALID_VERSION);
auto cacheVersion = cacheVersionHandle.get();
if (cacheVersion != CURRENT_VERSION) {
@ -35,20 +36,9 @@ KTXCache::KTXCache(const std::string& dir, const std::string& ext) :
}
}
KTXFilePointer KTXCache::writeFile(const char* data, Metadata&& metadata) {
FilePointer file = FileCache::writeFile(data, std::move(metadata), true);
return std::static_pointer_cast<KTXFile>(file);
}
KTXFilePointer KTXCache::getFile(const Key& key) {
return std::static_pointer_cast<KTXFile>(FileCache::getFile(key));
}
std::unique_ptr<File> KTXCache::createFile(Metadata&& metadata, const std::string& filepath) {
qCInfo(file_cache) << "Wrote KTX" << metadata.key.c_str();
return std::unique_ptr<File>(new KTXFile(std::move(metadata), filepath));
return FileCache::createFile(std::move(metadata), filepath);
}
KTXFile::KTXFile(Metadata&& metadata, const std::string& filepath) :
cache::File(std::move(metadata), filepath) {}

View file

@ -35,20 +35,10 @@ public:
KTXCache(const std::string& dir, const std::string& ext);
KTXFilePointer writeFile(const char* data, Metadata&& metadata);
KTXFilePointer getFile(const Key& key);
void initialize() override;
protected:
std::unique_ptr<cache::File> createFile(Metadata&& metadata, const std::string& filepath) override final;
};
class KTXFile : public cache::File {
Q_OBJECT
protected:
friend class KTXCache;
KTXFile(Metadata&& metadata, const std::string& filepath);
};
#endif // hifi_KTXCache_h

View file

@ -58,8 +58,8 @@ static const QUrl SPECTATOR_CAMERA_FRAME_URL("resource://spectatorCameraFrame");
static const float SKYBOX_LOAD_PRIORITY { 10.0f }; // Make sure skybox loads first
static const float HIGH_MIPS_LOAD_PRIORITY { 9.0f }; // Make sure high mips loads after skybox but before models
TextureCache::TextureCache() :
_ktxCache(KTX_DIRNAME, KTX_EXT) {
TextureCache::TextureCache() {
_ktxCache->initialize();
setUnusedResourceCacheSize(0);
setObjectName("TextureCache");
}
@ -718,7 +718,7 @@ void NetworkTexture::handleFinishedInitialLoad() {
gpu::TexturePointer texture = textureCache->getTextureByHash(hash);
if (!texture) {
KTXFilePointer ktxFile = textureCache->_ktxCache.getFile(hash);
auto ktxFile = textureCache->_ktxCache->getFile(hash);
if (ktxFile) {
texture = gpu::Texture::unserialize(ktxFile);
if (texture) {
@ -742,9 +742,9 @@ void NetworkTexture::handleFinishedInitialLoad() {
// Move ktx to file
const char* data = reinterpret_cast<const char*>(memKtx->_storage->data());
size_t length = memKtx->_storage->size();
KTXFilePointer file;
cache::FilePointer file;
auto& ktxCache = textureCache->_ktxCache;
if (!memKtx || !(file = ktxCache.writeFile(data, KTXCache::Metadata(filename, length)))) {
if (!memKtx || !(file = ktxCache->writeFile(data, KTXCache::Metadata(filename, length)))) {
qCWarning(modelnetworking) << url << " failed to write cache file";
QMetaObject::invokeMethod(resource.data(), "setImage",
Q_ARG(gpu::TexturePointer, nullptr),
@ -900,7 +900,7 @@ void ImageReader::read() {
// If there is no live texture, check if there's an existing KTX file
if (!texture) {
KTXFilePointer ktxFile = textureCache->_ktxCache.getFile(hash);
auto ktxFile = textureCache->_ktxCache->getFile(hash);
if (ktxFile) {
texture = gpu::Texture::unserialize(ktxFile);
if (texture) {
@ -950,7 +950,7 @@ void ImageReader::read() {
const char* data = reinterpret_cast<const char*>(memKtx->_storage->data());
size_t length = memKtx->_storage->size();
auto& ktxCache = textureCache->_ktxCache;
auto file = ktxCache.writeFile(data, KTXCache::Metadata(hash, length));
auto file = ktxCache->writeFile(data, KTXCache::Metadata(hash, length));
if (!file) {
qCWarning(modelnetworking) << _url << "file cache failed";
} else {

View file

@ -189,7 +189,7 @@ private:
static const std::string KTX_DIRNAME;
static const std::string KTX_EXT;
KTXCache _ktxCache;
std::shared_ptr<cache::FileCache> _ktxCache { std::make_shared<KTXCache>(KTX_DIRNAME, KTX_EXT) };
// Map from image hashes to texture weak pointers
std::unordered_map<std::string, std::weak_ptr<gpu::Texture>> _texturesByHashes;
std::mutex _texturesByHashesMutex;

View file

@ -353,13 +353,20 @@ void AssetClient::handleAssetGetReply(QSharedPointer<ReceivedMessage> message, S
connect(message.data(), &ReceivedMessage::progress, this, [this, weakNode, messageID, length](qint64 size) {
handleProgressCallback(weakNode, messageID, size, length);
});
connect(message.data(), &ReceivedMessage::completed, this, [this, weakNode, messageID]() {
handleCompleteCallback(weakNode, messageID);
connect(message.data(), &ReceivedMessage::completed, this, [this, weakNode, messageID, length]() {
handleCompleteCallback(weakNode, messageID, length);
});
if (message->isComplete()) {
disconnect(message.data(), nullptr, this, nullptr);
callbacks.completeCallback(true, error, message->readAll());
if (length != message->getBytesLeftToRead()) {
callbacks.completeCallback(false, error, QByteArray());
} else {
callbacks.completeCallback(true, error, message->readAll());
}
messageCallbackMap.erase(requestIt);
}
}
@ -391,7 +398,7 @@ void AssetClient::handleProgressCallback(const QWeakPointer<Node>& node, Message
callbacks.progressCallback(size, length);
}
void AssetClient::handleCompleteCallback(const QWeakPointer<Node>& node, MessageID messageID) {
void AssetClient::handleCompleteCallback(const QWeakPointer<Node>& node, MessageID messageID, DataOffset length) {
auto senderNode = node.toStrongRef();
if (!senderNode) {
@ -424,8 +431,7 @@ void AssetClient::handleCompleteCallback(const QWeakPointer<Node>& node, Message
return;
}
if (message->failed()) {
if (message->failed() || length != message->getBytesLeftToRead()) {
callbacks.completeCallback(false, AssetServerError::NoError, QByteArray());
} else {
callbacks.completeCallback(true, AssetServerError::NoError, message->readAll());

View file

@ -93,7 +93,7 @@ private:
bool cancelUploadAssetRequest(MessageID id);
void handleProgressCallback(const QWeakPointer<Node>& node, MessageID messageID, qint64 size, DataOffset length);
void handleCompleteCallback(const QWeakPointer<Node>& node, MessageID messageID);
void handleCompleteCallback(const QWeakPointer<Node>& node, MessageID messageID, DataOffset length);
void forceFailureOfPendingRequests(SharedNodePointer node);

View file

@ -104,12 +104,7 @@ void AssetRequest::start() {
break;
}
} else {
if (_byteRange.isSet()) {
// we had a byte range, the size of the data does not match what we expect, so we return an error
if (data.size() != _byteRange.size()) {
_error = SizeVerificationFailed;
}
} else if (hashData(data).toHex() != _hash) {
if (!_byteRange.isSet() && hashData(data).toHex() != _hash) {
// the hash of the received data does not match what we expect, so we return an error
_error = HashVerificationFailed;
}

View file

@ -1,6 +1,6 @@
//
// LocationScriptingInterface.cpp
// interface/src/scripting
// libraries/networking/src
//
// Created by Ryan Huffman on 4/29/14.
// Copyright 2014 High Fidelity, Inc.
@ -9,7 +9,7 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include <AddressManager.h>
#include "AddressManager.h"
#include "LocationScriptingInterface.h"

View file

@ -1,6 +1,6 @@
//
// LocationScriptingInterface.h
// interface/src/scripting
// libraries/networking/src
//
// Created by Ryan Huffman on 4/29/14.
// Copyright 2014 High Fidelity, Inc.

View file

@ -23,14 +23,16 @@ const uint16_t ObjectActionTractor::tractorVersion = 1;
ObjectActionTractor::ObjectActionTractor(const QUuid& id, EntityItemPointer ownerEntity) :
ObjectAction(DYNAMIC_TYPE_TRACTOR, id, ownerEntity),
_positionalTarget(glm::vec3(0.0f)),
_desiredPositionalTarget(glm::vec3(0.0f)),
_positionalTarget(0.0f),
_desiredPositionalTarget(0.0f),
_linearTimeScale(FLT_MAX),
_positionalTargetSet(true),
_rotationalTarget(glm::quat()),
_desiredRotationalTarget(glm::quat()),
_positionalTargetSet(false),
_rotationalTarget(),
_desiredRotationalTarget(),
_angularTimeScale(FLT_MAX),
_rotationalTargetSet(true) {
_rotationalTargetSet(true),
_linearVelocityTarget(0.0f)
{
#if WANT_DEBUG
qCDebug(physics) << "ObjectActionTractor::ObjectActionTractor";
#endif
@ -77,7 +79,6 @@ bool ObjectActionTractor::prepareForTractorUpdate(btScalar deltaTimeStep) {
glm::quat rotation;
glm::vec3 position;
glm::vec3 linearVelocity;
glm::vec3 angularVelocity;
bool linearValid = false;
@ -117,7 +118,6 @@ bool ObjectActionTractor::prepareForTractorUpdate(btScalar deltaTimeStep) {
linearValid = true;
linearTractorCount++;
position += positionForAction;
linearVelocity += linearVelocityForAction;
}
}
}
@ -126,9 +126,18 @@ bool ObjectActionTractor::prepareForTractorUpdate(btScalar deltaTimeStep) {
withWriteLock([&]{
if (linearValid && linearTractorCount > 0) {
position /= linearTractorCount;
linearVelocity /= linearTractorCount;
if (_positionalTargetSet) {
_lastPositionTarget = _positionalTarget;
} else {
_lastPositionTarget = position;
}
_positionalTarget = position;
_linearVelocityTarget = linearVelocity;
if (deltaTimeStep > EPSILON) {
// blend the new velocity with the old (low-pass filter)
glm::vec3 newVelocity = (1.0f / deltaTimeStep) * (position - _lastPositionTarget);
const float blend = 0.25f;
_linearVelocityTarget = (1.0f - blend) * _linearVelocityTarget + blend * newVelocity;
}
_positionalTargetSet = true;
_active = true;
}
@ -169,19 +178,19 @@ void ObjectActionTractor::updateActionWorker(btScalar deltaTimeStep) {
}
if (_linearTimeScale < MAX_TRACTOR_TIMESCALE) {
btVector3 targetVelocity(0.0f, 0.0f, 0.0f);
btVector3 offsetVelocity(0.0f, 0.0f, 0.0f);
btVector3 offset = rigidBody->getCenterOfMassPosition() - glmToBullet(_positionalTarget);
float offsetLength = offset.length();
if (offsetLength > FLT_EPSILON) {
float speed = glm::min(offsetLength / _linearTimeScale, TRACTOR_MAX_SPEED);
targetVelocity = (-speed / offsetLength) * offset;
offsetVelocity = (-speed / offsetLength) * offset;
if (speed > rigidBody->getLinearSleepingThreshold()) {
forceBodyNonStatic();
rigidBody->activate();
}
}
// this action is aggresively critically damped and defeats the current velocity
rigidBody->setLinearVelocity(targetVelocity);
rigidBody->setLinearVelocity(glmToBullet(_linearVelocityTarget) + offsetVelocity);
}
if (_angularTimeScale < MAX_TRACTOR_TIMESCALE) {

View file

@ -36,6 +36,7 @@ protected:
glm::vec3 _positionalTarget;
glm::vec3 _desiredPositionalTarget;
glm::vec3 _lastPositionTarget;
float _linearTimeScale;
bool _positionalTargetSet;

View file

@ -548,6 +548,7 @@ glm::ivec3 LightClusters::updateClusters() {
LightClusteringPass::LightClusteringPass() {
_lightClusters = std::make_shared<LightClusters>();
}
@ -566,12 +567,7 @@ void LightClusteringPass::run(const render::RenderContextPointer& renderContext,
auto deferredTransform = inputs.get0();
auto lightingModel = inputs.get1();
auto surfaceGeometryFramebuffer = inputs.get2();
if (!_lightClusters) {
_lightClusters = std::make_shared<LightClusters>();
}
// first update the Grid with the new frustum
if (!_freeze) {
_lightClusters->updateFrustum(args->getViewFrustum());

View file

@ -124,6 +124,11 @@ signals:
void dirtyEnabled();
};
class TConfigProxy {
public:
using Config = JobConfig;
};
class TaskConfig : public JobConfig {
Q_OBJECT
public:
@ -134,12 +139,37 @@ public:
TaskConfig() = default ;
TaskConfig(bool enabled) : JobConfig(enabled) {}
// Get a sub job config through task.getConfig(path)
// where path can be:
// - <job_name> search for the first job named job_name traversing the the sub graph of task and jobs (from this task as root)
// - <parent_name>.[<sub_parent_names>.]<job_name>
// Allowing to first look for the parent_name job (from this task as root) and then search from there for the
// optional sub_parent_names and finally from there looking for the job_name (assuming every job in the path were found)
//
// getter for qml integration, prefer the templated getter
Q_INVOKABLE QObject* getConfig(const QString& name) { return QObject::findChild<JobConfig*>(name); }
Q_INVOKABLE QObject* getConfig(const QString& name) { return getConfig<TConfigProxy>(name.toStdString()); }
// getter for cpp (strictly typed), prefer this getter
template <class T> typename T::Config* getConfig(std::string job = "") const {
QString name = job.empty() ? QString() : QString(job.c_str()); // an empty string is not a null string
return findChild<typename T::Config*>(name);
const TaskConfig* root = this;
QString path = (job.empty() ? QString() : QString(job.c_str())); // an empty string is not a null string
auto tokens = path.split('.', QString::SkipEmptyParts);
if (tokens.empty()) {
tokens.push_back(QString());
} else {
while (tokens.size() > 1) {
auto name = tokens.front();
tokens.pop_front();
root = QObject::findChild<TaskConfig*>(name);
if (!root) {
return nullptr;
}
}
}
return root->findChild<typename T::Config*>(tokens.front());
}
void connectChildConfig(QConfigPointer childConfig, const std::string& name);

View file

@ -11,6 +11,8 @@
#include "AudioScriptingInterface.h"
#include <QVector3D>
#include "ScriptAudioInjector.h"
#include "ScriptEngineLogging.h"
@ -19,6 +21,13 @@ void registerAudioMetaTypes(QScriptEngine* engine) {
qScriptRegisterMetaType(engine, soundSharedPointerToScriptValue, soundSharedPointerFromScriptValue);
}
ScriptAudioInjector* AudioScriptingInterface::playSystemSound(SharedSoundPointer sound, const QVector3D& position) {
AudioInjectorOptions options;
options.position = glm::vec3(position.x(), position.y(), position.z());
options.localOnly = true;
return playSound(sound, options);
}
ScriptAudioInjector* AudioScriptingInterface::playSound(SharedSoundPointer sound, const AudioInjectorOptions& injectorOptions) {
if (QThread::currentThread() != thread()) {
ScriptAudioInjector* injector = NULL;

View file

@ -30,8 +30,10 @@ public:
protected:
AudioScriptingInterface() {}
// this method is protected to stop C++ callers from calling, but invokable from script
// these methods are protected to stop C++ callers from calling, but invokable from script
Q_INVOKABLE ScriptAudioInjector* playSound(SharedSoundPointer sound, const AudioInjectorOptions& injectorOptions = AudioInjectorOptions());
// FIXME: there is no way to play a positionless sound
Q_INVOKABLE ScriptAudioInjector* playSystemSound(SharedSoundPointer sound, const QVector3D& position);
Q_INVOKABLE void setStereoInput(bool stereo);

View file

@ -0,0 +1,182 @@
//
// ConsoleScriptingInterface.cpp
// libraries/script-engine/src
//
// Created by NeetBhagat on 6/1/17.
// Copyright 2014 High Fidelity, Inc.
//
// ConsoleScriptingInterface is responsible for following functionality
// Printing logs with various tags and grouping on debug Window and Logs/log file.
// Debugging functionalities like Timer start-end, assertion, trace.
// To use these functionalities, use "console" object in Javascript files.
// For examples please refer "scripts/developer/tests/consoleObjectTest.js"
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "ConsoleScriptingInterface.h"
#include "ScriptEngine.h"
#define INDENTATION 4 // 1 Tab - 4 spaces
const QString LINE_SEPARATOR = "\n ";
const QString SPACE_SEPARATOR = " ";
const QString STACK_TRACE_FORMAT = "\n[Stacktrace]%1%2";
QList<QString> ConsoleScriptingInterface::_groupDetails = QList<QString>();
QScriptValue ConsoleScriptingInterface::info(QScriptContext* context, QScriptEngine* engine) {
if (ScriptEngine* scriptEngine = qobject_cast<ScriptEngine*>(engine)) {
scriptEngine->scriptInfoMessage(appendArguments(context));
}
return QScriptValue::NullValue;
}
QScriptValue ConsoleScriptingInterface::log(QScriptContext* context, QScriptEngine* engine) {
QString message = appendArguments(context);
if (_groupDetails.count() == 0) {
if (ScriptEngine* scriptEngine = qobject_cast<ScriptEngine*>(engine)) {
scriptEngine->scriptPrintedMessage(message);
}
} else {
logGroupMessage(message, engine);
}
return QScriptValue::NullValue;
}
QScriptValue ConsoleScriptingInterface::debug(QScriptContext* context, QScriptEngine* engine) {
if (ScriptEngine* scriptEngine = qobject_cast<ScriptEngine*>(engine)) {
scriptEngine->scriptPrintedMessage(appendArguments(context));
}
return QScriptValue::NullValue;
}
QScriptValue ConsoleScriptingInterface::warn(QScriptContext* context, QScriptEngine* engine) {
if (ScriptEngine* scriptEngine = qobject_cast<ScriptEngine*>(engine)) {
scriptEngine->scriptWarningMessage(appendArguments(context));
}
return QScriptValue::NullValue;
}
QScriptValue ConsoleScriptingInterface::error(QScriptContext* context, QScriptEngine* engine) {
if (ScriptEngine* scriptEngine = qobject_cast<ScriptEngine*>(engine)) {
scriptEngine->scriptErrorMessage(appendArguments(context));
}
return QScriptValue::NullValue;
}
QScriptValue ConsoleScriptingInterface::exception(QScriptContext* context, QScriptEngine* engine) {
if (ScriptEngine* scriptEngine = qobject_cast<ScriptEngine*>(engine)) {
scriptEngine->scriptErrorMessage(appendArguments(context));
}
return QScriptValue::NullValue;
}
void ConsoleScriptingInterface::time(QString labelName) {
_timerDetails.insert(labelName, QDateTime::currentDateTime().toUTC());
QString message = QString("%1: Timer started").arg(labelName);
if (ScriptEngine* scriptEngine = qobject_cast<ScriptEngine*>(engine())) {
scriptEngine->scriptPrintedMessage(message);
}
}
void ConsoleScriptingInterface::timeEnd(QString labelName) {
if (ScriptEngine* scriptEngine = qobject_cast<ScriptEngine*>(engine())) {
if (!_timerDetails.contains(labelName)) {
scriptEngine->scriptErrorMessage("No such label found " + labelName);
return;
}
if (_timerDetails.value(labelName).isNull()) {
_timerDetails.remove(labelName);
scriptEngine->scriptErrorMessage("Invalid start time for " + labelName);
return;
}
QDateTime _startTime = _timerDetails.value(labelName);
QDateTime _endTime = QDateTime::currentDateTime().toUTC();
qint64 diffInMS = _startTime.msecsTo(_endTime);
QString message = QString("%1: %2ms").arg(labelName).arg(QString::number(diffInMS));
_timerDetails.remove(labelName);
scriptEngine->scriptPrintedMessage(message);
}
}
QScriptValue ConsoleScriptingInterface::assertion(QScriptContext* context, QScriptEngine* engine) {
QString message;
bool condition = false;
for (int i = 0; i < context->argumentCount(); i++) {
if (i == 0) {
condition = context->argument(i).toBool(); // accept first value as condition
} else {
message += SPACE_SEPARATOR + context->argument(i).toString(); // accept other parameters as message
}
}
QString assertionResult;
if (!condition) {
if (message.isEmpty()) {
assertionResult = "Assertion failed";
} else {
assertionResult = QString("Assertion failed : %1").arg(message);
}
if (ScriptEngine* scriptEngine = qobject_cast<ScriptEngine*>(engine)) {
scriptEngine->scriptErrorMessage(assertionResult);
}
}
return QScriptValue::NullValue;
}
void ConsoleScriptingInterface::trace() {
if (ScriptEngine* scriptEngine = qobject_cast<ScriptEngine*>(engine())) {
scriptEngine->scriptPrintedMessage
(QString(STACK_TRACE_FORMAT).arg(LINE_SEPARATOR,
scriptEngine->currentContext()->backtrace().join(LINE_SEPARATOR)));
}
}
void ConsoleScriptingInterface::clear() {
if (ScriptEngine* scriptEngine = qobject_cast<ScriptEngine*>(engine())) {
scriptEngine->clearDebugLogWindow();
}
}
QScriptValue ConsoleScriptingInterface::group(QScriptContext* context, QScriptEngine* engine) {
logGroupMessage(context->argument(0).toString(), engine); // accept first parameter as label
_groupDetails.push_back(context->argument(0).toString());
return QScriptValue::NullValue;
}
QScriptValue ConsoleScriptingInterface::groupCollapsed(QScriptContext* context, QScriptEngine* engine) {
logGroupMessage(context->argument(0).toString(), engine); // accept first parameter as label
_groupDetails.push_back(context->argument(0).toString());
return QScriptValue::NullValue;
}
QScriptValue ConsoleScriptingInterface::groupEnd(QScriptContext* context, QScriptEngine* engine) {
ConsoleScriptingInterface::_groupDetails.removeLast();
return QScriptValue::NullValue;
}
QString ConsoleScriptingInterface::appendArguments(QScriptContext* context) {
QString message;
for (int i = 0; i < context->argumentCount(); i++) {
if (i > 0) {
message += SPACE_SEPARATOR;
}
message += context->argument(i).toString();
}
return message;
}
void ConsoleScriptingInterface::logGroupMessage(QString message, QScriptEngine* engine) {
int _addSpaces = _groupDetails.count() * INDENTATION;
QString logMessage;
for (int i = 0; i < _addSpaces; i++) {
logMessage.append(SPACE_SEPARATOR);
}
logMessage.append(message);
if (ScriptEngine* scriptEngine = qobject_cast<ScriptEngine*>(engine)) {
scriptEngine->scriptPrintedMessage(logMessage);
}
}

View file

@ -0,0 +1,56 @@
//
// ConsoleScriptingInterface.h
// libraries/script-engine/src
//
// Created by NeetBhagat on 6/1/17.
// Copyright 2014 High Fidelity, Inc.
//
// ConsoleScriptingInterface is responsible for following functionality
// Printing logs with various tags and grouping on debug Window and Logs/log file.
// Debugging functionalities like Timer start-end, assertion, trace.
// To use these functionalities, use "console" object in Javascript files.
// For examples please refer "scripts/developer/tests/consoleObjectTest.js"
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#pragma once
#ifndef hifi_ConsoleScriptingInterface_h
#define hifi_ConsoleScriptingInterface_h
#include <QtCore/QObject>
#include <QtCore/QString>
#include <QtScript/QScriptable>
#include <QList>
#include <QHash>
// Scriptable interface of "console" object. Used exclusively in the JavaScript API
class ConsoleScriptingInterface : public QObject, protected QScriptable {
Q_OBJECT
public:
static QScriptValue info(QScriptContext* context, QScriptEngine* engine);
static QScriptValue log(QScriptContext* context, QScriptEngine* engine);
static QScriptValue debug(QScriptContext* context, QScriptEngine* engine);
static QScriptValue warn(QScriptContext* context, QScriptEngine* engine);
static QScriptValue error(QScriptContext* context, QScriptEngine* engine);
static QScriptValue exception(QScriptContext* context, QScriptEngine* engine);
static QScriptValue assertion(QScriptContext* context, QScriptEngine* engine);
static QScriptValue group(QScriptContext* context, QScriptEngine* engine);
static QScriptValue groupCollapsed(QScriptContext* context, QScriptEngine* engine);
static QScriptValue groupEnd(QScriptContext* context, QScriptEngine* engine);
public slots:
void time(QString labelName);
void timeEnd(QString labelName);
void trace();
void clear();
private:
QHash<QString, QDateTime> _timerDetails;
static QList<QString> _groupDetails;
static void logGroupMessage(QString message, QScriptEngine* engine);
static QString appendArguments(QScriptContext* context);
};
#endif // hifi_ConsoleScriptingInterface_h

View file

@ -472,20 +472,24 @@ void ScriptEngine::scriptErrorMessage(const QString& message) {
}
void ScriptEngine::scriptWarningMessage(const QString& message) {
qCWarning(scriptengine) << message;
qCWarning(scriptengine) << qPrintable(message);
emit warningMessage(message, getFilename());
}
void ScriptEngine::scriptInfoMessage(const QString& message) {
qCInfo(scriptengine) << message;
qCInfo(scriptengine) << qPrintable(message);
emit infoMessage(message, getFilename());
}
void ScriptEngine::scriptPrintedMessage(const QString& message) {
qCDebug(scriptengine) << message;
qCDebug(scriptengine) << qPrintable(message);
emit printedMessage(message, getFilename());
}
void ScriptEngine::clearDebugLogWindow() {
emit clearDebugWindow();
}
// Even though we never pass AnimVariantMap directly to and from javascript, the queued invokeMethod of
// callAnimationStateHandler requires that the type be registered.
// These two are meaningful, if we ever do want to use them...
@ -668,8 +672,18 @@ void ScriptEngine::init() {
registerGlobalObject("Mat4", &_mat4Library);
registerGlobalObject("Uuid", &_uuidLibrary);
registerGlobalObject("Messages", DependencyManager::get<MessagesClient>().data());
registerGlobalObject("File", new FileScriptingInterface(this));
registerGlobalObject("console", &_consoleScriptingInterface);
registerFunction("console", "info", ConsoleScriptingInterface::info, currentContext()->argumentCount());
registerFunction("console", "log", ConsoleScriptingInterface::log, currentContext()->argumentCount());
registerFunction("console", "debug", ConsoleScriptingInterface::debug, currentContext()->argumentCount());
registerFunction("console", "warn", ConsoleScriptingInterface::warn, currentContext()->argumentCount());
registerFunction("console", "error", ConsoleScriptingInterface::error, currentContext()->argumentCount());
registerFunction("console", "exception", ConsoleScriptingInterface::exception, currentContext()->argumentCount());
registerFunction("console", "assert", ConsoleScriptingInterface::assertion, currentContext()->argumentCount());
registerFunction("console", "group", ConsoleScriptingInterface::group, 1);
registerFunction("console", "groupCollapsed", ConsoleScriptingInterface::groupCollapsed, 1);
registerFunction("console", "groupEnd", ConsoleScriptingInterface::groupEnd, 0);
qScriptRegisterMetaType(this, animVarMapToScriptValue, animVarMapFromScriptValue);
qScriptRegisterMetaType(this, resultHandlerToScriptValue, resultHandlerFromScriptValue);
@ -1006,6 +1020,7 @@ void ScriptEngine::run() {
emit runningStateChanged();
{
PROFILE_RANGE(script, _fileNameString);
evaluate(_scriptContents, _fileNameString);
maybeEmitUncaughtException(__FUNCTION__);
}
@ -1269,6 +1284,7 @@ void ScriptEngine::timerFired() {
// call the associated JS function, if it exists
if (timerData.function.isValid()) {
PROFILE_RANGE(script, __FUNCTION__);
auto preTimer = p_high_resolution_clock::now();
callWithEnvironment(timerData.definingEntityIdentifier, timerData.definingSandboxURL, timerData.function, timerData.function, QScriptValueList());
auto postTimer = p_high_resolution_clock::now();

View file

@ -41,6 +41,7 @@
#include "ScriptCache.h"
#include "ScriptUUID.h"
#include "Vec3.h"
#include "ConsoleScriptingInterface.h"
#include "SettingHandle.h"
class QScriptEngineDebugger;
@ -225,7 +226,7 @@ public:
void scriptWarningMessage(const QString& message);
void scriptInfoMessage(const QString& message);
void scriptPrintedMessage(const QString& message);
void clearDebugLogWindow();
int getNumRunningEntityScripts() const;
bool getEntityScriptDetails(const EntityItemID& entityID, EntityScriptDetails &details) const;
@ -245,6 +246,7 @@ signals:
void warningMessage(const QString& message, const QString& scriptName);
void infoMessage(const QString& message, const QString& scriptName);
void runningStateChanged();
void clearDebugWindow();
void loadScript(const QString& scriptName, bool isUserLoaded);
void reloadScript(const QString& scriptName, bool isUserLoaded);
void doneRunning();
@ -305,6 +307,7 @@ protected:
Vec3 _vec3Library;
Mat4 _mat4Library;
ScriptUUID _uuidLibrary;
ConsoleScriptingInterface _consoleScriptingInterface;
std::atomic<bool> _isUserLoaded { false };
bool _isReloading { false };

View file

@ -54,6 +54,10 @@ void ScriptEngines::onInfoMessage(const QString& message, const QString& scriptN
emit infoMessage(message, scriptName);
}
void ScriptEngines::onClearDebugWindow() {
emit clearDebugWindow();
}
void ScriptEngines::onErrorLoadingScript(const QString& url) {
emit errorLoadingScript(url);
}

View file

@ -79,6 +79,7 @@ signals:
void warningMessage(const QString& message, const QString& engineName);
void infoMessage(const QString& message, const QString& engineName);
void errorLoadingScript(const QString& url);
void clearDebugWindow();
public slots:
void onPrintedMessage(const QString& message, const QString& scriptName);
@ -86,6 +87,7 @@ public slots:
void onWarningMessage(const QString& message, const QString& scriptName);
void onInfoMessage(const QString& message, const QString& scriptName);
void onErrorLoadingScript(const QString& url);
void onClearDebugWindow();
protected slots:
void onScriptFinished(const QString& fileNameString, ScriptEngine* engine);

View file

@ -614,15 +614,6 @@ void TabletProxy::removeButton(QObject* tabletButtonProxy) {
}
}
void TabletProxy::updateAudioBar(const double micLevel) {
auto tablet = getQmlTablet();
if (!tablet) {
//qCCritical(scriptengine) << "Could not find tablet in TabletRoot.qml";
} else {
QMetaObject::invokeMethod(tablet, "setMicLevel", Qt::AutoConnection, Q_ARG(QVariant, QVariant(micLevel)));
}
}
void TabletProxy::emitScriptEvent(QVariant msg) {
if (!_toolbarMode && _qmlOffscreenSurface) {
QMetaObject::invokeMethod(_qmlOffscreenSurface, "emitScriptEvent", Qt::AutoConnection, Q_ARG(QVariant, msg));

View file

@ -152,13 +152,6 @@ public:
*/
Q_INVOKABLE void removeButton(QObject* tabletButtonProxy);
/**jsdoc
* Updates the audio bar in tablet to reflect latest mic level
* @function TabletProxy#updateAudioBar
* @param micLevel {double} mic level value between 0 and 1
*/
Q_INVOKABLE void updateAudioBar(const double micLevel);
/**jsdoc
* Used to send an event to the html/js embedded in the tablet
* @function TabletProxy#emitScriptEvent

View file

@ -118,12 +118,18 @@ void FileCache::initialize() {
_initialized = true;
}
std::unique_ptr<File> FileCache::createFile(Metadata&& metadata, const std::string& filepath) {
return std::unique_ptr<File>(new cache::File(std::move(metadata), filepath));
}
FilePointer FileCache::addFile(Metadata&& metadata, const std::string& filepath) {
FilePointer file(createFile(std::move(metadata), filepath).release(), std::mem_fn(&File::deleter));
File* rawFile = createFile(std::move(metadata), filepath).release();
FilePointer file(rawFile, std::bind(&File::deleter, rawFile));
if (file) {
_numTotalFiles += 1;
_totalFilesSize += file->getLength();
file->_cache = this;
file->_parent = shared_from_this();
file->_locked = true;
emit dirty();
_files[file->getKey()] = file;
@ -170,7 +176,7 @@ FilePointer FileCache::writeFile(const char* data, File::Metadata&& metadata, bo
} else {
qCWarning(file_cache, "[%s] Failed to write %s", _dirname.c_str(), metadata.key.c_str());
}
assert(!file || (file->_locked && file->_parent.lock()));
return file;
}
@ -192,8 +198,12 @@ FilePointer FileCache::getFile(const Key& key) {
file->touch();
// if it exists, it is active - remove it from the cache
if (_unusedFiles.erase(file)) {
assert(!file->_locked);
file->_locked = true;
_numUnusedFiles -= 1;
_unusedFilesSize -= file->getLength();
} else {
assert(file->_locked);
}
qCDebug(file_cache, "[%s] Found %s", _dirname.c_str(), key.c_str());
emit dirty();
@ -203,6 +213,7 @@ FilePointer FileCache::getFile(const Key& key) {
}
}
assert(!file || (file->_locked && file->_parent.lock()));
return file;
}
@ -213,7 +224,8 @@ std::string FileCache::getFilepath(const Key& key) {
// This is a non-public function that uses the mutex because it's
// essentially a public function specifically to a File object
void FileCache::addUnusedFile(const FilePointer& file) {
Lock lock(_mutex);
assert(file->_locked);
file->_locked = false;
_files[file->getKey()] = file;
_unusedFiles.insert(file);
_numUnusedFiles += 1;
@ -247,7 +259,7 @@ namespace cache {
}
void FileCache::eject(const FilePointer& file) {
file->_cache = nullptr;
file->_locked = false;
const auto& length = file->getLength();
const auto& key = file->getKey();
@ -300,20 +312,34 @@ void FileCache::clear() {
// Mark everything remaining as persisted while effectively ejecting from the cache
for (auto& file : _unusedFiles) {
file->_shouldPersist = true;
file->_cache = nullptr;
file->_parent.reset();
qCDebug(file_cache, "[%s] Persisting %s", _dirname.c_str(), file->getKey().c_str());
}
_unusedFiles.clear();
}
void File::deleter() {
if (_cache) {
_cache->addUnusedFile(FilePointer(this, std::mem_fn(&File::deleter)));
void FileCache::releaseFile(File* file) {
Lock lock(_mutex);
if (file->_locked) {
addUnusedFile(FilePointer(file, std::bind(&File::deleter, file)));
} else {
deleteLater();
delete file;
}
}
void File::deleter(File* file) {
// If the cache shut down before the file was destroyed, then we should leave the file alone (prevents crash on shutdown)
FileCachePointer cache = file->_parent.lock();
if (!cache) {
file->_shouldPersist = true;
delete file;
return;
}
// Any other operations we might do should be done inside a locked section, so we need to delegate to a FileCache member function
cache->releaseFile(file);
}
File::File(Metadata&& metadata, const std::string& filepath) :
_key(std::move(metadata.key)),
_length(metadata.length),
@ -333,3 +359,4 @@ void File::touch() {
utime(_filepath.c_str(), nullptr);
_modified = std::max<int64_t>(QFileInfo(_filepath.c_str()).lastRead().toMSecsSinceEpoch(), _modified);
}

View file

@ -24,12 +24,17 @@
Q_DECLARE_LOGGING_CATEGORY(file_cache)
class FileCacheTests;
namespace cache {
class File;
using FilePointer = std::shared_ptr<File>;
class FileCache;
using FileCachePointer = std::shared_ptr<FileCache>;
using FileCacheWeakPointer = std::weak_ptr<FileCache>;
class FileCache : public QObject {
class FileCache : public QObject, public std::enable_shared_from_this<FileCache> {
Q_OBJECT
Q_PROPERTY(size_t numTotal READ getNumTotalFiles NOTIFY dirty)
Q_PROPERTY(size_t numCached READ getNumCachedFiles NOTIFY dirty)
@ -40,6 +45,8 @@ class FileCache : public QObject {
static const size_t MAX_MAX_SIZE;
static const size_t DEFAULT_MIN_FREE_STORAGE_SPACE;
friend class ::FileCacheTests;
public:
// You can initialize the FileCache with a directory name (ex.: "temp_jpgs") that will be relative to the application local data, OR with a full path
// The file cache will ignore any file that doesn't match the extension provided
@ -86,14 +93,14 @@ signals:
public:
/// must be called after construction to create the cache on the fs and restore persisted files
void initialize();
virtual void initialize();
// Add file to the cache and return the cache entry.
FilePointer writeFile(const char* data, Metadata&& metadata, bool overwrite = false);
FilePointer getFile(const Key& key);
/// create a file
virtual std::unique_ptr<File> createFile(Metadata&& metadata, const std::string& filepath) = 0;
virtual std::unique_ptr<File> createFile(Metadata&& metadata, const std::string& filepath);
private:
using Mutex = std::recursive_mutex;
@ -108,6 +115,7 @@ private:
FilePointer addFile(Metadata&& metadata, const std::string& filepath);
void addUnusedFile(const FilePointer& file);
void releaseFile(File* file);
void clean();
void clear();
// Remove a file from the cache
@ -134,9 +142,7 @@ private:
Set _unusedFiles;
};
class File : public QObject {
Q_OBJECT
class File {
public:
using Key = FileCache::Key;
using Metadata = FileCache::Metadata;
@ -147,7 +153,7 @@ public:
virtual ~File();
/// overrides should call File::deleter to maintain caching behavior
virtual void deleter();
static void deleter(File* file);
protected:
/// when constructed, the file has already been created/written
@ -156,14 +162,16 @@ protected:
private:
friend class FileCache;
friend struct FilePointerComparator;
friend class ::FileCacheTests;
const Key _key;
const size_t _length;
const std::string _filepath;
void touch();
FileCache* _cache { nullptr };
FileCacheWeakPointer _parent;
int64_t _modified { 0 };
bool _locked { false };
bool _shouldPersist { false };
};

View file

@ -153,6 +153,9 @@ void QmlWindowClass::sendToQml(const QVariant& message) {
QMetaObject::invokeMethod(asQuickItem(), "fromScript", Qt::QueuedConnection, Q_ARG(QVariant, message));
}
void QmlWindowClass::clearDebugWindow() {
QMetaObject::invokeMethod(asQuickItem(), "clearDebugWindow", Qt::QueuedConnection);
}
void QmlWindowClass::emitScriptEvent(const QVariant& scriptMessage) {
if (QThread::currentThread() != thread()) {

View file

@ -53,6 +53,7 @@ public slots:
// Scripts can use this to send a message to the QML object
void sendToQml(const QVariant& message);
void clearDebugWindow();
// QmlWindow content may include WebView requiring EventBridge.
void emitScriptEvent(const QVariant& scriptMessage);

View file

@ -49,4 +49,8 @@ ScriptDiscoveryService.infoMessage.connect(function(message, scriptFileName) {
sendToLogWindow("INFO", message, scriptFileName);
});
}()); // END LOCAL_SCOPE
ScriptDiscoveryService.clearDebugWindow.connect(function() {
window.clearDebugWindow();
});
}());

View file

@ -40,6 +40,10 @@ Rectangle {
}
textArea.append(message);
}
function clearWindow() {
textArea.remove(0,textArea.length);
}
}

View file

@ -0,0 +1,114 @@
// Examples and understanding of console object. Include console methods like
// info, log, debug, warn, error, exception, trace, clear, asserts, group, groupCollapsed, groupEnd, time, timeEnd.
// Useful in debugging and exclusively made for JavaScript files.
// To view the logs click on Developer -> script logs [logs on debug window] and for text file logs go to Logs/log file.
main();
function main() {
var someObject = { str: "Some text", id: 5 };
var someValue = 5;
// console.info examples
console.info("[console.info] Hello World.");
console.info(5 + 6);
console.info(someObject.str);
console.info(a = 2 * 6);
console.info(someValue);
console.info('someObject id ' + someObject.id);
console.info('Hello World ', 'someObject.id = ', someObject.id, "a = 2 * 6 = ", a = 2 * 6);
// console.log examples
console.log("[console.log] Hello World");
console.log(5 + 6);
console.log(someObject.str);
console.log(a = 2 * 6);
console.log(someValue);
console.log('someObject id ' + someObject.id);
console.log('Hello World ', 'someObject.id = ', someObject.id, "a = 2 * 6 = ", a = 2 * 6);
// console.debug examples
console.debug("[console.debug] Hello World.");
console.debug(5 + 6);
console.debug(someObject.str);
console.debug(a = 2 * 6);
console.debug(someValue);
console.debug('someObject id ' + someObject.id);
console.debug('Hello World ', 'someObject.id = ', someObject.id, "a = 2 * 6 = ", a = 2 * 6);
// console.warn examples
console.warn("[console.warn] This is warning message.");
console.warn(5 + 6);
console.warn(someObject.str);
console.warn(a = 2 * 6);
console.warn(someValue);
console.warn('someObject id ' + someObject.id);
console.warn('Hello World ', 'someObject.id = ', someObject.id, "a = 2 * 6 = ", a = 2 * 6);
// console.error examples
console.error('An error occurred!');
console.error('An error occurred! ', 'Value = ', someValue);
console.error('An error occurred! ' + 'Value = ' + someValue);
// console.exception examples
console.exception('An exception occurred!');
console.exception('An exception occurred! ', 'Value = ', someValue);
console.exception('An exception occurred! ' + 'Value = ' + someValue);
// console.trace examples
function fooA() {
function fooB() {
function fooC() {
console.trace();
}
fooC();
}
fooB();
}
fooA();
// console.assert() examples
var valA = 1, valB = "1";
console.assert(valA === valB, "Value A doesn't equal to B");
console.assert(valA === valB);
console.assert(5 === 5, "5 equals to 5");
console.assert(5 === 5);
console.assert(5 > 6, "5 is not greater than 6");
console.assert(5 > 6, "5 is not greater than 6", "This assertion will fail");
// console.group() examples.
console.group("Group 1");
console.log("Sentence 1");
console.log("Sentence 2");
console.group("Group 2");
console.log("Sentence 3");
console.log("Sentence 4");
console.groupCollapsed("Group 3");
console.log("Sentence 5");
console.log("Sentence 6");
console.groupEnd();
console.log("Sentence 7");
console.groupEnd();
console.log("Sentence 8");
console.groupEnd();
console.log("Sentence 9");
// console.time(),console.timeEnd() examples
console.time('MyTimer');
// Do some process
sleep(1000);
console.timeEnd('MyTimer');
// use console.clear() to clean Debug Window logs
// console.clear();
}
function sleep(milliseconds) {
var start = new Date().getTime();
for (var i = 0; i < 1e7; i++) {
if ((new Date().getTime() - start) > milliseconds){
break;
}
}
}

View file

@ -49,11 +49,15 @@ Item {
property var valueMax : 1
property var _values : new Array()
property var _values
property var tick : 0
function createValues() {
if (!_values) {
_values = new Array();
}
for (var i =0; i < plots.length; i++) {
var plot = plots[i];
var object = plot["object"] || root.object;
var value = plot["prop"];
@ -93,7 +97,7 @@ Item {
var VALUE_HISTORY_SIZE = 100;
tick++;
var currentValueMax = 0
for (var i = 0; i < _values.length; i++) {
@ -128,7 +132,6 @@ Item {
if ((valueMax < currentValueMax) || (tick % VALUE_HISTORY_SIZE == 0)) {
valueMax = currentValueMax;
}
mycanvas.requestPaint()
}

View file

@ -32,7 +32,7 @@ Column {
ConfigSlider {
label: qsTr(modelData.split(":")[0])
integral: (modelData.split(":")[3] == 'true')
config: Render.getConfig("AmbientOcclusion")
config: Render.getConfig("RenderMainView.AmbientOcclusion")
property: modelData.split(":")[1]
max: modelData.split(":")[2]
min: 0.0
@ -50,8 +50,8 @@ Column {
]
CheckBox {
text: qsTr(modelData.split(":")[0])
checked: Render.getConfig("AmbientOcclusion")[modelData.split(":")[1]]
onCheckedChanged: { Render.getConfig("AmbientOcclusion")[modelData.split(":")[1]] = checked }
checked: Render.getConfig("RenderMainView.AmbientOcclusion")[modelData.split(":")[1]]
onCheckedChanged: { Render.getConfig("RenderMainView.AmbientOcclusion")[modelData.split(":")[1]] = checked }
}
}
}
@ -62,8 +62,8 @@ Column {
]
CheckBox {
text: qsTr(modelData.split(":")[0])
checked: Render.getConfig("DebugAmbientOcclusion")[modelData.split(":")[1]]
onCheckedChanged: { Render.getConfig("DebugAmbientOcclusion")[modelData.split(":")[1]] = checked }
checked: Render.getConfig("RenderMainView.DebugAmbientOcclusion")[modelData.split(":")[1]]
onCheckedChanged: { Render.getConfig("RenderMainView.DebugAmbientOcclusion")[modelData.split(":")[1]] = checked }
}
}
}
@ -72,7 +72,7 @@ Column {
PlotPerf {
title: "Timing"
height: 50
object: Render.getConfig("AmbientOcclusion")
object: Render.getConfig("RenderMainView.AmbientOcclusion")
valueUnit: "ms"
valueScale: 1
valueNumDigits: "3"

View file

@ -14,8 +14,8 @@ import "configSlider"
Column {
id: root
spacing: 8
property var sceneOctree: Render.getConfig("DrawSceneOctree");
property var itemSelection: Render.getConfig("DrawItemSelection");
property var sceneOctree: Render.getConfig("RenderMainView.DrawSceneOctree");
property var itemSelection: Render.getConfig("RenderMainView.DrawItemSelection");
Component.onCompleted: {
sceneOctree.enabled = true;
@ -30,8 +30,8 @@ Column {
Component.onDestruction: {
sceneOctree.enabled = false;
itemSelection.enabled = false;
Render.getConfig("FetchSceneSelection").freezeFrustum = false;
Render.getConfig("CullSceneSelection").freezeFrustum = false;
Render.getConfig("RenderMainView.FetchSceneSelection").freezeFrustum = false;
Render.getConfig("RenderMainView.CullSceneSelection").freezeFrustum = false;
}
GroupBox {
@ -45,8 +45,8 @@ Column {
text: "Freeze Culling Frustum"
checked: false
onCheckedChanged: {
Render.getConfig("FetchSceneSelection").freezeFrustum = checked;
Render.getConfig("CullSceneSelection").freezeFrustum = checked;
Render.getConfig("RenderMainView.FetchSceneSelection").freezeFrustum = checked;
Render.getConfig("RenderMainView.CullSceneSelection").freezeFrustum = checked;
}
}
Label {
@ -98,8 +98,8 @@ Column {
Column{
Repeater {
model: [ "Opaque:DrawOpaqueDeferred", "Transparent:DrawTransparentDeferred", "Light:DrawLight",
"Opaque Overlays:DrawOverlay3DOpaque", "Transparent Overlays:DrawOverlay3DTransparent" ]
model: [ "Opaque:RenderMainView.DrawOpaqueDeferred", "Transparent:RenderMainView.DrawTransparentDeferred", "Light:RenderMainView.DrawLight",
"Opaque Overlays:RenderMainView.DrawOverlay3DOpaque", "Transparent Overlays:RenderMainView.DrawOverlay3DTransparent" ]
ConfigSlider {
label: qsTr(modelData.split(":")[0])
integral: true

View file

@ -13,7 +13,7 @@ var qml = Script.resolvePath('ambientOcclusionPass.qml');
var window = new OverlayWindow({
title: 'Ambient Occlusion Pass',
source: qml,
width: 400, height: 250,
width: 400, height: 300,
});
window.setPosition(Window.innerWidth - 420, 50 + 550 + 50);
window.closed.connect(function() { Script.stop(); });
@ -34,5 +34,5 @@ function setDebugCursor(x, y) {
nx = (x / Window.innerWidth);
ny = 1.0 - ((y) / (Window.innerHeight - 32));
Render.getConfig("DebugAmbientOcclusion").debugCursorTexcoord = { x: nx, y: ny };
Render.getConfig("RenderMainView").getConfig("DebugAmbientOcclusion").debugCursorTexcoord = { x: nx, y: ny };
}

View file

@ -13,7 +13,7 @@ var qml = Script.resolvePath('deferredLighting.qml');
var window = new OverlayWindow({
title: 'Lighting',
source: qml,
width: 400, height:350,
width: 400, height:400,
});
window.setPosition(Window.innerWidth - 420, 50);
window.closed.connect(function() { Script.stop(); });

View file

@ -33,5 +33,5 @@ function setDebugCursor(x, y) {
nx = (x / Window.innerWidth);
ny = 1.0 - ((y) / (Window.innerHeight - 32));
Render.getConfig("DebugScattering").debugCursorTexcoord = { x: nx, y: ny };
Render.getConfig("RenderMainView").getConfig("DebugScattering").debugCursorTexcoord = { x: nx, y: ny };
}

View file

@ -13,6 +13,7 @@ import "configSlider"
Column {
spacing: 8
property var mainViewTask: Render.getConfig("RenderMainView")
Row {
spacing: 8
@ -29,8 +30,8 @@ Column {
]
CheckBox {
text: modelData.split(":")[0]
checked: Render.getConfig(modelData.split(":")[1])[modelData.split(":")[2]]
onCheckedChanged: { Render.getConfig(modelData.split(":")[1])[modelData.split(":")[2]] = checked }
checked: mainViewTask.getConfig(modelData.split(":")[1])[modelData.split(":")[2]]
onCheckedChanged: { mainViewTask.getConfig(modelData.split(":")[1])[modelData.split(":")[2]] = checked }
}
}
}
@ -49,8 +50,8 @@ Column {
]
CheckBox {
text: modelData.split(":")[0]
checked: Render.getConfig(modelData.split(":")[1])[modelData.split(":")[2]]
onCheckedChanged: { Render.getConfig(modelData.split(":")[1])[modelData.split(":")[2]] = checked }
checked: mainViewTask.getConfig(modelData.split(":")[1])[modelData.split(":")[2]]
onCheckedChanged: { mainViewTask.getConfig(modelData.split(":")[1])[modelData.split(":")[2]] = checked }
}
}
}
@ -69,8 +70,8 @@ Column {
]
CheckBox {
text: modelData.split(":")[0]
checked: Render.getConfig(modelData.split(":")[1])[modelData.split(":")[2]]
onCheckedChanged: { Render.getConfig(modelData.split(":")[1])[modelData.split(":")[2]] = checked }
checked: mainViewTask.getConfig(modelData.split(":")[1])[modelData.split(":")[2]]
onCheckedChanged: { mainViewTask.getConfig(modelData.split(":")[1])[modelData.split(":")[2]] = checked }
}
}
}
@ -83,7 +84,7 @@ Column {
ConfigSlider {
label: qsTr(modelData.split(":")[0])
integral: false
config: Render.getConfig(modelData.split(":")[1])
config: mainViewTask.getConfig(modelData.split(":")[1])
property: modelData.split(":")[2]
max: modelData.split(":")[3]
min: modelData.split(":")[4]
@ -107,7 +108,7 @@ Column {
ListElement { text: "Filmic"; color: "White" }
}
width: 200
onCurrentIndexChanged: { Render.getConfig("ToneMapping")["curve"] = currentIndex }
onCurrentIndexChanged: { mainViewTask.getConfig("ToneMapping")["curve"] = currentIndex }
}
}
}
@ -120,7 +121,7 @@ Column {
anchors.left: root.left
}
property var config: Render.getConfig("DebugDeferredBuffer")
property var config: mainViewTask.getConfig("DebugDeferredBuffer")
function setDebugMode(mode) {
framebuffer.config.enabled = (mode != 0);
@ -168,40 +169,40 @@ Column {
CheckBox {
text: "Opaques"
checked: Render.getConfig("DrawOpaqueBounds")["enabled"]
onCheckedChanged: { Render.getConfig("DrawOpaqueBounds")["enabled"] = checked }
checked: mainViewTask.getConfig("DrawOpaqueBounds")["enabled"]
onCheckedChanged: { mainViewTask.getConfig("DrawOpaqueBounds")["enabled"] = checked }
}
CheckBox {
text: "Transparents"
checked: Render.getConfig("DrawTransparentBounds")["enabled"]
onCheckedChanged: { Render.getConfig("DrawTransparentBounds")["enabled"] = checked }
checked: mainViewTask.getConfig("DrawTransparentBounds")["enabled"]
onCheckedChanged: { mainViewTask.getConfig("DrawTransparentBounds")["enabled"] = checked }
}
CheckBox {
text: "Overlay Opaques"
checked: Render.getConfig("DrawOverlayOpaqueBounds")["enabled"]
onCheckedChanged: { Render.getConfig("DrawOverlayOpaqueBounds")["enabled"] = checked }
checked: mainViewTask.getConfig("DrawOverlayOpaqueBounds")["enabled"]
onCheckedChanged: { mainViewTask.getConfig("DrawOverlayOpaqueBounds")["enabled"] = checked }
}
CheckBox {
text: "Overlay Transparents"
checked: Render.getConfig("DrawOverlayTransparentBounds")["enabled"]
onCheckedChanged: { Render.getConfig("DrawOverlayTransparentBounds")["enabled"] = checked }
checked: mainViewTask.getConfig("DrawOverlayTransparentBounds")["enabled"]
onCheckedChanged: { mainViewTask.getConfig("DrawOverlayTransparentBounds")["enabled"] = checked }
}
}
Column {
CheckBox {
text: "Metas"
checked: Render.getConfig("DrawMetaBounds")["enabled"]
onCheckedChanged: { Render.getConfig("DrawMetaBounds")["enabled"] = checked }
checked: mainViewTask.getConfig("DrawMetaBounds")["enabled"]
onCheckedChanged: { mainViewTask.getConfig("DrawMetaBounds")["enabled"] = checked }
}
CheckBox {
text: "Lights"
checked: Render.getConfig("DrawLightBounds")["enabled"]
onCheckedChanged: { Render.getConfig("DrawLightBounds")["enabled"] = checked; }
checked: mainViewTask.getConfig("DrawLightBounds")["enabled"]
onCheckedChanged: { mainViewTask.getConfig("DrawLightBounds")["enabled"] = checked; }
}
CheckBox {
text: "Zones"
checked: Render.getConfig("DrawZones")["enabled"]
onCheckedChanged: { Render.getConfig("ZoneRenderer")["enabled"] = checked; Render.getConfig("DrawZones")["enabled"] = checked; }
checked: mainViewTask.getConfig("DrawZones")["enabled"]
onCheckedChanged: { mainViewTask.getConfig("ZoneRenderer")["enabled"] = checked; mainViewTask.getConfig("DrawZones")["enabled"] = checked; }
}
}
}

View file

@ -15,7 +15,7 @@ var window = new OverlayWindow({
title: 'Light Clustering',
source: qml,
width: 400,
height: 300
height: 400
});
window.setPosition(Window.innerWidth - 420, 50 + 250 + 50 + 250 + 50 );
window.closed.connect(function() { Script.stop(); });

View file

@ -22,13 +22,13 @@ Column {
PlotPerf {
title: "Light CLustering Timing"
height: 50
object: Render.getConfig("LightClustering")
object: Render.getConfig("RenderMainView.LightClustering")
valueUnit: "ms"
valueScale: 1
valueNumDigits: "4"
plots: [
{
object: Render.getConfig("LightClustering"),
object: Render.getConfig("RenderMainView.LightClustering"),
prop: "cpuRunTime",
label: "time",
scale: 1,
@ -40,19 +40,19 @@ Column {
PlotPerf {
title: "Lights"
height: 50
object: Render.getConfig("LightClustering")
object: Render.getConfig("RenderMainView.LightClustering")
valueUnit: ""
valueScale: 1
valueNumDigits: "0"
plots: [
{
object: Render.getConfig("LightClustering"),
object: Render.getConfig("RenderMainView.LightClustering"),
prop: "numClusteredLights",
label: "visible",
color: "#D959FE"
},
{
object: Render.getConfig("LightClustering"),
object: Render.getConfig("RenderMainView.LightClustering"),
prop: "numInputLights",
label: "input",
color: "#FED959"
@ -63,25 +63,25 @@ Column {
PlotPerf {
title: "Scene Lights"
height: 80
object: Render.getConfig("LightClustering")
object: Render.getConfig("RenderMainView.LightClustering")
valueUnit: ""
valueScale: 1
valueNumDigits: "0"
plots: [
{
object: Render.getConfig("LightClustering"),
object: Render.getConfig("RenderMainView.LightClustering"),
prop: "numSceneLights",
label: "current",
color: "#00B4EF"
},
{
object: Render.getConfig("LightClustering"),
object: Render.getConfig("RenderMainView.LightClustering"),
prop: "numFreeSceneLights",
label: "free",
color: "#1AC567"
},
{
object: Render.getConfig("LightClustering"),
object: Render.getConfig("RenderMainView.LightClustering"),
prop: "numAllocatedSceneLights",
label: "allocated",
color: "#9495FF"
@ -92,7 +92,7 @@ Column {
ConfigSlider {
label: qsTr("Range Near [m]")
integral: false
config: Render.getConfig("LightClustering")
config: Render.getConfig("RenderMainView.LightClustering")
property: "rangeNear"
max: 20.0
min: 0.1
@ -100,7 +100,7 @@ Column {
ConfigSlider {
label: qsTr("Range Far [m]")
integral: false
config: Render.getConfig("LightClustering")
config: Render.getConfig("RenderMainView.LightClustering")
property: "rangeFar"
max: 500.0
min: 100.0
@ -108,7 +108,7 @@ Column {
ConfigSlider {
label: qsTr("Grid X")
integral: true
config: Render.getConfig("LightClustering")
config: Render.getConfig("RenderMainView.LightClustering")
property: "dimX"
max: 32
min: 1
@ -116,7 +116,7 @@ Column {
ConfigSlider {
label: qsTr("Grid Y")
integral: true
config: Render.getConfig("LightClustering")
config: Render.getConfig("RenderMainView.LightClustering")
property: "dimY"
max: 32
min: 1
@ -124,33 +124,33 @@ Column {
ConfigSlider {
label: qsTr("Grid Z")
integral: true
config: Render.getConfig("LightClustering")
config: Render.getConfig("RenderMainView.LightClustering")
property: "dimZ"
max: 31
min: 1
}
CheckBox {
text: "Freeze"
checked: Render.getConfig("LightClustering")["freeze"]
onCheckedChanged: { Render.getConfig("LightClustering")["freeze"] = checked }
checked: Render.getConfig("RenderMainView.LightClustering")["freeze"]
onCheckedChanged: { Render.getConfig("RenderMainView.LightClustering")["freeze"] = checked }
}
CheckBox {
text: "Draw Grid"
checked: Render.getConfig("DebugLightClusters")["doDrawGrid"]
onCheckedChanged: { Render.getConfig("DebugLightClusters")["doDrawGrid"] = checked }
checked: Render.getConfig("RenderMainView.DebugLightClusters")["doDrawGrid"]
onCheckedChanged: { Render.getConfig("RenderMainView.DebugLightClusters")["doDrawGrid"] = checked }
}
CheckBox {
text: "Draw Cluster From Depth"
checked: Render.getConfig("DebugLightClusters")["doDrawClusterFromDepth"]
onCheckedChanged: { Render.getConfig("DebugLightClusters")["doDrawClusterFromDepth"] = checked }
checked: Render.getConfig("RenderMainView.DebugLightClusters")["doDrawClusterFromDepth"]
onCheckedChanged: { Render.getConfig("RenderMainView.DebugLightClusters")["doDrawClusterFromDepth"] = checked }
}
CheckBox {
text: "Draw Content"
checked: Render.getConfig("DebugLightClusters")["doDrawContent"]
onCheckedChanged: { Render.getConfig("DebugLightClusters")["doDrawContent"] = checked }
checked: Render.getConfig("RenderMainView.DebugLightClusters")["doDrawContent"]
onCheckedChanged: { Render.getConfig("RenderMainView.DebugLightClusters")["doDrawContent"] = checked }
}
Label {
text: "Num Cluster Items = " + Render.getConfig("LightClustering")["numClusteredLightReferences"].toFixed(0)
text: "Num Cluster Items = " + Render.getConfig("RenderMainView.LightClustering")["numClusteredLightReferences"].toFixed(0)
}
}

View file

@ -14,8 +14,8 @@ var qml = Script.resolvePath('stats.qml');
var window = new OverlayWindow({
title: 'Render Stats',
source: qml,
width: 320,
height: 720
width: 400,
height: 400
});
window.setPosition(500, 50);
window.closed.connect(function() { Script.stop(); });

View file

@ -28,91 +28,6 @@ Item {
return (height - spacing * (children.length - 1)) / children.length
}
PlotPerf {
title: "Num Buffers"
height: parent.evalEvenHeight()
object: stats.config
plots: [
{
prop: "bufferCPUCount",
label: "CPU",
color: "#00B4EF"
},
{
prop: "bufferGPUCount",
label: "GPU",
color: "#1AC567"
}
]
}
PlotPerf {
title: "gpu::Buffer Memory"
height: parent.evalEvenHeight()
object: stats.config
valueScale: 1048576
valueUnit: "Mb"
valueNumDigits: "1"
plots: [
{
prop: "bufferCPUMemoryUsage",
label: "CPU",
color: "#00B4EF"
},
{
prop: "bufferGPUMemoryUsage",
label: "GPU",
color: "#1AC567"
}
]
}
PlotPerf {
title: "Num Textures"
height: parent.evalEvenHeight()
object: stats.config
plots: [
{
prop: "textureCPUCount",
label: "CPU",
color: "#00B4EF"
},
{
prop: "textureGPUCount",
label: "GPU",
color: "#1AC567"
},
{
prop: "texturePendingGPUTransferCount",
label: "Transfer",
color: "#9495FF"
}
]
}
PlotPerf {
title: "gpu::Texture Memory"
height: parent.evalEvenHeight()
object: stats.config
valueScale: 1048576
valueUnit: "Mb"
valueNumDigits: "1"
plots: [
{
prop: "textureCPUMemoryUsage",
label: "CPU",
color: "#00B4EF"
},
{
prop: "textureGPUMemoryUsage",
label: "GPU",
color: "#1AC567"
},
{
prop: "textureGPUVirtualMemoryUsage",
label: "GPU Virtual",
color: "#9495FF"
}
]
}
PlotPerf {
title: "Triangles"
height: parent.evalEvenHeight()
@ -182,9 +97,9 @@ Item {
]
}
property var drawOpaqueConfig: Render.getConfig("DrawOpaqueDeferred")
property var drawTransparentConfig: Render.getConfig("DrawTransparentDeferred")
property var drawLightConfig: Render.getConfig("DrawLight")
property var drawOpaqueConfig: Render.getConfig("RenderMainView.DrawOpaqueDeferred")
property var drawTransparentConfig: Render.getConfig("RenderMainView.DrawTransparentDeferred")
property var drawLightConfig: Render.getConfig("RenderMainView.DrawLight")
PlotPerf {
title: "Items"
@ -199,13 +114,13 @@ Item {
color: "#1AC567"
},
{
object: Render.getConfig("DrawTransparentDeferred"),
object: Render.getConfig("RenderMainView.DrawTransparentDeferred"),
prop: "numDrawn",
label: "Translucents",
color: "#00B4EF"
},
{
object: Render.getConfig("DrawLight"),
object: Render.getConfig("RenderMainView.DrawLight"),
prop: "numDrawn",
label: "Lights",
color: "#FED959"
@ -222,25 +137,25 @@ Item {
valueNumDigits: "2"
plots: [
{
object: Render.getConfig("DrawOpaqueDeferred"),
object: Render.getConfig("RenderMainView.DrawOpaqueDeferred"),
prop: "cpuRunTime",
label: "Opaques",
color: "#1AC567"
},
{
object: Render.getConfig("DrawTransparentDeferred"),
object: Render.getConfig("RenderMainView.DrawTransparentDeferred"),
prop: "cpuRunTime",
label: "Translucents",
color: "#00B4EF"
},
{
object: Render.getConfig("RenderDeferred"),
object: Render.getConfig("RenderMainView.RenderDeferred"),
prop: "cpuRunTime",
label: "Lighting",
color: "#FED959"
},
{
object: Render.getConfig("RenderDeferredTask"),
object: Render.getConfig("RenderMainView.RenderDeferredTask"),
prop: "cpuRunTime",
label: "RenderFrame",
color: "#E2334D"

View file

@ -20,9 +20,7 @@ Item {
id: stats
spacing: 8
anchors.fill:parent
property var config: Render.getConfig("Stats")
function evalEvenHeight() {
// Why do we have to do that manually ? cannot seem to find a qml / anchor / layout mode that does that ?
return (height - spacing * (children.length - 1)) / children.length
@ -38,31 +36,31 @@ Item {
valueNumDigits: "4"
plots: [
{
object: Render.getConfig("OpaqueRangeTimer"),
object: Render.getConfig("RenderMainView.OpaqueRangeTimer"),
prop: "gpuRunTime",
label: "Opaque",
color: "#FFFFFF"
},
{
object: Render.getConfig("LinearDepth"),
object: Render.getConfig("RenderMainView.LinearDepth"),
prop: "gpuRunTime",
label: "LinearDepth",
color: "#00FF00"
},{
object: Render.getConfig("SurfaceGeometry"),
object: Render.getConfig("RenderMainView.SurfaceGeometry"),
prop: "gpuRunTime",
label: "SurfaceGeometry",
color: "#00FFFF"
},
{
object: Render.getConfig("RenderDeferred"),
object: Render.getConfig("RenderMainView.RenderDeferred"),
prop: "gpuRunTime",
label: "DeferredLighting",
color: "#FF00FF"
}
,
{
object: Render.getConfig("ToneAndPostRangeTimer"),
object: Render.getConfig("RenderMainView.ToneAndPostRangeTimer"),
prop: "gpuRunTime",
label: "tone and post",
color: "#FF0000"
@ -78,31 +76,31 @@ Item {
valueNumDigits: "3"
plots: [
{
object: Render.getConfig("OpaqueRangeTimer"),
object: Render.getConfig("RenderMainView.OpaqueRangeTimer"),
prop: "batchRunTime",
label: "Opaque",
color: "#FFFFFF"
},
{
object: Render.getConfig("LinearDepth"),
object: Render.getConfig("RenderMainView.LinearDepth"),
prop: "batchRunTime",
label: "LinearDepth",
color: "#00FF00"
},{
object: Render.getConfig("SurfaceGeometry"),
object: Render.getConfig("RenderMainView.SurfaceGeometry"),
prop: "batchRunTime",
label: "SurfaceGeometry",
color: "#00FFFF"
},
{
object: Render.getConfig("RenderDeferred"),
object: Render.getConfig("RenderMainView.RenderDeferred"),
prop: "batchRunTime",
label: "DeferredLighting",
color: "#FF00FF"
}
,
{
object: Render.getConfig("ToneAndPostRangeTimer"),
object: Render.getConfig("RenderMainView.ToneAndPostRangeTimer"),
prop: "batchRunTime",
label: "tone and post",
color: "#FF0000"

View file

@ -17,27 +17,27 @@ Column {
id: scattering
spacing: 10
Column{
Column{
CheckBox {
text: "Scattering"
checked: Render.getConfig("Scattering").enableScattering
onCheckedChanged: { Render.getConfig("Scattering").enableScattering = checked }
checked: Render.getConfig("RenderMainView.Scattering").enableScattering
onCheckedChanged: { Render.getConfig("RenderMainView.Scattering").enableScattering = checked }
}
CheckBox {
text: "Show Scattering BRDF"
checked: Render.getConfig("Scattering").showScatteringBRDF
onCheckedChanged: { Render.getConfig("Scattering").showScatteringBRDF = checked }
checked: Render.getConfig("RenderMainView.Scattering").showScatteringBRDF
onCheckedChanged: { Render.getConfig("RenderMainView.Scattering").showScatteringBRDF = checked }
}
CheckBox {
text: "Show Curvature"
checked: Render.getConfig("Scattering").showCurvature
onCheckedChanged: { Render.getConfig("Scattering").showCurvature = checked }
checked: Render.getConfig("RenderMainView.Scattering").showCurvature
onCheckedChanged: { Render.getConfig("RenderMainView.Scattering").showCurvature = checked }
}
CheckBox {
text: "Show Diffused Normal"
checked: Render.getConfig("Scattering").showDiffusedNormal
onCheckedChanged: { Render.getConfig("Scattering").showDiffusedNormal = checked }
checked: Render.getConfig("RenderMainView.Scattering").showDiffusedNormal
onCheckedChanged: { Render.getConfig("RenderMainView.Scattering").showDiffusedNormal = checked }
}
Repeater {
model: [ "Scattering Bent Red:Scattering:bentRed:2.0",
@ -50,7 +50,7 @@ Column {
ConfigSlider {
label: qsTr(modelData.split(":")[0])
integral: false
config: Render.getConfig(modelData.split(":")[1])
config: mainViewTask.getConfig(modelData.split(":")[1])
property: modelData.split(":")[2]
max: modelData.split(":")[3]
min: 0.0
@ -58,23 +58,23 @@ Column {
}
CheckBox {
text: "Scattering Profile"
checked: Render.getConfig("DebugScattering").showProfile
onCheckedChanged: { Render.getConfig("DebugScattering").showProfile = checked }
checked: Render.getConfig("RenderMainView.DebugScattering").showProfile
onCheckedChanged: { Render.getConfig("RenderMainView.DebugScattering").showProfile = checked }
}
CheckBox {
text: "Scattering Table"
checked: Render.getConfig("DebugScattering").showLUT
onCheckedChanged: { Render.getConfig("DebugScattering").showLUT = checked }
checked: Render.getConfig("RenderMainView.DebugScattering").showLUT
onCheckedChanged: { Render.getConfig("RenderMainView.DebugScattering").showLUT = checked }
}
CheckBox {
text: "Cursor Pixel"
checked: Render.getConfig("DebugScattering").showCursorPixel
onCheckedChanged: { Render.getConfig("DebugScattering").showCursorPixel = checked }
checked: Render.getConfig("RenderMainView.DebugScattering").showCursorPixel
onCheckedChanged: { Render.getConfig("RenderMainView.DebugScattering").showCursorPixel = checked }
}
CheckBox {
text: "Skin Specular Beckmann"
checked: Render.getConfig("DebugScattering").showSpecularTable
onCheckedChanged: { Render.getConfig("DebugScattering").showSpecularTable = checked }
checked: Render.getConfig("RenderMainView.DebugScattering").showSpecularTable
onCheckedChanged: { Render.getConfig("RenderMainView.DebugScattering").showSpecularTable = checked }
}
}
}

View file

@ -21,7 +21,7 @@ Column {
ConfigSlider {
label: qsTr("Depth Threshold [cm]")
integral: false
config: Render.getConfig("SurfaceGeometry")
config: Render.getConfig("RenderMainView.SurfaceGeometry")
property: "depthThreshold"
max: 5.0
min: 0.0
@ -34,7 +34,7 @@ Column {
ConfigSlider {
label: qsTr(modelData.split(":")[0])
integral: (modelData.split(":")[3] == 'true')
config: Render.getConfig("SurfaceGeometry")
config: Render.getConfig("RenderMainView.SurfaceGeometry")
property: modelData.split(":")[1]
max: modelData.split(":")[2]
min: 0.0
@ -42,13 +42,13 @@ Column {
}
CheckBox {
text: "Half Resolution"
checked: Render.getConfig("SurfaceGeometry")["resolutionLevel"]
onCheckedChanged: { Render.getConfig("SurfaceGeometry")["resolutionLevel"] = checked }
checked: Render.getConfig("RenderMainView.SurfaceGeometry")["resolutionLevel"]
onCheckedChanged: { Render.getConfig("RenderMainView.SurfaceGeometry")["resolutionLevel"] = checked }
}
Repeater {
model: [ "Diffusion Scale:SurfaceGeometry:diffuseFilterScale:2.0",
"Diffusion Depth Threshold:SurfaceGeometry:diffuseDepthThreshold:1.0"
model: [ "Diffusion Scale:RenderMainView.SurfaceGeometry:diffuseFilterScale:2.0",
"Diffusion Depth Threshold:RenderMainView.SurfaceGeometry:diffuseDepthThreshold:1.0"
]
ConfigSlider {
label: qsTr(modelData.split(":")[0])

View file

@ -622,7 +622,7 @@ var toolBar = (function () {
}));
isActive = active;
activeButton.editProperties({isActive: isActive});
var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system");
if (!isActive) {
@ -1519,6 +1519,8 @@ function importSVO(importURL) {
// entities after they're imported so that they're all the correct distance in front of and with geometric mean
// centered on the avatar/camera direction.
var deltaPosition = Vec3.ZERO;
var entityPositions = [];
var entityParentIDs = [];
var properties = Entities.getEntityProperties(pastedEntityIDs[0], ["type"]);
var NO_ADJUST_ENTITY_TYPES = ["Zone", "Light", "ParticleEffect"];
@ -1534,10 +1536,9 @@ function importSVO(importURL) {
var targetPosition = getPositionToCreateEntity();
var deltaParallel = HALF_TREE_SCALE; // Distance to move entities parallel to targetDirection.
var deltaPerpendicular = Vec3.ZERO; // Distance to move entities perpendicular to targetDirection.
var entityPositions = [];
for (var i = 0, length = pastedEntityIDs.length; i < length; i++) {
var properties = Entities.getEntityProperties(pastedEntityIDs[i], ["position", "dimensions",
"registrationPoint", "rotation"]);
"registrationPoint", "rotation", "parentID"]);
var adjustedPosition = adjustPositionPerBoundingBox(targetPosition, targetDirection,
properties.registrationPoint, properties.dimensions, properties.rotation);
var delta = Vec3.subtract(adjustedPosition, properties.position);
@ -1546,6 +1547,7 @@ function importSVO(importURL) {
deltaPerpendicular = Vec3.sum(Vec3.subtract(delta, Vec3.multiply(distance, targetDirection)),
deltaPerpendicular);
entityPositions[i] = properties.position;
entityParentIDs[i] = properties.parentID;
}
deltaPerpendicular = Vec3.multiply(1 / pastedEntityIDs.length, deltaPerpendicular);
deltaPosition = Vec3.sum(Vec3.multiply(deltaParallel, targetDirection), deltaPerpendicular);
@ -1562,9 +1564,11 @@ function importSVO(importURL) {
if (!Vec3.equal(deltaPosition, Vec3.ZERO)) {
for (var i = 0, length = pastedEntityIDs.length; i < length; i++) {
Entities.editEntity(pastedEntityIDs[i], {
position: Vec3.sum(deltaPosition, entityPositions[i])
});
if (Uuid.isNull(entityParentIDs[i])) {
Entities.editEntity(pastedEntityIDs[i], {
position: Vec3.sum(deltaPosition, entityPositions[i])
});
}
}
}
}

View file

@ -183,11 +183,6 @@
return;
}
//TODO: move to tablet qml?
if (tabletShown) {
gTablet.updateAudioBar(getMicLevel());
}
if (now - validCheckTime > MSECS_PER_SEC) {
validCheckTime = now;
updateTabletWidthFromSettings();
@ -268,12 +263,6 @@
Script.setInterval(updateShowTablet, 100);
// Calculate microphone level with the same scaling equation (log scale, exponentially averaged) in AvatarInputs and pal.js
function getMicLevel() {
//reuse already existing C++ code
return AvatarInputs.loudnessToAudioLevel(MyAvatar.audioLoudness)
}
Script.scriptEnding.connect(function () {
// if we reload scripts in tablet mode make sure we close the currently open window, by calling gotoHomeScreen

View file

@ -10,7 +10,7 @@
#include "FileCacheTests.h"
#include <FileCache.h>
#include <shared/FileCache.h>
QTEST_GUILESS_MAIN(FileCacheTests)
@ -24,40 +24,6 @@ static std::string getFileKey(int i) {
return QString(QByteArray { 1, (char)i }.toHex()).toStdString();
}
class TestFile : public File {
using Parent = File;
public:
TestFile(Metadata&& metadata, const std::string& filepath)
: Parent(std::move(metadata), filepath) {
}
};
class TestFileCache : public FileCache {
using Parent = FileCache;
public:
TestFileCache(const std::string& dirname, const std::string& ext, QObject* parent = nullptr) : Parent(dirname, ext, nullptr) {
initialize();
}
std::unique_ptr<File> createFile(Metadata&& metadata, const std::string& filepath) override {
qCInfo(file_cache) << "Wrote KTX" << metadata.key.c_str();
return std::unique_ptr<File>(new TestFile(std::move(metadata), filepath));
}
};
using CachePointer = std::shared_ptr<TestFileCache>;
// The FileCache relies on deleteLater to clear unused files, but QTest classes don't run a conventional event loop
// so we need to call this function to force any pending deletes to occur in the File destructor
static void forceDeletes() {
while (QCoreApplication::hasPendingEvents()) {
QCoreApplication::sendPostedEvents();
QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
QCoreApplication::processEvents();
}
}
size_t FileCacheTests::getCacheDirectorySize() const {
size_t result = 0;
QDir dir(_testDir.path());
@ -67,8 +33,9 @@ size_t FileCacheTests::getCacheDirectorySize() const {
return result;
}
CachePointer makeFileCache(QString& location) {
auto result = std::make_shared<TestFileCache>(location.toStdString(), "tmp");
FileCachePointer makeFileCache(QString& location) {
auto result = std::make_shared<FileCache>(location.toStdString(), "tmp");
result->initialize();
result->setMaxSize(MAX_UNUSED_SIZE);
return result;
}
@ -83,26 +50,38 @@ void FileCacheTests::testUnusedFiles() {
{
for (int i = 0; i < 100; ++i) {
std::string key = getFileKey(i);
auto file = cache->writeFile(TEST_DATA.data(), TestFileCache::Metadata(key, TEST_DATA.size()));
auto file = cache->writeFile(TEST_DATA.data(), FileCache::Metadata(key, TEST_DATA.size()));
QVERIFY(file->_locked);
inUseFiles.push_back(file);
forceDeletes();
QThread::msleep(10);
}
QCOMPARE(cache->getNumCachedFiles(), (size_t)0);
QCOMPARE(cache->getNumTotalFiles(), (size_t)100);
// Release the in-use files
inUseFiles.clear();
// Cache state is updated, but the directory state is unchanged,
// because the file deletes are triggered by an event loop
QCOMPARE(cache->getNumCachedFiles(), (size_t)10);
QCOMPARE(cache->getNumTotalFiles(), (size_t)10);
QVERIFY(getCacheDirectorySize() > MAX_UNUSED_SIZE);
forceDeletes();
QCOMPARE(cache->getNumCachedFiles(), (size_t)10);
QCOMPARE(cache->getNumTotalFiles(), (size_t)10);
QVERIFY(getCacheDirectorySize() <= MAX_UNUSED_SIZE);
}
// Check behavior when destroying the cache BEFORE releasing the files
cache = makeFileCache(_testDir.path());
{
auto directorySize = getCacheDirectorySize();
// Test files 90 to 99 are present
for (int i = 90; i < 100; ++i) {
std::string key = getFileKey(i);
auto file = cache->getFile(key);
QVERIFY(file.get());
inUseFiles.push_back(file);
}
cache.reset();
inUseFiles.clear();
QCOMPARE(getCacheDirectorySize(), directorySize);
}
// Reset the cache
cache = makeFileCache(_testDir.path());
{
@ -151,8 +130,6 @@ void FileCacheTests::testFreeSpacePreservation() {
auto cache = makeFileCache(_testDir.path());
// Setting the min free space causes it to eject the oldest files that cause the cache to exceed the minimum space
cache->setMinFreeSize(targetFreeSpace);
QVERIFY(getFreeSpace() < targetFreeSpace);
forceDeletes();
QCOMPARE(cache->getNumCachedFiles(), (size_t)5);
QCOMPARE(cache->getNumTotalFiles(), (size_t)5);
QVERIFY(getFreeSpace() >= targetFreeSpace);
@ -176,8 +153,6 @@ void FileCacheTests::testWipe() {
cache->wipe();
QCOMPARE(cache->getNumCachedFiles(), (size_t)0);
QCOMPARE(cache->getNumTotalFiles(), (size_t)0);
QVERIFY(getCacheDirectorySize() > 0);
forceDeletes();
QCOMPARE(getCacheDirectorySize(), (size_t)0);
}