Merge branch 'master' of github.com:highfidelity/hifi into feat/js-script-console-auto-complete
|
@ -1,5 +1,7 @@
|
|||
# this guide is specific to Ubuntu 16.04.
|
||||
# deb packages of High Fidelity domain server and assignment client are stored on debian.highfidelity.com
|
||||
## This guide is specific to Ubuntu 16.04.
|
||||
Deb packages of High Fidelity domain server and assignment client are stored on debian.highfidelity.com
|
||||
|
||||
```
|
||||
sudo su -
|
||||
apt-get -y update
|
||||
apt-get install -y software-properties-common
|
||||
|
@ -8,20 +10,27 @@ add-apt-repository "deb http://debian.highfidelity.com stable main"
|
|||
apt-get -y update
|
||||
apt-get install -y hifi-domain-server
|
||||
apt-get install -y hifi-assignment-client
|
||||
```
|
||||
|
||||
# When installing master/dev builds, the packages are slightly different and you just need to change the last 2 steps to:
|
||||
When installing master/dev builds, the packages are slightly different and you just need to change the last 2 steps to:
|
||||
```
|
||||
apt-get install -y hifi-dev-domain-server
|
||||
apt-get install -y hifi-dev-assignment-client
|
||||
```
|
||||
|
||||
# domain server and assignment clients should already be running. The processes are controlled via:
|
||||
Domain server and assignment clients should already be running. The processes are controlled via:
|
||||
```
|
||||
systemctl start hifi-domain-server
|
||||
systemctl stop hifi-domain-server
|
||||
```
|
||||
|
||||
# Once the machine is setup and processes are running you should ensure that your firewall exposes port 40100 on TCP and all UDP ports. This will get your domain up and running and you could connect to it (for now) by using High Fidelity Interface and typing in the IP for the place name. (further customizations can be done via http://IPAddress:40100).
|
||||
|
||||
# The server always depends on both hifi-domain-server and hifi-assignment-client running at the same time.
|
||||
# As an additional step, you should ensure that your packages are automatically updated when a new version goes out. You could, for example, set the automatic update checks to happen every hour (though this could potentially result in the domain being unreachable for a whole hour by new clients when they are released - adjust the update checks accordingly).
|
||||
Once the machine is setup and processes are running, you should ensure that your firewall exposes port 40100 on TCP and all UDP ports. This will get your domain up and running and you could connect to it (for now) by using High Fidelity Interface and typing in the IP for the place name. (Further customizations can be done via http://IPAddress:40100).
|
||||
|
||||
The server always depends on both hifi-domain-server and hifi-assignment-client running at the same time.
|
||||
As an additional step, you should ensure that your packages are automatically updated when a new version goes out. You could, for example, set the automatic update checks to happen every hour (though this could potentially result in the domain being unreachable for a whole hour by new clients when they are released - adjust the update checks accordingly).
|
||||
To do this you can modify /etc/crontab by adding the following lines
|
||||
```
|
||||
0 */1 * * * root apt-get update
|
||||
1 */1 * * * root apt-get install --only-upgrade -y hifi-domain-server
|
||||
2 */1 * * * root apt-get install --only-upgrade -y hifi-assignment-client
|
||||
```
|
||||
|
|
|
@ -123,12 +123,12 @@ void ScriptableAvatar::update(float deltatime) {
|
|||
AnimPose& absPose = absPoses[i];
|
||||
if (data.rotation != absPose.rot()) {
|
||||
data.rotation = absPose.rot();
|
||||
data.rotationSet = true;
|
||||
data.rotationIsDefaultPose = false;
|
||||
}
|
||||
AnimPose& relPose = poses[i];
|
||||
if (data.translation != relPose.trans()) {
|
||||
data.translation = relPose.trans();
|
||||
data.translationSet = true;
|
||||
data.translationIsDefaultPose = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
21
interface/resources/icons/tablet-icons/EmoteAppIcon.svg
Normal file
|
@ -0,0 +1,21 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 22.0.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 100 100.8" style="enable-background:new 0 0 100 100.8;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:#FFFFFF;}
|
||||
</style>
|
||||
<path class="st0" d="M26.7,83.9c7.3,1.2,14.8,1.8,22.1,1.8c0.4,0,0.8,0,1.2,0c7.8-0.1,15.6-0.8,23.4-2.2l0,0
|
||||
c5.7-1.1,11.3-6.6,12.5-12.3C87.3,64.2,88,57,88,50s-0.7-14.2-2.1-21.2c-1.2-5.6-6.8-11.1-12.5-12.2c-7.7-1.4-15.6-2.2-23.4-2.2
|
||||
c-7.7-0.1-15.6,0.5-23.4,1.8c-5.7,1-11.4,6.5-12.6,12.3c-1.4,7.2-2.1,14.4-2.1,21.6s0.7,14.4,2.1,21.7
|
||||
C15.3,77.4,20.9,82.9,26.7,83.9z M20.9,29.8c0.6-2.9,4-6.3,6.9-6.8c7-1.1,14-1.7,21-1.7c0.4,0,0.8,0,1.2,0
|
||||
c7.4,0.1,14.8,0.8,22.1,2.1c2.9,0.6,6.4,3.9,6.9,6.7c1.3,6.6,1.9,13.3,1.9,19.9c0,6.6-0.6,13.3-1.9,19.8c-0.6,2.8-4,6.2-6.9,6.8
|
||||
c-7.3,1.3-14.8,2.1-22.1,2.1c-7.4,0.1-14.8-0.5-22.1-1.7c-2.9-0.5-6.3-3.9-6.9-6.7c-1.3-6.7-2-13.5-2-20.3
|
||||
C19,43.3,19.6,36.4,20.9,29.8z"/>
|
||||
<path class="st0" d="M32.3,61.4c-0.5,1.3-0.1,2.8,0.9,3.8c0.3,0.3,7.2,6.6,15.9,6.6c0.8,0,1.7-0.1,2.6-0.2
|
||||
c9.8-1.5,15.5-11.1,15.8-11.5c0.7-1.2,0.6-2.8-0.2-3.9c-0.9-1.1-2.3-1.6-3.7-1.3c-9.2,2.5-18.6,3.9-28.1,4.2
|
||||
C34,59.1,32.8,60,32.3,61.4z"/>
|
||||
<circle class="st0" cx="36.5" cy="42.8" r="9"/>
|
||||
<path class="st0" d="M61.4,44.1h6.1c1.9,0,3.3-1.5,3.3-3.3c0-1.9-1.5-3.3-3.3-3.3h-6.1c-1.9,0-3.3,1.5-3.3,3.3
|
||||
C58.1,42.7,59.6,44.1,61.4,44.1z"/>
|
||||
</svg>
|
After Width: | Height: | Size: 1.5 KiB |
BIN
interface/resources/images/preview-privacy.png
Normal file
After Width: | Height: | Size: 52 KiB |
|
@ -16,10 +16,9 @@ import "controls-uit" as HifiControls
|
|||
Item {
|
||||
id: root
|
||||
width: parent.width
|
||||
height: parent.height
|
||||
height: parent.height
|
||||
|
||||
property var hideQtMethods: true
|
||||
|
||||
property var maxUpdateValues: 20
|
||||
property var maxReloadValues: 200
|
||||
property var apiMembers: []
|
||||
|
@ -30,7 +29,7 @@ Item {
|
|||
property Component keyboard
|
||||
|
||||
Rectangle {
|
||||
color: "white"
|
||||
color: hifi.colors.baseGray
|
||||
width: parent.width
|
||||
height: parent.height
|
||||
}
|
||||
|
@ -51,32 +50,22 @@ Item {
|
|||
Row {
|
||||
id: topBar
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: 8
|
||||
anchors.leftMargin: 30
|
||||
anchors.top: parent.top
|
||||
anchors.topMargin: 30
|
||||
width: parent.width
|
||||
height: 50
|
||||
HifiControls.GlyphButton {
|
||||
id: search
|
||||
enabled: true
|
||||
glyph: hifi.glyphs.search
|
||||
color: hifi.colors.text
|
||||
size: 48
|
||||
width: 50
|
||||
height: 50
|
||||
onClicked: {
|
||||
addListElements(searchBar.text);
|
||||
focus = true;
|
||||
}
|
||||
}
|
||||
height: 40
|
||||
|
||||
HifiControls.GlyphButton {
|
||||
id: back;
|
||||
enabled: true;
|
||||
color: hifi.buttons.black
|
||||
glyph: hifi.glyphs.backward
|
||||
color: hifi.colors.text
|
||||
size: 48
|
||||
width: 30
|
||||
height: 50
|
||||
anchors.margins: 2
|
||||
size: 40
|
||||
width: 40
|
||||
height: 40
|
||||
anchors.left: search.right
|
||||
anchors.leftMargin: 12
|
||||
onClicked: {
|
||||
var text = searchBar.text;
|
||||
var chain = text.split(".");
|
||||
|
@ -99,17 +88,20 @@ Item {
|
|||
}
|
||||
}
|
||||
|
||||
TextField {
|
||||
id: searchBar
|
||||
HifiControls.TextField {
|
||||
id: searchBar
|
||||
focus: true
|
||||
font.pixelSize: 16
|
||||
width: 2*(parent.width-back.width-search.width-reload.width-update.width-evaluate.width-addMember.width-16)/3
|
||||
height: parent.height
|
||||
font.family: ralewayRegular.name
|
||||
isSearchField: true
|
||||
width: parent.width - 112
|
||||
height: 40
|
||||
colorScheme: hifi.colorSchemes.dark
|
||||
anchors.left: back.right
|
||||
anchors.leftMargin: 10
|
||||
font.family: firaSansSemiBold.name
|
||||
placeholderText: "Search"
|
||||
onAccepted: {
|
||||
console.log("Enter Pressed");
|
||||
search.clicked();
|
||||
addListElements(searchBar.text);
|
||||
}
|
||||
onActiveFocusChanged: {
|
||||
if (activeFocus && HMD.mounted) {
|
||||
|
@ -119,15 +111,27 @@ Item {
|
|||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
HifiControls.Button {
|
||||
Row {
|
||||
id: topBar2
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: 30
|
||||
anchors.top: topBar.bottom
|
||||
anchors.topMargin: 30
|
||||
width: parent.width -60
|
||||
height: 40
|
||||
|
||||
HifiControls.GlyphButton {
|
||||
id: addMember;
|
||||
enabled: true;
|
||||
text: "+"
|
||||
width: 50
|
||||
height: 50
|
||||
anchors.margins: 2
|
||||
color: hifi.buttons.black
|
||||
glyph: hifi.glyphs.maximize
|
||||
width: 40
|
||||
height: 40
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
onClicked: {
|
||||
addNewMember();
|
||||
updateList.start();
|
||||
|
@ -138,36 +142,48 @@ Item {
|
|||
HifiControls.Button {
|
||||
id: evaluate;
|
||||
enabled: true;
|
||||
color: hifi.buttons.black
|
||||
text: "Eval"
|
||||
width: 50
|
||||
height: 50
|
||||
anchors.margins: 2
|
||||
width: 40
|
||||
height: 40
|
||||
anchors.left: addMember.right
|
||||
anchors.leftMargin: 12
|
||||
onClicked: {
|
||||
evaluateMember();
|
||||
focus = true;
|
||||
}
|
||||
}
|
||||
TextField {
|
||||
id: valueBar
|
||||
focus: true
|
||||
|
||||
HifiControls.TextField {
|
||||
id: valueBar
|
||||
isSearchField: false
|
||||
font.pixelSize: 16
|
||||
width: (parent.width-back.width-search.width-reload.width-update.width-evaluate.width-addMember.width-16)/3
|
||||
height: parent.height
|
||||
font.family: ralewayRegular.name
|
||||
width: parent.width - 208
|
||||
height: 40
|
||||
colorScheme: hifi.colorSchemes.dark
|
||||
font.family: firaSansSemiBold.name
|
||||
placeholderText: "Value"
|
||||
textColor: "#4466DD"
|
||||
anchors.margins: 2
|
||||
anchors.left: evaluate.right
|
||||
anchors.leftMargin: 12
|
||||
onActiveFocusChanged: {
|
||||
if (activeFocus && HMD.mounted) {
|
||||
keyboard.raised = true;
|
||||
} else {
|
||||
keyboard.raised = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
HifiControls.GlyphButton {
|
||||
id: reload;
|
||||
enabled: false;
|
||||
color: hifi.buttons.black
|
||||
glyph: hifi.glyphs.reload
|
||||
color: hifi.colors.text
|
||||
size: 48
|
||||
width: 50
|
||||
height: 50
|
||||
anchors.margins: 2
|
||||
size: 40
|
||||
width: 40
|
||||
height: 40
|
||||
anchors.right: update.left
|
||||
anchors.rightMargin: 12
|
||||
onClicked: {
|
||||
reloadListValues();
|
||||
focus = true;
|
||||
|
@ -177,11 +193,12 @@ Item {
|
|||
HifiControls.GlyphButton {
|
||||
id: update;
|
||||
enabled: false;
|
||||
color: hifi.buttons.black
|
||||
glyph: hifi.glyphs.playback_play
|
||||
size: 48
|
||||
width: 50
|
||||
height: 50
|
||||
anchors.margins: 2
|
||||
size: 40
|
||||
width: 40
|
||||
height: 40
|
||||
anchors.right: parent.right
|
||||
onClicked: {
|
||||
if (isReloading) {
|
||||
update.glyph = hifi.glyphs.playback_play
|
||||
|
@ -196,71 +213,104 @@ Item {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
ListModel {
|
||||
id: memberModel
|
||||
}
|
||||
|
||||
Component {
|
||||
id: memberDelegate
|
||||
|
||||
Row {
|
||||
id: memberRow
|
||||
property var isMainKey: apiType === "class";
|
||||
spacing: 10
|
||||
Rectangle {
|
||||
width: isMainKey ? 20 : 40;
|
||||
height: parent.height
|
||||
}
|
||||
|
||||
RalewayRegular {
|
||||
text: apiMember
|
||||
size: !isMainKey ? 16 : 22
|
||||
MouseArea {
|
||||
width: list.width
|
||||
height: parent.height
|
||||
onClicked: {
|
||||
searchBar.text = apiType=="function()" ? apiMember + "()" : apiMember;
|
||||
valueBar.text = !apiValue ? "" : apiValue;
|
||||
list.currentIndex = index;
|
||||
evaluatingIdx = index;
|
||||
}
|
||||
onDoubleClicked: {
|
||||
if (apiType === "class") {
|
||||
addListElements(apiMember+".");
|
||||
} else {
|
||||
isolateElement(evaluatingIdx);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
RalewayRegular {
|
||||
text: apiType
|
||||
size: 14
|
||||
color: hifi.colors.baseGrayHighlight
|
||||
}
|
||||
|
||||
RalewayRegular {
|
||||
text: !apiValue ? "" : apiValue;
|
||||
size: 16
|
||||
color: "#4466DD"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Rectangle {
|
||||
id: membersBackground
|
||||
anchors {
|
||||
left: parent.left; right: parent.right; top: topBar.bottom; bottom: parent.bottom;
|
||||
margins: hifi.dimensions.contentMargin.x
|
||||
bottomMargin: hifi.dimensions.contentSpacing.y + 40
|
||||
left: parent.left; right: parent.right; top: topBar2.bottom; bottom: bottomBar.top;
|
||||
margins: 30
|
||||
}
|
||||
color: hifi.colors.tableBackgroundDark
|
||||
border.color: hifi.colors.lightGray
|
||||
border.width: 2
|
||||
radius: 5
|
||||
|
||||
ListModel {
|
||||
id: memberModel
|
||||
}
|
||||
|
||||
Component {
|
||||
id: memberDelegate
|
||||
Item {
|
||||
id: item
|
||||
width: parent.width
|
||||
anchors.left: parent.left
|
||||
height: 26
|
||||
clip: true
|
||||
|
||||
Rectangle {
|
||||
width: parent.width
|
||||
height: parent.height
|
||||
color: index % 2 == 0 ? hifi.colors.tableRowDarkEven : hifi.colors.tableRowDarkOdd
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
||||
Row {
|
||||
id: memberRow
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
spacing: 10
|
||||
|
||||
FiraSansSemiBold {
|
||||
property var isMainKey: apiType === "class";
|
||||
text: apiMember
|
||||
size: isMainKey ? 17 : 15
|
||||
font.bold: true
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
color: isMainKey ? hifi.colors.faintGray : hifi.colors.lightGrayText
|
||||
MouseArea {
|
||||
width: list.width
|
||||
height: parent.height
|
||||
onClicked: {
|
||||
searchBar.text = apiType=="function()" ? apiMember + "()" : apiMember;
|
||||
valueBar.text = !apiValue ? "" : apiValue;
|
||||
list.currentIndex = index;
|
||||
evaluatingIdx = index;
|
||||
}
|
||||
onDoubleClicked: {
|
||||
if (apiType === "class") {
|
||||
addListElements(apiMember+".");
|
||||
} else {
|
||||
isolateElement(evaluatingIdx);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FiraSansRegular {
|
||||
text: apiType
|
||||
anchors.left: apiMember.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
size: 13
|
||||
color: hifi.colors.lightGrayText
|
||||
}
|
||||
|
||||
FiraSansRegular {
|
||||
text: !apiValue ? "" : apiValue;
|
||||
anchors.left: apiType.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
size: 14
|
||||
color: hifi.colors.primaryHighlight
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: highlight
|
||||
Rectangle {
|
||||
anchors {
|
||||
left: list.left
|
||||
right: scrollBar.left
|
||||
leftMargin: 2
|
||||
rightMargin: 2
|
||||
}
|
||||
color: hifi.colors.primaryHighlight
|
||||
radius: 4
|
||||
z: 10
|
||||
opacity: 0.5
|
||||
}
|
||||
}
|
||||
color: "white"
|
||||
radius: 4
|
||||
|
||||
ListView {
|
||||
id: list
|
||||
|
@ -269,23 +319,16 @@ Item {
|
|||
left: parent.left
|
||||
right: scrollBar.left
|
||||
bottom: parent.bottom
|
||||
margins: 4
|
||||
topMargin: 2
|
||||
leftMargin: 2
|
||||
bottomMargin: 2
|
||||
}
|
||||
clip: true
|
||||
cacheBuffer: 4000
|
||||
model: memberModel
|
||||
delegate: memberDelegate
|
||||
highlightMoveDuration: 0
|
||||
|
||||
highlight: Rectangle {
|
||||
anchors {
|
||||
left: parent ? parent.left : undefined
|
||||
right: parent ? parent.right : undefined
|
||||
leftMargin: hifi.dimensions.borderWidth
|
||||
rightMargin: hifi.dimensions.borderWidth
|
||||
}
|
||||
color: "#BBDDFF"
|
||||
}
|
||||
highlight: highlight
|
||||
onMovementStarted: {
|
||||
scrollSlider.manual = true;
|
||||
}
|
||||
|
@ -310,12 +353,11 @@ Item {
|
|||
top: parent.top
|
||||
right: parent.right
|
||||
bottom: parent.bottom
|
||||
topMargin: 4
|
||||
bottomMargin: 4
|
||||
margins: 2
|
||||
}
|
||||
width: scrolling ? 18 : 0
|
||||
radius: 4
|
||||
color: hifi.colors.baseGrayShadow
|
||||
width: 22
|
||||
height: parent.height - 4
|
||||
color: hifi.colors.tableScrollBackgroundDark
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
|
@ -344,14 +386,12 @@ Item {
|
|||
y = index*(scrollBar.height - scrollSlider.height)/(list.count - 1);
|
||||
}
|
||||
|
||||
anchors {
|
||||
right: parent.right
|
||||
rightMargin: 3
|
||||
}
|
||||
width: 12
|
||||
height: (list.height / list.contentHeight) * list.height
|
||||
radius: width / 4
|
||||
color: "white"
|
||||
anchors.right: parent.right
|
||||
anchors.margins: 2
|
||||
width: 18
|
||||
height: ((list.height / list.contentHeight) * list.height) < 15 ? 15 : (list.height / list.contentHeight) * list.height
|
||||
radius: 5
|
||||
color: hifi.colors.tableScrollHandleDark
|
||||
|
||||
visible: scrollBar.scrolling;
|
||||
|
||||
|
@ -373,66 +413,75 @@ Item {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
HifiControls.GlyphButton {
|
||||
id: clipboard;
|
||||
enabled: true;
|
||||
glyph: hifi.glyphs.scriptNew
|
||||
size: 38
|
||||
width: 50
|
||||
height: 50
|
||||
|
||||
Row {
|
||||
id: bottomBar
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: 30
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.margins: 2
|
||||
anchors.leftMargin: 8
|
||||
onClicked: {
|
||||
var buffer = "";
|
||||
for (var i = 0; i < memberModel.count; i++) {
|
||||
var datarow = memberModel.get(i);
|
||||
buffer += "\n" + datarow.apiMember + " " + datarow.apiType + " " + datarow.apiValue;
|
||||
}
|
||||
Window.copyToClipboard(buffer);
|
||||
focus = true;
|
||||
}
|
||||
}
|
||||
|
||||
HifiControls.Button {
|
||||
id: debug;
|
||||
enabled: true;
|
||||
text: "Debug Script"
|
||||
width: 120
|
||||
height: 50
|
||||
anchors.right: parent.right
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.margins: 2
|
||||
anchors.rightMargin: 8
|
||||
onClicked: {
|
||||
sendToScript({type: "selectScript"});
|
||||
}
|
||||
}
|
||||
anchors.bottomMargin: 30
|
||||
width: parent.width
|
||||
height: 40
|
||||
|
||||
HifiControls.CheckBox {
|
||||
id: hideQt
|
||||
boxSize: 25
|
||||
boxRadius: 3
|
||||
checked: true
|
||||
anchors.left: clipboard.right
|
||||
anchors.leftMargin: 8
|
||||
anchors.verticalCenter: clipboard.verticalCenter
|
||||
anchors.margins: 2
|
||||
onClicked: {
|
||||
hideQtMethods = checked;
|
||||
addListElements();
|
||||
HifiControls.GlyphButton {
|
||||
id: clipboard;
|
||||
enabled: true;
|
||||
color: hifi.buttons.black
|
||||
glyph: hifi.glyphs.scriptNew
|
||||
size: 25
|
||||
width: 40
|
||||
height: 40
|
||||
anchors.left: parent.left
|
||||
onClicked: {
|
||||
var buffer = "";
|
||||
for (var i = 0; i < memberModel.count; i++) {
|
||||
var datarow = memberModel.get(i);
|
||||
buffer += "\n" + datarow.apiMember + " " + datarow.apiType + " " + datarow.apiValue;
|
||||
}
|
||||
Window.copyToClipboard(buffer);
|
||||
focus = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
HifiControls.Label {
|
||||
id: hideLabel
|
||||
anchors.left: hideQt.right
|
||||
anchors.verticalCenter: clipboard.verticalCenter
|
||||
anchors.margins: 2
|
||||
font.pixelSize: 15
|
||||
text: "Hide Qt Methods"
|
||||
HifiControls.CheckBox {
|
||||
id: hideQt
|
||||
colorScheme: hifi.checkbox.dark
|
||||
boxSize: 25
|
||||
boxRadius: 3
|
||||
checked: true
|
||||
anchors.left: clipboard.right
|
||||
anchors.leftMargin: 10
|
||||
anchors.verticalCenter: clipboard.verticalCenter
|
||||
onClicked: {
|
||||
hideQtMethods = checked;
|
||||
addListElements();
|
||||
}
|
||||
}
|
||||
|
||||
HifiControls.Label {
|
||||
id: hideLabel
|
||||
anchors.left: hideQt.right
|
||||
anchors.verticalCenter: clipboard.verticalCenter
|
||||
anchors.margins: 2
|
||||
font.pixelSize: 15
|
||||
text: "Hide Qt Methods"
|
||||
}
|
||||
|
||||
HifiControls.Button {
|
||||
id: debug;
|
||||
enabled: true;
|
||||
color: hifi.buttons.black
|
||||
text: "Debug Script"
|
||||
width: 120
|
||||
height: 40
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: 60
|
||||
anchors.bottom: parent.bottom
|
||||
|
||||
onClicked: {
|
||||
sendToScript({type: "selectScript"});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
HifiControls.Keyboard {
|
||||
|
@ -639,4 +688,4 @@ Item {
|
|||
}
|
||||
|
||||
signal sendToScript(var message);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,10 +24,13 @@ TextField {
|
|||
property bool isSearchField: false
|
||||
property string label: ""
|
||||
property real controlHeight: height + (textFieldLabel.visible ? textFieldLabel.height + 1 : 0)
|
||||
property bool hasDefocusedBorder: true;
|
||||
property bool hasRoundedBorder: false
|
||||
property int roundedBorderRadius: 4
|
||||
property bool error: false;
|
||||
property bool hasClearButton: false;
|
||||
property string leftPlaceholderGlyph: "";
|
||||
property string leftPermanentGlyph: "";
|
||||
property string centerPlaceholderGlyph: "";
|
||||
|
||||
placeholderText: textField.placeholderText
|
||||
|
||||
|
@ -101,12 +104,12 @@ TextField {
|
|||
}
|
||||
}
|
||||
border.color: textField.error ? hifi.colors.redHighlight :
|
||||
(textField.activeFocus ? hifi.colors.primaryHighlight : (isFaintGrayColorScheme ? hifi.colors.lightGrayText : hifi.colors.lightGray))
|
||||
(textField.activeFocus ? hifi.colors.primaryHighlight : (hasDefocusedBorder ? (isFaintGrayColorScheme ? hifi.colors.lightGrayText : hifi.colors.lightGray) : color))
|
||||
border.width: textField.activeFocus || hasRoundedBorder || textField.error ? 1 : 0
|
||||
radius: isSearchField ? textField.height / 2 : (hasRoundedBorder ? 4 : 0)
|
||||
radius: isSearchField ? textField.height / 2 : (hasRoundedBorder ? roundedBorderRadius : 0)
|
||||
|
||||
HiFiGlyphs {
|
||||
text: textField.leftPlaceholderGlyph;
|
||||
text: textField.leftPermanentGlyph;
|
||||
color: textColor;
|
||||
size: hifi.fontSizes.textFieldSearchIcon;
|
||||
anchors.left: parent.left;
|
||||
|
@ -115,6 +118,15 @@ TextField {
|
|||
visible: text;
|
||||
}
|
||||
|
||||
HiFiGlyphs {
|
||||
text: textField.centerPlaceholderGlyph;
|
||||
color: textColor;
|
||||
size: parent.height;
|
||||
anchors.horizontalCenter: parent.horizontalCenter;
|
||||
anchors.verticalCenter: parent.verticalCenter;
|
||||
visible: text && !textField.focus && textField.text === "";
|
||||
}
|
||||
|
||||
HiFiGlyphs {
|
||||
text: hifi.glyphs.search
|
||||
color: textColor
|
||||
|
@ -145,7 +157,7 @@ TextField {
|
|||
placeholderTextColor: isFaintGrayColorScheme ? hifi.colors.lightGrayText : hifi.colors.lightGray
|
||||
selectedTextColor: hifi.colors.black
|
||||
selectionColor: hifi.colors.primaryHighlight
|
||||
padding.left: ((isSearchField || textField.leftPlaceholderGlyph !== "") ? textField.height - 2 : 0) + hifi.dimensions.textPadding
|
||||
padding.left: hasRoundedBorder ? textField.height / 2 : ((isSearchField || textField.leftPermanentGlyph !== "") ? textField.height - 2 : 0) + hifi.dimensions.textPadding
|
||||
padding.right: (hasClearButton ? textField.height - 2 : 0) + hifi.dimensions.textPadding
|
||||
}
|
||||
|
||||
|
|
|
@ -389,7 +389,7 @@ Rectangle {
|
|||
//
|
||||
Item {
|
||||
id: tabButtonsContainer;
|
||||
visible: !needsLogIn.visible && root.activeView !== "passphraseChange" && root.activeView !== "securityImageChange";
|
||||
visible: !needsLogIn.visible && root.activeView !== "passphraseChange" && root.activeView !== "securityImageChange" && sendMoney.currentActiveView !== "sendMoneyStep";
|
||||
property int numTabs: 5;
|
||||
// Size
|
||||
width: root.width;
|
||||
|
|
|
@ -29,13 +29,13 @@ Item {
|
|||
property string userName;
|
||||
property string profilePicUrl;
|
||||
|
||||
height: 65;
|
||||
height: 75;
|
||||
width: parent.width;
|
||||
|
||||
Rectangle {
|
||||
id: mainContainer;
|
||||
// Style
|
||||
color: root.isSelected ? hifi.colors.faintGray : hifi.colors.white;
|
||||
color: root.isSelected ? hifi.colors.faintGray80 : hifi.colors.white;
|
||||
// Size
|
||||
anchors.left: parent.left;
|
||||
anchors.right: parent.right;
|
||||
|
@ -49,7 +49,7 @@ Item {
|
|||
anchors.verticalCenter: parent.verticalCenter;
|
||||
anchors.left: parent.left;
|
||||
anchors.leftMargin: 36;
|
||||
height: root.height - 15;
|
||||
height: 50;
|
||||
width: visible ? height : 0;
|
||||
clip: true;
|
||||
Image {
|
||||
|
@ -83,15 +83,15 @@ Item {
|
|||
RalewaySemiBold {
|
||||
id: userName;
|
||||
anchors.left: avatarImage.right;
|
||||
anchors.leftMargin: 16;
|
||||
anchors.leftMargin: 12;
|
||||
anchors.top: parent.top;
|
||||
anchors.bottom: parent.bottom;
|
||||
anchors.right: chooseButton.visible ? chooseButton.left : parent.right;
|
||||
anchors.rightMargin: chooseButton.visible ? 10 : 0;
|
||||
// Text size
|
||||
size: 20;
|
||||
size: 18;
|
||||
// Style
|
||||
color: hifi.colors.baseGray;
|
||||
color: hifi.colors.blueAccent;
|
||||
text: root.userName;
|
||||
elide: Text.ElideRight;
|
||||
// Alignment
|
||||
|
@ -107,9 +107,9 @@ Item {
|
|||
colorScheme: hifi.colorSchemes.dark;
|
||||
anchors.verticalCenter: parent.verticalCenter;
|
||||
anchors.right: parent.right;
|
||||
anchors.rightMargin: 24;
|
||||
height: root.height - 20;
|
||||
width: 110;
|
||||
anchors.rightMargin: 28;
|
||||
height: 35;
|
||||
width: 100;
|
||||
text: "CHOOSE";
|
||||
onClicked: {
|
||||
var msg = { method: 'chooseConnection', userName: root.userName, profilePicUrl: root.profilePicUrl };
|
||||
|
|
|
@ -29,6 +29,7 @@ Item {
|
|||
property string displayName;
|
||||
property string userName;
|
||||
property string profilePic;
|
||||
property string textColor: hifi.colors.white;
|
||||
|
||||
Item {
|
||||
visible: root.isDisplayingNearby;
|
||||
|
@ -46,7 +47,7 @@ Item {
|
|||
// Text size
|
||||
size: 18;
|
||||
// Style
|
||||
color: hifi.colors.baseGray;
|
||||
color: root.textColor;
|
||||
verticalAlignment: Text.AlignBottom;
|
||||
elide: Text.ElideRight;
|
||||
}
|
||||
|
@ -63,7 +64,7 @@ Item {
|
|||
// Text size
|
||||
size: 16;
|
||||
// Style
|
||||
color: hifi.colors.baseGray;
|
||||
color: root.textColor;
|
||||
verticalAlignment: Text.AlignTop;
|
||||
elide: Text.ElideRight;
|
||||
}
|
||||
|
@ -108,7 +109,7 @@ Item {
|
|||
// Text size
|
||||
size: 16;
|
||||
// Style
|
||||
color: hifi.colors.baseGray;
|
||||
color: root.textColor;
|
||||
verticalAlignment: Text.AlignVCenter;
|
||||
elide: Text.ElideRight;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 22.0.1, 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 512 512" style="enable-background:new 0 0 512 512;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:#1398BB;}
|
||||
</style>
|
||||
<path class="st0" d="M256.8,42C138.5,42,42.2,138.3,42.2,256.6s96.3,214.6,214.6,214.6c118.3,0,214.6-96.3,214.6-214.6
|
||||
S375.1,42,256.8,42z M256.8,444.4C153.2,444.4,69,360.1,69,256.6C69,153,153.2,68.7,256.8,68.7c103.6,0,187.8,84.3,187.8,187.8
|
||||
C444.6,360.1,360.4,444.4,256.8,444.4z"/>
|
||||
<circle class="st0" cx="260.6" cy="189.4" r="60.6"/>
|
||||
<path class="st0" d="M306.4,282.6h-87.6c-36.5,0-66.4,30.2-66.4,66.7v33.9c29.3,22.6,65.4,36.1,105,36.1c44.4,0,84.7-17,115.2-44.7
|
||||
v-25.3C372.7,312.7,342.9,282.6,306.4,282.6z"/>
|
||||
</svg>
|
After Width: | Height: | Size: 921 B |
After Width: | Height: | Size: 58 KiB |
|
@ -0,0 +1,27 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 22.0.1, 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 512 512" style="enable-background:new 0 0 512 512;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:#1398BB;}
|
||||
</style>
|
||||
<path class="st0" d="M144.3,155c-8.7-8.8-16.7-17.1-25.2-25.7c-22.4,25.4-36.6,53.9-43.6,86.2c-26.9,125.7,81.2,241.5,208.6,223.4
|
||||
C385.9,424.4,458,329.2,443.5,228.2c-4.4-30.6-15.4-58.9-34.1-83.9c-1.6-2.2-3.3-4.3-4.7-6.6c-4.4-7.4-3.1-14.9,3-19.5
|
||||
c6-4.5,14.2-3.6,19.2,3.5c8.2,11.7,16.6,23.4,22.9,36.1c41.9,84.9,25.7,181.7-41,248.7c-67,67.4-175.1,81.3-257.1,33.3
|
||||
c-35.3-20.7-63.1-48.6-82.7-84.5c-41-75-31.5-172.5,23.4-237.9c2.2-2.7,4.4-5.4,7.1-8.7c-8.1-8.5-16.1-16.9-24-25.3
|
||||
c-7.2-7.7-7.9-16.1-1.9-22.1c5.9-5.9,15-5.1,22.1,2.4c56.8,59.5,113.6,119,170.4,178.6c7.2,7.6,7.8,15.4,1.9,21.4
|
||||
c-6.1,6.2-14.7,5.5-22-2c-10.2-10.4-20.2-21-30.3-31.5c-17.4,24.7-3.3,62,27.2,72.5c23,7.9,48.8-2,60.7-23.1
|
||||
c1.2-2.1,2.3-4.3,3.7-6.3c4.5-6.6,11.7-8.6,18.3-5c6.6,3.5,8.9,10.5,5.9,18.1c-12.4,31.6-47.9,52.2-82.3,47.7
|
||||
c-36.1-4.8-64.3-32.6-68.4-67.7c-2.4-20.7,2.4-39.7,14.9-57.5c-10.4-10.9-20.8-21.6-31.7-33c-9.7,10.8-16.2,22.7-20.9,35.7
|
||||
c-31.3,86.4,38.9,175.1,130.1,164.3c74-8.7,122.3-80.9,103.3-154.3c-3.4-13.1-1-20.2,7.8-22.7c9.5-2.8,15.2,1.8,19.2,15.4
|
||||
c20.1,67.4-16,144.8-79.8,175.3c-67.5,32.2-147.5,9.7-188.8-49c-37.8-53.8-36.1-127.7,4.7-179.1C141.2,159.4,142.5,157.4,144.3,155z
|
||||
"/>
|
||||
<path class="st0" d="M236.2,262.2c-4.2-13.6,3.4-28.2,16.8-32.4c13.5-4.2,28.1,3.3,32.5,16.8c4.5,13.9-3.1,28.4-17.2,32.7
|
||||
C254.6,283.5,240.4,275.9,236.2,262.2z"/>
|
||||
<path class="st0" d="M319.6,101.9c-3.7-11,1.8-22.3,12.7-26c11.3-3.9,22.8,1.9,26.4,13.2c3.5,11-2.3,22.2-13.2,25.8
|
||||
C334.5,118.5,323.3,112.9,319.6,101.9z"/>
|
||||
<path class="st0" d="M214.9,66.2c3.2,10.2-2.2,20.5-12.5,23.6c-9.8,3-20-2.3-23.3-12c-3.3-9.9,2.2-20.8,12-24
|
||||
C201.1,50.5,211.8,56.2,214.9,66.2z"/>
|
||||
<path class="st0" d="M230.8,164.6c-3.7-11,1.8-22.3,12.7-26c11.3-3.9,22.8,1.9,26.4,13.2c3.5,11-2.3,22.2-13.2,25.8
|
||||
C245.7,181.2,234.5,175.6,230.8,164.6z"/>
|
||||
</svg>
|
After Width: | Height: | Size: 2.1 KiB |
|
@ -0,0 +1,27 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 22.0.1, 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 143.1 414.2" style="enable-background:new 0 0 143.1 414.2;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:#B9B9B9;}
|
||||
.st1{fill:#EE982D;}
|
||||
.st2{fill:#FF630A;}
|
||||
</style>
|
||||
<path class="st0" d="M78.8,7.1c3.9,4.3,7.7,8.7,11.8,12.9c3.7,3.8,5.1,7.9,4.4,12.9c-1,7.1-1.8,14-7.1,20.1
|
||||
c-4.2,4.7-1.3,12,4.6,14.5c5.6,2.3,11.1,4.6,16.7,7c8.1,3.5,12.3,9.6,13.2,17.4c0.8,6.3,0.8,12.6,1.2,18.9c0.1,2.2,0.3,4.3,0.7,6.4
|
||||
c0.3,2.2,1.9,4.4-1.3,6.1c-0.6,0.3-0.6,2.2-0.3,3.3c5.4,25.1-0.3,49.7-4.9,74.3c-1.2,6.3-3.1,12-5.5,17.9
|
||||
c-2.3,5.5-0.9,12.4-0.4,18.7c0.6,7.8,2,15.5,3.2,23.7c-2.5,0.5-4.5,1.4-6.3,1.2c-6.1-0.5-6.8,2.6-6.4,6.7c0.7,6.6,1.1,13.3,2.4,19.8
|
||||
c0.6,3.3,3.5,6.1,4.5,9.4c5,15.6,1.1,30.9-1.3,46.5c-1.7,10.6-0.6,21.5-1.5,32.2c-0.5,6.5-1.5,13.3-4,19.4c-2.2,5.4-9,3.9-14.1,4
|
||||
c-2.8,0.1-5.6-0.3-8.2-1.1c-1.2-0.3-2.6-2.2-2.5-3.3c0.1-1.9,0.7-4.1,1.9-5.7c11.5-16,9.1-33.7,8.2-51.2c-0.5-9.8-1.6-19.6-2.9-29.3
|
||||
c-1.2-8.8-3.1-17.5-5-26.2c-1.2-5.5-3.4-10.9-4.4-16.5c-0.8-3.9-2.4-5.3-7.4-3.8c-0.5,8.5-3.1,17.7-0.9,25.9
|
||||
c2.7,10.1,4.5,20,3.3,30.2c-1,8.8-2.9,17.5-3.9,26.2c-0.7,6.6-1.1,13,1.7,19.8c2.4,5.9,0.6,13.1,0.3,19.7c0,0.9-2.1,2.3-3.4,2.5
|
||||
c-4.8,0.7-9.8,0.8-14.5,1.6c-5.1,0.9-9.9,3-15,3.6c-5.7,0.6-11.5,0.1-17.3,0.1c-2.4-4,0-6.2,3.2-8.4c6.9-4.5,13.7-9,20.5-13.6
|
||||
c6.4-4.4,7.5-10.2,6.6-17.1c-2.1-17.2-6.4-34.1-5.2-51.6c0.8-11.3-0.7-22.5-4.2-33.5c-1.7-5.2-3.4-9.1-10.4-7.8
|
||||
c-0.4-0.8-0.9-1.2-0.9-1.7c-0.4-17.6-1.7-35.3-0.9-52.9c0.6-12.6,3.9-25,6-37.5c0.1-0.6,0.9-1.5,0.7-1.8c-5-8,2.9-15.7,0.6-23.9
|
||||
c-2.2-8-3.5-16.4-3.7-24.6c-0.2-6.9,2.3-13.8,2.8-20.7c0.8-12.2,8.8-18.8,20.6-23c7.3-2.6,9.1-5.2,8.2-11.3c-0.1-1.1-0.6-2.7-1.4-3
|
||||
c-10-4.1-8.9-12.3-9.9-19.9c-0.9-7.2-2.7-14.2-3.6-21.4c-0.3-2.9,0.2-5.9,3.6-8.1c6.4-4,13.1-5.3,20.7-3.4
|
||||
C73.7,8.3,76.3,7.4,78.8,7.1z M107.1,130.2c-1,0-2,0.1-3.1,0.1c0,3.1-0.2,6.1,0,9.2c0.7,9.3,5.9,19-2.4,27.7
|
||||
c-0.2,0.2-0.2,0.6-0.1,0.9c1.9,7.2,3.8,14.5,5.6,21.7c0.7-0.1,1.4-0.1,2.1-0.2C108.5,169.9,107.8,150,107.1,130.2z"/>
|
||||
<circle class="st1" cx="70.9" cy="109.9" r="26"/>
|
||||
<circle class="st2" cx="70.9" cy="109.9" r="19"/>
|
||||
</svg>
|
After Width: | Height: | Size: 2.3 KiB |
|
@ -0,0 +1,30 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 22.0.1, 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 143.1 414.2" style="enable-background:new 0 0 143.1 414.2;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:#B9B9B9;}
|
||||
.st1{fill:#009175;}
|
||||
.st2{fill:#1FC6A6;}
|
||||
.st3{fill:#FFFFFF;}
|
||||
</style>
|
||||
<path class="st0" d="M78.8,7.1c3.9,4.3,7.7,8.7,11.8,12.9c3.7,3.8,5.1,7.9,4.4,12.9c-1,7.1-1.8,14-7.1,20.1
|
||||
c-4.2,4.7-1.3,12,4.6,14.5c5.6,2.3,11.1,4.6,16.7,7c8.1,3.5,12.3,9.6,13.2,17.4c0.8,6.3,0.8,12.6,1.2,18.9c0.1,2.2,0.3,4.3,0.7,6.4
|
||||
c0.3,2.2,1.9,4.4-1.3,6.1c-0.6,0.3-0.6,2.2-0.3,3.3c5.4,25.1-0.3,49.7-4.9,74.3c-1.2,6.3-3.1,12-5.5,17.9
|
||||
c-2.3,5.5-0.9,12.4-0.4,18.7c0.6,7.8,2,15.5,3.2,23.7c-2.5,0.5-4.5,1.4-6.3,1.2c-6.1-0.5-6.8,2.6-6.4,6.7c0.7,6.6,1.1,13.3,2.4,19.8
|
||||
c0.6,3.3,3.5,6.1,4.5,9.4c5,15.6,1.1,30.9-1.3,46.5c-1.7,10.6-0.6,21.5-1.5,32.2c-0.5,6.5-1.5,13.3-4,19.4c-2.2,5.4-9,3.9-14.1,4
|
||||
c-2.8,0.1-5.6-0.3-8.2-1.1c-1.2-0.3-2.6-2.2-2.5-3.3c0.1-1.9,0.7-4.1,1.9-5.7c11.5-16,9.1-33.7,8.2-51.2c-0.5-9.8-1.6-19.6-2.9-29.3
|
||||
c-1.2-8.8-3.1-17.5-5-26.2c-1.2-5.5-3.4-10.9-4.4-16.5c-0.8-3.9-2.4-5.3-7.4-3.8c-0.5,8.5-3.1,17.7-0.9,25.9
|
||||
c2.7,10.1,4.5,20,3.3,30.2c-1,8.8-2.9,17.5-3.9,26.2c-0.7,6.6-1.1,13,1.7,19.8c2.4,5.9,0.6,13.1,0.3,19.7c0,0.9-2.1,2.3-3.4,2.5
|
||||
c-4.8,0.7-9.8,0.8-14.5,1.6c-5.1,0.9-9.9,3-15,3.6c-5.7,0.6-11.5,0.1-17.3,0.1c-2.4-4,0-6.2,3.2-8.4c6.9-4.5,13.7-9,20.5-13.6
|
||||
c6.4-4.4,7.5-10.2,6.6-17.1c-2.1-17.2-6.4-34.1-5.2-51.6c0.8-11.3-0.7-22.5-4.2-33.5c-1.7-5.2-3.4-9.1-10.4-7.8
|
||||
c-0.4-0.8-0.9-1.2-0.9-1.7c-0.4-17.6-1.7-35.3-0.9-52.9c0.6-12.6,3.9-25,6-37.5c0.1-0.6,0.9-1.5,0.7-1.8c-5-8,2.9-15.7,0.6-23.9
|
||||
c-2.2-8-3.5-16.4-3.7-24.6c-0.2-6.9,2.3-13.8,2.8-20.7c0.8-12.2,8.8-18.8,20.6-23c7.3-2.6,9.1-5.2,8.2-11.3c-0.1-1.1-0.6-2.7-1.4-3
|
||||
c-10-4.1-8.9-12.3-9.9-19.9c-0.9-7.2-2.7-14.2-3.6-21.4c-0.3-2.9,0.2-5.9,3.6-8.1c6.4-4,13.1-5.3,20.7-3.4
|
||||
C73.7,8.3,76.3,7.4,78.8,7.1z M107.1,130.2c-1,0-2,0.1-3.1,0.1c0,3.1-0.2,6.1,0,9.2c0.7,9.3,5.9,19-2.4,27.7
|
||||
c-0.2,0.2-0.2,0.6-0.1,0.9c1.9,7.2,3.8,14.5,5.6,21.7c0.7-0.1,1.4-0.1,2.1-0.2C108.5,169.9,107.8,150,107.1,130.2z"/>
|
||||
<circle class="st1" cx="70.9" cy="109.9" r="26"/>
|
||||
<circle class="st2" cx="70.9" cy="109.9" r="23"/>
|
||||
<path class="st3" d="M70.9,127.8c-9.9,0-17.9-8-17.9-17.9s8-17.9,17.9-17.9s17.9,8,17.9,17.9S80.7,127.8,70.9,127.8z M70.9,94.8
|
||||
c-8.3,0-15.1,6.8-15.1,15.1s6.8,15.1,15.1,15.1S86,118.3,86,109.9S79.2,94.8,70.9,94.8z"/>
|
||||
</svg>
|
After Width: | Height: | Size: 2.5 KiB |
|
@ -209,6 +209,7 @@
|
|||
#include "commerce/QmlCommerce.h"
|
||||
|
||||
#include "webbrowser/WebBrowserSuggestionsEngine.h"
|
||||
#include <DesktopPreviewProvider.h>
|
||||
|
||||
// On Windows PC, NVidia Optimus laptop, we want to enable NVIDIA GPU
|
||||
// FIXME seems to be broken.
|
||||
|
@ -503,7 +504,13 @@ public:
|
|||
}
|
||||
|
||||
if (message->message == WM_DEVICECHANGE) {
|
||||
Midi::USBchanged(); // re-scan the MIDI bus
|
||||
const float MIN_DELTA_SECONDS = 2.0f; // de-bounce signal
|
||||
static float lastTriggerTime = 0.0f;
|
||||
const float deltaSeconds = secTimestampNow() - lastTriggerTime;
|
||||
lastTriggerTime = secTimestampNow();
|
||||
if (deltaSeconds > MIN_DELTA_SECONDS) {
|
||||
Midi::USBchanged(); // re-scan the MIDI bus
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
|
@ -631,6 +638,7 @@ bool setupEssentials(int& argc, char** argv, bool runningMarkerExisted) {
|
|||
DependencyManager::set<PointerScriptingInterface>();
|
||||
DependencyManager::set<PickScriptingInterface>();
|
||||
DependencyManager::set<Cursor::Manager>();
|
||||
DependencyManager::set<DesktopPreviewProvider>();
|
||||
DependencyManager::set<AccountManager>(std::bind(&Application::getUserAgent, qApp));
|
||||
DependencyManager::set<StatTracker>();
|
||||
DependencyManager::set<ScriptEngines>(ScriptEngine::CLIENT_SCRIPT);
|
||||
|
@ -5763,6 +5771,7 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEnginePointe
|
|||
scriptEngine->registerFunction("OverlayWindow", QmlWindowClass::constructor);
|
||||
|
||||
scriptEngine->registerGlobalObject("Menu", MenuScriptingInterface::getInstance());
|
||||
scriptEngine->registerGlobalObject("DesktopPreviewProvider", DependencyManager::get<DesktopPreviewProvider>().data());
|
||||
scriptEngine->registerGlobalObject("Stats", Stats::getInstance());
|
||||
scriptEngine->registerGlobalObject("Settings", SettingsScriptingInterface::getInstance());
|
||||
scriptEngine->registerGlobalObject("Snapshot", DependencyManager::get<Snapshot>().data());
|
||||
|
|
|
@ -2019,8 +2019,7 @@ void MyAvatar::updateOrientation(float deltaTime) {
|
|||
_smoothOrientationTimer = 0.0f;
|
||||
}
|
||||
|
||||
getHead()->setBasePitch(getHead()->getBasePitch() + getDriveKey(PITCH) * _pitchSpeed * deltaTime);
|
||||
|
||||
Head* head = getHead();
|
||||
auto headPose = getControllerPoseInAvatarFrame(controller::Action::HEAD);
|
||||
if (headPose.isValid()) {
|
||||
glm::quat localOrientation = headPose.rotation * Quaternions::Y_180;
|
||||
|
@ -2032,6 +2031,10 @@ void MyAvatar::updateOrientation(float deltaTime) {
|
|||
head->setBaseYaw(YAW(euler));
|
||||
head->setBasePitch(PITCH(euler));
|
||||
head->setBaseRoll(ROLL(euler));
|
||||
} else {
|
||||
head->setBaseYaw(0.0f);
|
||||
head->setBasePitch(getHead()->getBasePitch() + getDriveKey(PITCH) * _pitchSpeed * deltaTime);
|
||||
head->setBaseRoll(0.0f);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -277,7 +277,8 @@ void WindowScriptingInterface::browseAsync(const QString& title, const QString&
|
|||
if (!result.isEmpty()) {
|
||||
setPreviousBrowseLocation(QFileInfo(result).absolutePath());
|
||||
}
|
||||
emit openFileChanged(result);
|
||||
emit browseChanged(result);
|
||||
emit openFileChanged(result); // Deprecated signal; to be removed in due course.
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -197,18 +197,19 @@ public slots:
|
|||
|
||||
/**jsdoc
|
||||
* Prompt the user to choose a file. Displays a non-modal dialog that navigates the directory tree. A
|
||||
* {@link Window.openFileChanged|openFileChanged} signal is emitted when a file is chosen; no signal is emitted if the user
|
||||
* {@link Window.browseChanged|browseChanged} signal is emitted when a file is chosen; no signal is emitted if the user
|
||||
* cancels the dialog.
|
||||
* @deprecated A deprecated {@link Window.openFileChanged|openFileChanged} signal is also emitted when a file is chosen.
|
||||
* @function Window.browseAsync
|
||||
* @param {string} title="" - The title to display at the top of the dialog.
|
||||
* @param {string} directory="" - The initial directory to start browsing at.
|
||||
* @param {string} nameFilter="" - The types of files to display. Examples: <code>"*.json"</code> and
|
||||
* <code>"Images (*.png *.jpg *.svg)"</code>. All files are displayed if a filter isn't specified.
|
||||
* @example <caption>Ask the user to choose an image file without waiting for the answer.</caption>
|
||||
* function onOpenFileChanged(filename) {
|
||||
* function onBrowseChanged(filename) {
|
||||
* print("File: " + filename);
|
||||
* }
|
||||
* Window.openFileChanged.connect(onOpenFileChanged);
|
||||
* Window.browseChanged.connect(onBrowseChanged);
|
||||
*
|
||||
* Window.browseAsync("Select Image File", Paths.resources, "Images (*.png *.jpg *.svg)");
|
||||
* print("Script continues without waiting");
|
||||
|
@ -659,9 +660,18 @@ signals:
|
|||
*/
|
||||
void saveFileChanged(QString filename);
|
||||
|
||||
/**jsdoc
|
||||
* Triggered when the user chooses a file in a {@link Window.browseAsync|browseAsync} dialog.
|
||||
* @function Window.browseChanged
|
||||
* @param {string} filename - The path and name of the file the user chose in the dialog.
|
||||
* @returns {Signal}
|
||||
*/
|
||||
void browseChanged(QString filename);
|
||||
|
||||
/**jsdoc
|
||||
* Triggered when the user chooses a file in a {@link Window.browseAsync|browseAsync} dialog.
|
||||
* @function Window.openFileChanged
|
||||
* @deprecated This signal is being replaced with {@link Window.browseChanged|browseChanged} and will be removed.
|
||||
* @param {string} filename - The path and name of the file the user chose in the dialog.
|
||||
* @returns {Signal}
|
||||
*/
|
||||
|
|
|
@ -564,9 +564,9 @@ void ModelOverlay::animate() {
|
|||
rotationMat * fbxJoints[index].postTransform);
|
||||
auto& jointData = jointsData[j];
|
||||
jointData.translation = extractTranslation(finalMat);
|
||||
jointData.translationSet = true;
|
||||
jointData.translationIsDefaultPose = false;
|
||||
jointData.rotation = glmExtractRotation(finalMat);
|
||||
jointData.rotationSet = true;
|
||||
jointData.rotationIsDefaultPose = false;
|
||||
}
|
||||
}
|
||||
// Set the data in the model
|
||||
|
|
|
@ -1705,16 +1705,16 @@ void Rig::copyJointsIntoJointData(QVector<JointData>& jointDataVec) const {
|
|||
// rotations are in absolute rig frame.
|
||||
glm::quat defaultAbsRot = geometryToRigPose.rot() * _animSkeleton->getAbsoluteDefaultPose(i).rot();
|
||||
data.rotation = _internalPoseSet._absolutePoses[i].rot();
|
||||
data.rotationSet = !isEqual(data.rotation, defaultAbsRot);
|
||||
data.rotationIsDefaultPose = isEqual(data.rotation, defaultAbsRot);
|
||||
|
||||
// translations are in relative frame but scaled so that they are in meters,
|
||||
// instead of geometry units.
|
||||
glm::vec3 defaultRelTrans = _geometryOffset.scale() * _animSkeleton->getRelativeDefaultPose(i).trans();
|
||||
data.translation = _geometryOffset.scale() * _internalPoseSet._relativePoses[i].trans();
|
||||
data.translationSet = !isEqual(data.translation, defaultRelTrans);
|
||||
data.translationIsDefaultPose = isEqual(data.translation, defaultRelTrans);
|
||||
} else {
|
||||
data.translationSet = false;
|
||||
data.rotationSet = false;
|
||||
data.translationIsDefaultPose = true;
|
||||
data.rotationIsDefaultPose = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1739,11 +1739,11 @@ void Rig::copyJointsFromJointData(const QVector<JointData>& jointDataVec) {
|
|||
const glm::quat rigToGeometryRot(glmExtractRotation(_rigToGeometryTransform));
|
||||
for (int i = 0; i < numJoints; i++) {
|
||||
const JointData& data = jointDataVec.at(i);
|
||||
if (data.rotationSet) {
|
||||
if (data.rotationIsDefaultPose) {
|
||||
rotations.push_back(absoluteDefaultPoses[i].rot());
|
||||
} else {
|
||||
// JointData rotations are in absolute rig-frame so we rotate them to absolute geometry-frame
|
||||
rotations.push_back(rigToGeometryRot * data.rotation);
|
||||
} else {
|
||||
rotations.push_back(absoluteDefaultPoses[i].rot());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1759,11 +1759,11 @@ void Rig::copyJointsFromJointData(const QVector<JointData>& jointDataVec) {
|
|||
const JointData& data = jointDataVec.at(i);
|
||||
_internalPoseSet._relativePoses[i].scale() = Vectors::ONE;
|
||||
_internalPoseSet._relativePoses[i].rot() = rotations[i];
|
||||
if (data.translationSet) {
|
||||
if (data.translationIsDefaultPose) {
|
||||
_internalPoseSet._relativePoses[i].trans() = relativeDefaultPoses[i].trans();
|
||||
} else {
|
||||
// JointData translations are in scaled relative-frame so we scale back to regular relative-frame
|
||||
_internalPoseSet._relativePoses[i].trans() = _invGeometryOffset.scale() * data.translation;
|
||||
} else {
|
||||
_internalPoseSet._relativePoses[i].trans() = relativeDefaultPoses[i].trans();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -791,10 +791,19 @@ bool Avatar::shouldRenderHead(const RenderArgs* renderArgs) const {
|
|||
|
||||
// virtual
|
||||
void Avatar::simulateAttachments(float deltaTime) {
|
||||
assert(_attachmentModels.size() == _attachmentModelsTexturesLoaded.size());
|
||||
PerformanceTimer perfTimer("attachments");
|
||||
for (int i = 0; i < (int)_attachmentModels.size(); i++) {
|
||||
const AttachmentData& attachment = _attachmentData.at(i);
|
||||
auto& model = _attachmentModels.at(i);
|
||||
bool texturesLoaded = _attachmentModelsTexturesLoaded.at(i);
|
||||
|
||||
// Watch for texture loading
|
||||
if (!texturesLoaded && model->getGeometry() && model->getGeometry()->areTexturesLoaded()) {
|
||||
_attachmentModelsTexturesLoaded[i] = true;
|
||||
model->updateRenderItems();
|
||||
}
|
||||
|
||||
int jointIndex = getJointIndex(attachment.jointName);
|
||||
glm::vec3 jointPosition;
|
||||
glm::quat jointRotation;
|
||||
|
@ -1319,6 +1328,7 @@ void Avatar::setAttachmentData(const QVector<AttachmentData>& attachmentData) {
|
|||
while ((int)_attachmentModels.size() > attachmentData.size()) {
|
||||
auto attachmentModel = _attachmentModels.back();
|
||||
_attachmentModels.pop_back();
|
||||
_attachmentModelsTexturesLoaded.pop_back();
|
||||
_attachmentsToRemove.push_back(attachmentModel);
|
||||
}
|
||||
|
||||
|
@ -1326,11 +1336,16 @@ void Avatar::setAttachmentData(const QVector<AttachmentData>& attachmentData) {
|
|||
if (i == (int)_attachmentModels.size()) {
|
||||
// if number of attachments has been increased, we need to allocate a new model
|
||||
_attachmentModels.push_back(allocateAttachmentModel(attachmentData[i].isSoft, _skeletonModel->getRig(), isMyAvatar()));
|
||||
}
|
||||
else if (i < oldAttachmentData.size() && oldAttachmentData[i].isSoft != attachmentData[i].isSoft) {
|
||||
_attachmentModelsTexturesLoaded.push_back(false);
|
||||
} else if (i < oldAttachmentData.size() && oldAttachmentData[i].isSoft != attachmentData[i].isSoft) {
|
||||
// if the attachment has changed type, we need to re-allocate a new one.
|
||||
_attachmentsToRemove.push_back(_attachmentModels[i]);
|
||||
_attachmentModels[i] = allocateAttachmentModel(attachmentData[i].isSoft, _skeletonModel->getRig(), isMyAvatar());
|
||||
_attachmentModelsTexturesLoaded[i] = false;
|
||||
}
|
||||
// If the model URL has changd, we need to wait for the textures to load
|
||||
if (_attachmentModels[i]->getURL() != attachmentData[i].modelURL) {
|
||||
_attachmentModelsTexturesLoaded[i] = false;
|
||||
}
|
||||
_attachmentModels[i]->setURL(attachmentData[i].modelURL);
|
||||
}
|
||||
|
|
|
@ -306,6 +306,7 @@ protected:
|
|||
|
||||
glm::vec3 _skeletonOffset;
|
||||
std::vector<std::shared_ptr<Model>> _attachmentModels;
|
||||
std::vector<bool> _attachmentModelsTexturesLoaded;
|
||||
std::vector<std::shared_ptr<Model>> _attachmentsToRemove;
|
||||
std::vector<std::shared_ptr<Model>> _attachmentsToDelete;
|
||||
|
||||
|
|
|
@ -39,6 +39,7 @@
|
|||
#include <AudioHelpers.h>
|
||||
#include <Profile.h>
|
||||
#include <VariantMapToScriptValue.h>
|
||||
#include <BitVectorHelpers.h>
|
||||
|
||||
#include "AvatarLogging.h"
|
||||
|
||||
|
@ -77,6 +78,16 @@ size_t AvatarDataPacket::maxJointDataSize(size_t numJoints) {
|
|||
return totalSize;
|
||||
}
|
||||
|
||||
size_t AvatarDataPacket::maxJointDefaultPoseFlagsSize(size_t numJoints) {
|
||||
const size_t bitVectorSize = calcBitVectorSize((int)numJoints);
|
||||
size_t totalSize = sizeof(uint8_t); // numJoints
|
||||
|
||||
// one set of bits for rotation and one for translation
|
||||
const size_t NUM_BIT_VECTORS_IN_DEFAULT_POSE_FLAGS_SECTION = 2;
|
||||
totalSize += NUM_BIT_VECTORS_IN_DEFAULT_POSE_FLAGS_SECTION * bitVectorSize;
|
||||
|
||||
return totalSize;
|
||||
}
|
||||
|
||||
AvatarData::AvatarData() :
|
||||
SpatiallyNestable(NestableType::Avatar, QUuid()),
|
||||
|
@ -272,6 +283,7 @@ QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail, quint64 lastSent
|
|||
|
||||
bool hasFaceTrackerInfo = false;
|
||||
bool hasJointData = false;
|
||||
bool hasJointDefaultPoseFlags = false;
|
||||
|
||||
if (sendPALMinimum) {
|
||||
hasAudioLoudness = true;
|
||||
|
@ -290,6 +302,7 @@ QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail, quint64 lastSent
|
|||
|
||||
hasFaceTrackerInfo = !dropFaceTracking && hasFaceTracker() && (sendAll || faceTrackerInfoChangedSince(lastSentTime));
|
||||
hasJointData = sendAll || !sendMinimum;
|
||||
hasJointDefaultPoseFlags = hasJointData;
|
||||
}
|
||||
|
||||
|
||||
|
@ -314,7 +327,8 @@ QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail, quint64 lastSent
|
|||
| (hasParentInfo ? AvatarDataPacket::PACKET_HAS_PARENT_INFO : 0)
|
||||
| (hasAvatarLocalPosition ? AvatarDataPacket::PACKET_HAS_AVATAR_LOCAL_POSITION : 0)
|
||||
| (hasFaceTrackerInfo ? AvatarDataPacket::PACKET_HAS_FACE_TRACKER_INFO : 0)
|
||||
| (hasJointData ? AvatarDataPacket::PACKET_HAS_JOINT_DATA : 0);
|
||||
| (hasJointData ? AvatarDataPacket::PACKET_HAS_JOINT_DATA : 0)
|
||||
| (hasJointDefaultPoseFlags ? AvatarDataPacket::PACKET_HAS_JOINT_DEFAULT_POSE_FLAGS : 0);
|
||||
|
||||
memcpy(destinationBuffer, &packetStateFlags, sizeof(packetStateFlags));
|
||||
destinationBuffer += sizeof(packetStateFlags);
|
||||
|
@ -541,14 +555,19 @@ QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail, quint64 lastSent
|
|||
|
||||
for (int i = 0; i < _jointData.size(); i++) {
|
||||
const JointData& data = _jointData[i];
|
||||
const JointData& last = lastSentJointData[i];
|
||||
|
||||
// The dot product for smaller rotations is a smaller number.
|
||||
// So if the dot() is less than the value, then the rotation is a larger angle of rotation
|
||||
bool largeEnoughRotation = fabsf(glm::dot(data.rotation, lastSentJointData[i].rotation)) < minRotationDOT;
|
||||
if (!data.rotationIsDefaultPose) {
|
||||
if (sendAll || last.rotationIsDefaultPose || last.rotation != data.rotation) {
|
||||
|
||||
if (sendAll || lastSentJointData[i].rotation != data.rotation) {
|
||||
if (sendAll || !cullSmallChanges || largeEnoughRotation) {
|
||||
if (data.rotationSet) {
|
||||
bool largeEnoughRotation = true;
|
||||
if (cullSmallChanges) {
|
||||
// The dot product for smaller rotations is a smaller number.
|
||||
// So if the dot() is less than the value, then the rotation is a larger angle of rotation
|
||||
largeEnoughRotation = fabsf(glm::dot(last.rotation, data.rotation)) < minRotationDOT;
|
||||
}
|
||||
|
||||
if (sendAll || !cullSmallChanges || largeEnoughRotation) {
|
||||
validity |= (1 << validityBit);
|
||||
#ifdef WANT_DEBUG
|
||||
rotationSentCount++;
|
||||
|
@ -557,8 +576,8 @@ QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail, quint64 lastSent
|
|||
|
||||
if (sentJointDataOut) {
|
||||
localSentJointDataOut[i].rotation = data.rotation;
|
||||
localSentJointDataOut[i].rotationIsDefaultPose = false;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -588,11 +607,10 @@ QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail, quint64 lastSent
|
|||
float maxTranslationDimension = 0.0;
|
||||
for (int i = 0; i < _jointData.size(); i++) {
|
||||
const JointData& data = _jointData[i];
|
||||
if (sendAll || lastSentJointData[i].translation != data.translation) {
|
||||
if (sendAll ||
|
||||
!cullSmallChanges ||
|
||||
glm::distance(data.translation, lastSentJointData[i].translation) > minTranslation) {
|
||||
if (data.translationSet) {
|
||||
|
||||
if (!data.translationIsDefaultPose) {
|
||||
if (sendAll || lastSentJointData[i].translation != data.translation) {
|
||||
if (sendAll || !cullSmallChanges || glm::distance(data.translation, lastSentJointData[i].translation) > minTranslation) {
|
||||
validity |= (1 << validityBit);
|
||||
#ifdef WANT_DEBUG
|
||||
translationSentCount++;
|
||||
|
@ -606,8 +624,8 @@ QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail, quint64 lastSent
|
|||
|
||||
if (sentJointDataOut) {
|
||||
localSentJointDataOut[i].translation = data.translation;
|
||||
localSentJointDataOut[i].translationIsDefaultPose = false;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -655,6 +673,30 @@ QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail, quint64 lastSent
|
|||
}
|
||||
}
|
||||
|
||||
if (hasJointDefaultPoseFlags) {
|
||||
auto startSection = destinationBuffer;
|
||||
QReadLocker readLock(&_jointDataLock);
|
||||
|
||||
// write numJoints
|
||||
int numJoints = _jointData.size();
|
||||
*destinationBuffer++ = (uint8_t)numJoints;
|
||||
|
||||
// write rotationIsDefaultPose bits
|
||||
destinationBuffer += writeBitVector(destinationBuffer, numJoints, [&](int i) {
|
||||
return _jointData[i].rotationIsDefaultPose;
|
||||
});
|
||||
|
||||
// write translationIsDefaultPose bits
|
||||
destinationBuffer += writeBitVector(destinationBuffer, numJoints, [&](int i) {
|
||||
return _jointData[i].translationIsDefaultPose;
|
||||
});
|
||||
|
||||
if (outboundDataRateOut) {
|
||||
size_t numBytes = destinationBuffer - startSection;
|
||||
outboundDataRateOut->jointDefaultPoseFlagsRate.increment(numBytes);
|
||||
}
|
||||
}
|
||||
|
||||
int avatarDataSize = destinationBuffer - startPosition;
|
||||
|
||||
if (avatarDataSize > (int)byteArraySize) {
|
||||
|
@ -664,6 +706,7 @@ QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail, quint64 lastSent
|
|||
|
||||
return avatarDataByteArray.left(avatarDataSize);
|
||||
}
|
||||
|
||||
// NOTE: This is never used in a "distanceAdjust" mode, so it's ok that it doesn't use a variable minimum rotation/translation
|
||||
void AvatarData::doneEncoding(bool cullSmallChanges) {
|
||||
// The server has finished sending this version of the joint-data to other nodes. Update _lastSentJointData.
|
||||
|
@ -674,7 +717,7 @@ void AvatarData::doneEncoding(bool cullSmallChanges) {
|
|||
if (_lastSentJointData[i].rotation != data.rotation) {
|
||||
if (!cullSmallChanges ||
|
||||
fabsf(glm::dot(data.rotation, _lastSentJointData[i].rotation)) <= AVATAR_MIN_ROTATION_DOT) {
|
||||
if (data.rotationSet) {
|
||||
if (!data.rotationIsDefaultPose) {
|
||||
_lastSentJointData[i].rotation = data.rotation;
|
||||
}
|
||||
}
|
||||
|
@ -682,7 +725,7 @@ void AvatarData::doneEncoding(bool cullSmallChanges) {
|
|||
if (_lastSentJointData[i].translation != data.translation) {
|
||||
if (!cullSmallChanges ||
|
||||
glm::distance(data.translation, _lastSentJointData[i].translation) > AVATAR_MIN_TRANSLATION) {
|
||||
if (data.translationSet) {
|
||||
if (!data.translationIsDefaultPose) {
|
||||
_lastSentJointData[i].translation = data.translation;
|
||||
}
|
||||
}
|
||||
|
@ -730,6 +773,7 @@ const unsigned char* unpackFauxJoint(const unsigned char* sourceBuffer, ThreadSa
|
|||
|
||||
// read data in packet starting at byte offset and return number of bytes parsed
|
||||
int AvatarData::parseDataFromBuffer(const QByteArray& buffer) {
|
||||
|
||||
// lazily allocate memory for HeadData in case we're not an Avatar instance
|
||||
lazyInitHeadData();
|
||||
|
||||
|
@ -745,18 +789,19 @@ int AvatarData::parseDataFromBuffer(const QByteArray& buffer) {
|
|||
|
||||
#define HAS_FLAG(B,F) ((B & F) == F)
|
||||
|
||||
bool hasAvatarGlobalPosition = HAS_FLAG(packetStateFlags, AvatarDataPacket::PACKET_HAS_AVATAR_GLOBAL_POSITION);
|
||||
bool hasAvatarBoundingBox = HAS_FLAG(packetStateFlags, AvatarDataPacket::PACKET_HAS_AVATAR_BOUNDING_BOX);
|
||||
bool hasAvatarOrientation = HAS_FLAG(packetStateFlags, AvatarDataPacket::PACKET_HAS_AVATAR_ORIENTATION);
|
||||
bool hasAvatarScale = HAS_FLAG(packetStateFlags, AvatarDataPacket::PACKET_HAS_AVATAR_SCALE);
|
||||
bool hasLookAtPosition = HAS_FLAG(packetStateFlags, AvatarDataPacket::PACKET_HAS_LOOK_AT_POSITION);
|
||||
bool hasAudioLoudness = HAS_FLAG(packetStateFlags, AvatarDataPacket::PACKET_HAS_AUDIO_LOUDNESS);
|
||||
bool hasSensorToWorldMatrix = HAS_FLAG(packetStateFlags, AvatarDataPacket::PACKET_HAS_SENSOR_TO_WORLD_MATRIX);
|
||||
bool hasAdditionalFlags = HAS_FLAG(packetStateFlags, AvatarDataPacket::PACKET_HAS_ADDITIONAL_FLAGS);
|
||||
bool hasParentInfo = HAS_FLAG(packetStateFlags, AvatarDataPacket::PACKET_HAS_PARENT_INFO);
|
||||
bool hasAvatarLocalPosition = HAS_FLAG(packetStateFlags, AvatarDataPacket::PACKET_HAS_AVATAR_LOCAL_POSITION);
|
||||
bool hasFaceTrackerInfo = HAS_FLAG(packetStateFlags, AvatarDataPacket::PACKET_HAS_FACE_TRACKER_INFO);
|
||||
bool hasJointData = HAS_FLAG(packetStateFlags, AvatarDataPacket::PACKET_HAS_JOINT_DATA);
|
||||
bool hasAvatarGlobalPosition = HAS_FLAG(packetStateFlags, AvatarDataPacket::PACKET_HAS_AVATAR_GLOBAL_POSITION);
|
||||
bool hasAvatarBoundingBox = HAS_FLAG(packetStateFlags, AvatarDataPacket::PACKET_HAS_AVATAR_BOUNDING_BOX);
|
||||
bool hasAvatarOrientation = HAS_FLAG(packetStateFlags, AvatarDataPacket::PACKET_HAS_AVATAR_ORIENTATION);
|
||||
bool hasAvatarScale = HAS_FLAG(packetStateFlags, AvatarDataPacket::PACKET_HAS_AVATAR_SCALE);
|
||||
bool hasLookAtPosition = HAS_FLAG(packetStateFlags, AvatarDataPacket::PACKET_HAS_LOOK_AT_POSITION);
|
||||
bool hasAudioLoudness = HAS_FLAG(packetStateFlags, AvatarDataPacket::PACKET_HAS_AUDIO_LOUDNESS);
|
||||
bool hasSensorToWorldMatrix = HAS_FLAG(packetStateFlags, AvatarDataPacket::PACKET_HAS_SENSOR_TO_WORLD_MATRIX);
|
||||
bool hasAdditionalFlags = HAS_FLAG(packetStateFlags, AvatarDataPacket::PACKET_HAS_ADDITIONAL_FLAGS);
|
||||
bool hasParentInfo = HAS_FLAG(packetStateFlags, AvatarDataPacket::PACKET_HAS_PARENT_INFO);
|
||||
bool hasAvatarLocalPosition = HAS_FLAG(packetStateFlags, AvatarDataPacket::PACKET_HAS_AVATAR_LOCAL_POSITION);
|
||||
bool hasFaceTrackerInfo = HAS_FLAG(packetStateFlags, AvatarDataPacket::PACKET_HAS_FACE_TRACKER_INFO);
|
||||
bool hasJointData = HAS_FLAG(packetStateFlags, AvatarDataPacket::PACKET_HAS_JOINT_DATA);
|
||||
bool hasJointDefaultPoseFlags = HAS_FLAG(packetStateFlags, AvatarDataPacket::PACKET_HAS_JOINT_DEFAULT_POSE_FLAGS);
|
||||
|
||||
quint64 now = usecTimestampNow();
|
||||
|
||||
|
@ -1055,7 +1100,7 @@ int AvatarData::parseDataFromBuffer(const QByteArray& buffer) {
|
|||
if (validRotations[i]) {
|
||||
sourceBuffer += unpackOrientationQuatFromSixBytes(sourceBuffer, data.rotation);
|
||||
_hasNewJointData = true;
|
||||
data.rotationSet = true;
|
||||
data.rotationIsDefaultPose = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1090,7 +1135,7 @@ int AvatarData::parseDataFromBuffer(const QByteArray& buffer) {
|
|||
if (validTranslations[i]) {
|
||||
sourceBuffer += unpackFloatVec3FromSignedTwoByteFixed(sourceBuffer, data.translation, TRANSLATION_COMPRESSION_RADIX);
|
||||
_hasNewJointData = true;
|
||||
data.translationSet = true;
|
||||
data.translationIsDefaultPose = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1110,6 +1155,32 @@ int AvatarData::parseDataFromBuffer(const QByteArray& buffer) {
|
|||
_jointDataUpdateRate.increment();
|
||||
}
|
||||
|
||||
if (hasJointDefaultPoseFlags) {
|
||||
auto startSection = sourceBuffer;
|
||||
|
||||
QWriteLocker writeLock(&_jointDataLock);
|
||||
|
||||
PACKET_READ_CHECK(JointDefaultPoseFlagsNumJoints, sizeof(uint8_t));
|
||||
int numJoints = (int)*sourceBuffer++;
|
||||
|
||||
_jointData.resize(numJoints);
|
||||
|
||||
size_t bitVectorSize = calcBitVectorSize(numJoints);
|
||||
PACKET_READ_CHECK(JointDefaultPoseFlagsRotationFlags, bitVectorSize);
|
||||
sourceBuffer += readBitVector(sourceBuffer, numJoints, [&](int i, bool value) {
|
||||
_jointData[i].rotationIsDefaultPose = value;
|
||||
});
|
||||
|
||||
PACKET_READ_CHECK(JointDefaultPoseFlagsTranslationFlags, bitVectorSize);
|
||||
sourceBuffer += readBitVector(sourceBuffer, numJoints, [&](int i, bool value) {
|
||||
_jointData[i].translationIsDefaultPose = value;
|
||||
});
|
||||
|
||||
int numBytesRead = sourceBuffer - startSection;
|
||||
_jointDefaultPoseFlagsRate.increment(numBytesRead);
|
||||
_jointDefaultPoseFlagsUpdateRate.increment();
|
||||
}
|
||||
|
||||
int numBytesRead = sourceBuffer - startPosition;
|
||||
_averageBytesReceived.updateAverage(numBytesRead);
|
||||
|
||||
|
@ -1146,6 +1217,8 @@ float AvatarData::getDataRate(const QString& rateName) const {
|
|||
return _faceTrackerRate.rate() / BYTES_PER_KILOBIT;
|
||||
} else if (rateName == "jointData") {
|
||||
return _jointDataRate.rate() / BYTES_PER_KILOBIT;
|
||||
} else if (rateName == "jointDefaultPoseFlagsRate") {
|
||||
return _jointDefaultPoseFlagsRate.rate() / BYTES_PER_KILOBIT;
|
||||
} else if (rateName == "globalPositionOutbound") {
|
||||
return _outboundDataRate.globalPositionRate.rate() / BYTES_PER_KILOBIT;
|
||||
} else if (rateName == "localPositionOutbound") {
|
||||
|
@ -1170,6 +1243,8 @@ float AvatarData::getDataRate(const QString& rateName) const {
|
|||
return _outboundDataRate.faceTrackerRate.rate() / BYTES_PER_KILOBIT;
|
||||
} else if (rateName == "jointDataOutbound") {
|
||||
return _outboundDataRate.jointDataRate.rate() / BYTES_PER_KILOBIT;
|
||||
} else if (rateName == "jointDefaultPoseFlagsOutbound") {
|
||||
return _outboundDataRate.jointDefaultPoseFlagsRate.rate() / BYTES_PER_KILOBIT;
|
||||
}
|
||||
return 0.0f;
|
||||
}
|
||||
|
@ -1236,9 +1311,9 @@ void AvatarData::setJointData(int index, const glm::quat& rotation, const glm::v
|
|||
}
|
||||
JointData& data = _jointData[index];
|
||||
data.rotation = rotation;
|
||||
data.rotationSet = true;
|
||||
data.rotationIsDefaultPose = false;
|
||||
data.translation = translation;
|
||||
data.translationSet = true;
|
||||
data.translationIsDefaultPose = false;
|
||||
}
|
||||
|
||||
void AvatarData::clearJointData(int index) {
|
||||
|
@ -1294,7 +1369,8 @@ void AvatarData::setJointData(const QString& name, const glm::quat& rotation, co
|
|||
auto& jointData = _jointData[index];
|
||||
jointData.rotation = rotation;
|
||||
jointData.translation = translation;
|
||||
jointData.rotationSet = jointData.translationSet = true;
|
||||
jointData.rotationIsDefaultPose = false;
|
||||
jointData.translationIsDefaultPose = false;
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -1304,7 +1380,7 @@ void AvatarData::setJointRotation(const QString& name, const glm::quat& rotation
|
|||
writeLockWithNamedJointIndex(name, [&](int index) {
|
||||
auto& data = _jointData[index];
|
||||
data.rotation = rotation;
|
||||
data.rotationSet = true;
|
||||
data.rotationIsDefaultPose = false;
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -1314,7 +1390,7 @@ void AvatarData::setJointTranslation(const QString& name, const glm::vec3& trans
|
|||
writeLockWithNamedJointIndex(name, [&](int index) {
|
||||
auto& data = _jointData[index];
|
||||
data.translation = translation;
|
||||
data.translationSet = true;
|
||||
data.translationIsDefaultPose = false;
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -1328,7 +1404,7 @@ void AvatarData::setJointRotation(int index, const glm::quat& rotation) {
|
|||
}
|
||||
JointData& data = _jointData[index];
|
||||
data.rotation = rotation;
|
||||
data.rotationSet = true;
|
||||
data.rotationIsDefaultPose = false;
|
||||
}
|
||||
|
||||
void AvatarData::setJointTranslation(int index, const glm::vec3& translation) {
|
||||
|
@ -1341,7 +1417,7 @@ void AvatarData::setJointTranslation(int index, const glm::vec3& translation) {
|
|||
}
|
||||
JointData& data = _jointData[index];
|
||||
data.translation = translation;
|
||||
data.translationSet = true;
|
||||
data.translationIsDefaultPose = false;
|
||||
}
|
||||
|
||||
void AvatarData::clearJointData(const QString& name) {
|
||||
|
@ -1397,7 +1473,7 @@ void AvatarData::setJointRotations(const QVector<glm::quat>& jointRotations) {
|
|||
for (int i = 0; i < size; ++i) {
|
||||
auto& data = _jointData[i];
|
||||
data.rotation = jointRotations[i];
|
||||
data.rotationSet = true;
|
||||
data.rotationIsDefaultPose = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1419,7 +1495,7 @@ void AvatarData::setJointTranslations(const QVector<glm::vec3>& jointTranslation
|
|||
for (int i = 0; i < size; ++i) {
|
||||
auto& data = _jointData[i];
|
||||
data.translation = jointTranslations[i];
|
||||
data.translationSet = true;
|
||||
data.translationIsDefaultPose = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1996,9 +2072,9 @@ JointData jointDataFromJsonValue(const QJsonValue& json) {
|
|||
if (json.isArray()) {
|
||||
QJsonArray array = json.toArray();
|
||||
result.rotation = quatFromJsonValue(array[0]);
|
||||
result.rotationSet = true;
|
||||
result.rotationIsDefaultPose = false;
|
||||
result.translation = vec3FromJsonValue(array[1]);
|
||||
result.translationSet = true;
|
||||
result.translationIsDefaultPose = false;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
|
|
@ -113,18 +113,19 @@ namespace AvatarDataPacket {
|
|||
// Packet State Flags - we store the details about the existence of other records in this bitset:
|
||||
// AvatarGlobalPosition, Avatar face tracker, eye tracking, and existence of
|
||||
using HasFlags = uint16_t;
|
||||
const HasFlags PACKET_HAS_AVATAR_GLOBAL_POSITION = 1U << 0;
|
||||
const HasFlags PACKET_HAS_AVATAR_BOUNDING_BOX = 1U << 1;
|
||||
const HasFlags PACKET_HAS_AVATAR_ORIENTATION = 1U << 2;
|
||||
const HasFlags PACKET_HAS_AVATAR_SCALE = 1U << 3;
|
||||
const HasFlags PACKET_HAS_LOOK_AT_POSITION = 1U << 4;
|
||||
const HasFlags PACKET_HAS_AUDIO_LOUDNESS = 1U << 5;
|
||||
const HasFlags PACKET_HAS_SENSOR_TO_WORLD_MATRIX = 1U << 6;
|
||||
const HasFlags PACKET_HAS_ADDITIONAL_FLAGS = 1U << 7;
|
||||
const HasFlags PACKET_HAS_PARENT_INFO = 1U << 8;
|
||||
const HasFlags PACKET_HAS_AVATAR_LOCAL_POSITION = 1U << 9;
|
||||
const HasFlags PACKET_HAS_FACE_TRACKER_INFO = 1U << 10;
|
||||
const HasFlags PACKET_HAS_JOINT_DATA = 1U << 11;
|
||||
const HasFlags PACKET_HAS_AVATAR_GLOBAL_POSITION = 1U << 0;
|
||||
const HasFlags PACKET_HAS_AVATAR_BOUNDING_BOX = 1U << 1;
|
||||
const HasFlags PACKET_HAS_AVATAR_ORIENTATION = 1U << 2;
|
||||
const HasFlags PACKET_HAS_AVATAR_SCALE = 1U << 3;
|
||||
const HasFlags PACKET_HAS_LOOK_AT_POSITION = 1U << 4;
|
||||
const HasFlags PACKET_HAS_AUDIO_LOUDNESS = 1U << 5;
|
||||
const HasFlags PACKET_HAS_SENSOR_TO_WORLD_MATRIX = 1U << 6;
|
||||
const HasFlags PACKET_HAS_ADDITIONAL_FLAGS = 1U << 7;
|
||||
const HasFlags PACKET_HAS_PARENT_INFO = 1U << 8;
|
||||
const HasFlags PACKET_HAS_AVATAR_LOCAL_POSITION = 1U << 9;
|
||||
const HasFlags PACKET_HAS_FACE_TRACKER_INFO = 1U << 10;
|
||||
const HasFlags PACKET_HAS_JOINT_DATA = 1U << 11;
|
||||
const HasFlags PACKET_HAS_JOINT_DEFAULT_POSE_FLAGS = 1U << 12;
|
||||
const size_t AVATAR_HAS_FLAGS_SIZE = 2;
|
||||
|
||||
using SixByteQuat = uint8_t[6];
|
||||
|
@ -256,6 +257,15 @@ namespace AvatarDataPacket {
|
|||
};
|
||||
*/
|
||||
size_t maxJointDataSize(size_t numJoints);
|
||||
|
||||
/*
|
||||
struct JointDefaultPoseFlags {
|
||||
uint8_t numJoints;
|
||||
uint8_t rotationIsDefaultPoseBits[ceil(numJoints / 8)];
|
||||
uint8_t translationIsDefaultPoseBits[ceil(numJoints / 8)];
|
||||
};
|
||||
*/
|
||||
size_t maxJointDefaultPoseFlagsSize(size_t numJoints);
|
||||
}
|
||||
|
||||
const float MAX_AUDIO_LOUDNESS = 1000.0f; // close enough for mouth animation
|
||||
|
@ -321,6 +331,7 @@ public:
|
|||
RateCounter<> parentInfoRate;
|
||||
RateCounter<> faceTrackerRate;
|
||||
RateCounter<> jointDataRate;
|
||||
RateCounter<> jointDefaultPoseFlagsRate;
|
||||
};
|
||||
|
||||
class AvatarPriority {
|
||||
|
@ -810,6 +821,7 @@ protected:
|
|||
RateCounter<> _parentInfoRate;
|
||||
RateCounter<> _faceTrackerRate;
|
||||
RateCounter<> _jointDataRate;
|
||||
RateCounter<> _jointDefaultPoseFlagsRate;
|
||||
|
||||
// Some rate data for incoming data updates
|
||||
RateCounter<> _parseBufferUpdateRate;
|
||||
|
@ -825,6 +837,7 @@ protected:
|
|||
RateCounter<> _parentInfoUpdateRate;
|
||||
RateCounter<> _faceTrackerUpdateRate;
|
||||
RateCounter<> _jointDataUpdateRate;
|
||||
RateCounter<> _jointDefaultPoseFlagsUpdateRate;
|
||||
|
||||
// Some rate data for outgoing data
|
||||
AvatarDataRate _outboundDataRate;
|
||||
|
|
|
@ -33,6 +33,7 @@
|
|||
#include "../Logging.h"
|
||||
#include "../CompositorHelper.h"
|
||||
|
||||
#include "DesktopPreviewProvider.h"
|
||||
#include "render-utils/hmd_ui_vert.h"
|
||||
#include "render-utils/hmd_ui_frag.h"
|
||||
|
||||
|
@ -254,17 +255,9 @@ void HmdDisplayPlugin::internalPresent() {
|
|||
swapBuffers();
|
||||
|
||||
} else if (_clearPreviewFlag) {
|
||||
QImage image;
|
||||
if (_vsyncEnabled) {
|
||||
image = QImage(PathUtils::resourcesPath() + "images/preview.png");
|
||||
} else {
|
||||
image = QImage(PathUtils::resourcesPath() + "images/preview-disabled.png");
|
||||
}
|
||||
|
||||
image = image.mirrored();
|
||||
image = image.convertToFormat(QImage::Format_RGBA8888);
|
||||
if (!_previewTexture) {
|
||||
_previewTexture = gpu::Texture::createStrict(
|
||||
QImage image = DesktopPreviewProvider::getInstance()->getPreviewDisabledImage(_vsyncEnabled);
|
||||
_previewTexture = gpu::Texture::createStrict(
|
||||
gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA),
|
||||
image.width(), image.height(),
|
||||
gpu::Texture::MAX_NUM_MIPS,
|
||||
|
@ -274,7 +267,6 @@ void HmdDisplayPlugin::internalPresent() {
|
|||
_previewTexture->setStoredMipFormat(gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA));
|
||||
_previewTexture->assignStoredMip(0, image.byteCount(), image.constBits());
|
||||
_previewTexture->setAutoGenerateMips(true);
|
||||
}
|
||||
|
||||
auto viewport = getViewportForSourceSize(uvec2(_previewTexture->getDimensions()));
|
||||
|
||||
|
|
|
@ -1050,7 +1050,7 @@ void ModelEntityRenderer::animate(const TypedEntityPointer& entity) {
|
|||
return;
|
||||
}
|
||||
|
||||
QVector<JointData> jointsData;
|
||||
QVector<EntityJointData> jointsData;
|
||||
|
||||
const QVector<FBXAnimationFrame>& frames = _animation->getFramesReference(); // NOTE: getFrames() is too heavy
|
||||
int frameCount = frames.size();
|
||||
|
@ -1394,8 +1394,14 @@ void ModelEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& sce
|
|||
// That is where _currentFrame and _lastAnimated were updated.
|
||||
if (_animating) {
|
||||
DETAILED_PROFILE_RANGE(simulation_physics, "Animate");
|
||||
|
||||
if (!jointsMapped()) {
|
||||
mapJoints(entity, model->getJointNames());
|
||||
//else the joint have been mapped before but we have a new animation to load
|
||||
} else if (_animation && (_animation->getURL().toString() != entity->getAnimationURL())) {
|
||||
_animation = DependencyManager::get<AnimationCache>()->getAnimation(entity->getAnimationURL());
|
||||
_jointMappingCompleted = false;
|
||||
mapJoints(entity, model->getJointNames());
|
||||
}
|
||||
if (!(entity->getAnimationFirstFrame() < 0) && !(entity->getAnimationFirstFrame() > entity->getAnimationLastFrame())) {
|
||||
animate(entity);
|
||||
|
|
|
@ -368,6 +368,7 @@ public:
|
|||
void* getPhysicsInfo() const { return _physicsInfo; }
|
||||
|
||||
void setPhysicsInfo(void* data) { _physicsInfo = data; }
|
||||
|
||||
EntityTreeElementPointer getElement() const { return _element; }
|
||||
EntityTreePointer getTree() const;
|
||||
virtual SpatialParentTree* getParentTree() const override;
|
||||
|
|
|
@ -452,7 +452,7 @@ void ModelEntityItem::resizeJointArrays(int newSize) {
|
|||
});
|
||||
}
|
||||
|
||||
void ModelEntityItem::setAnimationJointsData(const QVector<JointData>& jointsData) {
|
||||
void ModelEntityItem::setAnimationJointsData(const QVector<EntityJointData>& jointsData) {
|
||||
resizeJointArrays(jointsData.size());
|
||||
_jointDataLock.withWriteLock([&] {
|
||||
for (auto index = 0; index < jointsData.size(); ++index) {
|
||||
|
|
|
@ -124,7 +124,7 @@ public:
|
|||
virtual void setJointTranslations(const QVector<glm::vec3>& translations);
|
||||
virtual void setJointTranslationsSet(const QVector<bool>& translationsSet);
|
||||
|
||||
virtual void setAnimationJointsData(const QVector<JointData>& jointsData);
|
||||
virtual void setAnimationJointsData(const QVector<EntityJointData>& jointsData);
|
||||
|
||||
QVector<glm::quat> getJointRotations() const;
|
||||
QVector<bool> getJointRotationsSet() const;
|
||||
|
@ -150,7 +150,7 @@ protected:
|
|||
bool _jointTranslationsExplicitlySet{ false }; // were the joints set as a property or just side effect of animations
|
||||
|
||||
struct ModelJointData {
|
||||
JointData joint;
|
||||
EntityJointData joint;
|
||||
bool rotationDirty { false };
|
||||
bool translationDirty { false };
|
||||
};
|
||||
|
|
|
@ -163,7 +163,7 @@ void Midi::sendRawMessage(int device, int raw) {
|
|||
void Midi::sendMessage(int device, int channel, int type, int note, int velocity) {
|
||||
int message = (channel - 1) | (type << MIDI_SHIFT_STATUS);
|
||||
if (broadcastEnabled) {
|
||||
for (int i = 0; i < midihout.size(); i++) {
|
||||
for (int i = 1; i < midihout.size(); i++) { // Skip 0 (Microsoft GS Wavetable Synth)
|
||||
if (midihout[i] != NULL) {
|
||||
midiOutShortMsg(midihout[i], message | (note << MIDI_SHIFT_NOTE) | (velocity << MIDI_SHIFT_VELOCITY));
|
||||
}
|
||||
|
@ -174,9 +174,9 @@ void Midi::sendMessage(int device, int channel, int type, int note, int velocity
|
|||
}
|
||||
|
||||
void Midi::sendNote(int status, int note, int velocity) {
|
||||
for (int i = 0; i < midihout.size(); i++) {
|
||||
for (int i = 1; i < midihout.size(); i++) { // Skip 0 (Microsoft GS Wavetable Synth)
|
||||
if (midihout[i] != NULL) {
|
||||
midiOutShortMsg(midihout[i], status + (note << MIDI_SHIFT_NOTE) + (velocity << MIDI_SHIFT_VELOCITY));
|
||||
midiOutShortMsg(midihout[i], status | (note << MIDI_SHIFT_NOTE) | (velocity << MIDI_SHIFT_VELOCITY));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -283,9 +283,6 @@ void Midi::midiHardwareChange() {
|
|||
|
||||
Midi::Midi() {
|
||||
instance = this;
|
||||
#if defined Q_OS_WIN32
|
||||
midiOutExclude.push_back("Microsoft GS Wavetable Synth"); // we don't want to hear this thing (Lags)
|
||||
#endif
|
||||
MidiSetup();
|
||||
}
|
||||
|
||||
|
|
|
@ -38,7 +38,7 @@ PacketVersion versionForPacketType(PacketType packetType) {
|
|||
case PacketType::AvatarData:
|
||||
case PacketType::BulkAvatarData:
|
||||
case PacketType::KillAvatar:
|
||||
return static_cast<PacketVersion>(AvatarMixerPacketVersion::UpdatedMannequinDefaultAvatar);
|
||||
return static_cast<PacketVersion>(AvatarMixerPacketVersion::AvatarJointDefaultPoseFlags);
|
||||
case PacketType::MessagesData:
|
||||
return static_cast<PacketVersion>(MessageDataVersion::TextOrBinaryData);
|
||||
case PacketType::ICEServerHeartbeat:
|
||||
|
|
|
@ -247,7 +247,8 @@ enum class AvatarMixerPacketVersion : PacketVersion {
|
|||
AvatarIdentitySequenceFront,
|
||||
IsReplicatedInAvatarIdentity,
|
||||
AvatarIdentityLookAtSnapping,
|
||||
UpdatedMannequinDefaultAvatar
|
||||
UpdatedMannequinDefaultAvatar,
|
||||
AvatarJointDefaultPoseFlags
|
||||
};
|
||||
|
||||
enum class DomainConnectRequestVersion : PacketVersion {
|
||||
|
|
|
@ -256,25 +256,32 @@ void EntityMotionState::setWorldTransform(const btTransform& worldTrans) {
|
|||
assert(_entity);
|
||||
assert(entityTreeIsLocked());
|
||||
measureBodyAcceleration();
|
||||
bool positionSuccess;
|
||||
_entity->setWorldPosition(bulletToGLM(worldTrans.getOrigin()) + ObjectMotionState::getWorldOffset(), positionSuccess, false);
|
||||
if (!positionSuccess) {
|
||||
static QString repeatedMessage =
|
||||
LogHandler::getInstance().addRepeatedMessageRegex("EntityMotionState::setWorldTransform "
|
||||
"setPosition failed.*");
|
||||
qCDebug(physics) << "EntityMotionState::setWorldTransform setPosition failed" << _entity->getID();
|
||||
|
||||
// If transform or velocities are flagged as dirty it means a network or scripted change
|
||||
// occured between the beginning and end of the stepSimulation() and we DON'T want to apply
|
||||
// these physics simulation results.
|
||||
uint32_t flags = _entity->getDirtyFlags() & (Simulation::DIRTY_TRANSFORM | Simulation::DIRTY_VELOCITIES);
|
||||
if (!flags) {
|
||||
// flags are clear
|
||||
_entity->setWorldTransform(bulletToGLM(worldTrans.getOrigin()), bulletToGLM(worldTrans.getRotation()));
|
||||
_entity->setWorldVelocity(getBodyLinearVelocity());
|
||||
_entity->setWorldAngularVelocity(getBodyAngularVelocity());
|
||||
_entity->setLastSimulated(usecTimestampNow());
|
||||
} else {
|
||||
// only set properties NOT flagged
|
||||
if (!(flags & Simulation::DIRTY_TRANSFORM)) {
|
||||
_entity->setWorldTransform(bulletToGLM(worldTrans.getOrigin()), bulletToGLM(worldTrans.getRotation()));
|
||||
}
|
||||
if (!(flags & Simulation::DIRTY_LINEAR_VELOCITY)) {
|
||||
_entity->setWorldVelocity(getBodyLinearVelocity());
|
||||
}
|
||||
if (!(flags & Simulation::DIRTY_ANGULAR_VELOCITY)) {
|
||||
_entity->setWorldAngularVelocity(getBodyAngularVelocity());
|
||||
}
|
||||
if (flags != (Simulation::DIRTY_TRANSFORM | Simulation::DIRTY_VELOCITIES)) {
|
||||
_entity->setLastSimulated(usecTimestampNow());
|
||||
}
|
||||
}
|
||||
bool orientationSuccess;
|
||||
_entity->setWorldOrientation(bulletToGLM(worldTrans.getRotation()), orientationSuccess, false);
|
||||
if (!orientationSuccess) {
|
||||
static QString repeatedMessage =
|
||||
LogHandler::getInstance().addRepeatedMessageRegex("EntityMotionState::setWorldTransform "
|
||||
"setOrientation failed.*");
|
||||
qCDebug(physics) << "EntityMotionState::setWorldTransform setOrientation failed" << _entity->getID();
|
||||
}
|
||||
_entity->setVelocity(getBodyLinearVelocity());
|
||||
_entity->setAngularVelocity(getBodyAngularVelocity());
|
||||
_entity->setLastSimulated(usecTimestampNow());
|
||||
|
||||
if (_entity->getSimulatorID().isNull()) {
|
||||
_loopsWithoutOwner++;
|
||||
|
@ -530,9 +537,7 @@ void EntityMotionState::sendUpdate(OctreeEditPacketSender* packetSender, uint32_
|
|||
|
||||
if (!_body->isActive()) {
|
||||
// make sure all derivatives are zero
|
||||
_entity->setVelocity(Vectors::ZERO);
|
||||
_entity->setAngularVelocity(Vectors::ZERO);
|
||||
_entity->setAcceleration(Vectors::ZERO);
|
||||
zeroCleanObjectVelocities();
|
||||
_numInactiveUpdates++;
|
||||
} else {
|
||||
glm::vec3 gravity = _entity->getGravity();
|
||||
|
@ -559,9 +564,7 @@ void EntityMotionState::sendUpdate(OctreeEditPacketSender* packetSender, uint32_
|
|||
if (movingSlowly) {
|
||||
// velocities might not be zero, but we'll fake them as such, which will hopefully help convince
|
||||
// other simulating observers to deactivate their own copies
|
||||
glm::vec3 zero(0.0f);
|
||||
_entity->setVelocity(zero);
|
||||
_entity->setAngularVelocity(zero);
|
||||
zeroCleanObjectVelocities();
|
||||
}
|
||||
}
|
||||
_numInactiveUpdates = 0;
|
||||
|
@ -818,3 +821,22 @@ bool EntityMotionState::shouldBeLocallyOwned() const {
|
|||
void EntityMotionState::upgradeOutgoingPriority(uint8_t priority) {
|
||||
_outgoingPriority = glm::max<uint8_t>(_outgoingPriority, priority);
|
||||
}
|
||||
|
||||
void EntityMotionState::zeroCleanObjectVelocities() const {
|
||||
// If transform or velocities are flagged as dirty it means a network or scripted change
|
||||
// occured between the beginning and end of the stepSimulation() and we DON'T want to apply
|
||||
// these physics simulation results.
|
||||
uint32_t flags = _entity->getDirtyFlags() & (Simulation::DIRTY_TRANSFORM | Simulation::DIRTY_VELOCITIES);
|
||||
if (!flags) {
|
||||
_entity->setWorldVelocity(glm::vec3(0.0f));
|
||||
_entity->setWorldAngularVelocity(glm::vec3(0.0f));
|
||||
} else {
|
||||
if (!(flags & Simulation::DIRTY_LINEAR_VELOCITY)) {
|
||||
_entity->setWorldVelocity(glm::vec3(0.0f));
|
||||
}
|
||||
if (!(flags & Simulation::DIRTY_ANGULAR_VELOCITY)) {
|
||||
_entity->setWorldAngularVelocity(glm::vec3(0.0f));
|
||||
}
|
||||
}
|
||||
_entity->setAcceleration(glm::vec3(0.0f));
|
||||
}
|
||||
|
|
|
@ -87,6 +87,7 @@ public:
|
|||
protected:
|
||||
// changes _outgoingPriority only if priority is larger
|
||||
void upgradeOutgoingPriority(uint8_t priority);
|
||||
void zeroCleanObjectVelocities() const;
|
||||
|
||||
#ifdef WANT_DEBUG_ENTITY_TREE_LOCKS
|
||||
bool entityTreeIsLocked() const;
|
||||
|
|
|
@ -186,7 +186,7 @@ float specularDistribution(SurfaceData surface) {
|
|||
// Add geometric factors G1(n,l) and G1(n,v)
|
||||
float smithInvG1NdotL = evalSmithInvG1(surface.roughness4, surface.ndotl);
|
||||
denom *= surface.smithInvG1NdotV * smithInvG1NdotL;
|
||||
// Don't divide by PI as it will be done later
|
||||
// Don't divide by PI as this is part of the light normalization factor
|
||||
float power = surface.roughness4 / denom;
|
||||
return power;
|
||||
}
|
||||
|
@ -202,12 +202,11 @@ vec4 evalPBRShading(float metallic, vec3 fresnel, SurfaceData surface) {
|
|||
vec3 specular = fresnelColor * power * angleAttenuation;
|
||||
float diffuse = (1.0 - metallic) * angleAttenuation * (1.0 - fresnelColor.x);
|
||||
|
||||
diffuse /= 3.1415926;
|
||||
// Diffuse is divided by PI but specular isn't because an infinitesimal volume light source
|
||||
// has a multiplier of PI, says Naty Hoffman.
|
||||
// We don't divided by PI, as the "normalized" equations state we should, because we decide, as Naty Hoffman, that
|
||||
// we wish to have a similar color as raw albedo on a perfectly diffuse surface perpendicularly lit
|
||||
// by a white light of intensity 1. But this is an arbitrary normalization of what light intensity "means".
|
||||
// (see http://blog.selfshadow.com/publications/s2013-shading-course/hoffman/s2013_pbs_physics_math_notes.pdf
|
||||
// page 23 paragraph "Punctual light sources")
|
||||
|
||||
return vec4(specular, diffuse);
|
||||
}
|
||||
|
||||
|
@ -222,9 +221,9 @@ vec4 evalPBRShadingDielectric(SurfaceData surface, float fresnel) {
|
|||
vec3 specular = vec3(fresnelScalar) * power * angleAttenuation;
|
||||
float diffuse = angleAttenuation * (1.0 - fresnelScalar);
|
||||
|
||||
diffuse /= 3.1415926;
|
||||
// Diffuse is divided by PI but specular isn't because an infinitesimal volume light source
|
||||
// has a multiplier of PI, says Naty Hoffman.
|
||||
// We don't divided by PI, as the "normalized" equations state we should, because we decide, as Naty Hoffman, that
|
||||
// we wish to have a similar color as raw albedo on a perfectly diffuse surface perpendicularly lit
|
||||
// by a white light of intensity 1. But this is an arbitrary normalization of what light intensity "means".
|
||||
// (see http://blog.selfshadow.com/publications/s2013-shading-course/hoffman/s2013_pbs_physics_math_notes.pdf
|
||||
// page 23 paragraph "Punctual light sources")
|
||||
return vec4(specular, diffuse);
|
||||
|
@ -239,8 +238,9 @@ vec4 evalPBRShadingMetallic(SurfaceData surface, vec3 fresnel) {
|
|||
float power = specularDistribution(surface);
|
||||
vec3 specular = fresnelColor * power * angleAttenuation;
|
||||
|
||||
// Specular isn't divided by PI because an infinitesimal volume light source
|
||||
// has a multiplier of PI, says Naty Hoffman.
|
||||
// We don't divided by PI, as the "normalized" equations state we should, because we decide, as Naty Hoffman, that
|
||||
// we wish to have a similar color as raw albedo on a perfectly diffuse surface perpendicularly lit
|
||||
// by a white light of intensity 1. But this is an arbitrary normalization of what light intensity "means".
|
||||
// (see http://blog.selfshadow.com/publications/s2013-shading-course/hoffman/s2013_pbs_physics_math_notes.pdf
|
||||
// page 23 paragraph "Punctual light sources")
|
||||
return vec4(specular, 0.f);
|
||||
|
|
58
libraries/shared/src/BitVectorHelpers.h
Normal file
|
@ -0,0 +1,58 @@
|
|||
//
|
||||
// BitVectorHelpers.h
|
||||
// libraries/shared/src
|
||||
//
|
||||
// Created by Anthony Thibault on 1/19/18.
|
||||
// Copyright 2018 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_BitVectorHelpers_h
|
||||
#define hifi_BitVectorHelpers_h
|
||||
|
||||
size_t calcBitVectorSize(int numBits) {
|
||||
return ((numBits - 1) >> 3) + 1;
|
||||
}
|
||||
|
||||
// func should be of type bool func(int index)
|
||||
template <typename F>
|
||||
size_t writeBitVector(uint8_t* destinationBuffer, int numBits, const F& func) {
|
||||
size_t totalBytes = ((numBits - 1) >> 3) + 1;
|
||||
uint8_t* cursor = destinationBuffer;
|
||||
uint8_t byte = 0;
|
||||
uint8_t bit = 0;
|
||||
|
||||
for (int i = 0; i < numBits; i++) {
|
||||
if (func(i)) {
|
||||
byte |= (1 << bit);
|
||||
}
|
||||
if (++bit == BITS_IN_BYTE) {
|
||||
*cursor++ = byte;
|
||||
byte = 0;
|
||||
bit = 0;
|
||||
}
|
||||
}
|
||||
return totalBytes;
|
||||
}
|
||||
|
||||
// func should be of type 'void func(int index, bool value)'
|
||||
template <typename F>
|
||||
size_t readBitVector(const uint8_t* sourceBuffer, int numBits, const F& func) {
|
||||
size_t totalBytes = ((numBits - 1) >> 3) + 1;
|
||||
const uint8_t* cursor = sourceBuffer;
|
||||
uint8_t bit = 0;
|
||||
|
||||
for (int i = 0; i < numBits; i++) {
|
||||
bool value = (bool)(*cursor & (1 << bit));
|
||||
func(i, value);
|
||||
if (++bit == BITS_IN_BYTE) {
|
||||
cursor++;
|
||||
bit = 0;
|
||||
}
|
||||
}
|
||||
return totalBytes;
|
||||
}
|
||||
|
||||
#endif
|
|
@ -253,6 +253,7 @@ glm::vec2 getFacingDir2D(const glm::mat4& m);
|
|||
|
||||
inline bool isNaN(const glm::vec3& value) { return isNaN(value.x) || isNaN(value.y) || isNaN(value.z); }
|
||||
inline bool isNaN(const glm::quat& value) { return isNaN(value.w) || isNaN(value.x) || isNaN(value.y) || isNaN(value.z); }
|
||||
inline bool isNaN(const glm::mat3& value) { return isNaN(value * glm::vec3(1.0f)); }
|
||||
|
||||
glm::mat4 orthoInverse(const glm::mat4& m);
|
||||
|
||||
|
|
|
@ -5,14 +5,27 @@
|
|||
#include <glm/glm.hpp>
|
||||
#include <glm/gtc/quaternion.hpp>
|
||||
|
||||
// Used by the avatar mixer to describe a single joint
|
||||
// These are relative to their parent and translations are in meters
|
||||
class JointData {
|
||||
class EntityJointData {
|
||||
public:
|
||||
glm::quat rotation;
|
||||
glm::vec3 translation;
|
||||
bool rotationSet = false;
|
||||
glm::vec3 translation; // meters
|
||||
bool translationSet = false;
|
||||
};
|
||||
|
||||
// Used by the avatar mixer to describe a single joint
|
||||
// Translations relative to their parent and are in meters.
|
||||
// Rotations are absolute (i.e. not relative to parent) and are in rig space.
|
||||
class JointData {
|
||||
public:
|
||||
glm::quat rotation;
|
||||
glm::vec3 translation;
|
||||
|
||||
// This indicates that the rotation or translation is the same as the defaultPose for the avatar.
|
||||
// if true, it also means that the rotation or translation value in this structure is not valid and
|
||||
// should be replaced by the avatar's actual default pose value.
|
||||
bool rotationIsDefaultPose = true;
|
||||
bool translationIsDefaultPose = true;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -464,6 +464,36 @@ glm::vec3 SpatiallyNestable::localToWorldDimensions(const glm::vec3& dimensions,
|
|||
return dimensions;
|
||||
}
|
||||
|
||||
void SpatiallyNestable::setWorldTransform(const glm::vec3& position, const glm::quat& orientation) {
|
||||
// guard against introducing NaN into the transform
|
||||
if (isNaN(orientation) || isNaN(position)) {
|
||||
return;
|
||||
}
|
||||
|
||||
bool changed = false;
|
||||
bool success = true;
|
||||
Transform parentTransform = getParentTransform(success);
|
||||
_transformLock.withWriteLock([&] {
|
||||
Transform myWorldTransform;
|
||||
Transform::mult(myWorldTransform, parentTransform, _transform);
|
||||
if (myWorldTransform.getRotation() != orientation) {
|
||||
changed = true;
|
||||
myWorldTransform.setRotation(orientation);
|
||||
}
|
||||
if (myWorldTransform.getTranslation() != position) {
|
||||
changed = true;
|
||||
myWorldTransform.setTranslation(position);
|
||||
}
|
||||
if (changed) {
|
||||
Transform::inverseMult(_transform, parentTransform, myWorldTransform);
|
||||
_translationChanged = usecTimestampNow();
|
||||
}
|
||||
});
|
||||
if (success && changed) {
|
||||
locationChanged(false);
|
||||
}
|
||||
}
|
||||
|
||||
glm::vec3 SpatiallyNestable::getWorldPosition(bool& success) const {
|
||||
return getTransform(success).getTranslation();
|
||||
}
|
||||
|
|
|
@ -87,6 +87,7 @@ public:
|
|||
|
||||
virtual Transform getParentTransform(bool& success, int depth = 0) const;
|
||||
|
||||
void setWorldTransform(const glm::vec3& position, const glm::quat& orientation);
|
||||
virtual glm::vec3 getWorldPosition(bool& success) const;
|
||||
virtual glm::vec3 getWorldPosition() const;
|
||||
virtual void setWorldPosition(const glm::vec3& position, bool& success, bool tellPhysics = true);
|
||||
|
|
46
libraries/ui/src/DesktopPreviewProvider.cpp
Normal file
|
@ -0,0 +1,46 @@
|
|||
#include "DesktopPreviewProvider.h"
|
||||
#include <PathUtils.h>
|
||||
#include <QMetaEnum>
|
||||
#include <QtPlugin>
|
||||
#include <cassert>
|
||||
|
||||
DesktopPreviewProvider::DesktopPreviewProvider() {
|
||||
}
|
||||
|
||||
constexpr const char* DesktopPreviewProvider::imagePaths[];
|
||||
|
||||
QSharedPointer<DesktopPreviewProvider> DesktopPreviewProvider::getInstance() {
|
||||
static QSharedPointer<DesktopPreviewProvider> instance = DependencyManager::get<DesktopPreviewProvider>();
|
||||
return instance;
|
||||
}
|
||||
|
||||
QImage DesktopPreviewProvider::getPreviewDisabledImage(bool vsyncEnabled) const {
|
||||
|
||||
auto imageIndex = vsyncEnabled ? VSYNC : m_previewDisabledReason;
|
||||
assert(imageIndex >= 0 && imageIndex <= VSYNC);
|
||||
|
||||
return !m_previewDisabled[imageIndex].isNull() ? m_previewDisabled[imageIndex] : loadPreviewImage(m_previewDisabled[imageIndex], PathUtils::resourcesPath() + imagePaths[imageIndex]);
|
||||
}
|
||||
|
||||
void DesktopPreviewProvider::setPreviewDisabledReason(PreviewDisabledReasons reason) {
|
||||
if (reason == VSYNC) {
|
||||
qDebug() << "Preview disabled reason can't be forced to " << QMetaEnum::fromType<DesktopPreviewProvider::PreviewDisabledReasons>().valueToKey(reason);
|
||||
return; // Not settable via this interface, as VSYNC is controlled by HMD plugin..
|
||||
}
|
||||
|
||||
m_previewDisabledReason = reason;
|
||||
}
|
||||
|
||||
void DesktopPreviewProvider::setPreviewDisabledReason(const QString& reasonString) {
|
||||
PreviewDisabledReasons reason = USER;
|
||||
bool ok = false;
|
||||
|
||||
reason = (PreviewDisabledReasons) QMetaEnum::fromType<DesktopPreviewProvider::PreviewDisabledReasons>().keyToValue(reasonString.toLatin1().data(), &ok);
|
||||
if (ok) {
|
||||
setPreviewDisabledReason(reason);
|
||||
}
|
||||
}
|
||||
|
||||
QImage& DesktopPreviewProvider::loadPreviewImage(QImage& image, const QString& path) const {
|
||||
return image = QImage(path).mirrored().convertToFormat(QImage::Format_RGBA8888);
|
||||
}
|
47
libraries/ui/src/DesktopPreviewProvider.h
Normal file
|
@ -0,0 +1,47 @@
|
|||
//
|
||||
// Created by Alexander Ivash on 2018/01/08
|
||||
// Copyright 2018 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 <DependencyManager.h>
|
||||
#include <QImage>
|
||||
|
||||
class DesktopPreviewProvider : public QObject, public Dependency {
|
||||
SINGLETON_DEPENDENCY
|
||||
Q_OBJECT
|
||||
|
||||
DesktopPreviewProvider();
|
||||
DesktopPreviewProvider(const DesktopPreviewProvider& other) = delete;
|
||||
|
||||
constexpr static const char* imagePaths[] = {
|
||||
"images/preview-disabled.png", // USER
|
||||
"images/preview-privacy.png", // SECURE_SCREEN
|
||||
"images/preview.png", // VSYNC
|
||||
};
|
||||
|
||||
public:
|
||||
enum PreviewDisabledReasons {
|
||||
USER = 0,
|
||||
SECURE_SCREEN,
|
||||
VSYNC // Not settable via this interface, as VSYNC is controlled by HMD plugin..
|
||||
};
|
||||
Q_ENUM(PreviewDisabledReasons)
|
||||
|
||||
static QSharedPointer<DesktopPreviewProvider> getInstance();
|
||||
|
||||
QImage getPreviewDisabledImage(bool vsyncEnabled) const;
|
||||
void setPreviewDisabledReason(PreviewDisabledReasons reason);
|
||||
|
||||
public slots:
|
||||
void setPreviewDisabledReason(const QString& reason);
|
||||
|
||||
private:
|
||||
QImage& loadPreviewImage(QImage& image, const QString& path) const;
|
||||
|
||||
PreviewDisabledReasons m_previewDisabledReason = { USER };
|
||||
|
||||
mutable QImage m_previewDisabled[3];
|
||||
};
|
|
@ -31,6 +31,8 @@
|
|||
const QString SYSTEM_TOOLBAR = "com.highfidelity.interface.toolbar.system";
|
||||
const QString SYSTEM_TABLET = "com.highfidelity.interface.tablet.system";
|
||||
const QString TabletScriptingInterface::QML = "hifi/tablet/TabletRoot.qml";
|
||||
const QString BUTTON_SORT_ORDER_KEY = "sortOrder";
|
||||
const int DEFAULT_BUTTON_SORT_ORDER = 100;
|
||||
|
||||
static QString getUsername() {
|
||||
QString username = "Unknown user";
|
||||
|
@ -74,11 +76,21 @@ QVariant TabletButtonListModel::data(const QModelIndex& index, int role) const {
|
|||
}
|
||||
|
||||
TabletButtonProxy* TabletButtonListModel::addButton(const QVariant& properties) {
|
||||
auto tabletButtonProxy = QSharedPointer<TabletButtonProxy>(new TabletButtonProxy(properties.toMap()));
|
||||
QVariantMap newProperties = properties.toMap();
|
||||
if (newProperties.find(BUTTON_SORT_ORDER_KEY) == newProperties.end()) {
|
||||
newProperties[BUTTON_SORT_ORDER_KEY] = DEFAULT_BUTTON_SORT_ORDER;
|
||||
}
|
||||
int index = computeNewButtonIndex(newProperties);
|
||||
auto button = QSharedPointer<TabletButtonProxy>(new TabletButtonProxy(newProperties));
|
||||
beginResetModel();
|
||||
_buttons.push_back(tabletButtonProxy);
|
||||
int numButtons = (int)_buttons.size();
|
||||
if (index < numButtons) {
|
||||
_buttons.insert(_buttons.begin() + index, button);
|
||||
} else {
|
||||
_buttons.push_back(button);
|
||||
}
|
||||
endResetModel();
|
||||
return tabletButtonProxy.data();
|
||||
return button.data();
|
||||
}
|
||||
|
||||
void TabletButtonListModel::removeButton(TabletButtonProxy* button) {
|
||||
|
@ -92,6 +104,20 @@ void TabletButtonListModel::removeButton(TabletButtonProxy* button) {
|
|||
endResetModel();
|
||||
}
|
||||
|
||||
int TabletButtonListModel::computeNewButtonIndex(const QVariantMap& newButtonProperties) {
|
||||
int numButtons = (int)_buttons.size();
|
||||
int newButtonSortOrder = newButtonProperties[BUTTON_SORT_ORDER_KEY].toInt();
|
||||
if (newButtonSortOrder == DEFAULT_BUTTON_SORT_ORDER) return numButtons;
|
||||
for (int i = 0; i < numButtons; i++) {
|
||||
QVariantMap tabletButtonProperties = _buttons[i]->getProperties();
|
||||
int tabletButtonSortOrder = tabletButtonProperties[BUTTON_SORT_ORDER_KEY].toInt();
|
||||
if (newButtonSortOrder <= tabletButtonSortOrder) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return numButtons;
|
||||
}
|
||||
|
||||
TabletButtonsProxyModel::TabletButtonsProxyModel(QObject *parent)
|
||||
: QSortFilterProxyModel(parent) {
|
||||
}
|
||||
|
|
|
@ -115,6 +115,7 @@ protected:
|
|||
friend class TabletProxy;
|
||||
TabletButtonProxy* addButton(const QVariant& properties);
|
||||
void removeButton(TabletButtonProxy* button);
|
||||
int computeNewButtonIndex(const QVariantMap& newButtonProperties);
|
||||
using List = std::list<QSharedPointer<TabletButtonProxy>>;
|
||||
static QHash<int, QByteArray> _roles;
|
||||
static Qt::ItemFlags _flags;
|
||||
|
|
|
@ -22,8 +22,7 @@
|
|||
|
||||
var button = tablet.addButton({
|
||||
icon: Script.resolvePath("dynamicsTests.svg"),
|
||||
text: "Dynamics",
|
||||
sortOrder: 15
|
||||
text: "Dynamics"
|
||||
});
|
||||
|
||||
|
||||
|
|
|
@ -32,8 +32,7 @@
|
|||
var button = tablet.addButton({
|
||||
text: TABLET_BUTTON_NAME,
|
||||
icon: ICON_URL,
|
||||
activeIcon: ACTIVE_ICON_URL,
|
||||
sortOrder: 1
|
||||
activeIcon: ACTIVE_ICON_URL
|
||||
});
|
||||
|
||||
var hasEventBridge = false;
|
||||
|
|
|
@ -30,8 +30,7 @@
|
|||
var button = tablet.addButton({
|
||||
text: TABLET_BUTTON_NAME,
|
||||
icon: ICON_URL,
|
||||
activeIcon: ACTIVE_ICON_URL,
|
||||
sortOrder: 1
|
||||
activeIcon: ACTIVE_ICON_URL
|
||||
});
|
||||
|
||||
var hasEventBridge = false;
|
||||
|
|
|
@ -31,8 +31,7 @@
|
|||
var button = tablet.addButton({
|
||||
text: TABLET_BUTTON_NAME,
|
||||
icon: ICON_URL,
|
||||
activeIcon: ACTIVE_ICON_URL,
|
||||
sortOrder: 1
|
||||
activeIcon: ACTIVE_ICON_URL
|
||||
});
|
||||
|
||||
var hasEventBridge = false;
|
||||
|
|
|
@ -28,8 +28,8 @@
|
|||
var window = new OverlayWindow({
|
||||
title: 'API Debugger',
|
||||
source: qml,
|
||||
width: 1200,
|
||||
height: 500
|
||||
width: 500,
|
||||
height: 700
|
||||
});
|
||||
|
||||
window.closed.connect(function () {
|
||||
|
|
|
@ -16,10 +16,10 @@
|
|||
var button;
|
||||
// Used for animating and disappearing the bubble
|
||||
var bubbleOverlayTimestamp;
|
||||
// Used for rate limiting the bubble sound
|
||||
var lastBubbleSoundTimestamp = 0;
|
||||
// Used for flashing the HUD button upon activation
|
||||
var bubbleButtonFlashState = false;
|
||||
// Used for flashing the HUD button upon activation
|
||||
var bubbleButtonTimestamp;
|
||||
// Affects bubble height
|
||||
var BUBBLE_HEIGHT_SCALE = 0.15;
|
||||
// The bubble model itself
|
||||
|
@ -36,9 +36,11 @@
|
|||
var bubbleActivateSound = SoundCache.getSound(Script.resolvePath("assets/sounds/bubble.wav"));
|
||||
// Is the update() function connected?
|
||||
var updateConnected = false;
|
||||
var bubbleFlashTimer = false;
|
||||
|
||||
var BUBBLE_VISIBLE_DURATION_MS = 3000;
|
||||
var BUBBLE_RAISE_ANIMATION_DURATION_MS = 750;
|
||||
var BUBBLE_SOUND_RATE_LIMIT_MS = 15000;
|
||||
|
||||
// Hides the bubble model overlay and resets the button flash state
|
||||
function hideOverlays() {
|
||||
|
@ -50,11 +52,15 @@
|
|||
|
||||
// Make the bubble overlay visible, set its position, and play the sound
|
||||
function createOverlays() {
|
||||
Audio.playSound(bubbleActivateSound, {
|
||||
position: { x: MyAvatar.position.x, y: MyAvatar.position.y, z: MyAvatar.position.z },
|
||||
localOnly: true,
|
||||
volume: 0.2
|
||||
});
|
||||
var nowTimestamp = Date.now();
|
||||
if (nowTimestamp - lastBubbleSoundTimestamp >= BUBBLE_SOUND_RATE_LIMIT_MS) {
|
||||
Audio.playSound(bubbleActivateSound, {
|
||||
position: { x: MyAvatar.position.x, y: MyAvatar.position.y, z: MyAvatar.position.z },
|
||||
localOnly: true,
|
||||
volume: 0.2
|
||||
});
|
||||
lastBubbleSoundTimestamp = nowTimestamp;
|
||||
}
|
||||
hideOverlays();
|
||||
if (updateConnected === true) {
|
||||
updateConnected = false;
|
||||
|
@ -80,10 +86,17 @@
|
|||
},
|
||||
visible: true
|
||||
});
|
||||
bubbleOverlayTimestamp = Date.now();
|
||||
bubbleButtonTimestamp = bubbleOverlayTimestamp;
|
||||
bubbleOverlayTimestamp = nowTimestamp;
|
||||
Script.update.connect(update);
|
||||
updateConnected = true;
|
||||
|
||||
// Flash button
|
||||
if (!bubbleFlashTimer) {
|
||||
bubbleFlashTimer = Script.setInterval(function () {
|
||||
writeButtonProperties(bubbleButtonFlashState);
|
||||
bubbleButtonFlashState = !bubbleButtonFlashState;
|
||||
}, 500);
|
||||
}
|
||||
}
|
||||
|
||||
// Called from the C++ scripting interface to show the bubble overlay
|
||||
|
@ -103,12 +116,6 @@
|
|||
var delay = (timestamp - bubbleOverlayTimestamp);
|
||||
var overlayAlpha = 1.0 - (delay / BUBBLE_VISIBLE_DURATION_MS);
|
||||
if (overlayAlpha > 0) {
|
||||
// Flash button
|
||||
if ((timestamp - bubbleButtonTimestamp) >= BUBBLE_VISIBLE_DURATION_MS) {
|
||||
writeButtonProperties(bubbleButtonFlashState);
|
||||
bubbleButtonTimestamp = timestamp;
|
||||
bubbleButtonFlashState = !bubbleButtonFlashState;
|
||||
}
|
||||
|
||||
if (delay < BUBBLE_RAISE_ANIMATION_DURATION_MS) {
|
||||
Overlays.editOverlay(bubbleOverlay, {
|
||||
|
@ -157,8 +164,11 @@
|
|||
Script.update.disconnect(update);
|
||||
updateConnected = false;
|
||||
}
|
||||
var bubbleActive = Users.getIgnoreRadiusEnabled();
|
||||
writeButtonProperties(bubbleActive);
|
||||
if (bubbleFlashTimer) {
|
||||
Script.clearTimeout(bubbleFlashTimer);
|
||||
bubbleFlashTimer = false;
|
||||
}
|
||||
writeButtonProperties(Users.getIgnoreRadiusEnabled());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -166,6 +176,10 @@
|
|||
// NOTE: the c++ calls this with just the first param -- we added a second
|
||||
// just for not logging the initial state of the bubble when we startup.
|
||||
function onBubbleToggled(enabled, doNotLog) {
|
||||
if (bubbleFlashTimer) {
|
||||
Script.clearTimeout(bubbleFlashTimer);
|
||||
bubbleFlashTimer = false;
|
||||
}
|
||||
writeButtonProperties(enabled);
|
||||
if (doNotLog !== true) {
|
||||
UserActivityLogger.bubbleToggled(enabled);
|
||||
|
@ -200,6 +214,10 @@
|
|||
// Cleanup the tablet button and overlays when script is stopped
|
||||
Script.scriptEnding.connect(function () {
|
||||
button.clicked.disconnect(Users.toggleIgnoreRadius);
|
||||
if (bubbleFlashTimer) {
|
||||
Script.clearTimeout(bubbleFlashTimer);
|
||||
bubbleFlashTimer = false;
|
||||
}
|
||||
if (tablet) {
|
||||
tablet.removeButton(button);
|
||||
}
|
||||
|
|
|
@ -945,8 +945,7 @@
|
|||
tabletButton = tablet.addButton({
|
||||
icon: tabletButtonIcon,
|
||||
activeIcon: tabletButtonActiveIcon,
|
||||
text: tabletButtonName,
|
||||
sortOrder: 0
|
||||
text: tabletButtonName
|
||||
});
|
||||
|
||||
Messages.subscribe(channelName);
|
||||
|
|
|
@ -562,9 +562,11 @@
|
|||
break;
|
||||
case 'disableHmdPreview':
|
||||
isHmdPreviewDisabled = Menu.isOptionChecked("Disable Preview");
|
||||
DesktopPreviewProvider.setPreviewDisabledReason("SECURE_SCREEN");
|
||||
Menu.setIsOptionChecked("Disable Preview", true);
|
||||
break;
|
||||
case 'maybeEnableHmdPreview':
|
||||
DesktopPreviewProvider.setPreviewDisabledReason("USER");
|
||||
Menu.setIsOptionChecked("Disable Preview", isHmdPreviewDisabled);
|
||||
break;
|
||||
case 'passphraseReset':
|
||||
|
@ -591,10 +593,16 @@
|
|||
getConnectionData(false);
|
||||
break;
|
||||
case 'enable_ChooseRecipientNearbyMode':
|
||||
Script.update.connect(updateOverlays);
|
||||
if (!isUpdateOverlaysWired) {
|
||||
Script.update.connect(updateOverlays);
|
||||
isUpdateOverlaysWired = true;
|
||||
}
|
||||
break;
|
||||
case 'disable_ChooseRecipientNearbyMode':
|
||||
Script.update.disconnect(updateOverlays);
|
||||
if (isUpdateOverlaysWired) {
|
||||
Script.update.disconnect(updateOverlays);
|
||||
isUpdateOverlaysWired = false;
|
||||
}
|
||||
removeOverlays();
|
||||
break;
|
||||
default:
|
||||
|
@ -635,7 +643,11 @@
|
|||
// -Called when the TabletScriptingInterface::screenChanged() signal is emitted. The "type" argument can be either the string
|
||||
// value of "Home", "Web", "Menu", "QML", or "Closed". The "url" argument is only valid for Web and QML.
|
||||
function onTabletScreenChanged(type, url) {
|
||||
onWalletScreen = (type === "QML" && url === WALLET_QML_SOURCE);
|
||||
var onWalletScreenNow = (type === "QML" && url === WALLET_QML_SOURCE);
|
||||
if (!onWalletScreenNow && onWalletScreen) {
|
||||
DesktopPreviewProvider.setPreviewDisabledReason("USER");
|
||||
}
|
||||
onWalletScreen = onWalletScreenNow;
|
||||
wireEventBridge(onWalletScreen);
|
||||
// Change button to active when window is first openend, false otherwise.
|
||||
if (button) {
|
||||
|
@ -675,14 +687,18 @@
|
|||
}
|
||||
}
|
||||
var isWired = false;
|
||||
var isUpdateOverlaysWired = false;
|
||||
function off() {
|
||||
if (isWired) { // It is not ok to disconnect these twice, hence guard.
|
||||
Users.usernameFromIDReply.disconnect(usernameFromIDReply);
|
||||
Script.update.disconnect(updateOverlays);
|
||||
Controller.mousePressEvent.disconnect(handleMouseEvent);
|
||||
Controller.mouseMoveEvent.disconnect(handleMouseMoveEvent);
|
||||
isWired = false;
|
||||
}
|
||||
if (isUpdateOverlaysWired) {
|
||||
Script.update.disconnect(updateOverlays);
|
||||
isUpdateOverlaysWired = false;
|
||||
}
|
||||
triggerMapping.disable(); // It's ok if we disable twice.
|
||||
triggerPressMapping.disable(); // see above
|
||||
removeOverlays();
|
||||
|
|
|
@ -370,20 +370,23 @@ Script.include("/~/system/libraries/Xform.js");
|
|||
};
|
||||
|
||||
this.isReady = function (controllerData) {
|
||||
if (this.notPointingAtEntity(controllerData)) {
|
||||
return makeRunningValues(false, [], []);
|
||||
}
|
||||
if (HMD.active) {
|
||||
if (this.notPointingAtEntity(controllerData)) {
|
||||
return makeRunningValues(false, [], []);
|
||||
}
|
||||
|
||||
this.distanceHolding = false;
|
||||
this.distanceRotating = false;
|
||||
this.distanceHolding = false;
|
||||
this.distanceRotating = false;
|
||||
|
||||
if (controllerData.triggerValues[this.hand] > TRIGGER_ON_VALUE) {
|
||||
this.prepareDistanceRotatingData(controllerData);
|
||||
return makeRunningValues(true, [], []);
|
||||
} else {
|
||||
this.destroyContextOverlay();
|
||||
return makeRunningValues(false, [], []);
|
||||
if (controllerData.triggerValues[this.hand] > TRIGGER_ON_VALUE) {
|
||||
this.prepareDistanceRotatingData(controllerData);
|
||||
return makeRunningValues(true, [], []);
|
||||
} else {
|
||||
this.destroyContextOverlay();
|
||||
return makeRunningValues(false, [], []);
|
||||
}
|
||||
}
|
||||
return makeRunningValues(false, [], []);
|
||||
};
|
||||
|
||||
this.run = function (controllerData) {
|
||||
|
|
|
@ -89,7 +89,7 @@
|
|||
|
||||
this.isReady = function (controllerData) {
|
||||
var otherModuleRunning = this.getOtherModule().running;
|
||||
if (!otherModuleRunning) {
|
||||
if (!otherModuleRunning && HMD.active) {
|
||||
if (this.processLaser(controllerData)) {
|
||||
this.running = true;
|
||||
return ControllerDispatcherUtils.makeRunningValues(true, [], []);
|
||||
|
|
|
@ -448,7 +448,7 @@ var toolBar = (function () {
|
|||
});
|
||||
|
||||
addButton("importEntitiesButton", "assets-01.svg", function() {
|
||||
Window.openFileChanged.connect(onFileOpenChanged);
|
||||
Window.browseChanged.connect(onFileOpenChanged);
|
||||
Window.browseAsync("Select Model to Import", "", "*.json");
|
||||
});
|
||||
|
||||
|
@ -1497,7 +1497,7 @@ function onFileOpenChanged(filename) {
|
|||
// disconnect the event, otherwise the requests will stack up
|
||||
try {
|
||||
// Not all calls to onFileOpenChanged() connect an event.
|
||||
Window.openFileChanged.disconnect(onFileOpenChanged);
|
||||
Window.browseChanged.disconnect(onFileOpenChanged);
|
||||
} catch (e) {
|
||||
// Ignore.
|
||||
}
|
||||
|
@ -1549,7 +1549,7 @@ function handeMenuEvent(menuItem) {
|
|||
}
|
||||
} else if (menuItem === "Import Entities" || menuItem === "Import Entities from URL") {
|
||||
if (menuItem === "Import Entities") {
|
||||
Window.openFileChanged.connect(onFileOpenChanged);
|
||||
Window.browseChanged.connect(onFileOpenChanged);
|
||||
Window.browseAsync("Select Model to Import", "", "*.json");
|
||||
} else {
|
||||
Window.promptTextChanged.connect(onPromptTextChanged);
|
||||
|
|
|
@ -43,7 +43,7 @@ var activeTimer = false; // used to cancel active timer if a user plays an amima
|
|||
var activeEmote = false; // to keep track of the currently playing emote
|
||||
|
||||
button = tablet.addButton({
|
||||
//icon: "icons/tablet-icons/emote.svg", // TODO - we need graphics for this
|
||||
icon: "icons/tablet-icons/EmoteAppIcon.svg",
|
||||
text: EMOTE_LABEL,
|
||||
sortOrder: EMOTE_APP_SORT_ORDER
|
||||
});
|
||||
|
|
|
@ -37,8 +37,7 @@
|
|||
tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system");
|
||||
button = tablet.addButton({
|
||||
icon: "icons/tablet-icons/goto-i.svg",
|
||||
text: buttonName,
|
||||
sortOrder: 8
|
||||
text: buttonName
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -41,8 +41,7 @@ if (Settings.getValue("HUDUIEnabled")) {
|
|||
button = tablet.addButton({
|
||||
icon: "icons/tablet-icons/goto-i.svg",
|
||||
activeIcon: "icons/tablet-icons/goto-a.svg",
|
||||
text: buttonName,
|
||||
sortOrder: 8
|
||||
text: buttonName
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -44,11 +44,11 @@
|
|||
input[type=button] {
|
||||
font-family: 'Raleway';
|
||||
font-weight: bold;
|
||||
font-size: 13px;
|
||||
font-size: 20px;
|
||||
text-transform: uppercase;
|
||||
vertical-align: top;
|
||||
height: 28px;
|
||||
min-width: 120px;
|
||||
height: 105px;
|
||||
min-width: 190px;
|
||||
padding: 0px 18px;
|
||||
margin-right: 6px;
|
||||
border-radius: 5px;
|
||||
|
@ -98,14 +98,14 @@
|
|||
</div>
|
||||
<div class="content">
|
||||
<p>Click an emotion to Emote:<p>
|
||||
<p><input type="button" class="emote-button white" value="Crying"></p>
|
||||
<p><input type="button" class="emote-button white" value="Surprised"></p>
|
||||
<p><input type="button" class="emote-button white" value="Dancing"></p>
|
||||
<p><input type="button" class="emote-button white" value="Cheering"></p>
|
||||
<p><input type="button" class="emote-button white" value="Waving"></p>
|
||||
<p><input type="button" class="emote-button white" value="Fall"></p>
|
||||
<p><input type="button" class="emote-button white" value="Pointing"></p>
|
||||
<p><input type="button" class="emote-button white" value="Clapping"></p>
|
||||
<p><input type="button" class="emote-button white" value="Crying">
|
||||
<input type="button" class="emote-button white" value="Surprised"></p>
|
||||
<p><input type="button" class="emote-button white" value="Dancing">
|
||||
<input type="button" class="emote-button white" value="Cheering"></p>
|
||||
<p><input type="button" class="emote-button white" value="Waving">
|
||||
<input type="button" class="emote-button white" value="Fall"></p>
|
||||
<p><input type="button" class="emote-button white" value="Pointing">
|
||||
<input type="button" class="emote-button white" value="Clapping"></p>
|
||||
</div>
|
||||
|
||||
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
|
||||
|
|
|
@ -539,8 +539,14 @@
|
|||
return startingUp;
|
||||
}
|
||||
|
||||
function onDomainConnectionRefused(reason) {
|
||||
createNotification("Connection refused: " + reason, NotificationType.CONNECTION_REFUSED);
|
||||
function onDomainConnectionRefused(reason, reasonCode) {
|
||||
// the "login error" reason means that the DS couldn't decrypt the username signature
|
||||
// since this eventually resolves itself for good actors we don't need to show a notification for it
|
||||
var LOGIN_ERROR_REASON_CODE = 2;
|
||||
|
||||
if (reasonCode != LOGIN_ERROR_REASON_CODE) {
|
||||
createNotification("Connection refused: " + reason, NotificationType.CONNECTION_REFUSED);
|
||||
}
|
||||
}
|
||||
|
||||
function onEditError(msg) {
|
||||
|
|
|
@ -10,8 +10,7 @@
|
|||
var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system");
|
||||
var button = tablet.addButton({
|
||||
icon: Script.resolvePath("assets/images/run.svg"),
|
||||
text: "RUN",
|
||||
sortOrder: 15
|
||||
text: "RUN"
|
||||
});
|
||||
|
||||
function onClicked() {
|
||||
|
|
|
@ -37,8 +37,7 @@
|
|||
var button = tablet.addButton({
|
||||
icon: "icons/tablet-icons/users-i.svg",
|
||||
activeIcon: "icons/tablet-icons/users-a.svg",
|
||||
text: "USERS",
|
||||
sortOrder: 11
|
||||
text: "USERS"
|
||||
});
|
||||
|
||||
var onUsersScreen = false;
|
||||
|
|
|
@ -31,8 +31,7 @@ var activeButton = tablet.addButton({
|
|||
icon: whiteIcon,
|
||||
activeIcon: blackIcon,
|
||||
text: APP_NAME,
|
||||
isActive: isActive,
|
||||
sortOrder: 11
|
||||
isActive: isActive
|
||||
});
|
||||
|
||||
if (isActive) {
|
||||
|
|
|
@ -32,8 +32,7 @@
|
|||
var button = tablet.addButton({
|
||||
icon: ICONS.icon,
|
||||
activeIcon: ICONS.activeIcon,
|
||||
text: TABLET_BUTTON_NAME,
|
||||
sortOrder: 1
|
||||
text: TABLET_BUTTON_NAME
|
||||
});
|
||||
|
||||
var hasEventBridge = false;
|
||||
|
|