mirror of
https://github.com/overte-org/overte.git
synced 2025-07-22 21:28:54 +02:00
Merge branch 'master' of github.com:highfidelity/hifi into groups
This commit is contained in:
commit
e3b4612283
37 changed files with 947 additions and 537 deletions
25
interface/resources/QtWebEngine/UIDelegates/AlertDialog.qml
Normal file
25
interface/resources/QtWebEngine/UIDelegates/AlertDialog.qml
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
import QtQuick 2.5
|
||||||
|
import QtQuick.Controls 1.4
|
||||||
|
import QtQuick.Layouts 1.0
|
||||||
|
|
||||||
|
import "../../qml/dialogs"
|
||||||
|
|
||||||
|
QtObject {
|
||||||
|
id: root
|
||||||
|
signal accepted;
|
||||||
|
property var text;
|
||||||
|
|
||||||
|
property var messageDialogBuilder: Component { MessageDialog { } }
|
||||||
|
|
||||||
|
function open() {
|
||||||
|
console.log("prompt text " + text)
|
||||||
|
var dialog = messageDialogBuilder.createObject(desktop, {
|
||||||
|
text: root.text
|
||||||
|
});
|
||||||
|
|
||||||
|
dialog.selected.connect(function(button){
|
||||||
|
accepted();
|
||||||
|
dialog.destroy();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,31 @@
|
||||||
|
import QtQuick 2.4
|
||||||
|
|
||||||
|
import QtQuick.Dialogs 1.1 as OriginalDialogs
|
||||||
|
|
||||||
|
import "../../qml/dialogs"
|
||||||
|
|
||||||
|
QtObject {
|
||||||
|
id: root
|
||||||
|
signal accepted;
|
||||||
|
signal rejected;
|
||||||
|
property var text;
|
||||||
|
|
||||||
|
property var messageDialogBuilder: Component { MessageDialog { } }
|
||||||
|
|
||||||
|
function open() {
|
||||||
|
var dialog = messageDialogBuilder.createObject(desktop, {
|
||||||
|
text: root.text,
|
||||||
|
icon: OriginalDialogs.StandardIcon.Question,
|
||||||
|
buttons: OriginalDialogs.StandardButton.Ok | OriginalDialogs.StandardButton.Cancel
|
||||||
|
});
|
||||||
|
|
||||||
|
dialog.selected.connect(function(button){
|
||||||
|
if (button === OriginalDialogs.StandardButton.Ok) {
|
||||||
|
accepted()
|
||||||
|
} else {
|
||||||
|
rejected();
|
||||||
|
}
|
||||||
|
dialog.destroy();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
39
interface/resources/QtWebEngine/UIDelegates/FilePicker.qml
Normal file
39
interface/resources/QtWebEngine/UIDelegates/FilePicker.qml
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
import QtQuick 2.4
|
||||||
|
import QtQuick.Dialogs 1.1
|
||||||
|
import QtQuick.Controls 1.4
|
||||||
|
|
||||||
|
import "../../qml/dialogs"
|
||||||
|
|
||||||
|
QtObject {
|
||||||
|
id: root
|
||||||
|
signal filesSelected(var fileList);
|
||||||
|
signal rejected();
|
||||||
|
property var text;
|
||||||
|
property url fileUrl;
|
||||||
|
property var fileUrls;
|
||||||
|
property url folder;
|
||||||
|
property var nameFilters;
|
||||||
|
property bool selectExisting;
|
||||||
|
property bool selectFolder;
|
||||||
|
property bool selectMultiple;
|
||||||
|
property string selectedNameFilter;
|
||||||
|
property string title;
|
||||||
|
|
||||||
|
property var fileDialogBuilder: Component { FileDialog { } }
|
||||||
|
|
||||||
|
function open() {
|
||||||
|
var foo = root;
|
||||||
|
var dialog = fileDialogBuilder.createObject(desktop, {
|
||||||
|
});
|
||||||
|
|
||||||
|
dialog.canceled.connect(function(){
|
||||||
|
root.filesSelected([]);
|
||||||
|
dialog.destroy();
|
||||||
|
});
|
||||||
|
|
||||||
|
dialog.selectedFile.connect(function(file){
|
||||||
|
root.filesSelected(fileDialogHelper.urlToList(file));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
68
interface/resources/QtWebEngine/UIDelegates/Menu.qml
Normal file
68
interface/resources/QtWebEngine/UIDelegates/Menu.qml
Normal file
|
@ -0,0 +1,68 @@
|
||||||
|
import QtQuick 2.5
|
||||||
|
import QtQuick.Controls 1.4 as Controls
|
||||||
|
|
||||||
|
import "../../qml/menus"
|
||||||
|
import "../../qml/controls-uit"
|
||||||
|
import "../../qml/styles-uit"
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: menu
|
||||||
|
HifiConstants { id: hifi }
|
||||||
|
signal done()
|
||||||
|
implicitHeight: column.height
|
||||||
|
implicitWidth: column.width
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: background
|
||||||
|
anchors {
|
||||||
|
fill: parent
|
||||||
|
margins: -16
|
||||||
|
}
|
||||||
|
radius: hifi.dimensions.borderRadius
|
||||||
|
border.width: hifi.dimensions.borderWidth
|
||||||
|
border.color: hifi.colors.lightGrayText80
|
||||||
|
color: hifi.colors.faintGray80
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
id: closer
|
||||||
|
width: 8192
|
||||||
|
height: 8192
|
||||||
|
x: -4096
|
||||||
|
y: -4096
|
||||||
|
propagateComposedEvents: true
|
||||||
|
acceptedButtons: "AllButtons"
|
||||||
|
onClicked: {
|
||||||
|
menu.done();
|
||||||
|
mouse.accepted = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Column {
|
||||||
|
id: column
|
||||||
|
}
|
||||||
|
|
||||||
|
function popup() {
|
||||||
|
var position = Reticle.position;
|
||||||
|
var localPosition = menu.parent.mapFromItem(desktop, position.x, position.y);
|
||||||
|
x = localPosition.x
|
||||||
|
y = localPosition.y
|
||||||
|
console.log("Popup at " + x + " x " + y)
|
||||||
|
var moveChildren = [];
|
||||||
|
for (var i = 0; i < children.length; ++i) {
|
||||||
|
var child = children[i];
|
||||||
|
if (child.objectName !== "MenuItem") {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
moveChildren.push(child);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < moveChildren.length; ++i) {
|
||||||
|
child = moveChildren[i];
|
||||||
|
child.parent = column;
|
||||||
|
child.menu = menu
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
39
interface/resources/QtWebEngine/UIDelegates/MenuItem.qml
Normal file
39
interface/resources/QtWebEngine/UIDelegates/MenuItem.qml
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
|
||||||
|
import QtQuick 2.5
|
||||||
|
import QtQuick.Controls 1.4 as Controls
|
||||||
|
|
||||||
|
import "../../qml/controls-uit"
|
||||||
|
import "../../qml/styles-uit"
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: root
|
||||||
|
objectName: "MenuItem"
|
||||||
|
|
||||||
|
property alias text: label.text
|
||||||
|
property var menu;
|
||||||
|
property var shortcut;
|
||||||
|
signal triggered();
|
||||||
|
|
||||||
|
HifiConstants { id: hifi }
|
||||||
|
|
||||||
|
implicitHeight: 2 * label.implicitHeight
|
||||||
|
implicitWidth: 2 * hifi.dimensions.menuPadding.x + label.width
|
||||||
|
|
||||||
|
RalewaySemiBold {
|
||||||
|
id: label
|
||||||
|
size: hifi.fontSizes.rootMenu
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.leftMargin: hifi.dimensions.menuPadding.x
|
||||||
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
color: enabled ? hifi.colors.baseGrayShadow : hifi.colors.baseGrayShadow50
|
||||||
|
enabled: root.enabled
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
anchors.fill: parent
|
||||||
|
onClicked: {
|
||||||
|
root.triggered();
|
||||||
|
menu.done();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,6 @@
|
||||||
|
import QtQuick 2.5
|
||||||
|
|
||||||
|
Item {
|
||||||
|
width: 100
|
||||||
|
height: 20
|
||||||
|
}
|
|
@ -0,0 +1,4 @@
|
||||||
|
import QtQuick 2.5
|
||||||
|
|
||||||
|
Item {
|
||||||
|
}
|
42
interface/resources/QtWebEngine/UIDelegates/PromptDialog.qml
Normal file
42
interface/resources/QtWebEngine/UIDelegates/PromptDialog.qml
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
import QtQuick 2.5
|
||||||
|
import QtQuick.Controls 1.4
|
||||||
|
import QtQuick.Layouts 1.0
|
||||||
|
|
||||||
|
import "../../qml/controls-uit"
|
||||||
|
import "../../qml/styles-uit"
|
||||||
|
import "../../qml/dialogs"
|
||||||
|
|
||||||
|
QtObject {
|
||||||
|
id: root
|
||||||
|
signal input(string text);
|
||||||
|
signal accepted;
|
||||||
|
signal rejected;
|
||||||
|
signal closing(var close)
|
||||||
|
|
||||||
|
property var titleWidth;
|
||||||
|
property var text;
|
||||||
|
property var prompt;
|
||||||
|
|
||||||
|
property var inputDialogBuilder: Component { QueryDialog { } }
|
||||||
|
|
||||||
|
function open() {
|
||||||
|
console.log("prompt text " + text)
|
||||||
|
console.log("prompt prompt " + prompt)
|
||||||
|
|
||||||
|
var dialog = inputDialogBuilder.createObject(desktop, {
|
||||||
|
label: root.text,
|
||||||
|
current: root.prompt
|
||||||
|
});
|
||||||
|
|
||||||
|
dialog.selected.connect(function(result){
|
||||||
|
root.input(dialog.result)
|
||||||
|
root.accepted();
|
||||||
|
dialog.destroy();
|
||||||
|
});
|
||||||
|
|
||||||
|
dialog.canceled.connect(function(){
|
||||||
|
root.rejected();
|
||||||
|
dialog.destroy();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
8
interface/resources/QtWebEngine/UIDelegates/qmldir
Normal file
8
interface/resources/QtWebEngine/UIDelegates/qmldir
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
module QtWebEngine.UIDelegates
|
||||||
|
AlertDialog 1.0 AlertDialog.qml
|
||||||
|
ConfirmDialog 1.0 ConfirmDialog.qml
|
||||||
|
FilePicker 1.0 FilePicker.qml
|
||||||
|
PromptDialog 1.0 PromptDialog.qml
|
||||||
|
Menu 1.0 Menu.qml
|
||||||
|
MenuItem 1.0 MenuItem.qml
|
||||||
|
MenuSeparator 1.0 MenuSeparator.qml
|
|
@ -1,4 +1,4 @@
|
||||||
import QtQuick 2.3
|
import QtQuick 2.5
|
||||||
import QtQuick.Controls 1.2
|
import QtQuick.Controls 1.2
|
||||||
import QtWebEngine 1.1
|
import QtWebEngine 1.1
|
||||||
|
|
||||||
|
@ -16,6 +16,8 @@ ScrollingWindow {
|
||||||
destroyOnHidden: true
|
destroyOnHidden: true
|
||||||
width: 800
|
width: 800
|
||||||
height: 600
|
height: 600
|
||||||
|
property variant permissionsBar: {'securityOrigin':'none','feature':'none'}
|
||||||
|
property alias url: webview.url
|
||||||
property alias webView: webview
|
property alias webView: webview
|
||||||
x: 100
|
x: 100
|
||||||
y: 100
|
y: 100
|
||||||
|
@ -32,6 +34,19 @@ ScrollingWindow {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function showPermissionsBar(){
|
||||||
|
permissionsContainer.visible=true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function hidePermissionsBar(){
|
||||||
|
permissionsContainer.visible=false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function allowPermissions(){
|
||||||
|
webview.grantFeaturePermission(permissionsBar.securityOrigin, permissionsBar.feature, true);
|
||||||
|
hidePermissionsBar();
|
||||||
|
}
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
id:item
|
id:item
|
||||||
width: pane.contentWidth
|
width: pane.contentWidth
|
||||||
|
@ -70,6 +85,7 @@ ScrollingWindow {
|
||||||
size: 48
|
size: 48
|
||||||
MouseArea { anchors.fill: parent; onClicked: webview.goForward() }
|
MouseArea { anchors.fill: parent; onClicked: webview.goForward() }
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
|
@ -116,6 +132,7 @@ ScrollingWindow {
|
||||||
if (text.indexOf("http") != 0) {
|
if (text.indexOf("http") != 0) {
|
||||||
text = "http://" + text
|
text = "http://" + text
|
||||||
}
|
}
|
||||||
|
root.hidePermissionsBar();
|
||||||
webview.url = text
|
webview.url = text
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -123,14 +140,76 @@ ScrollingWindow {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id:permissionsContainer
|
||||||
|
visible:false
|
||||||
|
color: "#000000"
|
||||||
|
width: parent.width
|
||||||
|
anchors.top: buttons.bottom
|
||||||
|
height:40
|
||||||
|
z:100
|
||||||
|
gradient: Gradient {
|
||||||
|
GradientStop { position: 0.0; color: "black" }
|
||||||
|
GradientStop { position: 1.0; color: "grey" }
|
||||||
|
}
|
||||||
|
|
||||||
|
RalewayLight {
|
||||||
|
id: permissionsInfo
|
||||||
|
anchors.right:permissionsRow.left
|
||||||
|
anchors.rightMargin: 32
|
||||||
|
anchors.topMargin:8
|
||||||
|
anchors.top:parent.top
|
||||||
|
text: "This site wants to use your microphone/camera"
|
||||||
|
size: 18
|
||||||
|
color: hifi.colors.white
|
||||||
|
}
|
||||||
|
|
||||||
|
Row {
|
||||||
|
id: permissionsRow
|
||||||
|
spacing: 4
|
||||||
|
anchors.top:parent.top
|
||||||
|
anchors.topMargin: 8
|
||||||
|
anchors.right: parent.right
|
||||||
|
visible: true
|
||||||
|
z:101
|
||||||
|
|
||||||
|
Button {
|
||||||
|
id:allow
|
||||||
|
text: "Allow"
|
||||||
|
color: hifi.buttons.blue
|
||||||
|
colorScheme: root.colorScheme
|
||||||
|
width: 120
|
||||||
|
enabled: true
|
||||||
|
onClicked: root.allowPermissions();
|
||||||
|
z:101
|
||||||
|
}
|
||||||
|
|
||||||
|
Button {
|
||||||
|
id:block
|
||||||
|
text: "Block"
|
||||||
|
color: hifi.buttons.red
|
||||||
|
colorScheme: root.colorScheme
|
||||||
|
width: 120
|
||||||
|
enabled: true
|
||||||
|
onClicked: root.hidePermissionsBar();
|
||||||
|
z:101
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
WebEngineView {
|
WebEngineView {
|
||||||
id: webview
|
id: webview
|
||||||
url: "http://highfidelity.com"
|
url: "https://highfidelity.com"
|
||||||
anchors.top: buttons.bottom
|
anchors.top: buttons.bottom
|
||||||
anchors.topMargin: 8
|
anchors.topMargin: 8
|
||||||
anchors.bottom: parent.bottom
|
anchors.bottom: parent.bottom
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
|
onFeaturePermissionRequested: {
|
||||||
|
permissionsBar.securityOrigin = securityOrigin;
|
||||||
|
permissionsBar.feature = feature;
|
||||||
|
root.showPermissionsBar();
|
||||||
|
}
|
||||||
onLoadingChanged: {
|
onLoadingChanged: {
|
||||||
if (loadRequest.status === WebEngineView.LoadSucceededStatus) {
|
if (loadRequest.status === WebEngineView.LoadSucceededStatus) {
|
||||||
addressBar.text = loadRequest.url
|
addressBar.text = loadRequest.url
|
||||||
|
@ -139,9 +218,12 @@ ScrollingWindow {
|
||||||
onIconChanged: {
|
onIconChanged: {
|
||||||
console.log("New icon: " + icon)
|
console.log("New icon: " + icon)
|
||||||
}
|
}
|
||||||
|
onNewViewRequested:{
|
||||||
|
var component = Qt.createComponent("Browser.qml");
|
||||||
|
var newWindow = component.createObject(desktop);
|
||||||
|
request.openIn(newWindow.webView)
|
||||||
|
}
|
||||||
//profile: desktop.browserProfile
|
//profile: desktop.browserProfile
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // item
|
} // item
|
||||||
|
@ -157,4 +239,4 @@ ScrollingWindow {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} // dialog
|
} // dialog
|
|
@ -15,7 +15,7 @@ WebEngineView {
|
||||||
id: root
|
id: root
|
||||||
property var newUrl;
|
property var newUrl;
|
||||||
|
|
||||||
profile.httpUserAgent: "Mozilla/5.0 Chrome (HighFidelityInterface)"
|
profile.httpUserAgent: "Mozilla/5.0 Chrome/38.0 (HighFidelityInterface)"
|
||||||
|
|
||||||
Component.onCompleted: {
|
Component.onCompleted: {
|
||||||
console.log("Connecting JS messaging to Hifi Logging")
|
console.log("Connecting JS messaging to Hifi Logging")
|
||||||
|
@ -48,10 +48,6 @@ WebEngineView {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onFeaturePermissionRequested: {
|
|
||||||
grantFeaturePermission(securityOrigin, feature, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
onLoadingChanged: {
|
onLoadingChanged: {
|
||||||
// Required to support clicking on "hifi://" links
|
// Required to support clicking on "hifi://" links
|
||||||
if (WebEngineView.LoadStartedStatus == loadRequest.status) {
|
if (WebEngineView.LoadStartedStatus == loadRequest.status) {
|
||||||
|
|
|
@ -720,7 +720,9 @@ void MyAvatar::saveData() {
|
||||||
_fullAvatarURLFromPreferences.toString());
|
_fullAvatarURLFromPreferences.toString());
|
||||||
|
|
||||||
settings.setValue("fullAvatarModelName", _fullAvatarModelName);
|
settings.setValue("fullAvatarModelName", _fullAvatarModelName);
|
||||||
settings.setValue("animGraphURL", _animGraphUrl);
|
|
||||||
|
QUrl animGraphUrl = _prefOverrideAnimGraphUrl.get();
|
||||||
|
settings.setValue("animGraphURL", animGraphUrl);
|
||||||
|
|
||||||
settings.beginWriteArray("attachmentData");
|
settings.beginWriteArray("attachmentData");
|
||||||
for (int i = 0; i < _attachmentData.size(); i++) {
|
for (int i = 0; i < _attachmentData.size(); i++) {
|
||||||
|
@ -833,7 +835,7 @@ void MyAvatar::loadData() {
|
||||||
_targetScale = loadSetting(settings, "scale", 1.0f);
|
_targetScale = loadSetting(settings, "scale", 1.0f);
|
||||||
setScale(glm::vec3(_targetScale));
|
setScale(glm::vec3(_targetScale));
|
||||||
|
|
||||||
_animGraphUrl = settings.value("animGraphURL", "").toString();
|
_prefOverrideAnimGraphUrl.set(QUrl(settings.value("animGraphURL", "").toString()));
|
||||||
_fullAvatarURLFromPreferences = settings.value("fullAvatarURL", AvatarData::defaultFullAvatarModelUrl()).toUrl();
|
_fullAvatarURLFromPreferences = settings.value("fullAvatarURL", AvatarData::defaultFullAvatarModelUrl()).toUrl();
|
||||||
_fullAvatarModelName = settings.value("fullAvatarModelName", DEFAULT_FULL_AVATAR_MODEL_NAME).toString();
|
_fullAvatarModelName = settings.value("fullAvatarModelName", DEFAULT_FULL_AVATAR_MODEL_NAME).toString();
|
||||||
|
|
||||||
|
@ -1412,21 +1414,55 @@ void MyAvatar::initHeadBones() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QUrl MyAvatar::getAnimGraphOverrideUrl() const {
|
||||||
|
return _prefOverrideAnimGraphUrl.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MyAvatar::setAnimGraphOverrideUrl(QUrl value) {
|
||||||
|
_prefOverrideAnimGraphUrl.set(value);
|
||||||
|
if (!value.isEmpty()) {
|
||||||
|
setAnimGraphUrl(value);
|
||||||
|
} else {
|
||||||
|
initAnimGraph();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QUrl MyAvatar::getAnimGraphUrl() const {
|
||||||
|
return _currentAnimGraphUrl.get();
|
||||||
|
}
|
||||||
|
|
||||||
void MyAvatar::setAnimGraphUrl(const QUrl& url) {
|
void MyAvatar::setAnimGraphUrl(const QUrl& url) {
|
||||||
if (_animGraphUrl == url) {
|
|
||||||
|
if (QThread::currentThread() != thread()) {
|
||||||
|
QMetaObject::invokeMethod(this, "setAnimGraphUrl", Q_ARG(QUrl, url));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_currentAnimGraphUrl.get() == url) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
destroyAnimGraph();
|
destroyAnimGraph();
|
||||||
_skeletonModel->reset(); // Why is this necessary? Without this, we crash in the next render.
|
_skeletonModel->reset(); // Why is this necessary? Without this, we crash in the next render.
|
||||||
_animGraphUrl = url;
|
|
||||||
initAnimGraph();
|
_currentAnimGraphUrl.set(url);
|
||||||
|
_rig->initAnimGraph(url);
|
||||||
|
|
||||||
|
_bodySensorMatrix = deriveBodyFromHMDSensor(); // Based on current cached HMD position/rotation..
|
||||||
|
updateSensorToWorldMatrix(); // Uses updated position/orientation and _bodySensorMatrix changes
|
||||||
}
|
}
|
||||||
|
|
||||||
void MyAvatar::initAnimGraph() {
|
void MyAvatar::initAnimGraph() {
|
||||||
auto graphUrl =_animGraphUrl.isEmpty() ?
|
QUrl graphUrl;
|
||||||
QUrl::fromLocalFile(PathUtils::resourcesPath() + "avatar/avatar-animation.json") :
|
if (!_prefOverrideAnimGraphUrl.get().isEmpty()) {
|
||||||
QUrl(_animGraphUrl);
|
graphUrl = _prefOverrideAnimGraphUrl.get();
|
||||||
|
} else if (!_fstAnimGraphOverrideUrl.isEmpty()) {
|
||||||
|
graphUrl = _fstAnimGraphOverrideUrl;
|
||||||
|
} else {
|
||||||
|
graphUrl = QUrl::fromLocalFile(PathUtils::resourcesPath() + "avatar/avatar-animation.json");
|
||||||
|
}
|
||||||
|
|
||||||
_rig->initAnimGraph(graphUrl);
|
_rig->initAnimGraph(graphUrl);
|
||||||
|
_currentAnimGraphUrl.set(graphUrl);
|
||||||
|
|
||||||
_bodySensorMatrix = deriveBodyFromHMDSensor(); // Based on current cached HMD position/rotation..
|
_bodySensorMatrix = deriveBodyFromHMDSensor(); // Based on current cached HMD position/rotation..
|
||||||
updateSensorToWorldMatrix(); // Uses updated position/orientation and _bodySensorMatrix changes
|
updateSensorToWorldMatrix(); // Uses updated position/orientation and _bodySensorMatrix changes
|
||||||
|
@ -1444,6 +1480,7 @@ void MyAvatar::postUpdate(float deltaTime) {
|
||||||
if (_skeletonModel->initWhenReady(scene)) {
|
if (_skeletonModel->initWhenReady(scene)) {
|
||||||
initHeadBones();
|
initHeadBones();
|
||||||
_skeletonModel->setCauterizeBoneSet(_headBoneSet);
|
_skeletonModel->setCauterizeBoneSet(_headBoneSet);
|
||||||
|
_fstAnimGraphOverrideUrl = _skeletonModel->getGeometry()->getAnimGraphOverrideUrl();
|
||||||
initAnimGraph();
|
initAnimGraph();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -292,8 +292,6 @@ public slots:
|
||||||
|
|
||||||
Q_INVOKABLE void updateMotionBehaviorFromMenu();
|
Q_INVOKABLE void updateMotionBehaviorFromMenu();
|
||||||
|
|
||||||
Q_INVOKABLE QUrl getAnimGraphUrl() const { return _animGraphUrl; }
|
|
||||||
|
|
||||||
void setEnableDebugDrawDefaultPose(bool isEnabled);
|
void setEnableDebugDrawDefaultPose(bool isEnabled);
|
||||||
void setEnableDebugDrawAnimPose(bool isEnabled);
|
void setEnableDebugDrawAnimPose(bool isEnabled);
|
||||||
void setEnableDebugDrawPosition(bool isEnabled);
|
void setEnableDebugDrawPosition(bool isEnabled);
|
||||||
|
@ -303,7 +301,11 @@ public slots:
|
||||||
void setEnableMeshVisible(bool isEnabled);
|
void setEnableMeshVisible(bool isEnabled);
|
||||||
void setUseAnimPreAndPostRotations(bool isEnabled);
|
void setUseAnimPreAndPostRotations(bool isEnabled);
|
||||||
void setEnableInverseKinematics(bool isEnabled);
|
void setEnableInverseKinematics(bool isEnabled);
|
||||||
Q_INVOKABLE void setAnimGraphUrl(const QUrl& url);
|
|
||||||
|
QUrl getAnimGraphOverrideUrl() const; // thread-safe
|
||||||
|
void setAnimGraphOverrideUrl(QUrl value); // thread-safe
|
||||||
|
QUrl getAnimGraphUrl() const; // thread-safe
|
||||||
|
void setAnimGraphUrl(const QUrl& url); // thread-safe
|
||||||
|
|
||||||
glm::vec3 getPositionForAudio();
|
glm::vec3 getPositionForAudio();
|
||||||
glm::quat getOrientationForAudio();
|
glm::quat getOrientationForAudio();
|
||||||
|
@ -407,7 +409,9 @@ private:
|
||||||
// Avatar Preferences
|
// Avatar Preferences
|
||||||
QUrl _fullAvatarURLFromPreferences;
|
QUrl _fullAvatarURLFromPreferences;
|
||||||
QString _fullAvatarModelName;
|
QString _fullAvatarModelName;
|
||||||
QUrl _animGraphUrl {""};
|
ThreadSafeValueCache<QUrl> _currentAnimGraphUrl;
|
||||||
|
ThreadSafeValueCache<QUrl> _prefOverrideAnimGraphUrl;
|
||||||
|
QUrl _fstAnimGraphOverrideUrl;
|
||||||
bool _useSnapTurn { true };
|
bool _useSnapTurn { true };
|
||||||
bool _clearOverlayWhenMoving { true };
|
bool _clearOverlayWhenMoving { true };
|
||||||
|
|
||||||
|
|
|
@ -161,8 +161,8 @@ void setupPreferences() {
|
||||||
preferences->addPreference(preference);
|
preferences->addPreference(preference);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
auto getter = [=]()->QString { return myAvatar->getAnimGraphUrl().toString(); };
|
auto getter = [=]()->QString { return myAvatar->getAnimGraphOverrideUrl().toString(); };
|
||||||
auto setter = [=](const QString& value) { myAvatar->setAnimGraphUrl(value); };
|
auto setter = [=](const QString& value) { myAvatar->setAnimGraphOverrideUrl(QUrl(value)); };
|
||||||
auto preference = new EditPreference(AVATAR_TUNING, "Avatar animation JSON", getter, setter);
|
auto preference = new EditPreference(AVATAR_TUNING, "Avatar animation JSON", getter, setter);
|
||||||
preference->setPlaceholderText("default");
|
preference->setPlaceholderText("default");
|
||||||
preferences->addPreference(preference);
|
preferences->addPreference(preference);
|
||||||
|
|
|
@ -234,6 +234,29 @@ bool Overlays::editOverlay(unsigned int id, const QVariant& properties) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Overlays::editOverlays(const QVariant& propertiesById) {
|
||||||
|
QVariantMap map = propertiesById.toMap();
|
||||||
|
bool success = true;
|
||||||
|
QWriteLocker lock(&_lock);
|
||||||
|
for (const auto& key : map.keys()) {
|
||||||
|
bool convertSuccess;
|
||||||
|
unsigned int id = key.toUInt(&convertSuccess);
|
||||||
|
if (!convertSuccess) {
|
||||||
|
success = false;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
Overlay::Pointer thisOverlay = getOverlay(id);
|
||||||
|
if (!thisOverlay) {
|
||||||
|
success = false;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
QVariant properties = map[key];
|
||||||
|
thisOverlay->setProperties(properties.toMap());
|
||||||
|
}
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
||||||
void Overlays::deleteOverlay(unsigned int id) {
|
void Overlays::deleteOverlay(unsigned int id) {
|
||||||
Overlay::Pointer overlayToDelete;
|
Overlay::Pointer overlayToDelete;
|
||||||
|
|
||||||
|
|
|
@ -89,6 +89,10 @@ public slots:
|
||||||
/// successful edit, if the input id is for an unknown overlay this function will have no effect
|
/// successful edit, if the input id is for an unknown overlay this function will have no effect
|
||||||
bool editOverlay(unsigned int id, const QVariant& properties);
|
bool editOverlay(unsigned int id, const QVariant& properties);
|
||||||
|
|
||||||
|
/// edits an overlay updating only the included properties, will return the identified OverlayID in case of
|
||||||
|
/// successful edit, if the input id is for an unknown overlay this function will have no effect
|
||||||
|
bool editOverlays(const QVariant& propertiesById);
|
||||||
|
|
||||||
/// deletes a particle
|
/// deletes a particle
|
||||||
void deleteOverlay(unsigned int id);
|
void deleteOverlay(unsigned int id);
|
||||||
|
|
||||||
|
|
|
@ -428,7 +428,7 @@ const AnimPoseVec& AnimInverseKinematics::overlay(const AnimVariantMap& animVars
|
||||||
// shift hips according to the _hipsOffset from the previous frame
|
// shift hips according to the _hipsOffset from the previous frame
|
||||||
float offsetLength = glm::length(_hipsOffset);
|
float offsetLength = glm::length(_hipsOffset);
|
||||||
const float MIN_HIPS_OFFSET_LENGTH = 0.03f;
|
const float MIN_HIPS_OFFSET_LENGTH = 0.03f;
|
||||||
if (offsetLength > MIN_HIPS_OFFSET_LENGTH) {
|
if (offsetLength > MIN_HIPS_OFFSET_LENGTH && _hipsIndex >= 0) {
|
||||||
// but only if offset is long enough
|
// but only if offset is long enough
|
||||||
float scaleFactor = ((offsetLength - MIN_HIPS_OFFSET_LENGTH) / offsetLength);
|
float scaleFactor = ((offsetLength - MIN_HIPS_OFFSET_LENGTH) / offsetLength);
|
||||||
if (_hipsParentIndex == -1) {
|
if (_hipsParentIndex == -1) {
|
||||||
|
@ -861,7 +861,11 @@ void AnimInverseKinematics::setSkeletonInternal(AnimSkeleton::ConstPointer skele
|
||||||
_hipsIndex = _skeleton->nameToJointIndex("Hips");
|
_hipsIndex = _skeleton->nameToJointIndex("Hips");
|
||||||
|
|
||||||
// also cache the _hipsParentIndex for later
|
// also cache the _hipsParentIndex for later
|
||||||
_hipsParentIndex = _skeleton->getParentIndex(_hipsIndex);
|
if (_hipsIndex >= 0) {
|
||||||
|
_hipsParentIndex = _skeleton->getParentIndex(_hipsIndex);
|
||||||
|
} else {
|
||||||
|
_hipsParentIndex = -1;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
clearConstraints();
|
clearConstraints();
|
||||||
_headIndex = -1;
|
_headIndex = -1;
|
||||||
|
|
|
@ -203,8 +203,8 @@ bool AudioInjector::injectLocally() {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!success) {
|
if (!success) {
|
||||||
// we never started so we are finished, call our stop method
|
// we never started so we are finished with local injection
|
||||||
stop();
|
finishLocalInjection();
|
||||||
}
|
}
|
||||||
|
|
||||||
return success;
|
return success;
|
||||||
|
@ -217,8 +217,15 @@ static const int64_t NEXT_FRAME_DELTA_IMMEDIATELY = 0;
|
||||||
qint64 writeStringToStream(const QString& string, QDataStream& stream) {
|
qint64 writeStringToStream(const QString& string, QDataStream& stream) {
|
||||||
QByteArray data = string.toUtf8();
|
QByteArray data = string.toUtf8();
|
||||||
uint32_t length = data.length();
|
uint32_t length = data.length();
|
||||||
stream << static_cast<quint32>(length);
|
if (length == 0) {
|
||||||
stream << data;
|
stream << static_cast<quint32>(length);
|
||||||
|
} else {
|
||||||
|
// http://doc.qt.io/qt-5/datastreamformat.html
|
||||||
|
// QDataStream << QByteArray -
|
||||||
|
// If the byte array is null : 0xFFFFFFFF (quint32)
|
||||||
|
// Otherwise : the array size(quint32) followed by the array bytes, i.e.size bytes
|
||||||
|
stream << data;
|
||||||
|
}
|
||||||
return length + sizeof(uint32_t);
|
return length + sizeof(uint32_t);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -232,7 +239,7 @@ int64_t AudioInjector::injectNextFrame() {
|
||||||
static int positionOptionOffset = -1;
|
static int positionOptionOffset = -1;
|
||||||
static int volumeOptionOffset = -1;
|
static int volumeOptionOffset = -1;
|
||||||
static int audioDataOffset = -1;
|
static int audioDataOffset = -1;
|
||||||
|
|
||||||
if (!_currentPacket) {
|
if (!_currentPacket) {
|
||||||
if (_currentSendOffset < 0 ||
|
if (_currentSendOffset < 0 ||
|
||||||
_currentSendOffset >= _audioData.size()) {
|
_currentSendOffset >= _audioData.size()) {
|
||||||
|
@ -270,7 +277,7 @@ int64_t AudioInjector::injectNextFrame() {
|
||||||
|
|
||||||
// current injectors don't use codecs, so pack in the unknown codec name
|
// current injectors don't use codecs, so pack in the unknown codec name
|
||||||
QString noCodecForInjectors("");
|
QString noCodecForInjectors("");
|
||||||
writeStringToStream(noCodecForInjectors, audioPacketStream);
|
writeStringToStream(noCodecForInjectors, audioPacketStream);
|
||||||
|
|
||||||
// pack stream identifier (a generated UUID)
|
// pack stream identifier (a generated UUID)
|
||||||
audioPacketStream << QUuid::createUuid();
|
audioPacketStream << QUuid::createUuid();
|
||||||
|
@ -301,7 +308,6 @@ int64_t AudioInjector::injectNextFrame() {
|
||||||
volumeOptionOffset = _currentPacket->pos();
|
volumeOptionOffset = _currentPacket->pos();
|
||||||
quint8 volume = MAX_INJECTOR_VOLUME;
|
quint8 volume = MAX_INJECTOR_VOLUME;
|
||||||
audioPacketStream << volume;
|
audioPacketStream << volume;
|
||||||
|
|
||||||
audioPacketStream << _options.ignorePenumbra;
|
audioPacketStream << _options.ignorePenumbra;
|
||||||
|
|
||||||
audioDataOffset = _currentPacket->pos();
|
audioDataOffset = _currentPacket->pos();
|
||||||
|
@ -312,7 +318,6 @@ int64_t AudioInjector::injectNextFrame() {
|
||||||
return NEXT_FRAME_DELTA_ERROR_OR_FINISHED;
|
return NEXT_FRAME_DELTA_ERROR_OR_FINISHED;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!_frameTimer->isValid()) {
|
if (!_frameTimer->isValid()) {
|
||||||
// in the case where we have been restarted, the frame timer will be invalid and we need to start it back over here
|
// in the case where we have been restarted, the frame timer will be invalid and we need to start it back over here
|
||||||
_frameTimer->restart();
|
_frameTimer->restart();
|
||||||
|
@ -418,7 +423,7 @@ void AudioInjector::triggerDeleteAfterFinish() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_state == AudioInjectorState::Finished) {
|
if (stateHas(AudioInjectorState::Finished)) {
|
||||||
stopAndDeleteLater();
|
stopAndDeleteLater();
|
||||||
} else {
|
} else {
|
||||||
_state |= AudioInjectorState::PendingDelete;
|
_state |= AudioInjectorState::PendingDelete;
|
||||||
|
@ -484,23 +489,17 @@ AudioInjector* AudioInjector::playSound(const QByteArray& buffer, const AudioInj
|
||||||
// setup parameters required for injection
|
// setup parameters required for injection
|
||||||
injector->setupInjection();
|
injector->setupInjection();
|
||||||
|
|
||||||
// we always inject locally
|
// we always inject locally, except when there is no localInterface
|
||||||
//
|
injector->injectLocally();
|
||||||
if (!injector->injectLocally()) {
|
|
||||||
// failed, so don't bother sending to server
|
|
||||||
qDebug() << "AudioInjector::playSound failed to inject locally";
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
// if localOnly, we are done, just return injector.
|
// if localOnly, we are done, just return injector.
|
||||||
if (options.localOnly) {
|
if (!options.localOnly) {
|
||||||
return injector;
|
|
||||||
}
|
|
||||||
|
|
||||||
// send off to server for everyone else
|
// send off to server for everyone else
|
||||||
if (!injectorManager->threadInjector(injector)) {
|
if (!injectorManager->threadInjector(injector)) {
|
||||||
// we failed to thread the new injector (we are at the max number of injector threads)
|
// we failed to thread the new injector (we are at the max number of injector threads)
|
||||||
qDebug() << "AudioInjector::playSound failed to thread injector";
|
qDebug() << "AudioInjector::playSound failed to thread injector";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return injector;
|
return injector;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -256,6 +256,8 @@ QByteArray AvatarData::toByteArray(bool cullSmallChanges, bool sendAll) {
|
||||||
destinationBuffer += _headData->_blendshapeCoefficients.size() * sizeof(float);
|
destinationBuffer += _headData->_blendshapeCoefficients.size() * sizeof(float);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QReadLocker readLock(&_jointDataLock);
|
||||||
|
|
||||||
// joint rotation data
|
// joint rotation data
|
||||||
*destinationBuffer++ = _jointData.size();
|
*destinationBuffer++ = _jointData.size();
|
||||||
unsigned char* validityPosition = destinationBuffer;
|
unsigned char* validityPosition = destinationBuffer;
|
||||||
|
@ -378,6 +380,7 @@ QByteArray AvatarData::toByteArray(bool cullSmallChanges, bool sendAll) {
|
||||||
|
|
||||||
void AvatarData::doneEncoding(bool cullSmallChanges) {
|
void AvatarData::doneEncoding(bool cullSmallChanges) {
|
||||||
// The server has finished sending this version of the joint-data to other nodes. Update _lastSentJointData.
|
// The server has finished sending this version of the joint-data to other nodes. Update _lastSentJointData.
|
||||||
|
QReadLocker readLock(&_jointDataLock);
|
||||||
_lastSentJointData.resize(_jointData.size());
|
_lastSentJointData.resize(_jointData.size());
|
||||||
for (int i = 0; i < _jointData.size(); i ++) {
|
for (int i = 0; i < _jointData.size(); i ++) {
|
||||||
const JointData& data = _jointData[ i ];
|
const JointData& data = _jointData[ i ];
|
||||||
|
@ -551,8 +554,6 @@ int AvatarData::parseDataFromBuffer(const QByteArray& buffer) {
|
||||||
PACKET_READ_CHECK(NumJoints, sizeof(uint8_t));
|
PACKET_READ_CHECK(NumJoints, sizeof(uint8_t));
|
||||||
int numJoints = *sourceBuffer++;
|
int numJoints = *sourceBuffer++;
|
||||||
|
|
||||||
_jointData.resize(numJoints);
|
|
||||||
|
|
||||||
const int bytesOfValidity = (int)ceil((float)numJoints / (float)BITS_IN_BYTE);
|
const int bytesOfValidity = (int)ceil((float)numJoints / (float)BITS_IN_BYTE);
|
||||||
PACKET_READ_CHECK(JointRotationValidityBits, bytesOfValidity);
|
PACKET_READ_CHECK(JointRotationValidityBits, bytesOfValidity);
|
||||||
|
|
||||||
|
@ -576,6 +577,9 @@ int AvatarData::parseDataFromBuffer(const QByteArray& buffer) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// each joint rotation is stored in 6 bytes.
|
// each joint rotation is stored in 6 bytes.
|
||||||
|
QWriteLocker writeLock(&_jointDataLock);
|
||||||
|
_jointData.resize(numJoints);
|
||||||
|
|
||||||
const int COMPRESSED_QUATERNION_SIZE = 6;
|
const int COMPRESSED_QUATERNION_SIZE = 6;
|
||||||
PACKET_READ_CHECK(JointRotations, numValidJointRotations * COMPRESSED_QUATERNION_SIZE);
|
PACKET_READ_CHECK(JointRotations, numValidJointRotations * COMPRESSED_QUATERNION_SIZE);
|
||||||
for (int i = 0; i < numJoints; i++) {
|
for (int i = 0; i < numJoints; i++) {
|
||||||
|
@ -653,6 +657,7 @@ void AvatarData::setRawJointData(QVector<JointData> data) {
|
||||||
QMetaObject::invokeMethod(this, "setRawJointData", Q_ARG(QVector<JointData>, data));
|
QMetaObject::invokeMethod(this, "setRawJointData", Q_ARG(QVector<JointData>, data));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
QWriteLocker writeLock(&_jointDataLock);
|
||||||
_jointData = data;
|
_jointData = data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -664,6 +669,7 @@ void AvatarData::setJointData(int index, const glm::quat& rotation, const glm::v
|
||||||
QMetaObject::invokeMethod(this, "setJointData", Q_ARG(int, index), Q_ARG(const glm::quat&, rotation));
|
QMetaObject::invokeMethod(this, "setJointData", Q_ARG(int, index), Q_ARG(const glm::quat&, rotation));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
QWriteLocker writeLock(&_jointDataLock);
|
||||||
if (_jointData.size() <= index) {
|
if (_jointData.size() <= index) {
|
||||||
_jointData.resize(index + 1);
|
_jointData.resize(index + 1);
|
||||||
}
|
}
|
||||||
|
@ -682,6 +688,8 @@ void AvatarData::clearJointData(int index) {
|
||||||
QMetaObject::invokeMethod(this, "clearJointData", Q_ARG(int, index));
|
QMetaObject::invokeMethod(this, "clearJointData", Q_ARG(int, index));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
QWriteLocker writeLock(&_jointDataLock);
|
||||||
|
// FIXME: I don't understand how this "clears" the joint data at index
|
||||||
if (_jointData.size() <= index) {
|
if (_jointData.size() <= index) {
|
||||||
_jointData.resize(index + 1);
|
_jointData.resize(index + 1);
|
||||||
}
|
}
|
||||||
|
@ -710,6 +718,7 @@ glm::quat AvatarData::getJointRotation(int index) const {
|
||||||
Q_RETURN_ARG(glm::quat, result), Q_ARG(int, index));
|
Q_RETURN_ARG(glm::quat, result), Q_ARG(int, index));
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
QReadLocker readLock(&_jointDataLock);
|
||||||
return index < _jointData.size() ? _jointData.at(index).rotation : glm::quat();
|
return index < _jointData.size() ? _jointData.at(index).rotation : glm::quat();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -724,6 +733,7 @@ glm::vec3 AvatarData::getJointTranslation(int index) const {
|
||||||
Q_RETURN_ARG(glm::vec3, result), Q_ARG(int, index));
|
Q_RETURN_ARG(glm::vec3, result), Q_ARG(int, index));
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
QReadLocker readLock(&_jointDataLock);
|
||||||
return index < _jointData.size() ? _jointData.at(index).translation : glm::vec3();
|
return index < _jointData.size() ? _jointData.at(index).translation : glm::vec3();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -771,6 +781,7 @@ void AvatarData::setJointRotation(int index, const glm::quat& rotation) {
|
||||||
QMetaObject::invokeMethod(this, "setJointRotation", Q_ARG(int, index), Q_ARG(const glm::quat&, rotation));
|
QMetaObject::invokeMethod(this, "setJointRotation", Q_ARG(int, index), Q_ARG(const glm::quat&, rotation));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
QWriteLocker writeLock(&_jointDataLock);
|
||||||
if (_jointData.size() <= index) {
|
if (_jointData.size() <= index) {
|
||||||
_jointData.resize(index + 1);
|
_jointData.resize(index + 1);
|
||||||
}
|
}
|
||||||
|
@ -787,6 +798,7 @@ void AvatarData::setJointTranslation(int index, const glm::vec3& translation) {
|
||||||
QMetaObject::invokeMethod(this, "setJointTranslation", Q_ARG(int, index), Q_ARG(const glm::vec3&, translation));
|
QMetaObject::invokeMethod(this, "setJointTranslation", Q_ARG(int, index), Q_ARG(const glm::vec3&, translation));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
QWriteLocker writeLock(&_jointDataLock);
|
||||||
if (_jointData.size() <= index) {
|
if (_jointData.size() <= index) {
|
||||||
_jointData.resize(index + 1);
|
_jointData.resize(index + 1);
|
||||||
}
|
}
|
||||||
|
@ -831,6 +843,7 @@ QVector<glm::quat> AvatarData::getJointRotations() const {
|
||||||
Q_RETURN_ARG(QVector<glm::quat>, result));
|
Q_RETURN_ARG(QVector<glm::quat>, result));
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
QReadLocker readLock(&_jointDataLock);
|
||||||
QVector<glm::quat> jointRotations(_jointData.size());
|
QVector<glm::quat> jointRotations(_jointData.size());
|
||||||
for (int i = 0; i < _jointData.size(); ++i) {
|
for (int i = 0; i < _jointData.size(); ++i) {
|
||||||
jointRotations[i] = _jointData[i].rotation;
|
jointRotations[i] = _jointData[i].rotation;
|
||||||
|
@ -845,6 +858,7 @@ void AvatarData::setJointRotations(QVector<glm::quat> jointRotations) {
|
||||||
"setJointRotations", Qt::BlockingQueuedConnection,
|
"setJointRotations", Qt::BlockingQueuedConnection,
|
||||||
Q_ARG(QVector<glm::quat>, jointRotations));
|
Q_ARG(QVector<glm::quat>, jointRotations));
|
||||||
}
|
}
|
||||||
|
QWriteLocker writeLock(&_jointDataLock);
|
||||||
if (_jointData.size() < jointRotations.size()) {
|
if (_jointData.size() < jointRotations.size()) {
|
||||||
_jointData.resize(jointRotations.size());
|
_jointData.resize(jointRotations.size());
|
||||||
}
|
}
|
||||||
|
@ -862,6 +876,7 @@ void AvatarData::setJointTranslations(QVector<glm::vec3> jointTranslations) {
|
||||||
"setJointTranslations", Qt::BlockingQueuedConnection,
|
"setJointTranslations", Qt::BlockingQueuedConnection,
|
||||||
Q_ARG(QVector<glm::vec3>, jointTranslations));
|
Q_ARG(QVector<glm::vec3>, jointTranslations));
|
||||||
}
|
}
|
||||||
|
QWriteLocker writeLock(&_jointDataLock);
|
||||||
if (_jointData.size() < jointTranslations.size()) {
|
if (_jointData.size() < jointTranslations.size()) {
|
||||||
_jointData.resize(jointTranslations.size());
|
_jointData.resize(jointTranslations.size());
|
||||||
}
|
}
|
||||||
|
@ -873,11 +888,23 @@ void AvatarData::setJointTranslations(QVector<glm::vec3> jointTranslations) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void AvatarData::clearJointsData() {
|
void AvatarData::clearJointsData() {
|
||||||
|
// FIXME: this method is terribly inefficient and probably doesn't even work
|
||||||
|
// (see implementation of clearJointData(index))
|
||||||
for (int i = 0; i < _jointData.size(); ++i) {
|
for (int i = 0; i < _jointData.size(); ++i) {
|
||||||
clearJointData(i);
|
clearJointData(i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int AvatarData::getJointIndex(const QString& name) const {
|
||||||
|
QReadLocker readLock(&_jointDataLock);
|
||||||
|
return _jointIndices.value(name) - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
QStringList AvatarData::getJointNames() const {
|
||||||
|
QReadLocker readLock(&_jointDataLock);
|
||||||
|
return _jointNames;
|
||||||
|
}
|
||||||
|
|
||||||
void AvatarData::parseAvatarIdentityPacket(const QByteArray& data, Identity& identityOut) {
|
void AvatarData::parseAvatarIdentityPacket(const QByteArray& data, Identity& identityOut) {
|
||||||
QDataStream packetStream(data);
|
QDataStream packetStream(data);
|
||||||
|
|
||||||
|
@ -1027,38 +1054,41 @@ void AvatarData::detachAll(const QString& modelURL, const QString& jointName) {
|
||||||
void AvatarData::setJointMappingsFromNetworkReply() {
|
void AvatarData::setJointMappingsFromNetworkReply() {
|
||||||
QNetworkReply* networkReply = static_cast<QNetworkReply*>(sender());
|
QNetworkReply* networkReply = static_cast<QNetworkReply*>(sender());
|
||||||
|
|
||||||
QByteArray line;
|
{
|
||||||
while (!(line = networkReply->readLine()).isEmpty()) {
|
QWriteLocker writeLock(&_jointDataLock);
|
||||||
line = line.trimmed();
|
QByteArray line;
|
||||||
if (line.startsWith("filename")) {
|
while (!(line = networkReply->readLine()).isEmpty()) {
|
||||||
int filenameIndex = line.indexOf('=') + 1;
|
line = line.trimmed();
|
||||||
if (filenameIndex > 0) {
|
if (line.startsWith("filename")) {
|
||||||
_skeletonFBXURL = _skeletonModelURL.resolved(QString(line.mid(filenameIndex).trimmed()));
|
int filenameIndex = line.indexOf('=') + 1;
|
||||||
|
if (filenameIndex > 0) {
|
||||||
|
_skeletonFBXURL = _skeletonModelURL.resolved(QString(line.mid(filenameIndex).trimmed()));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
if (!line.startsWith("jointIndex")) {
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
if (!line.startsWith("jointIndex")) {
|
int jointNameIndex = line.indexOf('=') + 1;
|
||||||
continue;
|
if (jointNameIndex == 0) {
|
||||||
}
|
continue;
|
||||||
int jointNameIndex = line.indexOf('=') + 1;
|
}
|
||||||
if (jointNameIndex == 0) {
|
int secondSeparatorIndex = line.indexOf('=', jointNameIndex);
|
||||||
continue;
|
if (secondSeparatorIndex == -1) {
|
||||||
}
|
continue;
|
||||||
int secondSeparatorIndex = line.indexOf('=', jointNameIndex);
|
}
|
||||||
if (secondSeparatorIndex == -1) {
|
QString jointName = line.mid(jointNameIndex, secondSeparatorIndex - jointNameIndex).trimmed();
|
||||||
continue;
|
bool ok;
|
||||||
}
|
int jointIndex = line.mid(secondSeparatorIndex + 1).trimmed().toInt(&ok);
|
||||||
QString jointName = line.mid(jointNameIndex, secondSeparatorIndex - jointNameIndex).trimmed();
|
if (ok) {
|
||||||
bool ok;
|
while (_jointNames.size() < jointIndex + 1) {
|
||||||
int jointIndex = line.mid(secondSeparatorIndex + 1).trimmed().toInt(&ok);
|
_jointNames.append(QString());
|
||||||
if (ok) {
|
}
|
||||||
while (_jointNames.size() < jointIndex + 1) {
|
_jointNames[jointIndex] = jointName;
|
||||||
_jointNames.append(QString());
|
|
||||||
}
|
}
|
||||||
_jointNames[jointIndex] = jointName;
|
|
||||||
}
|
}
|
||||||
}
|
for (int i = 0; i < _jointNames.size(); i++) {
|
||||||
for (int i = 0; i < _jointNames.size(); i++) {
|
_jointIndices.insert(_jointNames.at(i), i + 1);
|
||||||
_jointIndices.insert(_jointNames.at(i), i + 1);
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
networkReply->deleteLater();
|
networkReply->deleteLater();
|
||||||
|
@ -1101,16 +1131,19 @@ void AvatarData::sendIdentityPacket() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void AvatarData::updateJointMappings() {
|
void AvatarData::updateJointMappings() {
|
||||||
_jointIndices.clear();
|
{
|
||||||
_jointNames.clear();
|
QWriteLocker writeLock(&_jointDataLock);
|
||||||
_jointData.clear();
|
_jointIndices.clear();
|
||||||
|
_jointNames.clear();
|
||||||
|
_jointData.clear();
|
||||||
|
}
|
||||||
|
|
||||||
if (_skeletonModelURL.fileName().toLower().endsWith(".fst")) {
|
if (_skeletonModelURL.fileName().toLower().endsWith(".fst")) {
|
||||||
QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();
|
QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();
|
||||||
QNetworkRequest networkRequest = QNetworkRequest(_skeletonModelURL);
|
QNetworkRequest networkRequest = QNetworkRequest(_skeletonModelURL);
|
||||||
networkRequest.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT);
|
networkRequest.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT);
|
||||||
QNetworkReply* networkReply = networkAccessManager.get(networkRequest);
|
QNetworkReply* networkReply = networkAccessManager.get(networkRequest);
|
||||||
connect(networkReply, SIGNAL(finished()), this, SLOT(setJointMappingsFromNetworkReply()));
|
connect(networkReply, &QNetworkReply::finished, this, &AvatarData::setJointMappingsFromNetworkReply);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -271,9 +271,9 @@ public:
|
||||||
Q_INVOKABLE virtual void clearJointsData();
|
Q_INVOKABLE virtual void clearJointsData();
|
||||||
|
|
||||||
/// Returns the index of the joint with the specified name, or -1 if not found/unknown.
|
/// Returns the index of the joint with the specified name, or -1 if not found/unknown.
|
||||||
Q_INVOKABLE virtual int getJointIndex(const QString& name) const { return _jointIndices.value(name) - 1; }
|
Q_INVOKABLE virtual int getJointIndex(const QString& name) const;
|
||||||
|
|
||||||
Q_INVOKABLE virtual QStringList getJointNames() const { return _jointNames; }
|
Q_INVOKABLE virtual QStringList getJointNames() const;
|
||||||
|
|
||||||
Q_INVOKABLE void setBlendshape(QString name, float val) { _headData->setBlendshape(name, val); }
|
Q_INVOKABLE void setBlendshape(QString name, float val) { _headData->setBlendshape(name, val); }
|
||||||
|
|
||||||
|
@ -374,6 +374,7 @@ protected:
|
||||||
|
|
||||||
QVector<JointData> _jointData; ///< the state of the skeleton joints
|
QVector<JointData> _jointData; ///< the state of the skeleton joints
|
||||||
QVector<JointData> _lastSentJointData; ///< the state of the skeleton joints last time we transmitted
|
QVector<JointData> _lastSentJointData; ///< the state of the skeleton joints last time we transmitted
|
||||||
|
mutable QReadWriteLock _jointDataLock;
|
||||||
|
|
||||||
// key state
|
// key state
|
||||||
KeyState _keyState;
|
KeyState _keyState;
|
||||||
|
|
|
@ -113,10 +113,12 @@ void HmdDisplayPlugin::customizeContext() {
|
||||||
|
|
||||||
updateReprojectionProgram();
|
updateReprojectionProgram();
|
||||||
updateOverlayProgram();
|
updateOverlayProgram();
|
||||||
|
#ifdef HMD_HAND_LASER_SUPPORT
|
||||||
updateLaserProgram();
|
updateLaserProgram();
|
||||||
|
|
||||||
_laserGeometry = loadLaser(_laserProgram);
|
_laserGeometry = loadLaser(_laserProgram);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
//#define LIVE_SHADER_RELOAD 1
|
//#define LIVE_SHADER_RELOAD 1
|
||||||
|
|
||||||
static QString readFile(const QString& filename) {
|
static QString readFile(const QString& filename) {
|
||||||
|
@ -162,6 +164,7 @@ void HmdDisplayPlugin::updateReprojectionProgram() {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef HMD_HAND_LASER_SUPPORT
|
||||||
void HmdDisplayPlugin::updateLaserProgram() {
|
void HmdDisplayPlugin::updateLaserProgram() {
|
||||||
static const QString vsFile = PathUtils::resourcesPath() + "/shaders/hmd_hand_lasers.vert";
|
static const QString vsFile = PathUtils::resourcesPath() + "/shaders/hmd_hand_lasers.vert";
|
||||||
static const QString gsFile = PathUtils::resourcesPath() + "/shaders/hmd_hand_lasers.geom";
|
static const QString gsFile = PathUtils::resourcesPath() + "/shaders/hmd_hand_lasers.geom";
|
||||||
|
@ -202,6 +205,7 @@ void HmdDisplayPlugin::updateLaserProgram() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
void HmdDisplayPlugin::updateOverlayProgram() {
|
void HmdDisplayPlugin::updateOverlayProgram() {
|
||||||
static const QString vsFile = PathUtils::resourcesPath() + "/shaders/hmd_ui_glow.vert";
|
static const QString vsFile = PathUtils::resourcesPath() + "/shaders/hmd_ui_glow.vert";
|
||||||
|
@ -249,8 +253,10 @@ void HmdDisplayPlugin::uncustomizeContext() {
|
||||||
_compositeFramebuffer.reset();
|
_compositeFramebuffer.reset();
|
||||||
_previewProgram.reset();
|
_previewProgram.reset();
|
||||||
_reprojectionProgram.reset();
|
_reprojectionProgram.reset();
|
||||||
|
#ifdef HMD_HAND_LASER_SUPPORT
|
||||||
_laserProgram.reset();
|
_laserProgram.reset();
|
||||||
_laserGeometry.reset();
|
_laserGeometry.reset();
|
||||||
|
#endif
|
||||||
Parent::uncustomizeContext();
|
Parent::uncustomizeContext();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -516,6 +522,7 @@ bool HmdDisplayPlugin::setHandLaser(uint32_t hands, HandLaserMode mode, const ve
|
||||||
}
|
}
|
||||||
|
|
||||||
void HmdDisplayPlugin::compositeExtra() {
|
void HmdDisplayPlugin::compositeExtra() {
|
||||||
|
#ifdef HMD_HAND_LASER_SUPPORT
|
||||||
// If neither hand laser is activated, exit
|
// If neither hand laser is activated, exit
|
||||||
if (!_presentHandLasers[0].valid() && !_presentHandLasers[1].valid()) {
|
if (!_presentHandLasers[0].valid() && !_presentHandLasers[1].valid()) {
|
||||||
return;
|
return;
|
||||||
|
@ -584,4 +591,5 @@ void HmdDisplayPlugin::compositeExtra() {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
glDisable(GL_BLEND);
|
glDisable(GL_BLEND);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,6 +14,10 @@
|
||||||
|
|
||||||
#include "../OpenGLDisplayPlugin.h"
|
#include "../OpenGLDisplayPlugin.h"
|
||||||
|
|
||||||
|
#ifdef Q_OS_WIN
|
||||||
|
#define HMD_HAND_LASER_SUPPORT
|
||||||
|
#endif
|
||||||
|
|
||||||
class HmdDisplayPlugin : public OpenGLDisplayPlugin {
|
class HmdDisplayPlugin : public OpenGLDisplayPlugin {
|
||||||
using Parent = OpenGLDisplayPlugin;
|
using Parent = OpenGLDisplayPlugin;
|
||||||
public:
|
public:
|
||||||
|
@ -93,7 +97,9 @@ protected:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void updateOverlayProgram();
|
void updateOverlayProgram();
|
||||||
|
#ifdef HMD_HAND_LASER_SUPPORT
|
||||||
void updateLaserProgram();
|
void updateLaserProgram();
|
||||||
|
#endif
|
||||||
void updateReprojectionProgram();
|
void updateReprojectionProgram();
|
||||||
|
|
||||||
bool _enablePreview { false };
|
bool _enablePreview { false };
|
||||||
|
@ -130,11 +136,13 @@ private:
|
||||||
|
|
||||||
ShapeWrapperPtr _sphereSection;
|
ShapeWrapperPtr _sphereSection;
|
||||||
|
|
||||||
|
#ifdef HMD_HAND_LASER_SUPPORT
|
||||||
ProgramPtr _laserProgram;
|
ProgramPtr _laserProgram;
|
||||||
struct LaserUniforms {
|
struct LaserUniforms {
|
||||||
int32_t mvp { -1 };
|
int32_t mvp { -1 };
|
||||||
int32_t color { -1 };
|
int32_t color { -1 };
|
||||||
} _laserUniforms;
|
} _laserUniforms;
|
||||||
ShapeWrapperPtr _laserGeometry;
|
ShapeWrapperPtr _laserGeometry;
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -24,6 +24,7 @@
|
||||||
#include <DependencyManager.h>
|
#include <DependencyManager.h>
|
||||||
#include <NumericalConstants.h>
|
#include <NumericalConstants.h>
|
||||||
#include <Finally.h>
|
#include <Finally.h>
|
||||||
|
#include <PathUtils.h>
|
||||||
|
|
||||||
#include "OffscreenGLCanvas.h"
|
#include "OffscreenGLCanvas.h"
|
||||||
#include "GLEscrow.h"
|
#include "GLEscrow.h"
|
||||||
|
@ -400,6 +401,10 @@ void OffscreenQmlSurface::create(QOpenGLContext* shareContext) {
|
||||||
|
|
||||||
// Create a QML engine.
|
// Create a QML engine.
|
||||||
_qmlEngine = new QQmlEngine;
|
_qmlEngine = new QQmlEngine;
|
||||||
|
|
||||||
|
auto importList = _qmlEngine->importPathList();
|
||||||
|
importList.insert(importList.begin(), PathUtils::resourcesPath());
|
||||||
|
_qmlEngine->setImportPathList(importList);
|
||||||
if (!_qmlEngine->incubationController()) {
|
if (!_qmlEngine->incubationController()) {
|
||||||
_qmlEngine->setIncubationController(_renderer->_quickWindow->incubationController());
|
_qmlEngine->setIncubationController(_renderer->_quickWindow->incubationController());
|
||||||
}
|
}
|
||||||
|
|
|
@ -67,6 +67,18 @@ void GeometryMappingResource::downloadFinished(const QByteArray& data) {
|
||||||
_textureBaseUrl = resolveTextureBaseUrl(url, _url.resolved(texdir));
|
_textureBaseUrl = resolveTextureBaseUrl(url, _url.resolved(texdir));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto animGraphVariant = mapping.value("animGraphUrl");
|
||||||
|
if (animGraphVariant.isValid()) {
|
||||||
|
QUrl fstUrl(animGraphVariant.toString());
|
||||||
|
if (fstUrl.isValid()) {
|
||||||
|
_animGraphOverrideUrl = _url.resolved(fstUrl);
|
||||||
|
} else {
|
||||||
|
_animGraphOverrideUrl = QUrl();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
_animGraphOverrideUrl = QUrl();
|
||||||
|
}
|
||||||
|
|
||||||
auto modelCache = DependencyManager::get<ModelCache>();
|
auto modelCache = DependencyManager::get<ModelCache>();
|
||||||
GeometryExtra extra{ mapping, _textureBaseUrl };
|
GeometryExtra extra{ mapping, _textureBaseUrl };
|
||||||
|
|
||||||
|
@ -284,6 +296,8 @@ Geometry::Geometry(const Geometry& geometry) {
|
||||||
for (const auto& material : geometry._materials) {
|
for (const auto& material : geometry._materials) {
|
||||||
_materials.push_back(std::make_shared<NetworkMaterial>(*material));
|
_materials.push_back(std::make_shared<NetworkMaterial>(*material));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_animGraphOverrideUrl = geometry._animGraphOverrideUrl;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Geometry::setTextures(const QVariantMap& textureMap) {
|
void Geometry::setTextures(const QVariantMap& textureMap) {
|
||||||
|
|
|
@ -52,6 +52,7 @@ public:
|
||||||
void setTextures(const QVariantMap& textureMap);
|
void setTextures(const QVariantMap& textureMap);
|
||||||
|
|
||||||
virtual bool areTexturesLoaded() const;
|
virtual bool areTexturesLoaded() const;
|
||||||
|
const QUrl& getAnimGraphOverrideUrl() const { return _animGraphOverrideUrl; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
friend class GeometryMappingResource;
|
friend class GeometryMappingResource;
|
||||||
|
@ -64,6 +65,8 @@ protected:
|
||||||
// Copied to each geometry, mutable throughout lifetime via setTextures
|
// Copied to each geometry, mutable throughout lifetime via setTextures
|
||||||
NetworkMaterials _materials;
|
NetworkMaterials _materials;
|
||||||
|
|
||||||
|
QUrl _animGraphOverrideUrl;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
mutable bool _areTexturesLoaded { false };
|
mutable bool _areTexturesLoaded { false };
|
||||||
};
|
};
|
||||||
|
|
|
@ -53,7 +53,6 @@ void UserActivityLogger::logAction(QString action, QJsonObject details, JSONCall
|
||||||
detailsPart.setBody(QJsonDocument(details).toJson(QJsonDocument::Compact));
|
detailsPart.setBody(QJsonDocument(details).toJson(QJsonDocument::Compact));
|
||||||
multipart->append(detailsPart);
|
multipart->append(detailsPart);
|
||||||
}
|
}
|
||||||
qCDebug(networking) << "Logging activity" << action;
|
|
||||||
|
|
||||||
// if no callbacks specified, call our owns
|
// if no callbacks specified, call our owns
|
||||||
if (params.isEmpty()) {
|
if (params.isEmpty()) {
|
||||||
|
|
|
@ -1552,6 +1552,12 @@ void GeometryCache::renderLine(gpu::Batch& batch, const glm::vec2& p1, const glm
|
||||||
|
|
||||||
void GeometryCache::renderGlowLine(gpu::Batch& batch, const glm::vec3& p1, const glm::vec3& p2,
|
void GeometryCache::renderGlowLine(gpu::Batch& batch, const glm::vec3& p1, const glm::vec3& p2,
|
||||||
const glm::vec4& color, float glowIntensity, float glowWidth, int id) {
|
const glm::vec4& color, float glowIntensity, float glowWidth, int id) {
|
||||||
|
|
||||||
|
// Disable glow lines on OSX
|
||||||
|
#ifndef Q_OS_WIN
|
||||||
|
glowIntensity = 0.0f;
|
||||||
|
#endif
|
||||||
|
|
||||||
if (glowIntensity <= 0) {
|
if (glowIntensity <= 0) {
|
||||||
renderLine(batch, p1, p2, color, id);
|
renderLine(batch, p1, p2, color, id);
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -1192,6 +1192,8 @@ void Model::deleteGeometry() {
|
||||||
_meshStates.clear();
|
_meshStates.clear();
|
||||||
_rig->destroyAnimGraph();
|
_rig->destroyAnimGraph();
|
||||||
_blendedBlendshapeCoefficients.clear();
|
_blendedBlendshapeCoefficients.clear();
|
||||||
|
_renderGeometry.reset();
|
||||||
|
_collisionGeometry.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
AABox Model::getRenderableMeshBound() const {
|
AABox Model::getRenderableMeshBound() const {
|
||||||
|
|
|
@ -20,7 +20,7 @@ struct Grid {
|
||||||
};
|
};
|
||||||
|
|
||||||
uniform gridBuffer { Grid grid; };
|
uniform gridBuffer { Grid grid; };
|
||||||
Grid getGrid() { return grid; };
|
Grid getGrid() { return grid; }
|
||||||
|
|
||||||
in vec2 varTexCoord0;
|
in vec2 varTexCoord0;
|
||||||
in vec4 varColor;
|
in vec4 varColor;
|
||||||
|
|
|
@ -108,3 +108,10 @@ QStringList FileDialogHelper::drives() {
|
||||||
void FileDialogHelper::openDirectory(const QString& path) {
|
void FileDialogHelper::openDirectory(const QString& path) {
|
||||||
QDesktopServices::openUrl(path);
|
QDesktopServices::openUrl(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QList<QUrl> FileDialogHelper::urlToList(const QUrl& url) {
|
||||||
|
QList<QUrl> results;
|
||||||
|
results.push_back(url);
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -58,6 +58,7 @@ public:
|
||||||
Q_INVOKABLE bool validFolder(const QString& path);
|
Q_INVOKABLE bool validFolder(const QString& path);
|
||||||
Q_INVOKABLE QUrl pathToUrl(const QString& path);
|
Q_INVOKABLE QUrl pathToUrl(const QString& path);
|
||||||
Q_INVOKABLE QUrl saveHelper(const QString& saveText, const QUrl& currentFolder, const QStringList& selectionFilters);
|
Q_INVOKABLE QUrl saveHelper(const QString& saveText, const QUrl& currentFolder, const QStringList& selectionFilters);
|
||||||
|
Q_INVOKABLE QList<QUrl> urlToList(const QUrl& url);
|
||||||
|
|
||||||
Q_INVOKABLE void openDirectory(const QString& path);
|
Q_INVOKABLE void openDirectory(const QString& path);
|
||||||
};
|
};
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -45,6 +45,19 @@ var TARGET_MODEL_DIMENSIONS = {
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
var COLORS_TELEPORT_CAN_TELEPORT = {
|
||||||
|
red: 97,
|
||||||
|
green: 247,
|
||||||
|
blue: 255
|
||||||
|
}
|
||||||
|
|
||||||
|
var COLORS_TELEPORT_CANNOT_TELEPORT = {
|
||||||
|
red: 0,
|
||||||
|
green: 121,
|
||||||
|
blue: 141
|
||||||
|
};
|
||||||
|
|
||||||
function ThumbPad(hand) {
|
function ThumbPad(hand) {
|
||||||
this.hand = hand;
|
this.hand = hand;
|
||||||
var _thisPad = this;
|
var _thisPad = this;
|
||||||
|
@ -269,17 +282,13 @@ function Teleporter() {
|
||||||
|
|
||||||
this.rightPickRay = rightPickRay;
|
this.rightPickRay = rightPickRay;
|
||||||
|
|
||||||
var location = Vec3.sum(rightPickRay.origin, Vec3.multiply(rightPickRay.direction, 500));
|
var location = Vec3.sum(rightPickRay.origin, Vec3.multiply(rightPickRay.direction, 50));
|
||||||
|
|
||||||
|
|
||||||
var rightIntersection = Entities.findRayIntersection(teleporter.rightPickRay, true, [], [this.targetEntity]);
|
var rightIntersection = Entities.findRayIntersection(teleporter.rightPickRay, true, [], [this.targetEntity]);
|
||||||
|
|
||||||
if (rightIntersection.intersects) {
|
if (rightIntersection.intersects) {
|
||||||
this.rightLineOn(rightPickRay.origin, rightIntersection.intersection, {
|
this.rightLineOn(rightPickRay.origin, rightIntersection.intersection, COLORS_TELEPORT_CAN_TELEPORT);
|
||||||
red: 7,
|
|
||||||
green: 36,
|
|
||||||
blue: 44
|
|
||||||
});
|
|
||||||
if (this.targetOverlay !== null) {
|
if (this.targetOverlay !== null) {
|
||||||
this.updateTargetOverlay(rightIntersection);
|
this.updateTargetOverlay(rightIntersection);
|
||||||
} else {
|
} else {
|
||||||
|
@ -289,11 +298,7 @@ function Teleporter() {
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
this.deleteTargetOverlay();
|
this.deleteTargetOverlay();
|
||||||
this.rightLineOn(rightPickRay.origin, location, {
|
this.rightLineOn(rightPickRay.origin, location, COLORS_TELEPORT_CANNOT_TELEPORT);
|
||||||
red: 7,
|
|
||||||
green: 36,
|
|
||||||
blue: 44
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -318,18 +323,14 @@ function Teleporter() {
|
||||||
|
|
||||||
this.leftPickRay = leftPickRay;
|
this.leftPickRay = leftPickRay;
|
||||||
|
|
||||||
var location = Vec3.sum(MyAvatar.position, Vec3.multiply(leftPickRay.direction, 500));
|
var location = Vec3.sum(MyAvatar.position, Vec3.multiply(leftPickRay.direction, 50));
|
||||||
|
|
||||||
|
|
||||||
var leftIntersection = Entities.findRayIntersection(teleporter.leftPickRay, true, [], [this.targetEntity]);
|
var leftIntersection = Entities.findRayIntersection(teleporter.leftPickRay, true, [], [this.targetEntity]);
|
||||||
|
|
||||||
if (leftIntersection.intersects) {
|
if (leftIntersection.intersects) {
|
||||||
|
|
||||||
this.leftLineOn(leftPickRay.origin, leftIntersection.intersection, {
|
this.leftLineOn(leftPickRay.origin, leftIntersection.intersection, COLORS_TELEPORT_CAN_TELEPORT);
|
||||||
red: 7,
|
|
||||||
green: 36,
|
|
||||||
blue: 44
|
|
||||||
});
|
|
||||||
if (this.targetOverlay !== null) {
|
if (this.targetOverlay !== null) {
|
||||||
this.updateTargetOverlay(leftIntersection);
|
this.updateTargetOverlay(leftIntersection);
|
||||||
} else {
|
} else {
|
||||||
|
@ -339,13 +340,8 @@ function Teleporter() {
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
|
|
||||||
this.deleteTargetOverlay();
|
this.deleteTargetOverlay();
|
||||||
this.leftLineOn(leftPickRay.origin, location, {
|
this.leftLineOn(leftPickRay.origin, location, COLORS_TELEPORT_CANNOT_TELEPORT);
|
||||||
red: 7,
|
|
||||||
green: 36,
|
|
||||||
blue: 44
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -622,4 +618,4 @@ function cleanup() {
|
||||||
if (teleporter.updateConnected !== null) {
|
if (teleporter.updateConnected !== null) {
|
||||||
Script.update.disconnect(teleporter.update);
|
Script.update.disconnect(teleporter.update);
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -39,6 +39,7 @@
|
||||||
#include <gpu/gl/GLFramebuffer.h>
|
#include <gpu/gl/GLFramebuffer.h>
|
||||||
#include <gpu/gl/GLTexture.h>
|
#include <gpu/gl/GLTexture.h>
|
||||||
|
|
||||||
|
#include <WebEntityItem.h>
|
||||||
#include <OctreeUtils.h>
|
#include <OctreeUtils.h>
|
||||||
#include <render/Engine.h>
|
#include <render/Engine.h>
|
||||||
#include <Model.h>
|
#include <Model.h>
|
||||||
|
@ -143,6 +144,19 @@ public:
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static QString toHumanSize(size_t size, size_t maxUnit = std::numeric_limits<size_t>::max()) {
|
||||||
|
static const std::vector<QString> SUFFIXES{ { "B", "KB", "MB", "GB", "TB", "PB" } };
|
||||||
|
const size_t maxIndex = std::min(maxUnit, SUFFIXES.size() - 1);
|
||||||
|
size_t suffixIndex = 0;
|
||||||
|
|
||||||
|
while (suffixIndex < maxIndex && size > 1024) {
|
||||||
|
size >>= 10;
|
||||||
|
++suffixIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
return QString("%1 %2").arg(size).arg(SUFFIXES[suffixIndex]);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Create a simple OpenGL window that renders text in various ways
|
// Create a simple OpenGL window that renders text in various ways
|
||||||
|
@ -211,6 +225,9 @@ public:
|
||||||
AbstractViewStateInterface::setInstance(this);
|
AbstractViewStateInterface::setInstance(this);
|
||||||
_octree = DependencyManager::set<EntityTreeRenderer>(false, this, nullptr);
|
_octree = DependencyManager::set<EntityTreeRenderer>(false, this, nullptr);
|
||||||
_octree->init();
|
_octree->init();
|
||||||
|
// Prevent web entities from rendering
|
||||||
|
REGISTER_ENTITY_TYPE_WITH_FACTORY(Web, WebEntityItem::factory)
|
||||||
|
|
||||||
DependencyManager::set<ParentFinder>(_octree->getTree());
|
DependencyManager::set<ParentFinder>(_octree->getTree());
|
||||||
getEntities()->setViewFrustum(_viewFrustum);
|
getEntities()->setViewFrustum(_viewFrustum);
|
||||||
auto nodeList = DependencyManager::get<LimitedNodeList>();
|
auto nodeList = DependencyManager::get<LimitedNodeList>();
|
||||||
|
@ -296,6 +313,10 @@ protected:
|
||||||
reloadScene();
|
reloadScene();
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
case Qt::Key_F4:
|
||||||
|
toggleStereo();
|
||||||
|
return;
|
||||||
|
|
||||||
case Qt::Key_F5:
|
case Qt::Key_F5:
|
||||||
goTo();
|
goTo();
|
||||||
return;
|
return;
|
||||||
|
@ -365,6 +386,17 @@ private:
|
||||||
|
|
||||||
renderArgs.setViewFrustum(_viewFrustum);
|
renderArgs.setViewFrustum(_viewFrustum);
|
||||||
|
|
||||||
|
renderArgs._context->enableStereo(_stereoEnabled);
|
||||||
|
if (_stereoEnabled) {
|
||||||
|
mat4 eyeOffsets[2];
|
||||||
|
mat4 eyeProjections[2];
|
||||||
|
for (size_t i = 0; i < 2; ++i) {
|
||||||
|
eyeProjections[i] = _viewFrustum.getProjection();
|
||||||
|
}
|
||||||
|
renderArgs._context->setStereoProjections(eyeProjections);
|
||||||
|
renderArgs._context->setStereoViews(eyeOffsets);
|
||||||
|
}
|
||||||
|
|
||||||
// Final framebuffer that will be handled to the display-plugin
|
// Final framebuffer that will be handled to the display-plugin
|
||||||
{
|
{
|
||||||
auto finalFramebuffer = framebufferCache->getFramebuffer();
|
auto finalFramebuffer = framebufferCache->getFramebuffer();
|
||||||
|
@ -388,7 +420,7 @@ private:
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
_textOverlay->render();
|
//_textOverlay->render();
|
||||||
}
|
}
|
||||||
|
|
||||||
_context.swapBuffers(this);
|
_context.swapBuffers(this);
|
||||||
|
@ -429,6 +461,9 @@ private:
|
||||||
const qint64& now;
|
const qint64& now;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void updateText() {
|
void updateText() {
|
||||||
//qDebug() << "FPS " << fps.rate();
|
//qDebug() << "FPS " << fps.rate();
|
||||||
{
|
{
|
||||||
|
@ -438,6 +473,11 @@ private:
|
||||||
infoTextBlock.push_back({ vec2(100, 10), std::to_string((uint32_t)_fps), TextOverlay::alignLeft });
|
infoTextBlock.push_back({ vec2(100, 10), std::to_string((uint32_t)_fps), TextOverlay::alignLeft });
|
||||||
infoTextBlock.push_back({ vec2(98, 30), "Culling: ", TextOverlay::alignRight });
|
infoTextBlock.push_back({ vec2(98, 30), "Culling: ", TextOverlay::alignRight });
|
||||||
infoTextBlock.push_back({ vec2(100, 30), _cullingEnabled ? "Enabled" : "Disabled", TextOverlay::alignLeft });
|
infoTextBlock.push_back({ vec2(100, 30), _cullingEnabled ? "Enabled" : "Disabled", TextOverlay::alignLeft });
|
||||||
|
|
||||||
|
setTitle(QString("FPS %1 Culling %2 TextureMemory GPU %3 CPU %4")
|
||||||
|
.arg(_fps).arg(_cullingEnabled)
|
||||||
|
.arg(toHumanSize(gpu::Context::getTextureGPUMemoryUsage(), 2))
|
||||||
|
.arg(toHumanSize(gpu::Texture::getTextureCPUMemoryUsage(), 2)));
|
||||||
}
|
}
|
||||||
|
|
||||||
_textOverlay->beginTextUpdate();
|
_textOverlay->beginTextUpdate();
|
||||||
|
@ -561,14 +601,29 @@ private:
|
||||||
void importScene(const QString& fileName) {
|
void importScene(const QString& fileName) {
|
||||||
auto assetClient = DependencyManager::get<AssetClient>();
|
auto assetClient = DependencyManager::get<AssetClient>();
|
||||||
QFileInfo fileInfo(fileName);
|
QFileInfo fileInfo(fileName);
|
||||||
//assetClient->loadLocalMappings(fileInfo.absolutePath() + "/" + fileInfo.baseName() + ".atp");
|
QString atpPath = fileInfo.absolutePath() + "/" + fileInfo.baseName() + ".atp";
|
||||||
|
qDebug() << atpPath;
|
||||||
|
QFileInfo atpPathInfo(atpPath);
|
||||||
|
if (atpPathInfo.exists()) {
|
||||||
|
QString atpUrl = QUrl::fromLocalFile(atpPath).toString();
|
||||||
|
ResourceManager::setUrlPrefixOverride("atp:/", atpUrl + "/");
|
||||||
|
}
|
||||||
_settings.setValue(LAST_SCENE_KEY, fileName);
|
_settings.setValue(LAST_SCENE_KEY, fileName);
|
||||||
_octree->clear();
|
_octree->clear();
|
||||||
_octree->getTree()->readFromURL(fileName);
|
_octree->getTree()->readFromURL(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
void importScene() {
|
void importScene() {
|
||||||
QString fileName = QFileDialog::getOpenFileName(nullptr, tr("Open File"), "/home", tr("Hifi Exports (*.json *.svo)"));
|
auto lastScene = _settings.value(LAST_SCENE_KEY);
|
||||||
|
QString openDir;
|
||||||
|
if (lastScene.isValid()) {
|
||||||
|
QFileInfo lastSceneInfo(lastScene.toString());
|
||||||
|
if (lastSceneInfo.absoluteDir().exists()) {
|
||||||
|
openDir = lastSceneInfo.absolutePath();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QString fileName = QFileDialog::getOpenFileName(nullptr, tr("Open File"), openDir, tr("Hifi Exports (*.json *.svo)"));
|
||||||
if (fileName.isNull()) {
|
if (fileName.isNull()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -617,6 +672,10 @@ private:
|
||||||
_cullingEnabled = !_cullingEnabled;
|
_cullingEnabled = !_cullingEnabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void toggleStereo() {
|
||||||
|
_stereoEnabled = !_stereoEnabled;
|
||||||
|
}
|
||||||
|
|
||||||
QSharedPointer<EntityTreeRenderer> getEntities() {
|
QSharedPointer<EntityTreeRenderer> getEntities() {
|
||||||
return _octree;
|
return _octree;
|
||||||
}
|
}
|
||||||
|
@ -665,6 +724,7 @@ private:
|
||||||
float _fps { 0 };
|
float _fps { 0 };
|
||||||
TextOverlay* _textOverlay;
|
TextOverlay* _textOverlay;
|
||||||
bool _cullingEnabled { true };
|
bool _cullingEnabled { true };
|
||||||
|
bool _stereoEnabled { false };
|
||||||
QSharedPointer<EntityTreeRenderer> _octree;
|
QSharedPointer<EntityTreeRenderer> _octree;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -13,6 +13,7 @@ import "../../../interface/resources/qml/styles-uit"
|
||||||
|
|
||||||
ApplicationWindow {
|
ApplicationWindow {
|
||||||
id: appWindow
|
id: appWindow
|
||||||
|
objectName: "MainWindow"
|
||||||
visible: true
|
visible: true
|
||||||
width: 1280
|
width: 1280
|
||||||
height: 800
|
height: 800
|
||||||
|
@ -93,9 +94,6 @@ ApplicationWindow {
|
||||||
onClicked: testButtons.lastButton.visible = !testButtons.lastButton.visible
|
onClicked: testButtons.lastButton.visible = !testButtons.lastButton.visible
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Error alerts
|
// Error alerts
|
||||||
/*
|
/*
|
||||||
Button {
|
Button {
|
||||||
|
@ -350,6 +348,11 @@ ApplicationWindow {
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
Browser {
|
||||||
|
url: "http://s3.amazonaws.com/DreamingContent/testUiDelegates.html"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
Window {
|
Window {
|
||||||
id: blue
|
id: blue
|
||||||
closable: true
|
closable: true
|
||||||
|
|
|
@ -16,6 +16,8 @@ QML_IMPORT_PATH = ../../interface/resources/qml
|
||||||
|
|
||||||
DISTFILES += \
|
DISTFILES += \
|
||||||
qml/*.qml \
|
qml/*.qml \
|
||||||
|
../../interface/resources/QtWebEngine/UIDelegates/original/*.qml \
|
||||||
|
../../interface/resources/QtWebEngine/UIDelegates/*.qml \
|
||||||
../../interface/resources/qml/*.qml \
|
../../interface/resources/qml/*.qml \
|
||||||
../../interface/resources/qml/controls/*.qml \
|
../../interface/resources/qml/controls/*.qml \
|
||||||
../../interface/resources/qml/controls-uit/*.qml \
|
../../interface/resources/qml/controls-uit/*.qml \
|
||||||
|
|
|
@ -33,6 +33,28 @@ protected:
|
||||||
const QString _name;
|
const QString _name;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class Reticle : public QObject {
|
||||||
|
Q_OBJECT
|
||||||
|
Q_PROPERTY(QPoint position READ getPosition CONSTANT)
|
||||||
|
public:
|
||||||
|
|
||||||
|
Reticle(QObject* parent) : QObject(parent) {
|
||||||
|
}
|
||||||
|
|
||||||
|
QPoint getPosition() {
|
||||||
|
if (!_window) {
|
||||||
|
return QPoint(0, 0);
|
||||||
|
}
|
||||||
|
return _window->mapFromGlobal(QCursor::pos());
|
||||||
|
}
|
||||||
|
|
||||||
|
void setWindow(QWindow* window) {
|
||||||
|
_window = window;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
QWindow* _window{nullptr};
|
||||||
|
};
|
||||||
|
|
||||||
QString getRelativeDir(const QString& relativePath = ".") {
|
QString getRelativeDir(const QString& relativePath = ".") {
|
||||||
QDir path(__FILE__); path.cdUp();
|
QDir path(__FILE__); path.cdUp();
|
||||||
|
@ -61,10 +83,8 @@ void setChild(QQmlApplicationEngine& engine, const char* name) {
|
||||||
qWarning() << "Could not find object named " << name;
|
qWarning() << "Could not find object named " << name;
|
||||||
}
|
}
|
||||||
|
|
||||||
void addImportPath(QQmlApplicationEngine& engine, const QString& relativePath) {
|
void addImportPath(QQmlApplicationEngine& engine, const QString& relativePath, bool insert = false) {
|
||||||
QString resolvedPath = getRelativeDir("../qml");
|
QString resolvedPath = getRelativeDir(relativePath);
|
||||||
QUrl resolvedUrl = QUrl::fromLocalFile(resolvedPath);
|
|
||||||
resolvedPath = resolvedUrl.toString();
|
|
||||||
engine.addImportPath(resolvedPath);
|
engine.addImportPath(resolvedPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -79,8 +99,9 @@ int main(int argc, char *argv[]) {
|
||||||
qmlRegisterType<Preference>("Hifi", 1, 0, "Preference");
|
qmlRegisterType<Preference>("Hifi", 1, 0, "Preference");
|
||||||
|
|
||||||
QQmlApplicationEngine engine;
|
QQmlApplicationEngine engine;
|
||||||
addImportPath(engine, "../qml");
|
addImportPath(engine, "qml");
|
||||||
addImportPath(engine, "../../../interface/resources/qml");
|
addImportPath(engine, "../../interface/resources/qml");
|
||||||
|
addImportPath(engine, "../../interface/resources");
|
||||||
engine.load(QUrl(QStringLiteral("qml/Stubs.qml")));
|
engine.load(QUrl(QStringLiteral("qml/Stubs.qml")));
|
||||||
|
|
||||||
setChild(engine, "offscreenFlags");
|
setChild(engine, "offscreenFlags");
|
||||||
|
@ -99,6 +120,15 @@ int main(int argc, char *argv[]) {
|
||||||
|
|
||||||
//engine.load(QUrl(QStringLiteral("qrc:/qml/gallery/main.qml")));
|
//engine.load(QUrl(QStringLiteral("qrc:/qml/gallery/main.qml")));
|
||||||
engine.load(QUrl(QStringLiteral("qml/main.qml")));
|
engine.load(QUrl(QStringLiteral("qml/main.qml")));
|
||||||
|
for (QObject* rootObject : engine.rootObjects()) {
|
||||||
|
if (rootObject->objectName() == "MainWindow") {
|
||||||
|
Reticle* reticle = new Reticle(rootObject);
|
||||||
|
reticle->setWindow((QWindow*)rootObject);
|
||||||
|
engine.rootContext()->setContextProperty("Reticle", reticle);
|
||||||
|
engine.rootContext()->setContextProperty("Window", rootObject);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
return app.exec();
|
return app.exec();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue