mirror of
https://github.com/overte-org/overte.git
synced 2025-04-20 03:44:02 +02:00
Merge branch 'tablet-ui' of github.com:highfidelity/hifi into tablet-notifcation
This commit is contained in:
commit
dac16af1df
37 changed files with 1759 additions and 340 deletions
|
@ -37,7 +37,6 @@ const QString AVATAR_MIXER_LOGGING_NAME = "avatar-mixer";
|
|||
|
||||
// FIXME - what we'd actually like to do is send to users at ~50% of their present rate down to 30hz. Assume 90 for now.
|
||||
const int AVATAR_MIXER_BROADCAST_FRAMES_PER_SECOND = 45;
|
||||
const unsigned int AVATAR_DATA_SEND_INTERVAL_MSECS = (1.0f / (float) AVATAR_MIXER_BROADCAST_FRAMES_PER_SECOND) * 1000;
|
||||
|
||||
AvatarMixer::AvatarMixer(ReceivedMessage& message) :
|
||||
ThreadedAssignment(message)
|
||||
|
|
46
interface/resources/icons/tablet-icons/scope-auto.svg
Normal file
46
interface/resources/icons/tablet-icons/scope-auto.svg
Normal file
|
@ -0,0 +1,46 @@
|
|||
<?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" id="Layer_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">
|
||||
<style type="text/css">
|
||||
.st0{fill:#FFFFFF;}
|
||||
</style>
|
||||
<g id="Layer_2">
|
||||
</g>
|
||||
<g>
|
||||
<path class="st0" d="M25.3,23.7c-0.6,0-1.2,0.2-1.6,0.7S23,25.4,23,26c0,0.6,0.2,1.1,0.7,1.6c0.5,0.5,1,0.7,1.6,0.7
|
||||
c0.6,0,1.2-0.2,1.6-0.7c0.5-0.5,0.7-1,0.7-1.6c0-0.6-0.2-1.2-0.7-1.6C26.5,23.9,26,23.7,25.3,23.7z"/>
|
||||
<path class="st0" d="M25.3,17.2c-5,0-9,4-9,9c0,5,4,9,9,9s9-4,9-9C34.4,21.2,30.3,17.2,25.3,17.2z M31.5,26c0,0.3-0.1,0.6-0.3,0.8
|
||||
c-0.2,0.2-0.5,0.3-0.8,0.3c-0.2,0-0.3,0-0.5-0.1c-0.1,0-0.3,0-0.5-0.1c-0.2,0.6-0.3,1.1-0.6,1.4c0.2,0.1,0.3,0.2,0.4,0.3h0.1
|
||||
c0.2,0.1,0.4,0.2,0.4,0.2c0.5,0.5,0.5,1.1,0,1.6c-0.2,0.2-0.5,0.3-0.8,0.3c-0.3,0-0.6-0.1-0.8-0.3c0,0-0.1-0.2-0.2-0.4
|
||||
c0,0,0-0.1-0.1-0.1c-0.1-0.1-0.2-0.2-0.2-0.4c-0.4,0.3-0.9,0.5-1.4,0.6c0.1,0.2,0.1,0.4,0.1,0.5c0.1,0.2,0.1,0.3,0.1,0.5
|
||||
c0,0.3-0.1,0.6-0.3,0.8c-0.2,0.2-0.5,0.3-0.8,0.3c-0.3,0-0.6-0.1-0.8-0.3c-0.2-0.2-0.3-0.5-0.3-0.8c0-0.2,0-0.3,0.1-0.5
|
||||
c0-0.1,0-0.3,0.1-0.5c-0.5-0.1-1-0.3-1.4-0.6c-0.1,0.2-0.2,0.3-0.2,0.4L22.8,30c0,0.1-0.1,0.2-0.2,0.4c-0.2,0.2-0.5,0.3-0.8,0.3
|
||||
c-0.3,0-0.6-0.1-0.8-0.3c-0.5-0.5-0.5-1.1,0-1.6c0.1-0.1,0.2-0.2,0.5-0.2c0-0.1,0.2-0.2,0.4-0.3c-0.2-0.3-0.4-0.8-0.6-1.4
|
||||
C21.1,27,20.9,27,20.7,27V27c-0.1,0.1-0.2,0.1-0.5,0.1c-0.3,0-0.6-0.1-0.8-0.3c-0.2-0.2-0.3-0.5-0.3-0.8c0-0.3,0.1-0.6,0.3-0.8
|
||||
c0.2-0.2,0.5-0.3,0.8-0.3c0.2,0,0.3,0,0.5,0.1c0.1,0,0.3,0,0.5,0.1c0.1-0.5,0.3-1,0.6-1.4c-0.1,0-0.2-0.1-0.4-0.2h-0.1
|
||||
c-0.2-0.1-0.3-0.2-0.4-0.3c-0.5-0.5-0.5-1,0-1.5c0.5-0.5,1.1-0.5,1.6,0c0.1,0.1,0.2,0.2,0.2,0.4c0.1,0.1,0.2,0.2,0.3,0.5
|
||||
c0.4-0.3,0.9-0.5,1.4-0.6c-0.1-0.2-0.1-0.3-0.1-0.5v-0.1c-0.1-0.2-0.1-0.3-0.1-0.5c0-0.3,0.1-0.6,0.3-0.8c0.2-0.2,0.5-0.3,0.8-0.3
|
||||
c0.3,0,0.6,0.1,0.8,0.3c0.2,0.2,0.3,0.5,0.3,0.8c0,0.2,0,0.3-0.1,0.5v0.1c0,0.2,0,0.3-0.1,0.5c0.4,0.1,0.9,0.3,1.4,0.6
|
||||
c0-0.2,0.1-0.3,0.2-0.5c0-0.1,0.1-0.2,0.3-0.4c0.2-0.2,0.4-0.3,0.8-0.3c0.3,0,0.6,0.1,0.8,0.3c0.5,0.5,0.5,1.1,0,1.6
|
||||
c-0.2,0.2-0.3,0.2-0.4,0.3c-0.1,0.1-0.2,0.2-0.5,0.2c0.3,0.5,0.5,1,0.6,1.4c0.2-0.1,0.3-0.1,0.5-0.1H30c0.2-0.1,0.3-0.1,0.5-0.1
|
||||
c0.3,0,0.6,0.1,0.8,0.3C31.4,25.5,31.5,25.7,31.5,26L31.5,26z"/>
|
||||
</g>
|
||||
<path class="st0" d="M22.3,15.4v-2.6c0-0.6-0.5-1.2-1.2-1.2c-0.6,0-1.2,0.5-1.2,1.2v3.5C20.7,15.9,21.5,15.6,22.3,15.4z"/>
|
||||
<path class="st0" d="M25.3,15c0.4,0,0.8,0,1.1,0.1V8.6c0-0.6-0.5-1.2-1.2-1.2S24.1,8,24.1,8.6V15C24.5,15,24.9,15,25.3,15z"/>
|
||||
<path class="st0" d="M30.6,16.3V3.6c0-0.6-0.5-1.2-1.2-1.2S28.3,3,28.3,3.6v11.7C29.1,15.6,29.9,15.9,30.6,16.3z"/>
|
||||
<path class="st0" d="M48,24.9h-0.6v-2.1c0-0.6-0.5-1.2-1.2-1.2S45,22.2,45,22.9v2.1h-1.8V12c0-0.6-0.5-1.2-1.2-1.2s-1.2,0.5-1.2,1.2
|
||||
v12.9H39v-9.6c0-0.6-0.5-1.2-1.2-1.2c-0.6,0-1.2,0.5-1.2,1.2V36c0,0.6,0.5,1.2,1.2,1.2c0.6,0,1.2-0.5,1.2-1.2v-8.7h1.8v12.9
|
||||
c0,0.6,0.5,1.2,1.2,1.2s1.2-0.5,1.2-1.2V27.3H45v2.9c0,0.6,0.5,1.2,1.2,1.2s1.2-0.5,1.2-1.2v-2.9H48c0.6,0,1.2-0.5,1.2-1.2
|
||||
S48.6,24.9,48,24.9z"/>
|
||||
<path class="st0" d="M13.9,12c0-0.6-0.5-1.2-1.2-1.2c-0.6,0-1.2,0.5-1.2,1.2v12.9H9.7v-4.6c0-0.6-0.5-1.2-1.2-1.2
|
||||
c-0.6,0-1.2,0.5-1.2,1.2v4.6H5.5v-2.1c0-0.6-0.5-1.2-1.2-1.2s-1.2,0.5-1.2,1.2v2.1H2.6c-0.6,0-1.2,0.5-1.2,1.2s0.5,1.2,1.2,1.2h0.6
|
||||
v2.1c0,0.6,0.5,1.2,1.2,1.2s1.2-0.5,1.2-1.2v-2.1h1.8v6.2c0,0.6,0.5,1.2,1.2,1.2c0.6,0,1.2-0.5,1.2-1.2v-6.2h1.8v12.1
|
||||
c0,0.6,0.5,1.2,1.2,1.2c0.6,0,1.2-0.5,1.2-1.2V12z"/>
|
||||
<path class="st0" d="M28.3,37v9.8c0,0.6,0.5,1.2,1.2,1.2s1.2-0.5,1.2-1.2V36.1C29.9,36.5,29.1,36.8,28.3,37z"/>
|
||||
<path class="st0" d="M25.3,37.5c-0.4,0-0.8,0-1.2-0.1v4.5c0,0.6,0.5,1.2,1.2,1.2s1.2-0.5,1.2-1.2v-4.5
|
||||
C26.1,37.4,25.7,37.5,25.3,37.5z"/>
|
||||
<path class="st0" d="M19.9,36.1v3.3c0,0.6,0.5,1.2,1.2,1.2c0.6,0,1.2-0.5,1.2-1.2V37C21.5,36.8,20.7,36.5,19.9,36.1z"/>
|
||||
<rect x="12" y="24.6" class="st0" width="6.9" height="3"/>
|
||||
<rect x="32.9" y="24.6" class="st0" width="5.1" height="3"/>
|
||||
</svg>
|
After Width: | Height: | Size: 4 KiB |
30
interface/resources/icons/tablet-icons/scope-pause.svg
Normal file
30
interface/resources/icons/tablet-icons/scope-pause.svg
Normal file
|
@ -0,0 +1,30 @@
|
|||
<?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" id="Layer_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">
|
||||
<style type="text/css">
|
||||
.st0{fill:#FFFFFF;}
|
||||
</style>
|
||||
<g id="Layer_2">
|
||||
</g>
|
||||
<path class="st0" d="M22.3,15.4v-2.6c0-0.6-0.5-1.2-1.2-1.2c-0.6,0-1.2,0.5-1.2,1.2v3.5C20.7,15.9,21.5,15.6,22.3,15.4z"/>
|
||||
<path class="st0" d="M25.3,15c0.4,0,0.8,0,1.1,0.1V8.6c0-0.6-0.5-1.2-1.2-1.2S24.1,8,24.1,8.6V15C24.5,15,24.9,15,25.3,15z"/>
|
||||
<path class="st0" d="M30.6,16.3V3.6c0-0.6-0.5-1.2-1.2-1.2S28.3,3,28.3,3.6v11.7C29.1,15.6,29.9,15.9,30.6,16.3z"/>
|
||||
<path class="st0" d="M48,24.9h-0.6v-2.1c0-0.6-0.5-1.2-1.2-1.2S45,22.2,45,22.9v2.1h-1.8V12c0-0.6-0.5-1.2-1.2-1.2s-1.2,0.5-1.2,1.2
|
||||
v12.9H39v-9.6c0-0.6-0.5-1.2-1.2-1.2c-0.6,0-1.2,0.5-1.2,1.2V36c0,0.6,0.5,1.2,1.2,1.2c0.6,0,1.2-0.5,1.2-1.2v-8.7h1.8v12.9
|
||||
c0,0.6,0.5,1.2,1.2,1.2s1.2-0.5,1.2-1.2V27.3H45v2.9c0,0.6,0.5,1.2,1.2,1.2s1.2-0.5,1.2-1.2v-2.9H48c0.6,0,1.2-0.5,1.2-1.2
|
||||
S48.6,24.9,48,24.9z"/>
|
||||
<path class="st0" d="M13.9,12c0-0.6-0.5-1.2-1.2-1.2c-0.6,0-1.2,0.5-1.2,1.2v12.9H9.7v-4.6c0-0.6-0.5-1.2-1.2-1.2
|
||||
c-0.6,0-1.2,0.5-1.2,1.2v4.6H5.5v-2.1c0-0.6-0.5-1.2-1.2-1.2s-1.2,0.5-1.2,1.2v2.1H2.6c-0.6,0-1.2,0.5-1.2,1.2s0.5,1.2,1.2,1.2h0.6
|
||||
v2.1c0,0.6,0.5,1.2,1.2,1.2s1.2-0.5,1.2-1.2v-2.1h1.8v6.2c0,0.6,0.5,1.2,1.2,1.2c0.6,0,1.2-0.5,1.2-1.2v-6.2h1.8v12.1
|
||||
c0,0.6,0.5,1.2,1.2,1.2c0.6,0,1.2-0.5,1.2-1.2V12z"/>
|
||||
<path class="st0" d="M28.3,37v9.8c0,0.6,0.5,1.2,1.2,1.2s1.2-0.5,1.2-1.2V36.1C29.9,36.5,29.1,36.8,28.3,37z"/>
|
||||
<path class="st0" d="M25.3,37.5c-0.4,0-0.8,0-1.2-0.1v4.5c0,0.6,0.5,1.2,1.2,1.2s1.2-0.5,1.2-1.2v-4.5
|
||||
C26.1,37.4,25.7,37.5,25.3,37.5z"/>
|
||||
<path class="st0" d="M19.9,36.1v3.3c0,0.6,0.5,1.2,1.2,1.2c0.6,0,1.2-0.5,1.2-1.2V37C21.5,36.8,20.7,36.5,19.9,36.1z"/>
|
||||
<rect x="12" y="24.6" class="st0" width="7.3" height="3"/>
|
||||
<rect x="32.9" y="24.6" class="st0" width="5.3" height="3"/>
|
||||
<path class="st0" d="M25.3,17c-5,0-9,4-9,9c0,5,4,9,9,9s9-4,9-9C34.4,21,30.3,17,25.3,17z M24.1,29.7c0,0.5-0.6,1-1.4,1
|
||||
s-1.4-0.4-1.4-1v-7.3c0-0.5,0.6-1,1.4-1s1.4,0.4,1.4,1V29.7z M29.3,29.7c0,0.5-0.6,1-1.4,1c-0.8,0-1.4-0.4-1.4-1v-7.3
|
||||
c0-0.5,0.6-1,1.4-1c0.8,0,1.4,0.4,1.4,1V29.7z"/>
|
||||
</svg>
|
After Width: | Height: | Size: 2.3 KiB |
30
interface/resources/icons/tablet-icons/scope-play.svg
Normal file
30
interface/resources/icons/tablet-icons/scope-play.svg
Normal file
|
@ -0,0 +1,30 @@
|
|||
<?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" id="Layer_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">
|
||||
<style type="text/css">
|
||||
.st0{fill:#FFFFFF;}
|
||||
</style>
|
||||
<g id="Layer_2">
|
||||
</g>
|
||||
<path class="st0" d="M22.3,15.4v-2.6c0-0.6-0.5-1.2-1.2-1.2c-0.6,0-1.2,0.5-1.2,1.2v3.5C20.7,15.9,21.5,15.6,22.3,15.4z"/>
|
||||
<path class="st0" d="M25.3,15c0.4,0,0.8,0,1.1,0.1V8.6c0-0.6-0.5-1.2-1.2-1.2S24.1,8,24.1,8.6V15C24.5,15,24.9,15,25.3,15z"/>
|
||||
<path class="st0" d="M30.6,16.3V3.6c0-0.6-0.5-1.2-1.2-1.2S28.3,3,28.3,3.6v11.7C29.1,15.6,29.9,15.9,30.6,16.3z"/>
|
||||
<path class="st0" d="M48,24.9h-0.6v-2.1c0-0.6-0.5-1.2-1.2-1.2S45,22.2,45,22.9v2.1h-1.8V12c0-0.6-0.5-1.2-1.2-1.2s-1.2,0.5-1.2,1.2
|
||||
v12.9H39v-9.6c0-0.6-0.5-1.2-1.2-1.2c-0.6,0-1.2,0.5-1.2,1.2V36c0,0.6,0.5,1.2,1.2,1.2c0.6,0,1.2-0.5,1.2-1.2v-8.7h1.8v12.9
|
||||
c0,0.6,0.5,1.2,1.2,1.2s1.2-0.5,1.2-1.2V27.3H45v2.9c0,0.6,0.5,1.2,1.2,1.2s1.2-0.5,1.2-1.2v-2.9H48c0.6,0,1.2-0.5,1.2-1.2
|
||||
S48.6,24.9,48,24.9z"/>
|
||||
<path class="st0" d="M13.9,12c0-0.6-0.5-1.2-1.2-1.2c-0.6,0-1.2,0.5-1.2,1.2v12.9H9.7v-4.6c0-0.6-0.5-1.2-1.2-1.2
|
||||
c-0.6,0-1.2,0.5-1.2,1.2v4.6H5.5v-2.1c0-0.6-0.5-1.2-1.2-1.2s-1.2,0.5-1.2,1.2v2.1H2.6c-0.6,0-1.2,0.5-1.2,1.2s0.5,1.2,1.2,1.2h0.6
|
||||
v2.1c0,0.6,0.5,1.2,1.2,1.2s1.2-0.5,1.2-1.2v-2.1h1.8v6.2c0,0.6,0.5,1.2,1.2,1.2c0.6,0,1.2-0.5,1.2-1.2v-6.2h1.8v12.1
|
||||
c0,0.6,0.5,1.2,1.2,1.2c0.6,0,1.2-0.5,1.2-1.2V12z"/>
|
||||
<path class="st0" d="M28.3,37v9.8c0,0.6,0.5,1.2,1.2,1.2s1.2-0.5,1.2-1.2V36.1C29.9,36.5,29.1,36.8,28.3,37z"/>
|
||||
<path class="st0" d="M25.3,37.5c-0.4,0-0.8,0-1.2-0.1v4.5c0,0.6,0.5,1.2,1.2,1.2s1.2-0.5,1.2-1.2v-4.5
|
||||
C26.1,37.4,25.7,37.5,25.3,37.5z"/>
|
||||
<path class="st0" d="M19.9,36.1v3.3c0,0.6,0.5,1.2,1.2,1.2c0.6,0,1.2-0.5,1.2-1.2V37C21.5,36.8,20.7,36.5,19.9,36.1z"/>
|
||||
<path class="st0" d="M25.3,17.2c-5,0-9,4-9,9c0,5,4,9,9,9s9-4,9-9C34.4,21.2,30.3,17.2,25.3,17.2z M30.3,26.1l-6,5.4
|
||||
c-0.1,0.1-0.2,0.1-0.4,0.1c-0.1,0-0.3,0-0.3-0.1c-0.3-0.1-0.5-0.4-0.5-0.5V20.9c0-0.1,0.2-0.4,0.4-0.5c0.1,0,0.2-0.1,0.3-0.1
|
||||
c0.2,0,0.3,0,0.4,0.1l6,4.8c0.1,0.1,0.2,0.3,0.2,0.4C30.5,25.8,30.4,26,30.3,26.1z"/>
|
||||
<rect x="12" y="24.6" class="st0" width="7.3" height="3"/>
|
||||
<rect x="32.9" y="24.6" class="st0" width="7.3" height="3"/>
|
||||
</svg>
|
After Width: | Height: | Size: 2.3 KiB |
|
@ -181,6 +181,31 @@ Item {
|
|||
root.avatarMixerOutPps + "pps, " +
|
||||
root.myAvatarSendRate.toFixed(2) + "hz";
|
||||
}
|
||||
StatText {
|
||||
visible: root.expanded;
|
||||
text: "Audio Mixer In: " + root.audioMixerInKbps + " kbps, " +
|
||||
root.audioMixerInPps + "pps";
|
||||
}
|
||||
StatText {
|
||||
visible: root.expanded;
|
||||
text: "Audio In Audio: " + root.audioAudioInboundPPS + " pps, " +
|
||||
"Silent: " + root.audioSilentInboundPPS + " pps";
|
||||
}
|
||||
StatText {
|
||||
visible: root.expanded;
|
||||
text: "Audio Mixer Out: " + root.audioMixerOutKbps + " kbps, " +
|
||||
root.audioMixerOutPps + "pps";
|
||||
}
|
||||
StatText {
|
||||
visible: root.expanded;
|
||||
text: "Audio Out Mic: " + root.audioMicOutboundPPS + " pps, " +
|
||||
"Silent: " + root.audioSilentOutboundPPS + " pps";
|
||||
}
|
||||
StatText {
|
||||
visible: root.expanded;
|
||||
text: "Audio Codec: " + root.audioCodec + " Noise Gate: " +
|
||||
root.audioNoiseGate;
|
||||
}
|
||||
StatText {
|
||||
visible: root.expanded;
|
||||
text: "Downloads: " + root.downloads + "/" + root.downloadLimit +
|
||||
|
|
244
interface/resources/qml/hifi/dialogs/TabletEntityStatistics.qml
Normal file
244
interface/resources/qml/hifi/dialogs/TabletEntityStatistics.qml
Normal file
|
@ -0,0 +1,244 @@
|
|||
//
|
||||
// TabletEntityStatistics.qml
|
||||
//
|
||||
// Created by Vlad Stelmahovsky on 3/11/17
|
||||
// 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 Qt.labs.settings 1.0
|
||||
|
||||
import "../../styles-uit"
|
||||
import "../../controls-uit" as HifiControls
|
||||
import "../../windows"
|
||||
|
||||
Rectangle {
|
||||
id: root
|
||||
objectName: "EntityStatistics"
|
||||
|
||||
property var eventBridge;
|
||||
signal sendToScript(var message);
|
||||
property bool isHMD: false
|
||||
|
||||
color: hifi.colors.baseGray
|
||||
|
||||
property int colorScheme: hifi.colorSchemes.dark
|
||||
|
||||
HifiConstants { id: hifi }
|
||||
|
||||
Component.onCompleted: {
|
||||
OctreeStats.startUpdates()
|
||||
}
|
||||
|
||||
Component.onDestruction: {
|
||||
OctreeStats.stopUpdates()
|
||||
}
|
||||
|
||||
Flickable {
|
||||
id: scrollView
|
||||
width: parent.width
|
||||
anchors.top: parent.top
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.bottomMargin: hifi.dimensions.tabletMenuHeader
|
||||
contentWidth: column.implicitWidth
|
||||
contentHeight: column.implicitHeight
|
||||
boundsBehavior: Flickable.StopAtBounds
|
||||
|
||||
Column {
|
||||
id: column
|
||||
anchors.margins: 10
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
y: hifi.dimensions.tabletMenuHeader //-bgNavBar
|
||||
spacing: 20
|
||||
|
||||
TabletEntityStatisticsItem {
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
titleText: qsTr("Elements on Servers:")
|
||||
text: OctreeStats.serverElements
|
||||
colorScheme: root.colorScheme
|
||||
color: OctreeStats.getColor()
|
||||
}
|
||||
|
||||
TabletEntityStatisticsItem {
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
titleText: qsTr("Local Elements:")
|
||||
text: OctreeStats.localElements
|
||||
colorScheme: root.colorScheme
|
||||
color: OctreeStats.getColor()
|
||||
}
|
||||
|
||||
TabletEntityStatisticsItem {
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
titleText: qsTr("Elements Memory:")
|
||||
text: OctreeStats.localElementsMemory
|
||||
colorScheme: root.colorScheme
|
||||
color: OctreeStats.getColor()
|
||||
}
|
||||
|
||||
TabletEntityStatisticsItem {
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
titleText: qsTr("Sending Mode:")
|
||||
text: OctreeStats.sendingMode
|
||||
colorScheme: root.colorScheme
|
||||
color: OctreeStats.getColor()
|
||||
}
|
||||
|
||||
TabletEntityStatisticsItem {
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
titleText: qsTr("Incoming Entity Packets:")
|
||||
text: OctreeStats.processedPackets
|
||||
colorScheme: root.colorScheme
|
||||
color: OctreeStats.getColor()
|
||||
}
|
||||
|
||||
TabletEntityStatisticsItem {
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
titleText: qsTr("Processed Packets Elements:")
|
||||
text: OctreeStats.processedPacketsElements
|
||||
colorScheme: root.colorScheme
|
||||
color: OctreeStats.getColor()
|
||||
}
|
||||
|
||||
TabletEntityStatisticsItem {
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
titleText: qsTr("Processed Packets Entities:")
|
||||
text: OctreeStats.processedPacketsEntities
|
||||
colorScheme: root.colorScheme
|
||||
color: OctreeStats.getColor()
|
||||
}
|
||||
|
||||
TabletEntityStatisticsItem {
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
titleText: qsTr("Processed Packets Timing:")
|
||||
text: OctreeStats.processedPacketsTiming
|
||||
colorScheme: root.colorScheme
|
||||
color: OctreeStats.getColor()
|
||||
}
|
||||
|
||||
TabletEntityStatisticsItem {
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
titleText: qsTr("Outbound Entity Packets:")
|
||||
text: OctreeStats.outboundEditPackets
|
||||
colorScheme: root.colorScheme
|
||||
color: OctreeStats.getColor()
|
||||
}
|
||||
|
||||
TabletEntityStatisticsItem {
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
titleText: qsTr("Entity Update Time:")
|
||||
text: OctreeStats.entityUpdateTime
|
||||
colorScheme: root.colorScheme
|
||||
color: OctreeStats.getColor()
|
||||
}
|
||||
|
||||
TabletEntityStatisticsItem {
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
titleText: qsTr("Entity Updates:")
|
||||
text: OctreeStats.entityUpdates
|
||||
colorScheme: root.colorScheme
|
||||
color: OctreeStats.getColor()
|
||||
}
|
||||
|
||||
Repeater {
|
||||
model: OctreeStats.serversNum
|
||||
|
||||
delegate: Column {
|
||||
id: serverColumn
|
||||
width: scrollView.width - 10
|
||||
x: 5
|
||||
spacing: 5
|
||||
|
||||
state: "less"
|
||||
|
||||
TabletEntityStatisticsItem {
|
||||
id: serverStats
|
||||
width: parent.width
|
||||
titleText: qsTr("Entity Server ") + (index+1) + ":"
|
||||
colorScheme: root.colorScheme
|
||||
color: OctreeStats.getColor()
|
||||
}
|
||||
|
||||
Row {
|
||||
id: buttonsRow
|
||||
width: parent.width
|
||||
height: 30
|
||||
spacing: 10
|
||||
|
||||
HifiControls.Button {
|
||||
id: moreButton
|
||||
color: hifi.buttons.blue
|
||||
colorScheme: root.colorScheme
|
||||
width: parent.width / 2 - 10
|
||||
height: 30
|
||||
onClicked: {
|
||||
if (serverColumn.state === "less") {
|
||||
serverColumn.state = "more"
|
||||
} else if (serverColumn.state === "more") {
|
||||
serverColumn.state = "most"
|
||||
} else {
|
||||
serverColumn.state = "more"
|
||||
}
|
||||
}
|
||||
}
|
||||
HifiControls.Button {
|
||||
id: mostButton
|
||||
color: hifi.buttons.blue
|
||||
colorScheme: root.colorScheme
|
||||
height: 30
|
||||
width: parent.width / 2 - 10
|
||||
onClicked: {
|
||||
if (serverColumn.state === "less") {
|
||||
serverColumn.state = "most"
|
||||
} else if (serverColumn.state === "more") {
|
||||
serverColumn.state = "less"
|
||||
} else {
|
||||
serverColumn.state = "less"
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
states: [
|
||||
State {
|
||||
name: "less"
|
||||
PropertyChanges { target: moreButton; text: qsTr("more..."); }
|
||||
PropertyChanges { target: mostButton; text: qsTr("most..."); }
|
||||
PropertyChanges { target: serverStats; text: OctreeStats.servers[index*3]; }
|
||||
},
|
||||
State {
|
||||
name: "more"
|
||||
PropertyChanges { target: moreButton; text: qsTr("most..."); }
|
||||
PropertyChanges { target: mostButton; text: qsTr("less..."); }
|
||||
PropertyChanges { target: serverStats; text: OctreeStats.servers[index*3] +
|
||||
OctreeStats.servers[index*3 + 1]; }
|
||||
},
|
||||
State {
|
||||
name: "most"
|
||||
PropertyChanges { target: moreButton; text: qsTr("less..."); }
|
||||
PropertyChanges { target: mostButton; text: qsTr("least..."); }
|
||||
PropertyChanges { target: serverStats; text: OctreeStats.servers[index*3] +
|
||||
OctreeStats.servers[index*3 + 1] +
|
||||
OctreeStats.servers[index*3 + 2]; }
|
||||
}
|
||||
]
|
||||
} //servers column
|
||||
} //repeater
|
||||
} //column
|
||||
} //flickable
|
||||
}
|
|
@ -0,0 +1,49 @@
|
|||
//
|
||||
// TabletEntityStatistics.qml
|
||||
//
|
||||
// Created by Vlad Stelmahovsky on 3/11/17
|
||||
// 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 Qt.labs.settings 1.0
|
||||
|
||||
import "../../styles-uit"
|
||||
import "../../controls-uit" as HifiControls
|
||||
|
||||
Column {
|
||||
id: root
|
||||
property int colorScheme: hifi.colorSchemes.dark
|
||||
|
||||
property alias titleText: titleLabel.text
|
||||
property alias text: valueLabel.text
|
||||
property alias color: valueLabel.color
|
||||
|
||||
HifiConstants { id: hifi }
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
spacing: 10
|
||||
|
||||
HifiControls.Label {
|
||||
id: titleLabel
|
||||
size: 20
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
colorScheme: root.colorScheme
|
||||
}
|
||||
|
||||
RalewaySemiBold {
|
||||
id: valueLabel
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
wrapMode: Text.WordWrap
|
||||
size: 16
|
||||
color: enabled ? (root.colorScheme == hifi.colorSchemes.light ? hifi.colors.lightGray : hifi.colors.lightGrayText)
|
||||
: (root.colorScheme == hifi.colorSchemes.light ? hifi.colors.lightGrayText : hifi.colors.baseGrayHighlight);
|
||||
}
|
||||
}
|
|
@ -79,266 +79,297 @@ Rectangle {
|
|||
scripts.stopAllScripts();
|
||||
}
|
||||
|
||||
Column {
|
||||
Flickable {
|
||||
id: flickable
|
||||
width: parent.width
|
||||
HifiControls.TabletContentSection {
|
||||
name: "Currently Running"
|
||||
isFirst: true
|
||||
height: parent.height - (keyboard.raised ? keyboard.raisedHeight : 0)
|
||||
contentWidth: parent.width
|
||||
contentHeight: column.childrenRect.height
|
||||
clip: true
|
||||
|
||||
HifiControls.VerticalSpacer {}
|
||||
Column {
|
||||
id: column
|
||||
width: parent.width
|
||||
HifiControls.TabletContentSection {
|
||||
id: firstSection
|
||||
name: "Currently Running"
|
||||
isFirst: true
|
||||
|
||||
Row {
|
||||
spacing: hifi.dimensions.contentSpacing.x
|
||||
HifiControls.VerticalSpacer {}
|
||||
|
||||
HifiControls.Button {
|
||||
text: "Reload All"
|
||||
color: hifi.buttons.black
|
||||
onClicked: reloadAll()
|
||||
}
|
||||
Row {
|
||||
spacing: hifi.dimensions.contentSpacing.x
|
||||
|
||||
HifiControls.Button {
|
||||
text: "Remove All"
|
||||
color: hifi.buttons.red
|
||||
onClicked: stopAll()
|
||||
}
|
||||
}
|
||||
|
||||
HifiControls.VerticalSpacer {
|
||||
height: hifi.dimensions.controlInterlineHeight + 2 // Add space for border
|
||||
}
|
||||
|
||||
HifiControls.Table {
|
||||
model: runningScriptsModel
|
||||
id: table
|
||||
height: 185
|
||||
colorScheme: hifi.colorSchemes.dark
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
expandSelectedRow: true
|
||||
|
||||
itemDelegate: Item {
|
||||
anchors {
|
||||
left: parent ? parent.left : undefined
|
||||
leftMargin: hifi.dimensions.tablePadding
|
||||
right: parent ? parent.right : undefined
|
||||
rightMargin: hifi.dimensions.tablePadding
|
||||
HifiControls.Button {
|
||||
text: "Reload All"
|
||||
color: hifi.buttons.black
|
||||
onClicked: reloadAll()
|
||||
}
|
||||
|
||||
FiraSansSemiBold {
|
||||
id: textItem
|
||||
text: styleData.value
|
||||
size: hifi.fontSizes.tableText
|
||||
color: table.colorScheme == hifi.colorSchemes.light
|
||||
? (styleData.selected ? hifi.colors.black : hifi.colors.baseGrayHighlight)
|
||||
: (styleData.selected ? hifi.colors.black : hifi.colors.lightGrayText)
|
||||
HifiControls.Button {
|
||||
text: "Remove All"
|
||||
color: hifi.buttons.red
|
||||
onClicked: stopAll()
|
||||
}
|
||||
}
|
||||
|
||||
HifiControls.VerticalSpacer {
|
||||
height: hifi.dimensions.controlInterlineHeight + 2 // Add space for border
|
||||
}
|
||||
|
||||
HifiControls.Table {
|
||||
model: runningScriptsModel
|
||||
id: table
|
||||
height: 185
|
||||
colorScheme: hifi.colorSchemes.dark
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
expandSelectedRow: true
|
||||
|
||||
itemDelegate: Item {
|
||||
anchors {
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
top: parent.top
|
||||
topMargin: 3
|
||||
left: parent ? parent.left : undefined
|
||||
leftMargin: hifi.dimensions.tablePadding
|
||||
right: parent ? parent.right : undefined
|
||||
rightMargin: hifi.dimensions.tablePadding
|
||||
}
|
||||
|
||||
HiFiGlyphs {
|
||||
id: reloadButton
|
||||
text: hifi.glyphs.reloadSmall
|
||||
color: reloadButtonArea.pressed ? hifi.colors.white : parent.color
|
||||
FiraSansSemiBold {
|
||||
id: textItem
|
||||
text: styleData.value
|
||||
size: hifi.fontSizes.tableText
|
||||
color: table.colorScheme == hifi.colorSchemes.light
|
||||
? (styleData.selected ? hifi.colors.black : hifi.colors.baseGrayHighlight)
|
||||
: (styleData.selected ? hifi.colors.black : hifi.colors.lightGrayText)
|
||||
anchors {
|
||||
top: parent.top
|
||||
right: stopButton.left
|
||||
verticalCenter: parent.verticalCenter
|
||||
}
|
||||
MouseArea {
|
||||
id: reloadButtonArea
|
||||
anchors { fill: parent; margins: -2 }
|
||||
onClicked: reloadScript(model.url)
|
||||
}
|
||||
}
|
||||
|
||||
HiFiGlyphs {
|
||||
id: stopButton
|
||||
text: hifi.glyphs.closeSmall
|
||||
color: stopButtonArea.pressed ? hifi.colors.white : parent.color
|
||||
anchors {
|
||||
top: parent.top
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
verticalCenter: parent.verticalCenter
|
||||
top: parent.top
|
||||
topMargin: 3
|
||||
}
|
||||
MouseArea {
|
||||
id: stopButtonArea
|
||||
anchors { fill: parent; margins: -2 }
|
||||
onClicked: stopScript(model.url)
|
||||
|
||||
HiFiGlyphs {
|
||||
id: reloadButton
|
||||
text: hifi.glyphs.reloadSmall
|
||||
color: reloadButtonArea.pressed ? hifi.colors.white : parent.color
|
||||
anchors {
|
||||
top: parent.top
|
||||
right: stopButton.left
|
||||
verticalCenter: parent.verticalCenter
|
||||
}
|
||||
MouseArea {
|
||||
id: reloadButtonArea
|
||||
anchors { fill: parent; margins: -2 }
|
||||
onClicked: reloadScript(model.url)
|
||||
}
|
||||
}
|
||||
|
||||
HiFiGlyphs {
|
||||
id: stopButton
|
||||
text: hifi.glyphs.closeSmall
|
||||
color: stopButtonArea.pressed ? hifi.colors.white : parent.color
|
||||
anchors {
|
||||
top: parent.top
|
||||
right: parent.right
|
||||
verticalCenter: parent.verticalCenter
|
||||
}
|
||||
MouseArea {
|
||||
id: stopButtonArea
|
||||
anchors { fill: parent; margins: -2 }
|
||||
onClicked: stopScript(model.url)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
FiraSansSemiBold {
|
||||
text: runningScriptsModel.get(styleData.row) ? runningScriptsModel.get(styleData.row).url : ""
|
||||
elide: Text.ElideMiddle
|
||||
size: hifi.fontSizes.tableText
|
||||
color: table.colorScheme == hifi.colorSchemes.light
|
||||
? (styleData.selected ? hifi.colors.black : hifi.colors.lightGray)
|
||||
: (styleData.selected ? hifi.colors.black : hifi.colors.lightGrayText)
|
||||
anchors {
|
||||
top: textItem.bottom
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
}
|
||||
visible: styleData.selected
|
||||
}
|
||||
}
|
||||
|
||||
TableViewColumn {
|
||||
role: "name"
|
||||
}
|
||||
}
|
||||
|
||||
HifiControls.VerticalSpacer {
|
||||
height: hifi.dimensions.controlInterlineHeight + 2 // Add space for border
|
||||
}
|
||||
}
|
||||
|
||||
HifiControls.TabletContentSection {
|
||||
name: "Load Scripts"
|
||||
|
||||
HifiControls.VerticalSpacer {}
|
||||
|
||||
Row {
|
||||
spacing: hifi.dimensions.contentSpacing.x
|
||||
|
||||
HifiControls.Button {
|
||||
text: "from URL"
|
||||
color: hifi.buttons.black
|
||||
height: 26
|
||||
onClicked: fromUrlTimer.running = true
|
||||
|
||||
// For some reason trigginer an API that enters
|
||||
// an internal event loop directly from the button clicked
|
||||
// trigger below causes the appliction to behave oddly.
|
||||
// Most likely because the button onClicked handling is never
|
||||
// completed until the function returns.
|
||||
// FIXME find a better way of handling the input dialogs that
|
||||
// doesn't trigger this.
|
||||
Timer {
|
||||
id: fromUrlTimer
|
||||
interval: 5
|
||||
repeat: false
|
||||
running: false
|
||||
onTriggered: ApplicationInterface.loadScriptURLDialog();
|
||||
}
|
||||
}
|
||||
|
||||
HifiControls.Button {
|
||||
text: "from Disk"
|
||||
color: hifi.buttons.black
|
||||
height: 26
|
||||
onClicked: fromDiskTimer.running = true
|
||||
|
||||
Timer {
|
||||
id: fromDiskTimer
|
||||
interval: 5
|
||||
repeat: false
|
||||
running: false
|
||||
onTriggered: ApplicationInterface.loadDialog();
|
||||
}
|
||||
}
|
||||
|
||||
HifiControls.Button {
|
||||
text: "Load Defaults"
|
||||
color: hifi.buttons.black
|
||||
height: 26
|
||||
onClicked: loadDefaults()
|
||||
}
|
||||
}
|
||||
|
||||
HifiControls.VerticalSpacer {}
|
||||
|
||||
HifiControls.TextField {
|
||||
id: filterEdit
|
||||
isSearchField: true
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
colorScheme: hifi.colorSchemes.dark
|
||||
placeholderText: "Filter"
|
||||
onTextChanged: scriptsModel.filterRegExp = new RegExp("^.*" + text + ".*$", "i")
|
||||
Component.onCompleted: scriptsModel.filterRegExp = new RegExp("^.*$", "i")
|
||||
onActiveFocusChanged: {
|
||||
// raise the keyboard
|
||||
keyboard.raised = activeFocus;
|
||||
|
||||
// scroll to the bottom of the content area.
|
||||
if (activeFocus) {
|
||||
flickable.contentY = (flickable.contentHeight - flickable.height);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
HifiControls.VerticalSpacer {
|
||||
height: hifi.dimensions.controlInterlineHeight + 2 // Add space for border
|
||||
}
|
||||
|
||||
HifiControls.Tree {
|
||||
id: treeView
|
||||
height: 155
|
||||
treeModel: scriptsModel
|
||||
colorScheme: hifi.colorSchemes.dark
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
}
|
||||
|
||||
HifiControls.VerticalSpacer {
|
||||
height: hifi.dimensions.controlInterlineHeight + 2 // Add space for border
|
||||
}
|
||||
|
||||
HifiControls.TextField {
|
||||
id: selectedScript
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: loadButton.width + hifi.dimensions.contentSpacing.x
|
||||
|
||||
colorScheme: hifi.colorSchemes.dark
|
||||
readOnly: true
|
||||
|
||||
Connections {
|
||||
target: treeView
|
||||
onCurrentIndexChanged: {
|
||||
var path = scriptsModel.data(treeView.currentIndex, 0x100)
|
||||
if (path) {
|
||||
selectedScript.text = path
|
||||
} else {
|
||||
selectedScript.text = ""
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
FiraSansSemiBold {
|
||||
text: runningScriptsModel.get(styleData.row) ? runningScriptsModel.get(styleData.row).url : ""
|
||||
elide: Text.ElideMiddle
|
||||
size: hifi.fontSizes.tableText
|
||||
color: table.colorScheme == hifi.colorSchemes.light
|
||||
? (styleData.selected ? hifi.colors.black : hifi.colors.lightGray)
|
||||
: (styleData.selected ? hifi.colors.black : hifi.colors.lightGrayText)
|
||||
anchors {
|
||||
top: textItem.bottom
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
}
|
||||
visible: styleData.selected
|
||||
}
|
||||
}
|
||||
|
||||
TableViewColumn {
|
||||
role: "name"
|
||||
}
|
||||
}
|
||||
|
||||
HifiControls.VerticalSpacer {
|
||||
height: hifi.dimensions.controlInterlineHeight + 2 // Add space for border
|
||||
}
|
||||
}
|
||||
|
||||
HifiControls.TabletContentSection {
|
||||
name: "Load Scripts"
|
||||
|
||||
HifiControls.VerticalSpacer {}
|
||||
|
||||
Row {
|
||||
spacing: hifi.dimensions.contentSpacing.x
|
||||
|
||||
HifiControls.Button {
|
||||
text: "from URL"
|
||||
color: hifi.buttons.black
|
||||
height: 26
|
||||
onClicked: fromUrlTimer.running = true
|
||||
|
||||
// For some reason trigginer an API that enters
|
||||
// an internal event loop directly from the button clicked
|
||||
// trigger below causes the appliction to behave oddly.
|
||||
// Most likely because the button onClicked handling is never
|
||||
// completed until the function returns.
|
||||
// FIXME find a better way of handling the input dialogs that
|
||||
// doesn't trigger this.
|
||||
Timer {
|
||||
id: fromUrlTimer
|
||||
interval: 5
|
||||
repeat: false
|
||||
running: false
|
||||
onTriggered: ApplicationInterface.loadScriptURLDialog();
|
||||
}
|
||||
}
|
||||
|
||||
HifiControls.Button {
|
||||
text: "from Disk"
|
||||
color: hifi.buttons.black
|
||||
height: 26
|
||||
onClicked: fromDiskTimer.running = true
|
||||
|
||||
Timer {
|
||||
id: fromDiskTimer
|
||||
interval: 5
|
||||
repeat: false
|
||||
running: false
|
||||
onTriggered: ApplicationInterface.loadDialog();
|
||||
}
|
||||
}
|
||||
|
||||
HifiControls.Button {
|
||||
text: "Load Defaults"
|
||||
color: hifi.buttons.black
|
||||
height: 26
|
||||
onClicked: loadDefaults()
|
||||
}
|
||||
}
|
||||
|
||||
HifiControls.VerticalSpacer {}
|
||||
|
||||
HifiControls.TextField {
|
||||
id: filterEdit
|
||||
isSearchField: true
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
colorScheme: hifi.colorSchemes.dark
|
||||
placeholderText: "Filter"
|
||||
onTextChanged: scriptsModel.filterRegExp = new RegExp("^.*" + text + ".*$", "i")
|
||||
Component.onCompleted: scriptsModel.filterRegExp = new RegExp("^.*$", "i")
|
||||
}
|
||||
|
||||
HifiControls.VerticalSpacer {
|
||||
height: hifi.dimensions.controlInterlineHeight + 2 // Add space for border
|
||||
}
|
||||
|
||||
HifiControls.Tree {
|
||||
id: treeView
|
||||
height: 155
|
||||
treeModel: scriptsModel
|
||||
colorScheme: hifi.colorSchemes.dark
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
}
|
||||
|
||||
HifiControls.VerticalSpacer {
|
||||
height: hifi.dimensions.controlInterlineHeight + 2 // Add space for border
|
||||
}
|
||||
|
||||
HifiControls.TextField {
|
||||
id: selectedScript
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: loadButton.width + hifi.dimensions.contentSpacing.x
|
||||
|
||||
colorScheme: hifi.colorSchemes.dark
|
||||
readOnly: true
|
||||
|
||||
Connections {
|
||||
target: treeView
|
||||
onCurrentIndexChanged: {
|
||||
var path = scriptsModel.data(treeView.currentIndex, 0x100)
|
||||
if (path) {
|
||||
selectedScript.text = path
|
||||
} else {
|
||||
selectedScript.text = ""
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
// Take the loadButton out of the column flow.
|
||||
id: loadButtonContainer
|
||||
anchors.top: selectedScript.top
|
||||
anchors.right: parent.right
|
||||
|
||||
HifiControls.Button {
|
||||
id: loadButton
|
||||
Item {
|
||||
// Take the loadButton out of the column flow.
|
||||
id: loadButtonContainer
|
||||
anchors.top: selectedScript.top
|
||||
anchors.right: parent.right
|
||||
|
||||
text: "Load"
|
||||
color: hifi.buttons.blue
|
||||
enabled: selectedScript.text != ""
|
||||
onClicked: root.loadScript(selectedScript.text)
|
||||
HifiControls.Button {
|
||||
id: loadButton
|
||||
anchors.right: parent.right
|
||||
|
||||
text: "Load"
|
||||
color: hifi.buttons.blue
|
||||
enabled: selectedScript.text != ""
|
||||
onClicked: root.loadScript(selectedScript.text)
|
||||
}
|
||||
}
|
||||
|
||||
HifiControls.VerticalSpacer {
|
||||
height: hifi.dimensions.controlInterlineHeight - (!isHMD ? 3 : 0)
|
||||
}
|
||||
|
||||
HifiControls.TextAction {
|
||||
id: directoryButton
|
||||
icon: hifi.glyphs.script
|
||||
iconSize: 24
|
||||
text: "Reveal Scripts Folder"
|
||||
onClicked: fileDialogHelper.openDirectory(scripts.defaultScriptsPath)
|
||||
colorScheme: hifi.colorSchemes.dark
|
||||
anchors.left: parent.left
|
||||
visible: !isHMD
|
||||
}
|
||||
|
||||
HifiControls.VerticalSpacer {
|
||||
height: hifi.dimensions.controlInterlineHeight - 3
|
||||
visible: !isHMD
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
HifiControls.VerticalSpacer {
|
||||
height: hifi.dimensions.controlInterlineHeight - (!isHMD ? 3 : 0)
|
||||
}
|
||||
|
||||
HifiControls.TextAction {
|
||||
id: directoryButton
|
||||
icon: hifi.glyphs.script
|
||||
iconSize: 24
|
||||
text: "Reveal Scripts Folder"
|
||||
onClicked: fileDialogHelper.openDirectory(scripts.defaultScriptsPath)
|
||||
colorScheme: hifi.colorSchemes.dark
|
||||
anchors.left: parent.left
|
||||
visible: !isHMD
|
||||
}
|
||||
|
||||
HifiControls.VerticalSpacer {
|
||||
height: hifi.dimensions.controlInterlineHeight - 3
|
||||
visible: !isHMD
|
||||
}
|
||||
HifiControls.Keyboard {
|
||||
id: keyboard
|
||||
raised: false
|
||||
numeric: false
|
||||
anchors {
|
||||
bottom: parent.bottom
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,7 +17,7 @@ Item {
|
|||
id: root
|
||||
anchors.fill: parent
|
||||
objectName: "tabletMenuHandlerItem"
|
||||
|
||||
|
||||
StackView {
|
||||
anchors.fill: parent
|
||||
id: d
|
||||
|
@ -54,6 +54,10 @@ Item {
|
|||
d.currentItem.focus = true;
|
||||
d.currentItem.forceActiveFocus();
|
||||
breadcrumbText.text = d.currentItem.title;
|
||||
if (typeof bgNavBar !== "undefined") {
|
||||
d.currentItem.y = bgNavBar.height;
|
||||
d.currentItem.height -= bgNavBar.height;
|
||||
}
|
||||
}
|
||||
|
||||
function popSource() {
|
||||
|
|
|
@ -173,6 +173,7 @@
|
|||
#include "ui/overlays/Overlays.h"
|
||||
#include "Util.h"
|
||||
#include "InterfaceParentFinder.h"
|
||||
#include "ui/OctreeStatsProvider.h"
|
||||
|
||||
#include "FrameTimingsScriptingInterface.h"
|
||||
#include <GPUIdent.h>
|
||||
|
@ -523,6 +524,7 @@ bool setupEssentials(int& argc, char** argv) {
|
|||
DependencyManager::set<OffscreenQmlSurfaceCache>();
|
||||
DependencyManager::set<EntityScriptClient>();
|
||||
DependencyManager::set<EntityScriptServerLogClient>();
|
||||
DependencyManager::set<OctreeStatsProvider>(nullptr, qApp->getOcteeSceneStats());
|
||||
return previousSessionCrashed;
|
||||
}
|
||||
|
||||
|
@ -550,7 +552,7 @@ const float DEFAULT_DESKTOP_TABLET_SCALE_PERCENT = 75.0f;
|
|||
const bool DEFAULT_DESKTOP_TABLET_BECOMES_TOOLBAR = true;
|
||||
const bool DEFAULT_HMD_TABLET_BECOMES_TOOLBAR = false;
|
||||
const bool DEFAULT_TABLET_VISIBLE_TO_OTHERS = false;
|
||||
const bool DEFAULT_PREFER_AVATAR_FINGER_OVER_STYLUS = false;
|
||||
const bool DEFAULT_PREFER_AVATAR_FINGER_OVER_STYLUS = true;
|
||||
|
||||
Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bool runServer, QString runServerPathOption) :
|
||||
QApplication(argc, argv),
|
||||
|
@ -1188,6 +1190,10 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
|
|||
// set the local loopback interface for local sounds
|
||||
AudioInjector::setLocalAudioInterface(audioIO.data());
|
||||
AudioScriptingInterface::getInstance().setLocalAudioInterface(audioIO.data());
|
||||
connect(audioIO.data(), &AudioClient::noiseGateOpened, &AudioScriptingInterface::getInstance(), &AudioScriptingInterface::noiseGateOpened);
|
||||
connect(audioIO.data(), &AudioClient::noiseGateClosed, &AudioScriptingInterface::getInstance(), &AudioScriptingInterface::noiseGateClosed);
|
||||
connect(audioIO.data(), &AudioClient::inputReceived, &AudioScriptingInterface::getInstance(), &AudioScriptingInterface::inputReceived);
|
||||
|
||||
|
||||
this->installEventFilter(this);
|
||||
|
||||
|
@ -1625,9 +1631,10 @@ QString Application::getUserAgent() {
|
|||
|
||||
void Application::toggleTabletUI() const {
|
||||
auto tabletScriptingInterface = DependencyManager::get<TabletScriptingInterface>();
|
||||
auto hmd = DependencyManager::get<HMDScriptingInterface>();
|
||||
TabletProxy* tablet = dynamic_cast<TabletProxy*>(tabletScriptingInterface->getTablet("com.highfidelity.interface.tablet.system"));
|
||||
bool messageOpen = tablet->isMessageDialogOpen();
|
||||
if (!messageOpen) {
|
||||
if (!messageOpen || (messageOpen && !hmd->getShouldShowTablet())) {
|
||||
auto HMD = DependencyManager::get<HMDScriptingInterface>();
|
||||
HMD->toggleShouldShowTablet();
|
||||
}
|
||||
|
@ -1803,6 +1810,7 @@ Application::~Application() {
|
|||
DependencyManager::destroy<GeometryCache>();
|
||||
DependencyManager::destroy<ScriptCache>();
|
||||
DependencyManager::destroy<SoundCache>();
|
||||
DependencyManager::destroy<OctreeStatsProvider>();
|
||||
|
||||
ResourceManager::cleanup();
|
||||
|
||||
|
@ -1950,6 +1958,8 @@ void Application::initializeUi() {
|
|||
rootContext->setContextProperty("ApplicationInterface", this);
|
||||
rootContext->setContextProperty("Audio", &AudioScriptingInterface::getInstance());
|
||||
rootContext->setContextProperty("AudioStats", DependencyManager::get<AudioClient>()->getStats().data());
|
||||
rootContext->setContextProperty("AudioScope", DependencyManager::get<AudioScope>().data());
|
||||
|
||||
rootContext->setContextProperty("Controller", DependencyManager::get<controller::ScriptingInterface>().data());
|
||||
rootContext->setContextProperty("Entities", DependencyManager::get<EntityScriptingInterface>().data());
|
||||
_fileDownload = new FileScriptingInterface(engine);
|
||||
|
@ -3178,7 +3188,23 @@ void Application::mousePressEvent(QMouseEvent* event) {
|
|||
}
|
||||
}
|
||||
|
||||
void Application::mouseDoublePressEvent(QMouseEvent* event) const {
|
||||
void Application::mouseDoublePressEvent(QMouseEvent* event) {
|
||||
auto offscreenUi = DependencyManager::get<OffscreenUi>();
|
||||
auto eventPosition = getApplicationCompositor().getMouseEventPosition(event);
|
||||
QPointF transformedPos = offscreenUi->mapToVirtualScreen(eventPosition, _glWidget);
|
||||
QMouseEvent mappedEvent(event->type(),
|
||||
transformedPos,
|
||||
event->screenPos(), event->button(),
|
||||
event->buttons(), event->modifiers());
|
||||
|
||||
if (!_aboutToQuit) {
|
||||
getOverlays().mouseDoublePressEvent(&mappedEvent);
|
||||
if (!_controllerScriptingInterface->areEntityClicksCaptured()) {
|
||||
getEntities()->mouseDoublePressEvent(&mappedEvent);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// if one of our scripts have asked to capture this event, then stop processing it
|
||||
if (_controllerScriptingInterface->isMouseCaptured()) {
|
||||
return;
|
||||
|
@ -5525,6 +5551,7 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEngine* scri
|
|||
scriptEngine->registerGlobalObject("Settings", SettingsScriptingInterface::getInstance());
|
||||
scriptEngine->registerGlobalObject("AudioDevice", AudioDeviceScriptingInterface::getInstance());
|
||||
scriptEngine->registerGlobalObject("AudioStats", DependencyManager::get<AudioClient>()->getStats().data());
|
||||
scriptEngine->registerGlobalObject("AudioScope", DependencyManager::get<AudioScope>().data());
|
||||
|
||||
// Caches
|
||||
scriptEngine->registerGlobalObject("AnimationCache", DependencyManager::get<AnimationCache>().data());
|
||||
|
@ -6375,6 +6402,18 @@ void Application::loadLODToolsDialog() {
|
|||
|
||||
}
|
||||
|
||||
|
||||
void Application::loadEntityStatisticsDialog() {
|
||||
auto tabletScriptingInterface = DependencyManager::get<TabletScriptingInterface>();
|
||||
auto tablet = dynamic_cast<TabletProxy*>(tabletScriptingInterface->getTablet("com.highfidelity.interface.tablet.system"));
|
||||
if (tablet->getToolbarMode() || (!tablet->getTabletRoot() && !isHMDMode())) {
|
||||
auto dialogsManager = DependencyManager::get<DialogsManager>();
|
||||
dialogsManager->octreeStatsDetails();
|
||||
} else {
|
||||
tablet->pushOntoStack("../../hifi/dialogs/TabletEntityStatistics.qml");
|
||||
}
|
||||
|
||||
}
|
||||
void Application::toggleLogDialog() {
|
||||
if (! _logDialog) {
|
||||
_logDialog = new LogDialog(nullptr, getLogger());
|
||||
|
|
|
@ -404,6 +404,7 @@ public slots:
|
|||
|
||||
Q_INVOKABLE void toggleMuteAudio();
|
||||
void loadLODToolsDialog();
|
||||
void loadEntityStatisticsDialog();
|
||||
|
||||
private slots:
|
||||
void showDesktop();
|
||||
|
@ -497,7 +498,7 @@ private:
|
|||
|
||||
void mouseMoveEvent(QMouseEvent* event);
|
||||
void mousePressEvent(QMouseEvent* event);
|
||||
void mouseDoublePressEvent(QMouseEvent* event) const;
|
||||
void mouseDoublePressEvent(QMouseEvent* event);
|
||||
void mouseReleaseEvent(QMouseEvent* event);
|
||||
|
||||
void touchBeginEvent(QTouchEvent* event);
|
||||
|
|
|
@ -547,8 +547,10 @@ Menu::Menu() {
|
|||
|
||||
// Developer > Entities >>>
|
||||
MenuWrapper* entitiesOptionsMenu = developerMenu->addMenu("Entities");
|
||||
|
||||
addActionToQMenuAndActionHash(entitiesOptionsMenu, MenuOption::OctreeStats, 0,
|
||||
dialogsManager.data(), SLOT(octreeStatsDetails()));
|
||||
qApp, SLOT(loadEntityStatisticsDialog()));
|
||||
|
||||
addCheckableActionToQMenuAndActionHash(entitiesOptionsMenu, MenuOption::ShowRealtimeEntityStats);
|
||||
|
||||
// Developer > Network >>>
|
||||
|
|
|
@ -52,12 +52,14 @@ AudioScope::AudioScope() :
|
|||
connect(audioIO.data(), &AudioClient::inputReceived, this, &AudioScope::addInputToScope);
|
||||
}
|
||||
|
||||
void AudioScope::toggle() {
|
||||
_isEnabled = !_isEnabled;
|
||||
if (_isEnabled) {
|
||||
allocateScope();
|
||||
} else {
|
||||
freeScope();
|
||||
void AudioScope::setVisible(bool visible) {
|
||||
if (_isEnabled != visible) {
|
||||
_isEnabled = visible;
|
||||
if (_isEnabled) {
|
||||
allocateScope();
|
||||
} else {
|
||||
freeScope();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -34,8 +34,14 @@ public:
|
|||
void render(RenderArgs* renderArgs, int width, int height);
|
||||
|
||||
public slots:
|
||||
void toggle();
|
||||
void toggle() { setVisible(!_isEnabled); }
|
||||
void setVisible(bool visible);
|
||||
bool getVisible() const { return _isEnabled; }
|
||||
|
||||
void togglePause() { _isPaused = !_isPaused; }
|
||||
void setPause(bool paused) { _isPaused = paused; }
|
||||
bool getPause() { return _isPaused; }
|
||||
|
||||
void selectAudioScopeFiveFrames();
|
||||
void selectAudioScopeTwentyFrames();
|
||||
void selectAudioScopeFiftyFrames();
|
||||
|
@ -74,7 +80,6 @@ private:
|
|||
int _inputID;
|
||||
int _outputLeftID;
|
||||
int _outputRightD;
|
||||
|
||||
};
|
||||
|
||||
#endif // hifi_AudioScope_h
|
||||
|
|
377
interface/src/ui/OctreeStatsProvider.cpp
Normal file
377
interface/src/ui/OctreeStatsProvider.cpp
Normal file
|
@ -0,0 +1,377 @@
|
|||
//
|
||||
// OctreeStatsProvider.cpp
|
||||
// interface/src/ui
|
||||
//
|
||||
// Created by Vlad Stelmahovsky on 3/12/17.
|
||||
// Copyright 2013 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include "Application.h"
|
||||
|
||||
#include "../octree/OctreePacketProcessor.h"
|
||||
#include "ui/OctreeStatsProvider.h"
|
||||
|
||||
OctreeStatsProvider::OctreeStatsProvider(QObject* parent, NodeToOctreeSceneStats* model) :
|
||||
QObject(parent),
|
||||
_model(model)
|
||||
, _statCount(0)
|
||||
, _averageUpdatesPerSecond(SAMPLES_PER_SECOND)
|
||||
{
|
||||
//schedule updates
|
||||
connect(&_updateTimer, &QTimer::timeout, this, &OctreeStatsProvider::updateOctreeStatsData);
|
||||
_updateTimer.setInterval(100);
|
||||
//timer will be rescheduled on each new timeout
|
||||
_updateTimer.setSingleShot(true);
|
||||
}
|
||||
|
||||
/*
|
||||
* Start updates statistics
|
||||
*/
|
||||
void OctreeStatsProvider::startUpdates() {
|
||||
_updateTimer.start();
|
||||
}
|
||||
|
||||
/*
|
||||
* Stop updates statistics
|
||||
*/
|
||||
void OctreeStatsProvider::stopUpdates() {
|
||||
_updateTimer.stop();
|
||||
}
|
||||
|
||||
QColor OctreeStatsProvider::getColor() const {
|
||||
static int statIndex = 1;
|
||||
static quint32 rotatingColors[] = { GREENISH, YELLOWISH, GREYISH };
|
||||
quint32 colorRGBA = rotatingColors[statIndex % (sizeof(rotatingColors)/sizeof(rotatingColors[0]))];
|
||||
quint32 rgb = colorRGBA >> 8;
|
||||
const quint32 colorpart1 = 0xfefefeu;
|
||||
const quint32 colorpart2 = 0xf8f8f8;
|
||||
rgb = ((rgb & colorpart1) >> 1) + ((rgb & colorpart2) >> 3);
|
||||
statIndex++;
|
||||
return QColor::fromRgb(rgb);
|
||||
}
|
||||
|
||||
OctreeStatsProvider::~OctreeStatsProvider() {
|
||||
_updateTimer.stop();
|
||||
}
|
||||
|
||||
int OctreeStatsProvider::serversNum() const {
|
||||
return m_serversNum;
|
||||
}
|
||||
|
||||
void OctreeStatsProvider::updateOctreeStatsData() {
|
||||
|
||||
// Processed Entities Related stats
|
||||
auto entities = qApp->getEntities();
|
||||
auto entitiesTree = entities->getTree();
|
||||
|
||||
// Do this ever paint event... even if we don't update
|
||||
auto totalTrackedEdits = entitiesTree->getTotalTrackedEdits();
|
||||
|
||||
const quint64 SAMPLING_WINDOW = USECS_PER_SECOND / SAMPLES_PER_SECOND;
|
||||
quint64 now = usecTimestampNow();
|
||||
quint64 sinceLastWindow = now - _lastWindowAt;
|
||||
auto editsInLastWindow = totalTrackedEdits - _lastKnownTrackedEdits;
|
||||
float sinceLastWindowInSeconds = (float)sinceLastWindow / (float)USECS_PER_SECOND;
|
||||
float recentUpdatesPerSecond = (float)editsInLastWindow / sinceLastWindowInSeconds;
|
||||
if (sinceLastWindow > SAMPLING_WINDOW) {
|
||||
_averageUpdatesPerSecond.updateAverage(recentUpdatesPerSecond);
|
||||
_lastWindowAt = now;
|
||||
_lastKnownTrackedEdits = totalTrackedEdits;
|
||||
}
|
||||
|
||||
// Only refresh our stats every once in a while, unless asked for realtime
|
||||
quint64 REFRESH_AFTER = Menu::getInstance()->isOptionChecked(MenuOption::ShowRealtimeEntityStats) ? 0 : USECS_PER_SECOND;
|
||||
quint64 sinceLastRefresh = now - _lastRefresh;
|
||||
if (sinceLastRefresh < REFRESH_AFTER) {
|
||||
_updateTimer.start((REFRESH_AFTER - sinceLastRefresh)/1000);
|
||||
return;
|
||||
}
|
||||
// Only refresh our stats every once in a while, unless asked for realtime
|
||||
//if no realtime, then update once per second. Otherwise consider 60FPS update, ie 16ms interval
|
||||
//int updateinterval = Menu::getInstance()->isOptionChecked(MenuOption::ShowRealtimeEntityStats) ? 16 : 1000;
|
||||
_updateTimer.start(REFRESH_AFTER/1000);
|
||||
|
||||
const int FLOATING_POINT_PRECISION = 3;
|
||||
|
||||
m_localElementsMemory = QString("Elements RAM: %1MB").arg(OctreeElement::getTotalMemoryUsage() / 1000000.0f, 5, 'f', 4);
|
||||
emit localElementsMemoryChanged(m_localElementsMemory);
|
||||
|
||||
// Local Elements
|
||||
m_localElements = QString("Total: %1 / Internal: %2 / Leaves: %3").
|
||||
arg(OctreeElement::getNodeCount()).
|
||||
arg(OctreeElement::getInternalNodeCount()).
|
||||
arg(OctreeElement::getLeafNodeCount());
|
||||
emit localElementsChanged(m_localElements);
|
||||
|
||||
// iterate all the current octree stats, and list their sending modes, total their octree elements, etc...
|
||||
int serverCount = 0;
|
||||
int movingServerCount = 0;
|
||||
unsigned long totalNodes = 0;
|
||||
unsigned long totalInternal = 0;
|
||||
unsigned long totalLeaves = 0;
|
||||
|
||||
m_sendingMode.clear();
|
||||
NodeToOctreeSceneStats* sceneStats = qApp->getOcteeSceneStats();
|
||||
sceneStats->withReadLock([&] {
|
||||
for (NodeToOctreeSceneStatsIterator i = sceneStats->begin(); i != sceneStats->end(); i++) {
|
||||
//const QUuid& uuid = i->first;
|
||||
OctreeSceneStats& stats = i->second;
|
||||
serverCount++;
|
||||
|
||||
// calculate server node totals
|
||||
totalNodes += stats.getTotalElements();
|
||||
totalInternal += stats.getTotalInternal();
|
||||
totalLeaves += stats.getTotalLeaves();
|
||||
|
||||
// Sending mode
|
||||
if (serverCount > 1) {
|
||||
m_sendingMode += ",";
|
||||
}
|
||||
if (stats.isMoving()) {
|
||||
m_sendingMode += "M";
|
||||
movingServerCount++;
|
||||
} else {
|
||||
m_sendingMode += "S";
|
||||
}
|
||||
if (stats.isFullScene()) {
|
||||
m_sendingMode += "F";
|
||||
} else {
|
||||
m_sendingMode += "p";
|
||||
}
|
||||
}
|
||||
});
|
||||
m_sendingMode += QString(" - %1 servers").arg(serverCount);
|
||||
if (movingServerCount > 0) {
|
||||
m_sendingMode += " <SCENE NOT STABLE> ";
|
||||
} else {
|
||||
m_sendingMode += " <SCENE STABLE> ";
|
||||
}
|
||||
|
||||
emit sendingModeChanged(m_sendingMode);
|
||||
|
||||
// Server Elements
|
||||
m_serverElements = QString("Total: %1 / Internal: %2 / Leaves: %3").
|
||||
arg(totalNodes).arg(totalInternal).arg(totalLeaves);
|
||||
emit serverElementsChanged(m_serverElements);
|
||||
|
||||
|
||||
// Processed Packets Elements
|
||||
auto averageElementsPerPacket = entities->getAverageElementsPerPacket();
|
||||
auto averageEntitiesPerPacket = entities->getAverageEntitiesPerPacket();
|
||||
|
||||
auto averageElementsPerSecond = entities->getAverageElementsPerSecond();
|
||||
auto averageEntitiesPerSecond = entities->getAverageEntitiesPerSecond();
|
||||
|
||||
auto averageWaitLockPerPacket = entities->getAverageWaitLockPerPacket();
|
||||
auto averageUncompressPerPacket = entities->getAverageUncompressPerPacket();
|
||||
auto averageReadBitstreamPerPacket = entities->getAverageReadBitstreamPerPacket();
|
||||
|
||||
const OctreePacketProcessor& entitiesPacketProcessor = qApp->getOctreePacketProcessor();
|
||||
|
||||
auto incomingPacketsDepth = entitiesPacketProcessor.packetsToProcessCount();
|
||||
auto incomingPPS = entitiesPacketProcessor.getIncomingPPS();
|
||||
auto processedPPS = entitiesPacketProcessor.getProcessedPPS();
|
||||
auto treeProcessedPPS = entities->getAveragePacketsPerSecond();
|
||||
|
||||
m_processedPackets = QString("Queue Size: %1 Packets / Network IN: %2 PPS / Queue OUT: %3 PPS / Tree IN: %4 PPS")
|
||||
.arg(incomingPacketsDepth)
|
||||
.arg(incomingPPS, 5, 'f', FLOATING_POINT_PRECISION)
|
||||
.arg(processedPPS, 5, 'f', FLOATING_POINT_PRECISION)
|
||||
.arg(treeProcessedPPS, 5, 'f', FLOATING_POINT_PRECISION);
|
||||
emit processedPacketsChanged(m_processedPackets);
|
||||
|
||||
m_processedPacketsElements = QString("%1 per packet / %2 per second")
|
||||
.arg(averageElementsPerPacket, 5, 'f', FLOATING_POINT_PRECISION)
|
||||
.arg(averageElementsPerSecond, 5, 'f', FLOATING_POINT_PRECISION);
|
||||
emit processedPacketsElementsChanged(m_processedPacketsElements);
|
||||
|
||||
m_processedPacketsEntities = QString("%1 per packet / %2 per second")
|
||||
.arg(averageEntitiesPerPacket, 5, 'f', FLOATING_POINT_PRECISION)
|
||||
.arg(averageEntitiesPerSecond, 5, 'f', FLOATING_POINT_PRECISION);
|
||||
emit processedPacketsEntitiesChanged(m_processedPacketsEntities);
|
||||
|
||||
m_processedPacketsTiming = QString("Lock Wait: %1 (usecs) / Uncompress: %2 (usecs) / Process: %3 (usecs)")
|
||||
.arg(averageWaitLockPerPacket)
|
||||
.arg(averageUncompressPerPacket)
|
||||
.arg(averageReadBitstreamPerPacket);
|
||||
emit processedPacketsTimingChanged(m_processedPacketsTiming);
|
||||
|
||||
auto entitiesEditPacketSender = qApp->getEntityEditPacketSender();
|
||||
auto outboundPacketsDepth = entitiesEditPacketSender->packetsToSendCount();
|
||||
auto outboundQueuedPPS = entitiesEditPacketSender->getLifetimePPSQueued();
|
||||
auto outboundSentPPS = entitiesEditPacketSender->getLifetimePPS();
|
||||
|
||||
m_outboundEditPackets = QString("Queue Size: %1 packets / Queued IN: %2 PPS / Sent OUT: %3 PPS")
|
||||
.arg(outboundPacketsDepth)
|
||||
.arg(outboundQueuedPPS, 5, 'f', FLOATING_POINT_PRECISION)
|
||||
.arg(outboundSentPPS, 5, 'f', FLOATING_POINT_PRECISION);
|
||||
emit outboundEditPacketsChanged(m_outboundEditPackets);
|
||||
|
||||
// Entity Edits update time
|
||||
auto averageEditDelta = entitiesTree->getAverageEditDeltas();
|
||||
auto maxEditDelta = entitiesTree->getMaxEditDelta();
|
||||
|
||||
m_entityUpdateTime = QString("Average: %1 (usecs) / Max: %2 (usecs)")
|
||||
.arg(averageEditDelta)
|
||||
.arg(maxEditDelta);
|
||||
emit entityUpdateTimeChanged(m_entityUpdateTime);
|
||||
|
||||
// Entity Edits
|
||||
auto bytesPerEdit = entitiesTree->getAverageEditBytes();
|
||||
|
||||
auto updatesPerSecond = _averageUpdatesPerSecond.getAverage();
|
||||
if (updatesPerSecond < 1) {
|
||||
updatesPerSecond = 0; // we don't really care about small updates per second so suppress those
|
||||
}
|
||||
|
||||
m_entityUpdates = QString("%1 updates per second / %2 total updates / Average Size: %3 bytes")
|
||||
.arg(updatesPerSecond, 5, 'f', FLOATING_POINT_PRECISION)
|
||||
.arg(totalTrackedEdits)
|
||||
.arg(bytesPerEdit);
|
||||
emit entityUpdatesChanged(m_entityUpdates);
|
||||
|
||||
updateOctreeServers();
|
||||
}
|
||||
|
||||
void OctreeStatsProvider::updateOctreeServers() {
|
||||
int serverCount = 0;
|
||||
|
||||
showOctreeServersOfType(serverCount, NodeType::EntityServer, "Entity",
|
||||
qApp->getEntityServerJurisdictions());
|
||||
if (m_serversNum != serverCount) {
|
||||
m_serversNum = serverCount;
|
||||
emit serversNumChanged(m_serversNum);
|
||||
}
|
||||
}
|
||||
|
||||
void OctreeStatsProvider::showOctreeServersOfType(int& serverCount, NodeType_t serverType, const char* serverTypeName,
|
||||
NodeToJurisdictionMap& serverJurisdictions) {
|
||||
|
||||
m_servers.clear();
|
||||
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
nodeList->eachNode([&](const SharedNodePointer& node) {
|
||||
|
||||
// only send to the NodeTypes that are NodeType_t_VOXEL_SERVER
|
||||
if (node->getType() == serverType) {
|
||||
serverCount++;
|
||||
|
||||
QString lesserDetails;
|
||||
QString moreDetails;
|
||||
QString mostDetails;
|
||||
|
||||
if (node->getActiveSocket()) {
|
||||
lesserDetails += "active ";
|
||||
} else {
|
||||
lesserDetails += "inactive ";
|
||||
}
|
||||
|
||||
QUuid nodeUUID = node->getUUID();
|
||||
|
||||
// lookup our nodeUUID in the jurisdiction map, if it's missing then we're
|
||||
// missing at least one jurisdiction
|
||||
serverJurisdictions.withReadLock([&] {
|
||||
if (serverJurisdictions.find(nodeUUID) == serverJurisdictions.end()) {
|
||||
lesserDetails += " unknown jurisdiction ";
|
||||
return;
|
||||
}
|
||||
const JurisdictionMap& map = serverJurisdictions[nodeUUID];
|
||||
|
||||
auto rootCode = map.getRootOctalCode();
|
||||
|
||||
if (rootCode) {
|
||||
QString rootCodeHex = octalCodeToHexString(rootCode.get());
|
||||
|
||||
VoxelPositionSize rootDetails;
|
||||
voxelDetailsForCode(rootCode.get(), rootDetails);
|
||||
AACube serverBounds(glm::vec3(rootDetails.x, rootDetails.y, rootDetails.z), rootDetails.s);
|
||||
lesserDetails += QString(" jurisdiction: %1 [%2, %3, %4: %5]")
|
||||
.arg(rootCodeHex)
|
||||
.arg(rootDetails.x)
|
||||
.arg(rootDetails.y)
|
||||
.arg(rootDetails.z)
|
||||
.arg(rootDetails.s);
|
||||
} else {
|
||||
lesserDetails += " jurisdiction has no rootCode";
|
||||
} // root code
|
||||
});
|
||||
|
||||
// now lookup stats details for this server...
|
||||
NodeToOctreeSceneStats* sceneStats = qApp->getOcteeSceneStats();
|
||||
sceneStats->withReadLock([&] {
|
||||
if (sceneStats->find(nodeUUID) != sceneStats->end()) {
|
||||
OctreeSceneStats& stats = sceneStats->at(nodeUUID);
|
||||
|
||||
float lastFullEncode = stats.getLastFullTotalEncodeTime() / USECS_PER_MSEC;
|
||||
float lastFullSend = stats.getLastFullElapsedTime() / USECS_PER_MSEC;
|
||||
float lastFullSendInSeconds = stats.getLastFullElapsedTime() / USECS_PER_SECOND;
|
||||
float lastFullPackets = stats.getLastFullTotalPackets();
|
||||
float lastFullPPS = lastFullPackets;
|
||||
if (lastFullSendInSeconds > 0) {
|
||||
lastFullPPS = lastFullPackets / lastFullSendInSeconds;
|
||||
}
|
||||
|
||||
mostDetails += QString("<br/><br/>Last Full Scene... Encode: %1 ms Send: %2 ms Packets: %3 Bytes: %4 Rate: %5 PPS")
|
||||
.arg(lastFullEncode)
|
||||
.arg(lastFullSend)
|
||||
.arg(lastFullPackets)
|
||||
.arg(stats.getLastFullTotalBytes())
|
||||
.arg(lastFullPPS);
|
||||
|
||||
for (int i = 0; i < OctreeSceneStats::ITEM_COUNT; i++) {
|
||||
OctreeSceneStats::Item item = (OctreeSceneStats::Item)(i);
|
||||
OctreeSceneStats::ItemInfo& itemInfo = stats.getItemInfo(item);
|
||||
mostDetails += QString("<br/> %1 %2")
|
||||
.arg(itemInfo.caption).arg(stats.getItemValue(item));
|
||||
}
|
||||
|
||||
moreDetails += "<br/>Node UUID: " +nodeUUID.toString() + " ";
|
||||
|
||||
moreDetails += QString("<br/>Elements: %1 total %2 internal %3 leaves ")
|
||||
.arg(stats.getTotalElements())
|
||||
.arg(stats.getTotalInternal())
|
||||
.arg(stats.getTotalLeaves());
|
||||
|
||||
const SequenceNumberStats& seqStats = stats.getIncomingOctreeSequenceNumberStats();
|
||||
qint64 clockSkewInUsecs = node->getClockSkewUsec();
|
||||
qint64 clockSkewInMS = clockSkewInUsecs / (qint64)USECS_PER_MSEC;
|
||||
|
||||
moreDetails += QString("<br/>Incoming Packets: %1/ Lost: %2/ Recovered: %3")
|
||||
.arg(stats.getIncomingPackets())
|
||||
.arg(seqStats.getLost())
|
||||
.arg(seqStats.getRecovered());
|
||||
|
||||
moreDetails += QString("<br/> Out of Order: %1/ Early: %2/ Late: %3/ Unreasonable: %4")
|
||||
.arg(seqStats.getOutOfOrder())
|
||||
.arg(seqStats.getEarly())
|
||||
.arg(seqStats.getLate())
|
||||
.arg(seqStats.getUnreasonable());
|
||||
|
||||
moreDetails += QString("<br/> Average Flight Time: %1 msecs")
|
||||
.arg(stats.getIncomingFlightTimeAverage());
|
||||
|
||||
moreDetails += QString("<br/> Average Ping Time: %1 msecs")
|
||||
.arg(node->getPingMs());
|
||||
|
||||
moreDetails += QString("<br/> Average Clock Skew: %1 msecs [%2]")
|
||||
.arg(clockSkewInMS)
|
||||
.arg(formatUsecTime(clockSkewInUsecs));
|
||||
|
||||
|
||||
moreDetails += QString("<br/>Incoming Bytes: %1 Wasted Bytes: %2")
|
||||
.arg(stats.getIncomingBytes())
|
||||
.arg(stats.getIncomingWastedBytes());
|
||||
}
|
||||
});
|
||||
m_servers.append(lesserDetails);
|
||||
m_servers.append(moreDetails);
|
||||
m_servers.append(mostDetails);
|
||||
} // is VOXEL_SERVER
|
||||
});
|
||||
emit serversChanged(m_servers);
|
||||
}
|
||||
|
||||
|
154
interface/src/ui/OctreeStatsProvider.h
Normal file
154
interface/src/ui/OctreeStatsProvider.h
Normal file
|
@ -0,0 +1,154 @@
|
|||
//
|
||||
// OctreeStatsProvider.h
|
||||
// interface/src/ui
|
||||
//
|
||||
// Created by Vlad Stelmahovsky on 3/12/17.
|
||||
// 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
|
||||
//
|
||||
|
||||
#ifndef hifi_OctreeStatsProvider_h
|
||||
#define hifi_OctreeStatsProvider_h
|
||||
|
||||
#include <OctreeSceneStats.h>
|
||||
#include <QTimer>
|
||||
#include <QColor>
|
||||
|
||||
#include "DependencyManager.h"
|
||||
|
||||
#define MAX_STATS 100
|
||||
#define MAX_VOXEL_SERVERS 50
|
||||
#define DEFAULT_COLOR 0
|
||||
|
||||
class OctreeStatsProvider : public QObject, public Dependency {
|
||||
Q_OBJECT
|
||||
SINGLETON_DEPENDENCY
|
||||
|
||||
Q_PROPERTY(int serversNum READ serversNum NOTIFY serversNumChanged)
|
||||
Q_PROPERTY(QString serverElements READ serverElements NOTIFY serverElementsChanged)
|
||||
Q_PROPERTY(QString localElements READ localElements NOTIFY localElementsChanged)
|
||||
Q_PROPERTY(QString localElementsMemory READ localElementsMemory NOTIFY localElementsMemoryChanged)
|
||||
Q_PROPERTY(QString sendingMode READ sendingMode NOTIFY sendingModeChanged)
|
||||
Q_PROPERTY(QString processedPackets READ processedPackets NOTIFY processedPacketsChanged)
|
||||
Q_PROPERTY(QString processedPacketsElements READ processedPacketsElements NOTIFY processedPacketsElementsChanged)
|
||||
Q_PROPERTY(QString processedPacketsEntities READ processedPacketsEntities NOTIFY processedPacketsEntitiesChanged)
|
||||
Q_PROPERTY(QString processedPacketsTiming READ processedPacketsTiming NOTIFY processedPacketsTimingChanged)
|
||||
Q_PROPERTY(QString outboundEditPackets READ outboundEditPackets NOTIFY outboundEditPacketsChanged)
|
||||
Q_PROPERTY(QString entityUpdateTime READ entityUpdateTime NOTIFY entityUpdateTimeChanged)
|
||||
Q_PROPERTY(QString entityUpdates READ entityUpdates NOTIFY entityUpdatesChanged)
|
||||
|
||||
Q_PROPERTY(QStringList servers READ servers NOTIFY serversChanged)
|
||||
|
||||
public:
|
||||
OctreeStatsProvider(QObject* parent, NodeToOctreeSceneStats* model);
|
||||
~OctreeStatsProvider();
|
||||
|
||||
int serversNum() const;
|
||||
|
||||
QString serverElements() const {
|
||||
return m_serverElements;
|
||||
}
|
||||
|
||||
QString localElements() const {
|
||||
return m_localElements;
|
||||
}
|
||||
|
||||
QString localElementsMemory() const {
|
||||
return m_localElementsMemory;
|
||||
}
|
||||
|
||||
QString sendingMode() const {
|
||||
return m_sendingMode;
|
||||
}
|
||||
|
||||
QString processedPackets() const {
|
||||
return m_processedPackets;
|
||||
}
|
||||
|
||||
QString processedPacketsElements() const {
|
||||
return m_processedPacketsElements;
|
||||
}
|
||||
|
||||
QString processedPacketsEntities() const {
|
||||
return m_processedPacketsEntities;
|
||||
}
|
||||
|
||||
QString processedPacketsTiming() const {
|
||||
return m_processedPacketsTiming;
|
||||
}
|
||||
|
||||
QString outboundEditPackets() const {
|
||||
return m_outboundEditPackets;
|
||||
}
|
||||
|
||||
QString entityUpdateTime() const {
|
||||
return m_entityUpdateTime;
|
||||
}
|
||||
|
||||
QString entityUpdates() const {
|
||||
return m_entityUpdates;
|
||||
}
|
||||
|
||||
QStringList servers() const {
|
||||
return m_servers;
|
||||
}
|
||||
|
||||
signals:
|
||||
|
||||
void serversNumChanged(int serversNum);
|
||||
void serverElementsChanged(const QString &serverElements);
|
||||
void localElementsChanged(const QString &localElements);
|
||||
void sendingModeChanged(const QString &sendingMode);
|
||||
void processedPacketsChanged(const QString &processedPackets);
|
||||
void localElementsMemoryChanged(const QString &localElementsMemory);
|
||||
void processedPacketsElementsChanged(const QString &processedPacketsElements);
|
||||
void processedPacketsEntitiesChanged(const QString &processedPacketsEntities);
|
||||
void processedPacketsTimingChanged(const QString &processedPacketsTiming);
|
||||
void outboundEditPacketsChanged(const QString &outboundEditPackets);
|
||||
void entityUpdateTimeChanged(const QString &entityUpdateTime);
|
||||
void entityUpdatesChanged(const QString &entityUpdates);
|
||||
|
||||
void serversChanged(const QStringList &servers);
|
||||
|
||||
public slots:
|
||||
void startUpdates();
|
||||
void stopUpdates();
|
||||
QColor getColor() const;
|
||||
|
||||
private slots:
|
||||
void updateOctreeStatsData();
|
||||
protected:
|
||||
void updateOctreeServers();
|
||||
void showOctreeServersOfType(int& serverNumber, NodeType_t serverType,
|
||||
const char* serverTypeName, NodeToJurisdictionMap& serverJurisdictions);
|
||||
|
||||
private:
|
||||
NodeToOctreeSceneStats* _model;
|
||||
int _statCount;
|
||||
|
||||
const int SAMPLES_PER_SECOND = 10;
|
||||
SimpleMovingAverage _averageUpdatesPerSecond;
|
||||
quint64 _lastWindowAt = usecTimestampNow();
|
||||
quint64 _lastKnownTrackedEdits = 0;
|
||||
|
||||
quint64 _lastRefresh = 0;
|
||||
|
||||
QTimer _updateTimer;
|
||||
int m_serversNum {0};
|
||||
QString m_serverElements;
|
||||
QString m_localElements;
|
||||
QString m_localElementsMemory;
|
||||
QString m_sendingMode;
|
||||
QString m_processedPackets;
|
||||
QString m_processedPacketsElements;
|
||||
QString m_processedPacketsEntities;
|
||||
QString m_processedPacketsTiming;
|
||||
QString m_outboundEditPackets;
|
||||
QString m_entityUpdateTime;
|
||||
QString m_entityUpdates;
|
||||
QStringList m_servers;
|
||||
};
|
||||
|
||||
#endif // hifi_OctreeStatsProvider_h
|
|
@ -198,15 +198,16 @@ void Stats::updateStats(bool force) {
|
|||
STAT_UPDATE(avatarMixerInPps, roundf(bandwidthRecorder->getAverageInputPacketsPerSecond(NodeType::AvatarMixer)));
|
||||
STAT_UPDATE(avatarMixerOutKbps, roundf(bandwidthRecorder->getAverageOutputKilobitsPerSecond(NodeType::AvatarMixer)));
|
||||
STAT_UPDATE(avatarMixerOutPps, roundf(bandwidthRecorder->getAverageOutputPacketsPerSecond(NodeType::AvatarMixer)));
|
||||
STAT_UPDATE(myAvatarSendRate, avatarManager->getMyAvatarSendRate());
|
||||
} else {
|
||||
STAT_UPDATE(avatarMixerInKbps, -1);
|
||||
STAT_UPDATE(avatarMixerInPps, -1);
|
||||
STAT_UPDATE(avatarMixerOutKbps, -1);
|
||||
STAT_UPDATE(avatarMixerOutPps, -1);
|
||||
STAT_UPDATE(myAvatarSendRate, avatarManager->getMyAvatarSendRate());
|
||||
}
|
||||
STAT_UPDATE(myAvatarSendRate, avatarManager->getMyAvatarSendRate());
|
||||
|
||||
SharedNodePointer audioMixerNode = nodeList->soloNodeOfType(NodeType::AudioMixer);
|
||||
auto audioClient = DependencyManager::get<AudioClient>();
|
||||
if (audioMixerNode || force) {
|
||||
STAT_UPDATE(audioMixerKbps, roundf(
|
||||
bandwidthRecorder->getAverageInputKilobitsPerSecond(NodeType::AudioMixer) +
|
||||
|
@ -214,10 +215,30 @@ void Stats::updateStats(bool force) {
|
|||
STAT_UPDATE(audioMixerPps, roundf(
|
||||
bandwidthRecorder->getAverageInputPacketsPerSecond(NodeType::AudioMixer) +
|
||||
bandwidthRecorder->getAverageOutputPacketsPerSecond(NodeType::AudioMixer)));
|
||||
|
||||
STAT_UPDATE(audioMixerInKbps, roundf(bandwidthRecorder->getAverageInputKilobitsPerSecond(NodeType::AudioMixer)));
|
||||
STAT_UPDATE(audioMixerInPps, roundf(bandwidthRecorder->getAverageInputPacketsPerSecond(NodeType::AudioMixer)));
|
||||
STAT_UPDATE(audioMixerOutKbps, roundf(bandwidthRecorder->getAverageOutputKilobitsPerSecond(NodeType::AudioMixer)));
|
||||
STAT_UPDATE(audioMixerOutPps, roundf(bandwidthRecorder->getAverageOutputPacketsPerSecond(NodeType::AudioMixer)));
|
||||
STAT_UPDATE(audioMicOutboundPPS, audioClient->getMicAudioOutboundPPS());
|
||||
STAT_UPDATE(audioSilentOutboundPPS, audioClient->getSilentOutboundPPS());
|
||||
STAT_UPDATE(audioAudioInboundPPS, audioClient->getAudioInboundPPS());
|
||||
STAT_UPDATE(audioSilentInboundPPS, audioClient->getSilentInboundPPS());
|
||||
} else {
|
||||
STAT_UPDATE(audioMixerKbps, -1);
|
||||
STAT_UPDATE(audioMixerPps, -1);
|
||||
STAT_UPDATE(audioMixerInKbps, -1);
|
||||
STAT_UPDATE(audioMixerInPps, -1);
|
||||
STAT_UPDATE(audioMixerOutKbps, -1);
|
||||
STAT_UPDATE(audioMixerOutPps, -1);
|
||||
STAT_UPDATE(audioMicOutboundPPS, -1);
|
||||
STAT_UPDATE(audioSilentOutboundPPS, -1);
|
||||
STAT_UPDATE(audioAudioInboundPPS, -1);
|
||||
STAT_UPDATE(audioSilentInboundPPS, -1);
|
||||
}
|
||||
STAT_UPDATE(audioCodec, audioClient->getSelectedAudioFormat());
|
||||
STAT_UPDATE(audioNoiseGate, audioClient->getNoiseGateOpen() ? "Open" : "Closed");
|
||||
|
||||
|
||||
auto loadingRequests = ResourceCache::getLoadingRequests();
|
||||
STAT_UPDATE(downloads, loadingRequests.size());
|
||||
|
|
|
@ -70,8 +70,20 @@ class Stats : public QQuickItem {
|
|||
STATS_PROPERTY(int, avatarMixerOutKbps, 0)
|
||||
STATS_PROPERTY(int, avatarMixerOutPps, 0)
|
||||
STATS_PROPERTY(float, myAvatarSendRate, 0)
|
||||
|
||||
STATS_PROPERTY(int, audioMixerInKbps, 0)
|
||||
STATS_PROPERTY(int, audioMixerInPps, 0)
|
||||
STATS_PROPERTY(int, audioMixerOutKbps, 0)
|
||||
STATS_PROPERTY(int, audioMixerOutPps, 0)
|
||||
STATS_PROPERTY(int, audioMixerKbps, 0)
|
||||
STATS_PROPERTY(int, audioMixerPps, 0)
|
||||
STATS_PROPERTY(int, audioMicOutboundPPS, 0)
|
||||
STATS_PROPERTY(int, audioSilentOutboundPPS, 0)
|
||||
STATS_PROPERTY(int, audioAudioInboundPPS, 0)
|
||||
STATS_PROPERTY(int, audioSilentInboundPPS, 0)
|
||||
STATS_PROPERTY(QString, audioCodec, QString())
|
||||
STATS_PROPERTY(QString, audioNoiseGate, QString())
|
||||
|
||||
STATS_PROPERTY(int, downloads, 0)
|
||||
STATS_PROPERTY(int, downloadLimit, 0)
|
||||
STATS_PROPERTY(int, downloadsPending, 0)
|
||||
|
@ -180,8 +192,19 @@ signals:
|
|||
void avatarMixerOutKbpsChanged();
|
||||
void avatarMixerOutPpsChanged();
|
||||
void myAvatarSendRateChanged();
|
||||
void audioMixerInKbpsChanged();
|
||||
void audioMixerInPpsChanged();
|
||||
void audioMixerOutKbpsChanged();
|
||||
void audioMixerOutPpsChanged();
|
||||
void audioMixerKbpsChanged();
|
||||
void audioMixerPpsChanged();
|
||||
void audioMicOutboundPPSChanged();
|
||||
void audioSilentOutboundPPSChanged();
|
||||
void audioAudioInboundPPSChanged();
|
||||
void audioSilentInboundPPSChanged();
|
||||
void audioCodecChanged();
|
||||
void audioNoiseGateChanged();
|
||||
|
||||
void downloadsChanged();
|
||||
void downloadLimitChanged();
|
||||
void downloadsPendingChanged();
|
||||
|
|
|
@ -768,6 +768,26 @@ bool Overlays::mousePressEvent(QMouseEvent* event) {
|
|||
return false;
|
||||
}
|
||||
|
||||
bool Overlays::mouseDoublePressEvent(QMouseEvent* event) {
|
||||
PerformanceTimer perfTimer("Overlays::mouseDoublePressEvent");
|
||||
|
||||
PickRay ray = qApp->computePickRay(event->x(), event->y());
|
||||
RayToOverlayIntersectionResult rayPickResult = findRayIntersectionForMouseEvent(ray);
|
||||
if (rayPickResult.intersects) {
|
||||
_currentClickingOnOverlayID = rayPickResult.overlayID;
|
||||
|
||||
// Only Web overlays can have focus.
|
||||
auto thisOverlay = std::dynamic_pointer_cast<Web3DOverlay>(getOverlay(_currentClickingOnOverlayID));
|
||||
if (thisOverlay) {
|
||||
auto pointerEvent = calculatePointerEvent(thisOverlay, ray, rayPickResult, event, PointerEvent::Press);
|
||||
emit mouseDoublePressOnOverlay(_currentClickingOnOverlayID, pointerEvent);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
emit mouseDoublePressOffOverlay();
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Overlays::mouseReleaseEvent(QMouseEvent* event) {
|
||||
PerformanceTimer perfTimer("Overlays::mouseReleaseEvent");
|
||||
|
||||
|
|
|
@ -101,6 +101,7 @@ public:
|
|||
OverlayID addOverlay(Overlay::Pointer overlay);
|
||||
|
||||
bool mousePressEvent(QMouseEvent* event);
|
||||
bool mouseDoublePressEvent(QMouseEvent* event);
|
||||
bool mouseReleaseEvent(QMouseEvent* event);
|
||||
bool mouseMoveEvent(QMouseEvent* event);
|
||||
|
||||
|
@ -300,9 +301,11 @@ signals:
|
|||
void panelDeleted(OverlayID id);
|
||||
|
||||
void mousePressOnOverlay(OverlayID overlayID, const PointerEvent& event);
|
||||
void mouseDoublePressOnOverlay(OverlayID overlayID, const PointerEvent& event);
|
||||
void mouseReleaseOnOverlay(OverlayID overlayID, const PointerEvent& event);
|
||||
void mouseMoveOnOverlay(OverlayID overlayID, const PointerEvent& event);
|
||||
void mousePressOffOverlay();
|
||||
void mouseDoublePressOffOverlay();
|
||||
|
||||
void hoverEnterOverlay(OverlayID overlayID, const PointerEvent& event);
|
||||
void hoverOverOverlay(OverlayID overlayID, const PointerEvent& event);
|
||||
|
|
|
@ -44,6 +44,7 @@
|
|||
#include "avatar/AvatarManager.h"
|
||||
#include "AudioClient.h"
|
||||
#include "LODManager.h"
|
||||
#include "ui/OctreeStatsProvider.h"
|
||||
|
||||
static const float DPI = 30.47f;
|
||||
static const float INCHES_TO_METERS = 1.0f / 39.3701f;
|
||||
|
@ -185,6 +186,7 @@ void Web3DOverlay::loadSourceURL() {
|
|||
_webSurface->getRootContext()->setContextProperty("Tablet", DependencyManager::get<TabletScriptingInterface>().data());
|
||||
_webSurface->getRootContext()->setContextProperty("Assets", DependencyManager::get<AssetMappingsScriptingInterface>().data());
|
||||
_webSurface->getRootContext()->setContextProperty("LODManager", DependencyManager::get<LODManager>().data());
|
||||
_webSurface->getRootContext()->setContextProperty("OctreeStats", DependencyManager::get<OctreeStatsProvider>().data());
|
||||
_webSurface->getRootContext()->setContextProperty("pathToFonts", "../../");
|
||||
tabletScriptingInterface->setQmlTabletRoot("com.highfidelity.interface.tablet.system", _webSurface->getRootItem(), _webSurface.data());
|
||||
|
||||
|
|
|
@ -608,6 +608,13 @@ void AudioClient::handleAudioEnvironmentDataPacket(QSharedPointer<ReceivedMessag
|
|||
}
|
||||
|
||||
void AudioClient::handleAudioDataPacket(QSharedPointer<ReceivedMessage> message) {
|
||||
|
||||
if (message->getType() == PacketType::SilentAudioFrame) {
|
||||
_silentInbound.increment();
|
||||
} else {
|
||||
_audioInbound.increment();
|
||||
}
|
||||
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
nodeList->flagTimeForConnectionStep(LimitedNodeList::ConnectionStep::ReceiveFirstAudioPacket);
|
||||
|
||||
|
@ -1021,9 +1028,10 @@ void AudioClient::handleAudioInput() {
|
|||
// if we performed the noise gate we can get values from it instead of enumerating the samples again
|
||||
_lastInputLoudness = _inputGate.getLastLoudness();
|
||||
|
||||
if (_inputGate.clippedInLastFrame()) {
|
||||
if (_inputGate.clippedInLastBlock()) {
|
||||
_timeSinceLastClip = 0.0f;
|
||||
}
|
||||
|
||||
} else {
|
||||
float loudness = 0.0f;
|
||||
|
||||
|
@ -1041,6 +1049,12 @@ void AudioClient::handleAudioInput() {
|
|||
|
||||
emit inputReceived({ reinterpret_cast<char*>(networkAudioSamples), numNetworkBytes });
|
||||
|
||||
if (_inputGate.openedInLastBlock()) {
|
||||
emit noiseGateOpened();
|
||||
} else if (_inputGate.closedInLastBlock()) {
|
||||
emit noiseGateClosed();
|
||||
}
|
||||
|
||||
} else {
|
||||
// our input loudness is 0, since we're muted
|
||||
_lastInputLoudness = 0;
|
||||
|
@ -1057,9 +1071,13 @@ void AudioClient::handleAudioInput() {
|
|||
// the output from the input gate (eventually, this could be crossfaded)
|
||||
// and allow the codec to properly encode down to silent/zero. If we still
|
||||
// have _lastInputLoudness of 0 in our NEXT frame, we will send a silent packet
|
||||
if (_lastInputLoudness == 0 && !_inputGate.closedInLastFrame()) {
|
||||
if (_lastInputLoudness == 0 && !_inputGate.closedInLastBlock()) {
|
||||
packetType = PacketType::SilentAudioFrame;
|
||||
_silentOutbound.increment();
|
||||
} else {
|
||||
_micAudioOutbound.increment();
|
||||
}
|
||||
|
||||
Transform audioTransform;
|
||||
audioTransform.setTranslation(_positionGetter());
|
||||
audioTransform.setRotation(_orientationGetter());
|
||||
|
@ -1084,6 +1102,7 @@ void AudioClient::handleAudioInput() {
|
|||
}
|
||||
}
|
||||
|
||||
// FIXME - should this go through the noise gate and honor mute and echo?
|
||||
void AudioClient::handleRecordedAudioInput(const QByteArray& audio) {
|
||||
Transform audioTransform;
|
||||
audioTransform.setTranslation(_positionGetter());
|
||||
|
@ -1096,6 +1115,8 @@ void AudioClient::handleRecordedAudioInput(const QByteArray& audio) {
|
|||
encodedBuffer = audio;
|
||||
}
|
||||
|
||||
_micAudioOutbound.increment();
|
||||
|
||||
// FIXME check a flag to see if we should echo audio?
|
||||
emitAudioPacket(encodedBuffer.data(), encodedBuffer.size(), _outgoingAvatarAudioSequenceNumber,
|
||||
audioTransform, avatarBoundingBoxCorner, avatarBoundingBoxScale,
|
||||
|
|
|
@ -46,6 +46,8 @@
|
|||
#include <AudioLimiter.h>
|
||||
#include <AudioConstants.h>
|
||||
|
||||
#include <shared/RateCounter.h>
|
||||
|
||||
#include <plugins/CodecPlugin.h>
|
||||
|
||||
#include "AudioIOStats.h"
|
||||
|
@ -121,6 +123,13 @@ public:
|
|||
void negotiateAudioFormat();
|
||||
void selectAudioFormat(const QString& selectedCodecName);
|
||||
|
||||
Q_INVOKABLE QString getSelectedAudioFormat() const { return _selectedCodecName; }
|
||||
Q_INVOKABLE bool getNoiseGateOpen() const { return _inputGate.isOpen(); }
|
||||
Q_INVOKABLE float getSilentOutboundPPS() const { return _silentOutbound.rate(); }
|
||||
Q_INVOKABLE float getMicAudioOutboundPPS() const { return _micAudioOutbound.rate(); }
|
||||
Q_INVOKABLE float getSilentInboundPPS() const { return _silentInbound.rate(); }
|
||||
Q_INVOKABLE float getAudioInboundPPS() const { return _audioInbound.rate(); }
|
||||
|
||||
const MixedProcessedAudioStream& getReceivedAudioStream() const { return _receivedAudioStream; }
|
||||
MixedProcessedAudioStream& getReceivedAudioStream() { return _receivedAudioStream; }
|
||||
|
||||
|
@ -218,6 +227,8 @@ signals:
|
|||
void inputReceived(const QByteArray& inputSamples);
|
||||
void outputBytesToNetwork(int numBytes);
|
||||
void inputBytesFromNetwork(int numBytes);
|
||||
void noiseGateOpened();
|
||||
void noiseGateClosed();
|
||||
|
||||
void changeDevice(const QAudioDeviceInfo& outputDeviceInfo);
|
||||
void deviceChanged();
|
||||
|
@ -382,6 +393,11 @@ private:
|
|||
Encoder* _encoder { nullptr }; // for outbound mic stream
|
||||
|
||||
QThread* _checkDevicesThread { nullptr };
|
||||
|
||||
RateCounter<> _silentOutbound;
|
||||
RateCounter<> _micAudioOutbound;
|
||||
RateCounter<> _silentInbound;
|
||||
RateCounter<> _audioInbound;
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -19,16 +19,16 @@
|
|||
const float AudioNoiseGate::CLIPPING_THRESHOLD = 0.90f;
|
||||
|
||||
AudioNoiseGate::AudioNoiseGate() :
|
||||
_inputFrameCounter(0),
|
||||
_inputBlockCounter(0),
|
||||
_lastLoudness(0.0f),
|
||||
_quietestFrame(std::numeric_limits<float>::max()),
|
||||
_loudestFrame(0.0f),
|
||||
_didClipInLastFrame(false),
|
||||
_quietestBlock(std::numeric_limits<float>::max()),
|
||||
_loudestBlock(0.0f),
|
||||
_didClipInLastBlock(false),
|
||||
_dcOffset(0.0f),
|
||||
_measuredFloor(0.0f),
|
||||
_sampleCounter(0),
|
||||
_isOpen(false),
|
||||
_framesToClose(0)
|
||||
_blocksToClose(0)
|
||||
{
|
||||
|
||||
}
|
||||
|
@ -37,7 +37,7 @@ void AudioNoiseGate::removeDCOffset(int16_t* samples, int numSamples) {
|
|||
//
|
||||
// DC Offset correction
|
||||
//
|
||||
// Measure the DC offset over a trailing number of frames, and remove it from the input signal.
|
||||
// Measure the DC offset over a trailing number of blocks, and remove it from the input signal.
|
||||
// This causes the noise background measurements and server muting to be more accurate. Many off-board
|
||||
// ADC's have a noticeable DC offset.
|
||||
//
|
||||
|
@ -51,7 +51,7 @@ void AudioNoiseGate::removeDCOffset(int16_t* samples, int numSamples) {
|
|||
// Update measured DC offset
|
||||
measuredDcOffset /= numSamples;
|
||||
if (_dcOffset == 0.0f) {
|
||||
// On first frame, copy over measured offset
|
||||
// On first block, copy over measured offset
|
||||
_dcOffset = measuredDcOffset;
|
||||
} else {
|
||||
_dcOffset = DC_OFFSET_AVERAGING * _dcOffset + (1.0f - DC_OFFSET_AVERAGING) * measuredDcOffset;
|
||||
|
@ -69,13 +69,13 @@ void AudioNoiseGate::gateSamples(int16_t* samples, int numSamples) {
|
|||
//
|
||||
// NOISE_GATE_HEIGHT: How loud you have to speak relative to noise background to open the gate.
|
||||
// Make this value lower for more sensitivity and less rejection of noise.
|
||||
// NOISE_GATE_WIDTH: The number of samples in an audio frame for which the height must be exceeded
|
||||
// NOISE_GATE_WIDTH: The number of samples in an audio block for which the height must be exceeded
|
||||
// to open the gate.
|
||||
// NOISE_GATE_CLOSE_FRAME_DELAY: Once the noise is below the gate height for the frame, how many frames
|
||||
// NOISE_GATE_CLOSE_BLOCK_DELAY: Once the noise is below the gate height for the block, how many blocks
|
||||
// will we wait before closing the gate.
|
||||
// NOISE_GATE_FRAMES_TO_AVERAGE: How many audio frames should we average together to compute noise floor.
|
||||
// NOISE_GATE_BLOCKS_TO_AVERAGE: How many audio blocks should we average together to compute noise floor.
|
||||
// More means better rejection but also can reject continuous things like singing.
|
||||
// NUMBER_OF_NOISE_SAMPLE_FRAMES: How often should we re-evaluate the noise floor?
|
||||
// NUMBER_OF_NOISE_SAMPLE_BLOCKS: How often should we re-evaluate the noise floor?
|
||||
|
||||
float loudness = 0;
|
||||
int thisSample = 0;
|
||||
|
@ -83,16 +83,16 @@ void AudioNoiseGate::gateSamples(int16_t* samples, int numSamples) {
|
|||
|
||||
const float NOISE_GATE_HEIGHT = 7.0f;
|
||||
const int NOISE_GATE_WIDTH = 5;
|
||||
const int NOISE_GATE_CLOSE_FRAME_DELAY = 5;
|
||||
const int NOISE_GATE_FRAMES_TO_AVERAGE = 5;
|
||||
const int NOISE_GATE_CLOSE_BLOCK_DELAY = 5;
|
||||
const int NOISE_GATE_BLOCKS_TO_AVERAGE = 5;
|
||||
|
||||
// Check clipping, and check if should open noise gate
|
||||
_didClipInLastFrame = false;
|
||||
_didClipInLastBlock = false;
|
||||
|
||||
for (int i = 0; i < numSamples; i++) {
|
||||
thisSample = std::abs(samples[i]);
|
||||
if (thisSample >= ((float) AudioConstants::MAX_SAMPLE_VALUE * CLIPPING_THRESHOLD)) {
|
||||
_didClipInLastFrame = true;
|
||||
_didClipInLastBlock = true;
|
||||
}
|
||||
|
||||
loudness += thisSample;
|
||||
|
@ -104,54 +104,81 @@ void AudioNoiseGate::gateSamples(int16_t* samples, int numSamples) {
|
|||
|
||||
_lastLoudness = fabs(loudness / numSamples);
|
||||
|
||||
if (_quietestFrame > _lastLoudness) {
|
||||
_quietestFrame = _lastLoudness;
|
||||
if (_quietestBlock > _lastLoudness) {
|
||||
_quietestBlock = _lastLoudness;
|
||||
}
|
||||
if (_loudestFrame < _lastLoudness) {
|
||||
_loudestFrame = _lastLoudness;
|
||||
if (_loudestBlock < _lastLoudness) {
|
||||
_loudestBlock = _lastLoudness;
|
||||
}
|
||||
|
||||
const int FRAMES_FOR_NOISE_DETECTION = 400;
|
||||
if (_inputFrameCounter++ > FRAMES_FOR_NOISE_DETECTION) {
|
||||
_quietestFrame = std::numeric_limits<float>::max();
|
||||
_loudestFrame = 0.0f;
|
||||
_inputFrameCounter = 0;
|
||||
if (_inputBlockCounter++ > FRAMES_FOR_NOISE_DETECTION) {
|
||||
_quietestBlock = std::numeric_limits<float>::max();
|
||||
_loudestBlock = 0.0f;
|
||||
_inputBlockCounter = 0;
|
||||
}
|
||||
|
||||
// If Noise Gate is enabled, check and turn the gate on and off
|
||||
float averageOfAllSampleFrames = 0.0f;
|
||||
_sampleFrames[_sampleCounter++] = _lastLoudness;
|
||||
if (_sampleCounter == NUMBER_OF_NOISE_SAMPLE_FRAMES) {
|
||||
float averageOfAllSampleBlocks = 0.0f;
|
||||
_sampleBlocks[_sampleCounter++] = _lastLoudness;
|
||||
if (_sampleCounter == NUMBER_OF_NOISE_SAMPLE_BLOCKS) {
|
||||
float smallestSample = std::numeric_limits<float>::max();
|
||||
for (int i = 0; i <= NUMBER_OF_NOISE_SAMPLE_FRAMES - NOISE_GATE_FRAMES_TO_AVERAGE; i += NOISE_GATE_FRAMES_TO_AVERAGE) {
|
||||
for (int i = 0; i <= NUMBER_OF_NOISE_SAMPLE_BLOCKS - NOISE_GATE_BLOCKS_TO_AVERAGE; i += NOISE_GATE_BLOCKS_TO_AVERAGE) {
|
||||
float thisAverage = 0.0f;
|
||||
for (int j = i; j < i + NOISE_GATE_FRAMES_TO_AVERAGE; j++) {
|
||||
thisAverage += _sampleFrames[j];
|
||||
averageOfAllSampleFrames += _sampleFrames[j];
|
||||
for (int j = i; j < i + NOISE_GATE_BLOCKS_TO_AVERAGE; j++) {
|
||||
thisAverage += _sampleBlocks[j];
|
||||
averageOfAllSampleBlocks += _sampleBlocks[j];
|
||||
}
|
||||
thisAverage /= NOISE_GATE_FRAMES_TO_AVERAGE;
|
||||
thisAverage /= NOISE_GATE_BLOCKS_TO_AVERAGE;
|
||||
|
||||
if (thisAverage < smallestSample) {
|
||||
smallestSample = thisAverage;
|
||||
}
|
||||
}
|
||||
averageOfAllSampleFrames /= NUMBER_OF_NOISE_SAMPLE_FRAMES;
|
||||
averageOfAllSampleBlocks /= NUMBER_OF_NOISE_SAMPLE_BLOCKS;
|
||||
_measuredFloor = smallestSample;
|
||||
_sampleCounter = 0;
|
||||
|
||||
}
|
||||
|
||||
_closedInLastBlock = false;
|
||||
_openedInLastBlock = false;
|
||||
|
||||
if (samplesOverNoiseGate > NOISE_GATE_WIDTH) {
|
||||
_openedInLastBlock = !_isOpen;
|
||||
_isOpen = true;
|
||||
_framesToClose = NOISE_GATE_CLOSE_FRAME_DELAY;
|
||||
_blocksToClose = NOISE_GATE_CLOSE_BLOCK_DELAY;
|
||||
} else {
|
||||
if (--_framesToClose == 0) {
|
||||
_closedInLastFrame = !_isOpen;
|
||||
if (--_blocksToClose == 0) {
|
||||
_closedInLastBlock = _isOpen;
|
||||
_isOpen = false;
|
||||
}
|
||||
}
|
||||
if (!_isOpen) {
|
||||
memset(samples, 0, numSamples * sizeof(int16_t));
|
||||
// First block after being closed gets faded to silence, we fade across
|
||||
// the entire block on fading out. All subsequent blocks are muted by being slammed
|
||||
// to zeros
|
||||
if (_closedInLastBlock) {
|
||||
float fadeSlope = (1.0f / numSamples);
|
||||
for (int i = 0; i < numSamples; i++) {
|
||||
float fadedSample = (1.0f - ((float)i * fadeSlope)) * (float)samples[i];
|
||||
samples[i] = (int16_t)fadedSample;
|
||||
}
|
||||
} else {
|
||||
memset(samples, 0, numSamples * sizeof(int16_t));
|
||||
}
|
||||
_lastLoudness = 0;
|
||||
}
|
||||
|
||||
if (_openedInLastBlock) {
|
||||
// would be nice to do a little crossfade from silence, but we only want to fade
|
||||
// across the first 1/10th of the block, because we don't want to miss early
|
||||
// transients.
|
||||
int fadeSamples = numSamples / 10; // fade over 1/10th of the samples
|
||||
float fadeSlope = (1.0f / fadeSamples);
|
||||
for (int i = 0; i < fadeSamples; i++) {
|
||||
float fadedSample = (float)i * fadeSlope * (float)samples[i];
|
||||
samples[i] = (int16_t)fadedSample;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
|
||||
#include <stdint.h>
|
||||
|
||||
const int NUMBER_OF_NOISE_SAMPLE_FRAMES = 300;
|
||||
const int NUMBER_OF_NOISE_SAMPLE_BLOCKS = 300;
|
||||
|
||||
class AudioNoiseGate {
|
||||
public:
|
||||
|
@ -23,26 +23,29 @@ public:
|
|||
void gateSamples(int16_t* samples, int numSamples);
|
||||
void removeDCOffset(int16_t* samples, int numSamples);
|
||||
|
||||
bool clippedInLastFrame() const { return _didClipInLastFrame; }
|
||||
bool closedInLastFrame() const { return _closedInLastFrame; }
|
||||
bool clippedInLastBlock() const { return _didClipInLastBlock; }
|
||||
bool closedInLastBlock() const { return _closedInLastBlock; }
|
||||
bool openedInLastBlock() const { return _openedInLastBlock; }
|
||||
bool isOpen() const { return _isOpen; }
|
||||
float getMeasuredFloor() const { return _measuredFloor; }
|
||||
float getLastLoudness() const { return _lastLoudness; }
|
||||
|
||||
static const float CLIPPING_THRESHOLD;
|
||||
|
||||
private:
|
||||
int _inputFrameCounter;
|
||||
int _inputBlockCounter;
|
||||
float _lastLoudness;
|
||||
float _quietestFrame;
|
||||
float _loudestFrame;
|
||||
bool _didClipInLastFrame;
|
||||
float _quietestBlock;
|
||||
float _loudestBlock;
|
||||
bool _didClipInLastBlock;
|
||||
float _dcOffset;
|
||||
float _measuredFloor;
|
||||
float _sampleFrames[NUMBER_OF_NOISE_SAMPLE_FRAMES];
|
||||
float _sampleBlocks[NUMBER_OF_NOISE_SAMPLE_BLOCKS];
|
||||
int _sampleCounter;
|
||||
bool _isOpen;
|
||||
bool _closedInLastFrame { false };
|
||||
int _framesToClose;
|
||||
bool _closedInLastBlock { false };
|
||||
bool _openedInLastBlock { false };
|
||||
int _blocksToClose;
|
||||
};
|
||||
|
||||
#endif // hifi_AudioNoiseGate_h
|
|
@ -736,6 +736,52 @@ void EntityTreeRenderer::mousePressEvent(QMouseEvent* event) {
|
|||
}
|
||||
}
|
||||
|
||||
void EntityTreeRenderer::mouseDoublePressEvent(QMouseEvent* event) {
|
||||
// If we don't have a tree, or we're in the process of shutting down, then don't
|
||||
// process these events.
|
||||
if (!_tree || _shuttingDown) {
|
||||
return;
|
||||
}
|
||||
PerformanceTimer perfTimer("EntityTreeRenderer::mouseDoublePressEvent");
|
||||
PickRay ray = _viewState->computePickRay(event->x(), event->y());
|
||||
|
||||
bool precisionPicking = !_dontDoPrecisionPicking;
|
||||
RayToEntityIntersectionResult rayPickResult = findRayIntersectionWorker(ray, Octree::Lock, precisionPicking);
|
||||
if (rayPickResult.intersects) {
|
||||
//qCDebug(entitiesrenderer) << "mouseDoublePressEvent over entity:" << rayPickResult.entityID;
|
||||
|
||||
QString urlString = rayPickResult.properties.getHref();
|
||||
QUrl url = QUrl(urlString, QUrl::StrictMode);
|
||||
if (url.isValid() && !url.isEmpty()){
|
||||
DependencyManager::get<AddressManager>()->handleLookupString(urlString);
|
||||
}
|
||||
|
||||
glm::vec2 pos2D = projectOntoEntityXYPlane(rayPickResult.entity, ray, rayPickResult);
|
||||
PointerEvent pointerEvent(PointerEvent::Press, MOUSE_POINTER_ID,
|
||||
pos2D, rayPickResult.intersection,
|
||||
rayPickResult.surfaceNormal, ray.direction,
|
||||
toPointerButton(*event), toPointerButtons(*event), Qt::NoModifier);
|
||||
|
||||
emit mouseDoublePressOnEntity(rayPickResult.entityID, pointerEvent);
|
||||
|
||||
if (_entitiesScriptEngine) {
|
||||
_entitiesScriptEngine->callEntityScriptMethod(rayPickResult.entityID, "mouseDoublePressOnEntity", pointerEvent);
|
||||
}
|
||||
|
||||
_currentClickingOnEntityID = rayPickResult.entityID;
|
||||
emit clickDownOnEntity(_currentClickingOnEntityID, pointerEvent);
|
||||
if (_entitiesScriptEngine) {
|
||||
_entitiesScriptEngine->callEntityScriptMethod(_currentClickingOnEntityID, "doubleclickOnEntity", pointerEvent);
|
||||
}
|
||||
|
||||
_lastPointerEvent = pointerEvent;
|
||||
_lastPointerEventValid = true;
|
||||
|
||||
} else {
|
||||
emit mouseDoublePressOffEntity();
|
||||
}
|
||||
}
|
||||
|
||||
void EntityTreeRenderer::mouseReleaseEvent(QMouseEvent* event) {
|
||||
// If we don't have a tree, or we're in the process of shutting down, then don't
|
||||
// process these events.
|
||||
|
|
|
@ -90,6 +90,7 @@ public:
|
|||
// event handles which may generate entity related events
|
||||
void mouseReleaseEvent(QMouseEvent* event);
|
||||
void mousePressEvent(QMouseEvent* event);
|
||||
void mouseDoublePressEvent(QMouseEvent* event);
|
||||
void mouseMoveEvent(QMouseEvent* event);
|
||||
|
||||
/// connect our signals to anEntityScriptingInterface for firing of events related clicking,
|
||||
|
@ -103,9 +104,11 @@ public:
|
|||
|
||||
signals:
|
||||
void mousePressOnEntity(const EntityItemID& entityItemID, const PointerEvent& event);
|
||||
void mouseDoublePressOnEntity(const EntityItemID& entityItemID, const PointerEvent& event);
|
||||
void mouseMoveOnEntity(const EntityItemID& entityItemID, const PointerEvent& event);
|
||||
void mouseReleaseOnEntity(const EntityItemID& entityItemID, const PointerEvent& event);
|
||||
void mousePressOffEntity();
|
||||
void mouseDoublePressOffEntity();
|
||||
|
||||
void clickDownOnEntity(const EntityItemID& entityItemID, const PointerEvent& event);
|
||||
void holdingClickOnEntity(const EntityItemID& entityItemID, const PointerEvent& event);
|
||||
|
|
|
@ -143,12 +143,35 @@ void PhysicsEngine::addObjectToDynamicsWorld(ObjectMotionState* motionState) {
|
|||
}
|
||||
|
||||
void PhysicsEngine::removeObjects(const VectorOfMotionStates& objects) {
|
||||
// first bump and prune contacts for all objects in the list
|
||||
// bump and prune contacts for all objects in the list
|
||||
for (auto object : objects) {
|
||||
bumpAndPruneContacts(object);
|
||||
}
|
||||
|
||||
// then remove them
|
||||
if (_activeStaticBodies.size() > 0) {
|
||||
// _activeStaticBodies was not cleared last frame.
|
||||
// The only way to get here is if a static object were moved but we did not actually step the simulation last
|
||||
// frame (because the framerate is faster than our physics simulation rate). When this happens we must scan
|
||||
// _activeStaticBodies for objects that were recently deleted so we don't try to access a dangling pointer.
|
||||
for (auto object : objects) {
|
||||
btRigidBody* body = object->getRigidBody();
|
||||
|
||||
std::vector<btRigidBody*>::reverse_iterator itr = _activeStaticBodies.rbegin();
|
||||
while (itr != _activeStaticBodies.rend()) {
|
||||
if (body == *itr) {
|
||||
if (*itr != *(_activeStaticBodies.rbegin())) {
|
||||
// swap with rbegin
|
||||
*itr = *(_activeStaticBodies.rbegin());
|
||||
}
|
||||
_activeStaticBodies.pop_back();
|
||||
break;
|
||||
}
|
||||
++itr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// remove bodies
|
||||
for (auto object : objects) {
|
||||
btRigidBody* body = object->getRigidBody();
|
||||
if (body) {
|
||||
|
|
|
@ -32,10 +32,13 @@ protected:
|
|||
Q_INVOKABLE void setStereoInput(bool stereo);
|
||||
|
||||
signals:
|
||||
void mutedByMixer();
|
||||
void environmentMuted();
|
||||
void receivedFirstPacket();
|
||||
void disconnected();
|
||||
void mutedByMixer(); /// the client has been muted by the mixer
|
||||
void environmentMuted(); /// the entire environment has been muted by the mixer
|
||||
void receivedFirstPacket(); /// the client has received its first packet from the audio mixer
|
||||
void disconnected(); /// the client has been disconnected from the audio mixer
|
||||
void noiseGateOpened(); /// the noise gate has opened
|
||||
void noiseGateClosed(); /// the noise gate has closed
|
||||
void inputReceived(const QByteArray& inputSamples); /// a frame of mic input audio has been received and processed
|
||||
|
||||
private:
|
||||
AudioScriptingInterface();
|
||||
|
|
|
@ -142,7 +142,7 @@ QString encodeEntityIdIntoEntityUrl(const QString& url, const QString& entityID)
|
|||
|
||||
QString ScriptEngine::logException(const QScriptValue& exception) {
|
||||
auto message = formatException(exception);
|
||||
scriptErrorMessage(qPrintable(message));
|
||||
scriptErrorMessage(message);
|
||||
return message;
|
||||
}
|
||||
|
||||
|
@ -453,7 +453,7 @@ void ScriptEngine::loadURL(const QUrl& scriptURL, bool reload) {
|
|||
}
|
||||
|
||||
void ScriptEngine::scriptErrorMessage(const QString& message) {
|
||||
qCCritical(scriptengine) << message;
|
||||
qCCritical(scriptengine) << qPrintable(message);
|
||||
emit errorMessage(message);
|
||||
}
|
||||
|
||||
|
|
|
@ -48,6 +48,9 @@ QScriptValue PointerEvent::toScriptValue(QScriptEngine* engine, const PointerEve
|
|||
case Press:
|
||||
obj.setProperty("type", "Press");
|
||||
break;
|
||||
case DoublePress:
|
||||
obj.setProperty("type", "DoublePress");
|
||||
break;
|
||||
case Release:
|
||||
obj.setProperty("type", "Release");
|
||||
break;
|
||||
|
@ -131,6 +134,8 @@ void PointerEvent::fromScriptValue(const QScriptValue& object, PointerEvent& eve
|
|||
QString typeStr = type.isString() ? type.toString() : "Move";
|
||||
if (typeStr == "Press") {
|
||||
event._type = Press;
|
||||
} else if (typeStr == "DoublePress") {
|
||||
event._type = DoublePress;
|
||||
} else if (typeStr == "Release") {
|
||||
event._type = Release;
|
||||
} else {
|
||||
|
|
|
@ -28,9 +28,10 @@ public:
|
|||
};
|
||||
|
||||
enum EventType {
|
||||
Press, // A button has just been pressed
|
||||
Release, // A button has just been released
|
||||
Move // The pointer has just moved
|
||||
Press, // A button has just been pressed
|
||||
DoublePress, // A button has just been double pressed
|
||||
Release, // A button has just been released
|
||||
Move // The pointer has just moved
|
||||
};
|
||||
|
||||
PointerEvent();
|
||||
|
|
|
@ -24,29 +24,34 @@ public:
|
|||
RateCounter() { _rate = 0; } // avoid use of std::atomic copy ctor
|
||||
|
||||
void increment(size_t count = 1) {
|
||||
auto now = usecTimestampNow();
|
||||
float currentIntervalMs = (now - _start) / (float) USECS_PER_MSEC;
|
||||
if (currentIntervalMs > (float) INTERVAL) {
|
||||
float currentCount = _count;
|
||||
float intervalSeconds = currentIntervalMs / (float) MSECS_PER_SECOND;
|
||||
_rate = roundf(currentCount / intervalSeconds * _scale) / _scale;
|
||||
_start = now;
|
||||
_count = 0;
|
||||
};
|
||||
checkRate();
|
||||
_count += count;
|
||||
}
|
||||
|
||||
float rate() const { return _rate; }
|
||||
float rate() const { checkRate(); return _rate; }
|
||||
|
||||
uint8_t precision() const { return PRECISION; }
|
||||
|
||||
uint32_t interval() const { return INTERVAL; }
|
||||
|
||||
private:
|
||||
uint64_t _start { usecTimestampNow() };
|
||||
size_t _count { 0 };
|
||||
mutable uint64_t _start { usecTimestampNow() };
|
||||
mutable size_t _count { 0 };
|
||||
const float _scale { powf(10, PRECISION) };
|
||||
std::atomic<float> _rate;
|
||||
mutable std::atomic<float> _rate;
|
||||
|
||||
void checkRate() const {
|
||||
auto now = usecTimestampNow();
|
||||
float currentIntervalMs = (now - _start) / (float)USECS_PER_MSEC;
|
||||
if (currentIntervalMs > (float)INTERVAL) {
|
||||
float currentCount = _count;
|
||||
float intervalSeconds = currentIntervalMs / (float)MSECS_PER_SECOND;
|
||||
_rate = roundf(currentCount / intervalSeconds * _scale) / _scale;
|
||||
_start = now;
|
||||
_count = 0;
|
||||
};
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
19
script-archive/entityScripts/doubleClickExample.js
Normal file
19
script-archive/entityScripts/doubleClickExample.js
Normal file
|
@ -0,0 +1,19 @@
|
|||
(function() {
|
||||
var _this;
|
||||
function DoubleClickExample() {
|
||||
_this = this;
|
||||
return;
|
||||
}
|
||||
|
||||
DoubleClickExample.prototype = {
|
||||
clickDownOnEntity: function() {
|
||||
print("clickDownOnEntity");
|
||||
},
|
||||
|
||||
doubleclickOnEntity: function() {
|
||||
print("doubleclickOnEntity");
|
||||
}
|
||||
|
||||
};
|
||||
return new DoubleClickExample();
|
||||
});
|
|
@ -11,7 +11,7 @@
|
|||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
var DEFAULT_SCRIPTS = [
|
||||
var DEFAULT_SCRIPTS_COMBINED = [
|
||||
"system/progress.js",
|
||||
"system/away.js",
|
||||
"system/audio.js",
|
||||
|
@ -27,17 +27,13 @@ var DEFAULT_SCRIPTS = [
|
|||
"system/tablet-users.js",
|
||||
"system/selectAudioDevice.js",
|
||||
"system/notifications.js",
|
||||
"system/controllers/squeezeHands.js",
|
||||
"system/controllers/controllerDisplayManager.js",
|
||||
"system/controllers/handControllerGrab.js",
|
||||
"system/controllers/handControllerPointer.js",
|
||||
"system/controllers/grab.js",
|
||||
"system/controllers/teleport.js",
|
||||
"system/controllers/toggleAdvancedMovementForHandControllers.js",
|
||||
"system/dialTone.js",
|
||||
"system/firstPersonHMD.js",
|
||||
"system/tablet-ui/tabletUI.js"
|
||||
];
|
||||
var DEFAULT_SCRIPTS_SEPARATE = [
|
||||
"system/controllers/controllerScripts.js"
|
||||
];
|
||||
|
||||
// add a menu item for debugging
|
||||
var MENU_CATEGORY = "Developer";
|
||||
|
@ -64,16 +60,24 @@ if (Menu.menuExists(MENU_CATEGORY) && !Menu.menuItemExists(MENU_CATEGORY, MENU_I
|
|||
});
|
||||
}
|
||||
|
||||
function runDefaultsTogether() {
|
||||
for (var j in DEFAULT_SCRIPTS) {
|
||||
Script.include(DEFAULT_SCRIPTS[j]);
|
||||
function loadSeparateDefaults() {
|
||||
for (var i in DEFAULT_SCRIPTS_SEPARATE) {
|
||||
Script.load(DEFAULT_SCRIPTS_SEPARATE[i]);
|
||||
}
|
||||
}
|
||||
|
||||
function runDefaultsSeparately() {
|
||||
for (var i in DEFAULT_SCRIPTS) {
|
||||
Script.load(DEFAULT_SCRIPTS[i]);
|
||||
function runDefaultsTogether() {
|
||||
for (var i in DEFAULT_SCRIPTS_COMBINED) {
|
||||
Script.include(DEFAULT_SCRIPTS_COMBINED[i]);
|
||||
}
|
||||
loadSeparateDefaults();
|
||||
}
|
||||
|
||||
function runDefaultsSeparately() {
|
||||
for (var i in DEFAULT_SCRIPTS_COMBINED) {
|
||||
Script.load(DEFAULT_SCRIPTS_COMBINED[i]);
|
||||
}
|
||||
loadSeparateDefaults();
|
||||
}
|
||||
|
||||
// start all scripts
|
||||
|
|
95
scripts/system/audioScope.js
Normal file
95
scripts/system/audioScope.js
Normal file
|
@ -0,0 +1,95 @@
|
|||
"use strict";
|
||||
//
|
||||
// audioScope.js
|
||||
// scripts/system/
|
||||
//
|
||||
// Created by Brad Hefta-Gaub on 3/10/2016
|
||||
// Copyright 2016 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
/* global Script, Tablet, AudioScope, Audio */
|
||||
|
||||
(function () { // BEGIN LOCAL_SCOPE
|
||||
|
||||
var scopeVisibile = AudioScope.getVisible();
|
||||
var scopePaused = AudioScope.getPause();
|
||||
var autoPause = false;
|
||||
|
||||
var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system");
|
||||
var showScopeButton = tablet.addButton({
|
||||
icon: "icons/tablet-icons/scope.svg",
|
||||
text: "Audio Scope",
|
||||
isActive: scopeVisibile
|
||||
});
|
||||
|
||||
var scopePauseImage = "icons/tablet-icons/scope-pause.svg";
|
||||
var scopePlayImage = "icons/tablet-icons/scope-play.svg";
|
||||
|
||||
var pauseScopeButton = tablet.addButton({
|
||||
icon: scopePaused ? scopePlayImage : scopePauseImage,
|
||||
text: scopePaused ? "Unpause" : "Pause",
|
||||
isActive: scopePaused
|
||||
});
|
||||
|
||||
var autoPauseScopeButton = tablet.addButton({
|
||||
icon: "icons/tablet-icons/scope-auto.svg",
|
||||
text: "Auto Pause",
|
||||
isActive: autoPause
|
||||
});
|
||||
|
||||
function setScopePause(paused) {
|
||||
scopePaused = paused;
|
||||
pauseScopeButton.editProperties({
|
||||
isActive: scopePaused,
|
||||
icon: scopePaused ? scopePlayImage : scopePauseImage,
|
||||
text: scopePaused ? "Unpause" : "Pause"
|
||||
});
|
||||
AudioScope.setPause(scopePaused);
|
||||
}
|
||||
|
||||
showScopeButton.clicked.connect(function () {
|
||||
// toggle button active state
|
||||
scopeVisibile = !scopeVisibile;
|
||||
showScopeButton.editProperties({
|
||||
isActive: scopeVisibile
|
||||
});
|
||||
|
||||
AudioScope.setVisible(scopeVisibile);
|
||||
});
|
||||
|
||||
pauseScopeButton.clicked.connect(function () {
|
||||
// toggle button active state
|
||||
setScopePause(!scopePaused);
|
||||
});
|
||||
|
||||
autoPauseScopeButton.clicked.connect(function () {
|
||||
// toggle button active state
|
||||
autoPause = !autoPause;
|
||||
autoPauseScopeButton.editProperties({
|
||||
isActive: autoPause,
|
||||
text: autoPause ? "Auto Pause" : "Manual"
|
||||
});
|
||||
});
|
||||
|
||||
Script.scriptEnding.connect(function () {
|
||||
tablet.removeButton(showScopeButton);
|
||||
tablet.removeButton(pauseScopeButton);
|
||||
tablet.removeButton(autoPauseScopeButton);
|
||||
});
|
||||
|
||||
Audio.noiseGateOpened.connect(function(){
|
||||
if (autoPause) {
|
||||
setScopePause(false);
|
||||
}
|
||||
});
|
||||
|
||||
Audio.noiseGateClosed.connect(function(){
|
||||
// noise gate closed
|
||||
if (autoPause) {
|
||||
setScopePause(true);
|
||||
}
|
||||
});
|
||||
|
||||
}()); // END LOCAL_SCOPE
|
41
scripts/system/controllers/controllerScripts.js
Normal file
41
scripts/system/controllers/controllerScripts.js
Normal file
|
@ -0,0 +1,41 @@
|
|||
"use strict";
|
||||
|
||||
// controllerScripts.js
|
||||
//
|
||||
// Created by David Rowe on 15 Mar 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
|
||||
//
|
||||
|
||||
var CONTOLLER_SCRIPTS = [
|
||||
"squeezeHands.js",
|
||||
"controllerDisplayManager.js",
|
||||
"handControllerGrab.js",
|
||||
"handControllerPointer.js",
|
||||
"grab.js",
|
||||
"teleport.js",
|
||||
"toggleAdvancedMovementForHandControllers.js",
|
||||
];
|
||||
|
||||
var DEBUG_MENU_ITEM = "Debug defaultScripts.js";
|
||||
|
||||
|
||||
function runDefaultsTogether() {
|
||||
for (var j in CONTOLLER_SCRIPTS) {
|
||||
Script.include(CONTOLLER_SCRIPTS[j]);
|
||||
}
|
||||
}
|
||||
|
||||
function runDefaultsSeparately() {
|
||||
for (var i in CONTOLLER_SCRIPTS) {
|
||||
Script.load(CONTOLLER_SCRIPTS[i]);
|
||||
}
|
||||
}
|
||||
|
||||
if (Menu.isOptionChecked(DEBUG_MENU_ITEM)) {
|
||||
runDefaultsSeparately();
|
||||
} else {
|
||||
runDefaultsTogether();
|
||||
}
|
Loading…
Reference in a new issue