mirror of
https://github.com/overte-org/overte.git
synced 2025-08-07 16:30:39 +02:00
Merge branch 'groups' of github.com:sethalves/hifi into groups
This commit is contained in:
commit
4d0580c28a
50 changed files with 1559 additions and 961 deletions
|
@ -25,6 +25,7 @@ FocusScope {
|
||||||
property rect recommendedRect: Qt.rect(0,0,0,0);
|
property rect recommendedRect: Qt.rect(0,0,0,0);
|
||||||
property var expectedChildren;
|
property var expectedChildren;
|
||||||
property bool repositionLocked: true
|
property bool repositionLocked: true
|
||||||
|
property bool hmdHandMouseActive: false
|
||||||
|
|
||||||
onRepositionLockedChanged: {
|
onRepositionLockedChanged: {
|
||||||
if (!repositionLocked) {
|
if (!repositionLocked) {
|
||||||
|
|
|
@ -7,7 +7,7 @@ PreferencesDialog {
|
||||||
id: root
|
id: root
|
||||||
objectName: "AvatarPreferencesDialog"
|
objectName: "AvatarPreferencesDialog"
|
||||||
title: "Avatar Settings"
|
title: "Avatar Settings"
|
||||||
showCategories: [ "Avatar Basics", "Avatar Tuning", "Avatar Camera" ]
|
showCategories: [ "Avatar Basics", "Snapshots", "Avatar Tuning", "Avatar Camera" ]
|
||||||
property var settings: Settings {
|
property var settings: Settings {
|
||||||
category: root.objectName
|
category: root.objectName
|
||||||
property alias x: root.x
|
property alias x: root.x
|
||||||
|
|
|
@ -1,117 +0,0 @@
|
||||||
import QtQuick 2.5
|
|
||||||
import QtQuick.Controls 1.4
|
|
||||||
import QtQuick.Controls.Styles 1.4
|
|
||||||
import QtQuick.XmlListModel 2.0
|
|
||||||
|
|
||||||
import "../../windows"
|
|
||||||
import "../../js/Utils.js" as Utils
|
|
||||||
import "../models"
|
|
||||||
|
|
||||||
Window {
|
|
||||||
id: root
|
|
||||||
resizable: true
|
|
||||||
width: 516
|
|
||||||
height: 616
|
|
||||||
minSize: Qt.vector2d(500, 600);
|
|
||||||
maxSize: Qt.vector2d(1000, 800);
|
|
||||||
|
|
||||||
property alias source: image.source
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
anchors.fill: parent
|
|
||||||
color: "white"
|
|
||||||
|
|
||||||
Item {
|
|
||||||
anchors { fill: parent; margins: 8 }
|
|
||||||
|
|
||||||
Image {
|
|
||||||
id: image
|
|
||||||
anchors { top: parent.top; left: parent.left; right: parent.right; bottom: notesLabel.top; bottomMargin: 8 }
|
|
||||||
fillMode: Image.PreserveAspectFit
|
|
||||||
}
|
|
||||||
|
|
||||||
Text {
|
|
||||||
id: notesLabel
|
|
||||||
anchors { left: parent.left; bottom: notes.top; bottomMargin: 8; }
|
|
||||||
text: "Notes about this image"
|
|
||||||
font.pointSize: 14
|
|
||||||
font.bold: true
|
|
||||||
color: "#666"
|
|
||||||
}
|
|
||||||
|
|
||||||
TextArea {
|
|
||||||
id: notes
|
|
||||||
anchors { left: parent.left; bottom: parent.bottom; right: shareButton.left; rightMargin: 8 }
|
|
||||||
height: 60
|
|
||||||
}
|
|
||||||
|
|
||||||
Button {
|
|
||||||
id: shareButton
|
|
||||||
anchors { verticalCenter: notes.verticalCenter; right: parent.right; }
|
|
||||||
width: 120; height: 50
|
|
||||||
text: "Share"
|
|
||||||
|
|
||||||
style: ButtonStyle {
|
|
||||||
background: Rectangle {
|
|
||||||
implicitWidth: 120
|
|
||||||
implicitHeight: 50
|
|
||||||
border.width: control.activeFocus ? 2 : 1
|
|
||||||
color: "#333"
|
|
||||||
radius: 9
|
|
||||||
}
|
|
||||||
label: Text {
|
|
||||||
color: shareButton.enabled ? "white" : "gray"
|
|
||||||
font.pixelSize: 18
|
|
||||||
font.bold: true
|
|
||||||
verticalAlignment: Text.AlignVCenter
|
|
||||||
horizontalAlignment: Text.AlignHCenter
|
|
||||||
anchors.fill: parent
|
|
||||||
text: shareButton.text
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
onClicked: {
|
|
||||||
enabled = false;
|
|
||||||
uploadTimer.start();
|
|
||||||
}
|
|
||||||
|
|
||||||
Timer {
|
|
||||||
id: uploadTimer
|
|
||||||
running: false
|
|
||||||
interval: 5
|
|
||||||
repeat: false
|
|
||||||
onTriggered: {
|
|
||||||
var uploaded = SnapshotUploader.uploadSnapshot(root.source.toString())
|
|
||||||
console.log("Uploaded result " + uploaded)
|
|
||||||
if (!uploaded) {
|
|
||||||
console.log("Upload failed ");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Action {
|
|
||||||
id: shareAction
|
|
||||||
text: qsTr("OK")
|
|
||||||
enabled: root.result ? true : false
|
|
||||||
shortcut: Qt.Key_Return
|
|
||||||
onTriggered: {
|
|
||||||
root.destroy();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Action {
|
|
||||||
id: cancelAction
|
|
||||||
text: qsTr("Cancel")
|
|
||||||
shortcut: Qt.Key_Escape
|
|
||||||
onTriggered: {
|
|
||||||
root.destroy();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -21,7 +21,19 @@ Window {
|
||||||
height: content.height
|
height: content.height
|
||||||
visible: true
|
visible: true
|
||||||
// Disable this window from being able to call 'desktop.raise() and desktop.showDesktop'
|
// Disable this window from being able to call 'desktop.raise() and desktop.showDesktop'
|
||||||
activator: Item {}
|
activator: MouseArea {
|
||||||
|
width: frame.decoration ? frame.decoration.width : window.width
|
||||||
|
height: frame.decoration ? frame.decoration.height : window.height
|
||||||
|
x: frame.decoration ? frame.decoration.anchors.leftMargin : 0
|
||||||
|
y: frame.decoration ? frame.decoration.anchors.topMargin : 0
|
||||||
|
propagateComposedEvents: true
|
||||||
|
acceptedButtons: Qt.AllButtons
|
||||||
|
enabled: window.visible
|
||||||
|
hoverEnabled: true
|
||||||
|
onPressed: mouse.accepted = false;
|
||||||
|
onEntered: window.mouseEntered();
|
||||||
|
onExited: window.mouseExited();
|
||||||
|
}
|
||||||
property bool horizontal: true
|
property bool horizontal: true
|
||||||
property real buttonSize: 50;
|
property real buttonSize: 50;
|
||||||
property var buttons: []
|
property var buttons: []
|
||||||
|
|
71
interface/resources/qml/windows/Decoration.qml
Normal file
71
interface/resources/qml/windows/Decoration.qml
Normal file
|
@ -0,0 +1,71 @@
|
||||||
|
//
|
||||||
|
// DefaultFrame.qml
|
||||||
|
//
|
||||||
|
// Created by Bradley Austin Davis on 12 Jan 2016
|
||||||
|
// Copyright 2016 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
|
||||||
|
import QtQuick 2.5
|
||||||
|
import QtGraphicalEffects 1.0
|
||||||
|
|
||||||
|
import "."
|
||||||
|
import "../styles-uit"
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
HifiConstants { id: hifi }
|
||||||
|
|
||||||
|
signal inflateDecorations();
|
||||||
|
signal deflateDecorations();
|
||||||
|
|
||||||
|
property int frameMargin: 9
|
||||||
|
property int frameMarginLeft: frameMargin
|
||||||
|
property int frameMarginRight: frameMargin
|
||||||
|
property int frameMarginTop: 2 * frameMargin + iconSize
|
||||||
|
property int frameMarginBottom: iconSize + 11
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
topMargin: -frameMarginTop
|
||||||
|
leftMargin: -frameMarginLeft
|
||||||
|
rightMargin: -frameMarginRight
|
||||||
|
bottomMargin: -frameMarginBottom
|
||||||
|
}
|
||||||
|
anchors.fill: parent
|
||||||
|
color: hifi.colors.baseGrayHighlight40
|
||||||
|
border {
|
||||||
|
width: hifi.dimensions.borderWidth
|
||||||
|
color: hifi.colors.faintGray50
|
||||||
|
}
|
||||||
|
radius: hifi.dimensions.borderRadius
|
||||||
|
|
||||||
|
// Enable dragging of the window,
|
||||||
|
// detect mouseover of the window (including decoration)
|
||||||
|
MouseArea {
|
||||||
|
anchors.fill: parent
|
||||||
|
drag.target: window
|
||||||
|
}
|
||||||
|
Connections {
|
||||||
|
target: window
|
||||||
|
onMouseEntered: {
|
||||||
|
if (desktop.hmdHandMouseActive) {
|
||||||
|
root.inflateDecorations()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
onMouseExited: root.deflateDecorations();
|
||||||
|
}
|
||||||
|
Connections {
|
||||||
|
target: desktop
|
||||||
|
onHmdHandMouseActiveChanged: {
|
||||||
|
if (desktop.hmdHandMouseActive) {
|
||||||
|
if (window.activator.containsMouse) {
|
||||||
|
root.inflateDecorations();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
root.deflateDecorations();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -16,104 +16,6 @@ import "../styles-uit"
|
||||||
|
|
||||||
Frame {
|
Frame {
|
||||||
HifiConstants { id: hifi }
|
HifiConstants { id: hifi }
|
||||||
|
DefaultFrameDecoration {}
|
||||||
Rectangle {
|
|
||||||
// Dialog frame
|
|
||||||
id: frameContent
|
|
||||||
|
|
||||||
readonly property int iconSize: hifi.dimensions.frameIconSize
|
|
||||||
readonly property int frameMargin: 9
|
|
||||||
readonly property int frameMarginLeft: frameMargin
|
|
||||||
readonly property int frameMarginRight: frameMargin
|
|
||||||
readonly property int frameMarginTop: 2 * frameMargin + iconSize
|
|
||||||
readonly property int frameMarginBottom: iconSize + 11
|
|
||||||
|
|
||||||
anchors {
|
|
||||||
topMargin: -frameMarginTop
|
|
||||||
leftMargin: -frameMarginLeft
|
|
||||||
rightMargin: -frameMarginRight
|
|
||||||
bottomMargin: -frameMarginBottom
|
|
||||||
}
|
|
||||||
anchors.fill: parent
|
|
||||||
color: hifi.colors.baseGrayHighlight40
|
|
||||||
border {
|
|
||||||
width: hifi.dimensions.borderWidth
|
|
||||||
color: hifi.colors.faintGray50
|
|
||||||
}
|
|
||||||
radius: hifi.dimensions.borderRadius
|
|
||||||
|
|
||||||
// Enable dragging of the window
|
|
||||||
MouseArea {
|
|
||||||
anchors.fill: parent
|
|
||||||
drag.target: window
|
|
||||||
}
|
|
||||||
|
|
||||||
Row {
|
|
||||||
id: controlsRow
|
|
||||||
anchors {
|
|
||||||
right: parent.right;
|
|
||||||
top: parent.top;
|
|
||||||
topMargin: frameContent.frameMargin + 1 // Move down a little to visually align with the title
|
|
||||||
rightMargin: frameContent.frameMarginRight;
|
|
||||||
}
|
|
||||||
spacing: frameContent.iconSize / 4
|
|
||||||
|
|
||||||
HiFiGlyphs {
|
|
||||||
// "Pin" button
|
|
||||||
visible: window.pinnable
|
|
||||||
text: window.pinned ? hifi.glyphs.pinInverted : hifi.glyphs.pin
|
|
||||||
color: pinClickArea.pressed ? hifi.colors.redHighlight : hifi.colors.white
|
|
||||||
size: frameContent.iconSize
|
|
||||||
MouseArea {
|
|
||||||
id: pinClickArea
|
|
||||||
anchors.fill: parent
|
|
||||||
hoverEnabled: true
|
|
||||||
propagateComposedEvents: true
|
|
||||||
onClicked: window.pinned = !window.pinned;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
HiFiGlyphs {
|
|
||||||
// "Close" button
|
|
||||||
visible: window ? window.closable : false
|
|
||||||
text: closeClickArea.containsPress ? hifi.glyphs.closeInverted : hifi.glyphs.close
|
|
||||||
color: closeClickArea.containsMouse ? hifi.colors.redHighlight : hifi.colors.white
|
|
||||||
size: frameContent.iconSize
|
|
||||||
MouseArea {
|
|
||||||
id: closeClickArea
|
|
||||||
anchors.fill: parent
|
|
||||||
hoverEnabled: true
|
|
||||||
onClicked: window.shown = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
RalewayRegular {
|
|
||||||
// Title
|
|
||||||
id: titleText
|
|
||||||
anchors {
|
|
||||||
left: parent.left
|
|
||||||
leftMargin: frameContent.frameMarginLeft + hifi.dimensions.contentMargin.x
|
|
||||||
right: controlsRow.left
|
|
||||||
rightMargin: frameContent.iconSize
|
|
||||||
top: parent.top
|
|
||||||
topMargin: frameContent.frameMargin
|
|
||||||
}
|
|
||||||
text: window ? window.title : ""
|
|
||||||
color: hifi.colors.white
|
|
||||||
size: hifi.fontSizes.overlayTitle
|
|
||||||
}
|
|
||||||
|
|
||||||
DropShadow {
|
|
||||||
source: titleText
|
|
||||||
anchors.fill: titleText
|
|
||||||
horizontalOffset: 2
|
|
||||||
verticalOffset: 2
|
|
||||||
samples: 2
|
|
||||||
color: hifi.colors.baseGrayShadow60
|
|
||||||
visible: (window && window.focus)
|
|
||||||
cached: true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
113
interface/resources/qml/windows/DefaultFrameDecoration.qml
Normal file
113
interface/resources/qml/windows/DefaultFrameDecoration.qml
Normal file
|
@ -0,0 +1,113 @@
|
||||||
|
//
|
||||||
|
// DefaultFrame.qml
|
||||||
|
//
|
||||||
|
// Created by Bradley Austin Davis on 12 Jan 2016
|
||||||
|
// Copyright 2016 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
|
||||||
|
import QtQuick 2.5
|
||||||
|
import QtGraphicalEffects 1.0
|
||||||
|
|
||||||
|
import "."
|
||||||
|
import "../styles-uit"
|
||||||
|
|
||||||
|
Decoration {
|
||||||
|
HifiConstants { id: hifi }
|
||||||
|
|
||||||
|
// Dialog frame
|
||||||
|
id: root
|
||||||
|
|
||||||
|
property int iconSize: hifi.dimensions.frameIconSize
|
||||||
|
frameMargin: 9
|
||||||
|
frameMarginLeft: frameMargin
|
||||||
|
frameMarginRight: frameMargin
|
||||||
|
frameMarginTop: 2 * frameMargin + iconSize
|
||||||
|
frameMarginBottom: iconSize + 11
|
||||||
|
|
||||||
|
onInflateDecorations: {
|
||||||
|
if (!HMD.active) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
root.frameMargin = 18
|
||||||
|
titleText.size = hifi.fontSizes.overlayTitle * 2
|
||||||
|
root.iconSize = hifi.dimensions.frameIconSize * 2
|
||||||
|
}
|
||||||
|
|
||||||
|
onDeflateDecorations: {
|
||||||
|
root.frameMargin = 9
|
||||||
|
titleText.size = hifi.fontSizes.overlayTitle
|
||||||
|
root.iconSize = hifi.dimensions.frameIconSize
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Row {
|
||||||
|
id: controlsRow
|
||||||
|
anchors {
|
||||||
|
right: parent.right;
|
||||||
|
top: parent.top;
|
||||||
|
topMargin: root.frameMargin + 1 // Move down a little to visually align with the title
|
||||||
|
rightMargin: root.frameMarginRight;
|
||||||
|
}
|
||||||
|
spacing: root.iconSize / 4
|
||||||
|
|
||||||
|
HiFiGlyphs {
|
||||||
|
// "Pin" button
|
||||||
|
visible: window.pinnable
|
||||||
|
text: window.pinned ? hifi.glyphs.pinInverted : hifi.glyphs.pin
|
||||||
|
color: pinClickArea.pressed ? hifi.colors.redHighlight : hifi.colors.white
|
||||||
|
size: root.iconSize
|
||||||
|
MouseArea {
|
||||||
|
id: pinClickArea
|
||||||
|
anchors.fill: parent
|
||||||
|
hoverEnabled: true
|
||||||
|
propagateComposedEvents: true
|
||||||
|
onClicked: window.pinned = !window.pinned;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
HiFiGlyphs {
|
||||||
|
// "Close" button
|
||||||
|
visible: window ? window.closable : false
|
||||||
|
text: closeClickArea.containsPress ? hifi.glyphs.closeInverted : hifi.glyphs.close
|
||||||
|
color: closeClickArea.containsMouse ? hifi.colors.redHighlight : hifi.colors.white
|
||||||
|
size: root.iconSize
|
||||||
|
MouseArea {
|
||||||
|
id: closeClickArea
|
||||||
|
anchors.fill: parent
|
||||||
|
hoverEnabled: true
|
||||||
|
onClicked: window.shown = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RalewayRegular {
|
||||||
|
// Title
|
||||||
|
id: titleText
|
||||||
|
anchors {
|
||||||
|
left: parent.left
|
||||||
|
leftMargin: root.frameMarginLeft + hifi.dimensions.contentMargin.x
|
||||||
|
right: controlsRow.left
|
||||||
|
rightMargin: root.iconSize
|
||||||
|
top: parent.top
|
||||||
|
topMargin: root.frameMargin
|
||||||
|
}
|
||||||
|
text: window ? window.title : ""
|
||||||
|
color: hifi.colors.white
|
||||||
|
size: hifi.fontSizes.overlayTitle
|
||||||
|
}
|
||||||
|
|
||||||
|
DropShadow {
|
||||||
|
source: titleText
|
||||||
|
anchors.fill: titleText
|
||||||
|
horizontalOffset: 2
|
||||||
|
verticalOffset: 2
|
||||||
|
samples: 2
|
||||||
|
color: hifi.colors.baseGrayShadow60
|
||||||
|
visible: (window && window.focus)
|
||||||
|
cached: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -22,10 +22,10 @@ Item {
|
||||||
|
|
||||||
property bool gradientsSupported: desktop.gradientsSupported
|
property bool gradientsSupported: desktop.gradientsSupported
|
||||||
|
|
||||||
readonly property int frameMarginLeft: frameContent.frameMarginLeft
|
readonly property int frameMarginLeft: frame.decoration ? frame.decoration.frameMarginLeft : 0
|
||||||
readonly property int frameMarginRight: frameContent.frameMarginRight
|
readonly property int frameMarginRight: frame.decoration ? frame.decoration.frameMarginRight : 0
|
||||||
readonly property int frameMarginTop: frameContent.frameMarginTop
|
readonly property int frameMarginTop: frame.decoration ? frame.decoration.frameMarginTop : 0
|
||||||
readonly property int frameMarginBottom: frameContent.frameMarginBottom
|
readonly property int frameMarginBottom: frame.decoration ? frame.decoration.frameMarginBottom : 0
|
||||||
|
|
||||||
// Frames always fill their parents, but their decorations may extend
|
// Frames always fill their parents, but their decorations may extend
|
||||||
// beyond the window via negative margin sizes
|
// beyond the window via negative margin sizes
|
||||||
|
@ -103,16 +103,14 @@ Item {
|
||||||
}
|
}
|
||||||
onReleased: {
|
onReleased: {
|
||||||
if (hid) {
|
if (hid) {
|
||||||
pane.visible = true
|
window.content.visible = true
|
||||||
frameContent.visible = true
|
|
||||||
hid = false;
|
hid = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
onPositionChanged: {
|
onPositionChanged: {
|
||||||
if (pressed) {
|
if (pressed) {
|
||||||
if (pane.visible) {
|
if (window.content.visible) {
|
||||||
pane.visible = false;
|
window.content.visible = false;
|
||||||
frameContent.visible = false
|
|
||||||
hid = true;
|
hid = true;
|
||||||
}
|
}
|
||||||
var delta = Qt.vector2d(mouseX, mouseY).minus(pressOrigin);
|
var delta = Qt.vector2d(mouseX, mouseY).minus(pressOrigin);
|
||||||
|
|
|
@ -16,81 +16,11 @@ import "../styles-uit"
|
||||||
|
|
||||||
Frame {
|
Frame {
|
||||||
HifiConstants { id: hifi }
|
HifiConstants { id: hifi }
|
||||||
property bool horizontalSpacers: false
|
property alias horizontalSpacers: decoration.horizontalSpacers
|
||||||
property bool verticalSpacers: false
|
property alias verticalSpacers: decoration.verticalSpacers
|
||||||
|
|
||||||
Rectangle {
|
ToolFrameDecoration {
|
||||||
// Dialog frame
|
id: decoration
|
||||||
id: frameContent
|
|
||||||
readonly property int frameMargin: 6
|
|
||||||
readonly property int frameMarginLeft: frameMargin + (horizontalSpacers ? 12 : 0)
|
|
||||||
readonly property int frameMarginRight: frameMargin + (horizontalSpacers ? 12 : 0)
|
|
||||||
readonly property int frameMarginTop: frameMargin + (verticalSpacers ? 12 : 0)
|
|
||||||
readonly property int frameMarginBottom: frameMargin + (verticalSpacers ? 12 : 0)
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
visible: horizontalSpacers
|
|
||||||
anchors.left: parent.left
|
|
||||||
anchors.leftMargin: 6
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
width: 8
|
|
||||||
height: window.height
|
|
||||||
color: "gray";
|
|
||||||
radius: 4
|
|
||||||
}
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
visible: horizontalSpacers
|
|
||||||
anchors.right: parent.right
|
|
||||||
anchors.rightMargin: 6
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
width: 8
|
|
||||||
height: window.height
|
|
||||||
color: "gray";
|
|
||||||
radius: 4
|
|
||||||
}
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
visible: verticalSpacers
|
|
||||||
anchors.top: parent.top
|
|
||||||
anchors.topMargin: 6
|
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
|
||||||
height: 8
|
|
||||||
width: window.width
|
|
||||||
color: "gray";
|
|
||||||
radius: 4
|
|
||||||
}
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
visible: verticalSpacers
|
|
||||||
anchors.bottom: parent.bottom
|
|
||||||
anchors.bottomMargin: 6
|
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
|
||||||
height: 8
|
|
||||||
width: window.width
|
|
||||||
color: "gray";
|
|
||||||
radius: 4
|
|
||||||
}
|
|
||||||
|
|
||||||
anchors {
|
|
||||||
leftMargin: -frameMarginLeft
|
|
||||||
rightMargin: -frameMarginRight
|
|
||||||
topMargin: -frameMarginTop
|
|
||||||
bottomMargin: -frameMarginBottom
|
|
||||||
}
|
|
||||||
anchors.fill: parent
|
|
||||||
color: hifi.colors.baseGrayHighlight40
|
|
||||||
border {
|
|
||||||
width: hifi.dimensions.borderWidth
|
|
||||||
color: hifi.colors.faintGray50
|
|
||||||
}
|
|
||||||
radius: hifi.dimensions.borderRadius / 2
|
|
||||||
|
|
||||||
// Enable dragging of the window
|
|
||||||
MouseArea {
|
|
||||||
anchors.fill: parent
|
|
||||||
drag.target: window
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
96
interface/resources/qml/windows/ToolFrameDecoration.qml
Normal file
96
interface/resources/qml/windows/ToolFrameDecoration.qml
Normal file
|
@ -0,0 +1,96 @@
|
||||||
|
//
|
||||||
|
// DefaultFrame.qml
|
||||||
|
//
|
||||||
|
// Created by Bradley Austin Davis on 12 Jan 2016
|
||||||
|
// Copyright 2016 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
|
||||||
|
import QtQuick 2.5
|
||||||
|
import QtGraphicalEffects 1.0
|
||||||
|
|
||||||
|
import "."
|
||||||
|
import "../styles-uit"
|
||||||
|
|
||||||
|
Decoration {
|
||||||
|
id: root
|
||||||
|
HifiConstants { id: hifi }
|
||||||
|
|
||||||
|
property bool horizontalSpacers: false
|
||||||
|
property bool verticalSpacers: false
|
||||||
|
|
||||||
|
// Dialog frame
|
||||||
|
property int spacerWidth: 8
|
||||||
|
property int spacerRadius: 4
|
||||||
|
property int spacerMargin: 12
|
||||||
|
frameMargin: 6
|
||||||
|
frameMarginLeft: frameMargin + (horizontalSpacers ? spacerMargin : 0)
|
||||||
|
frameMarginRight: frameMargin + (horizontalSpacers ? spacerMargin : 0)
|
||||||
|
frameMarginTop: frameMargin + (verticalSpacers ? spacerMargin : 0)
|
||||||
|
frameMarginBottom: frameMargin + (verticalSpacers ? spacerMargin : 0)
|
||||||
|
radius: hifi.dimensions.borderRadius / 2
|
||||||
|
|
||||||
|
onInflateDecorations: {
|
||||||
|
if (!HMD.active) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
root.frameMargin = 18
|
||||||
|
root.spacerWidth = 16
|
||||||
|
root.spacerRadius = 8
|
||||||
|
root.spacerMargin = 8
|
||||||
|
}
|
||||||
|
|
||||||
|
onDeflateDecorations: {
|
||||||
|
root.frameMargin = 6
|
||||||
|
root.spacerWidth = 8
|
||||||
|
root.spacerRadius = 4
|
||||||
|
root.spacerMargin = 12
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
visible: horizontalSpacers
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.leftMargin: 6
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
width: root.spacerWidth
|
||||||
|
height: decoration.height - 12
|
||||||
|
color: "gray";
|
||||||
|
radius: root.spacerRadius
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
visible: horizontalSpacers
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.rightMargin: 6
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
width: root.spacerWidth
|
||||||
|
height: decoration.height - 12
|
||||||
|
color: "gray";
|
||||||
|
radius: root.spacerRadius
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
visible: verticalSpacers
|
||||||
|
anchors.top: parent.top
|
||||||
|
anchors.topMargin: 6
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
height: root.spacerWidth
|
||||||
|
width: decoration.width - 12
|
||||||
|
color: "gray";
|
||||||
|
radius: root.spacerRadius
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
visible: verticalSpacers
|
||||||
|
anchors.bottom: parent.bottom
|
||||||
|
anchors.bottomMargin: 6
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
height: root.spacerWidth
|
||||||
|
width: decoration.width - 12
|
||||||
|
color: "gray";
|
||||||
|
radius: root.spacerRadius
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -31,6 +31,8 @@ Fadable {
|
||||||
// Signals
|
// Signals
|
||||||
//
|
//
|
||||||
signal windowDestroyed();
|
signal windowDestroyed();
|
||||||
|
signal mouseEntered();
|
||||||
|
signal mouseExited();
|
||||||
|
|
||||||
//
|
//
|
||||||
// Native properties
|
// Native properties
|
||||||
|
@ -113,11 +115,14 @@ Fadable {
|
||||||
propagateComposedEvents: true
|
propagateComposedEvents: true
|
||||||
acceptedButtons: Qt.AllButtons
|
acceptedButtons: Qt.AllButtons
|
||||||
enabled: window.visible
|
enabled: window.visible
|
||||||
|
hoverEnabled: true
|
||||||
onPressed: {
|
onPressed: {
|
||||||
//console.log("Pressed on activator area");
|
//console.log("Pressed on activator area");
|
||||||
window.raise();
|
window.raise();
|
||||||
mouse.accepted = false;
|
mouse.accepted = false;
|
||||||
}
|
}
|
||||||
|
onEntered: window.mouseEntered();
|
||||||
|
onExited: window.mouseExited();
|
||||||
}
|
}
|
||||||
|
|
||||||
// This mouse area serves to swallow mouse events while the mouse is over the window
|
// This mouse area serves to swallow mouse events while the mouse is over the window
|
||||||
|
@ -287,4 +292,7 @@ Fadable {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onMouseEntered: console.log("Mouse entered " + window)
|
||||||
|
onMouseExited: console.log("Mouse exited " + window)
|
||||||
}
|
}
|
||||||
|
|
35
interface/resources/shaders/hmd_hand_lasers.frag
Normal file
35
interface/resources/shaders/hmd_hand_lasers.frag
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
//
|
||||||
|
// Created by Bradley Austin Davis on 2016/07/11
|
||||||
|
// Copyright 2013-2016 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
|
||||||
|
#version 410 core
|
||||||
|
|
||||||
|
uniform vec4 color = vec4(1.0, 1.0, 1.0, 1.0);
|
||||||
|
|
||||||
|
layout(location = 0) in vec3 inLineDistance;
|
||||||
|
|
||||||
|
out vec4 FragColor;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
vec2 d = inLineDistance.xy;
|
||||||
|
d.y = abs(d.y);
|
||||||
|
d.x = abs(d.x);
|
||||||
|
if (d.x > 1.0) {
|
||||||
|
d.x = (d.x - 1.0) / 0.02;
|
||||||
|
} else {
|
||||||
|
d.x = 0.0;
|
||||||
|
}
|
||||||
|
float alpha = 1.0 - length(d);
|
||||||
|
if (alpha <= 0.0) {
|
||||||
|
discard;
|
||||||
|
}
|
||||||
|
alpha = pow(alpha, 10.0);
|
||||||
|
if (alpha < 0.05) {
|
||||||
|
discard;
|
||||||
|
}
|
||||||
|
FragColor = vec4(color.rgb, alpha);
|
||||||
|
}
|
70
interface/resources/shaders/hmd_hand_lasers.geom
Normal file
70
interface/resources/shaders/hmd_hand_lasers.geom
Normal file
|
@ -0,0 +1,70 @@
|
||||||
|
//
|
||||||
|
// Created by Bradley Austin Davis on 2016/07/11
|
||||||
|
// Copyright 2013-2016 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
#version 410 core
|
||||||
|
#extension GL_EXT_geometry_shader4 : enable
|
||||||
|
|
||||||
|
layout(location = 0) out vec3 outLineDistance;
|
||||||
|
|
||||||
|
layout(lines) in;
|
||||||
|
layout(triangle_strip, max_vertices = 24) out;
|
||||||
|
|
||||||
|
vec3[2] getOrthogonals(in vec3 n, float scale) {
|
||||||
|
float yDot = abs(dot(n, vec3(0, 1, 0)));
|
||||||
|
|
||||||
|
vec3 result[2];
|
||||||
|
if (yDot < 0.9) {
|
||||||
|
result[0] = normalize(cross(n, vec3(0, 1, 0)));
|
||||||
|
} else {
|
||||||
|
result[0] = normalize(cross(n, vec3(1, 0, 0)));
|
||||||
|
}
|
||||||
|
// The cross of result[0] and n is orthogonal to both, which are orthogonal to each other
|
||||||
|
result[1] = cross(result[0], n);
|
||||||
|
result[0] *= scale;
|
||||||
|
result[1] *= scale;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
vec2 orthogonal(vec2 v) {
|
||||||
|
vec2 result = v.yx;
|
||||||
|
result.y *= -1.0;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
vec2 endpoints[2];
|
||||||
|
for (int i = 0; i < 2; ++i) {
|
||||||
|
endpoints[i] = gl_PositionIn[i].xy / gl_PositionIn[i].w;
|
||||||
|
}
|
||||||
|
vec2 lineNormal = normalize(endpoints[1] - endpoints[0]);
|
||||||
|
vec2 lineOrthogonal = orthogonal(lineNormal);
|
||||||
|
lineNormal *= 0.02;
|
||||||
|
lineOrthogonal *= 0.02;
|
||||||
|
|
||||||
|
gl_Position = gl_PositionIn[0];
|
||||||
|
gl_Position.xy -= lineOrthogonal;
|
||||||
|
outLineDistance = vec3(-1.02, -1, gl_Position.z);
|
||||||
|
EmitVertex();
|
||||||
|
|
||||||
|
gl_Position = gl_PositionIn[0];
|
||||||
|
gl_Position.xy += lineOrthogonal;
|
||||||
|
outLineDistance = vec3(-1.02, 1, gl_Position.z);
|
||||||
|
EmitVertex();
|
||||||
|
|
||||||
|
gl_Position = gl_PositionIn[1];
|
||||||
|
gl_Position.xy -= lineOrthogonal;
|
||||||
|
outLineDistance = vec3(1.02, -1, gl_Position.z);
|
||||||
|
EmitVertex();
|
||||||
|
|
||||||
|
gl_Position = gl_PositionIn[1];
|
||||||
|
gl_Position.xy += lineOrthogonal;
|
||||||
|
outLineDistance = vec3(1.02, 1, gl_Position.z);
|
||||||
|
EmitVertex();
|
||||||
|
|
||||||
|
EndPrimitive();
|
||||||
|
}
|
15
interface/resources/shaders/hmd_hand_lasers.vert
Normal file
15
interface/resources/shaders/hmd_hand_lasers.vert
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
//
|
||||||
|
// Created by Bradley Austin Davis on 2016/07/11
|
||||||
|
// Copyright 2013-2016 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
#version 410 core
|
||||||
|
uniform mat4 mvp = mat4(1);
|
||||||
|
|
||||||
|
in vec3 Position;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
gl_Position = mvp * vec4(Position, 1);
|
||||||
|
}
|
78
interface/resources/shaders/hmd_reproject.frag
Normal file
78
interface/resources/shaders/hmd_reproject.frag
Normal file
|
@ -0,0 +1,78 @@
|
||||||
|
//
|
||||||
|
// Created by Bradley Austin Davis on 2016/07/11
|
||||||
|
// Copyright 2013-2016 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
|
||||||
|
#version 410 core
|
||||||
|
|
||||||
|
uniform sampler2D sampler;
|
||||||
|
uniform mat3 reprojection = mat3(1);
|
||||||
|
uniform mat4 inverseProjections[2];
|
||||||
|
uniform mat4 projections[2];
|
||||||
|
|
||||||
|
in vec2 vTexCoord;
|
||||||
|
in vec3 vPosition;
|
||||||
|
|
||||||
|
out vec4 FragColor;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
vec2 uv = vTexCoord;
|
||||||
|
|
||||||
|
mat4 eyeInverseProjection;
|
||||||
|
mat4 eyeProjection;
|
||||||
|
|
||||||
|
float xoffset = 1.0;
|
||||||
|
vec2 uvmin = vec2(0.0);
|
||||||
|
vec2 uvmax = vec2(1.0);
|
||||||
|
// determine the correct projection and inverse projection to use.
|
||||||
|
if (vTexCoord.x < 0.5) {
|
||||||
|
uvmax.x = 0.5;
|
||||||
|
eyeInverseProjection = inverseProjections[0];
|
||||||
|
eyeProjection = projections[0];
|
||||||
|
} else {
|
||||||
|
xoffset = -1.0;
|
||||||
|
uvmin.x = 0.5;
|
||||||
|
uvmax.x = 1.0;
|
||||||
|
eyeInverseProjection = inverseProjections[1];
|
||||||
|
eyeProjection = projections[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Account for stereo in calculating the per-eye NDC coordinates
|
||||||
|
vec4 ndcSpace = vec4(vPosition, 1.0);
|
||||||
|
ndcSpace.x *= 2.0;
|
||||||
|
ndcSpace.x += xoffset;
|
||||||
|
|
||||||
|
// Convert from NDC to eyespace
|
||||||
|
vec4 eyeSpace = eyeInverseProjection * ndcSpace;
|
||||||
|
eyeSpace /= eyeSpace.w;
|
||||||
|
|
||||||
|
// Convert to a noramlized ray
|
||||||
|
vec3 ray = eyeSpace.xyz;
|
||||||
|
ray = normalize(ray);
|
||||||
|
|
||||||
|
// Adjust the ray by the rotation
|
||||||
|
ray = reprojection * ray;
|
||||||
|
|
||||||
|
// Project back on to the texture plane
|
||||||
|
ray *= eyeSpace.z / ray.z;
|
||||||
|
|
||||||
|
// Update the eyespace vector
|
||||||
|
eyeSpace.xyz = ray;
|
||||||
|
|
||||||
|
// Reproject back into NDC
|
||||||
|
ndcSpace = eyeProjection * eyeSpace;
|
||||||
|
ndcSpace /= ndcSpace.w;
|
||||||
|
ndcSpace.x -= xoffset;
|
||||||
|
ndcSpace.x /= 2.0;
|
||||||
|
|
||||||
|
// Calculate the new UV coordinates
|
||||||
|
uv = (ndcSpace.xy / 2.0) + 0.5;
|
||||||
|
if (any(greaterThan(uv, uvmax)) || any(lessThan(uv, uvmin))) {
|
||||||
|
FragColor = vec4(0.0, 0.0, 0.0, 1.0);
|
||||||
|
} else {
|
||||||
|
FragColor = texture(sampler, uv);
|
||||||
|
}
|
||||||
|
}
|
20
interface/resources/shaders/hmd_reproject.vert
Normal file
20
interface/resources/shaders/hmd_reproject.vert
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
//
|
||||||
|
// Created by Bradley Austin Davis on 2016/07/11
|
||||||
|
// Copyright 2013-2016 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
|
||||||
|
#version 410 core
|
||||||
|
in vec3 Position;
|
||||||
|
in vec2 TexCoord;
|
||||||
|
|
||||||
|
out vec3 vPosition;
|
||||||
|
out vec2 vTexCoord;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
gl_Position = vec4(Position, 1);
|
||||||
|
vTexCoord = TexCoord;
|
||||||
|
vPosition = Position;
|
||||||
|
}
|
65
interface/resources/shaders/hmd_ui_glow.frag
Normal file
65
interface/resources/shaders/hmd_ui_glow.frag
Normal file
|
@ -0,0 +1,65 @@
|
||||||
|
//
|
||||||
|
// Created by Bradley Austin Davis on 2016/07/11
|
||||||
|
// Copyright 2013-2016 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
|
||||||
|
#version 410 core
|
||||||
|
|
||||||
|
uniform sampler2D sampler;
|
||||||
|
uniform float alpha = 1.0;
|
||||||
|
uniform vec4 glowPoints = vec4(-1);
|
||||||
|
uniform vec4 glowColors[2];
|
||||||
|
uniform vec2 resolution = vec2(3960.0, 1188.0);
|
||||||
|
uniform float radius = 0.005;
|
||||||
|
|
||||||
|
in vec3 vPosition;
|
||||||
|
in vec2 vTexCoord;
|
||||||
|
in vec4 vGlowPoints;
|
||||||
|
|
||||||
|
out vec4 FragColor;
|
||||||
|
|
||||||
|
float easeInOutCubic(float f) {
|
||||||
|
const float d = 1.0;
|
||||||
|
const float b = 0.0;
|
||||||
|
const float c = 1.0;
|
||||||
|
float t = f;
|
||||||
|
if ((t /= d / 2.0) < 1.0) return c / 2.0 * t * t * t + b;
|
||||||
|
return c / 2.0 * ((t -= 2.0) * t * t + 2.0) + b;
|
||||||
|
}
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
vec2 aspect = resolution;
|
||||||
|
aspect /= resolution.x;
|
||||||
|
FragColor = texture(sampler, vTexCoord);
|
||||||
|
|
||||||
|
float glowIntensity = 0.0;
|
||||||
|
float dist1 = distance(vTexCoord * aspect, glowPoints.xy * aspect);
|
||||||
|
float dist2 = distance(vTexCoord * aspect, glowPoints.zw * aspect);
|
||||||
|
float dist = min(dist1, dist2);
|
||||||
|
vec3 glowColor = glowColors[0].rgb;
|
||||||
|
if (dist2 < dist1) {
|
||||||
|
glowColor = glowColors[1].rgb;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dist <= radius) {
|
||||||
|
glowIntensity = 1.0 - (dist / radius);
|
||||||
|
glowColor.rgb = pow(glowColor, vec3(1.0 - glowIntensity));
|
||||||
|
glowIntensity = easeInOutCubic(glowIntensity);
|
||||||
|
glowIntensity = pow(glowIntensity, 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (alpha <= 0.0) {
|
||||||
|
if (glowIntensity <= 0.0) {
|
||||||
|
discard;
|
||||||
|
}
|
||||||
|
|
||||||
|
FragColor = vec4(glowColor, glowIntensity);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
FragColor.rgb = mix(FragColor.rgb, glowColor.rgb, glowIntensity);
|
||||||
|
FragColor.a *= alpha;
|
||||||
|
}
|
23
interface/resources/shaders/hmd_ui_glow.vert
Normal file
23
interface/resources/shaders/hmd_ui_glow.vert
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
//
|
||||||
|
// Created by Bradley Austin Davis on 2016/07/11
|
||||||
|
// Copyright 2013-2016 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
|
||||||
|
#version 410 core
|
||||||
|
|
||||||
|
uniform mat4 mvp = mat4(1);
|
||||||
|
|
||||||
|
in vec3 Position;
|
||||||
|
in vec2 TexCoord;
|
||||||
|
|
||||||
|
out vec3 vPosition;
|
||||||
|
out vec2 vTexCoord;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
gl_Position = mvp * vec4(Position, 1);
|
||||||
|
vTexCoord = TexCoord;
|
||||||
|
vPosition = Position;
|
||||||
|
}
|
|
@ -1525,7 +1525,6 @@ void Application::initializeUi() {
|
||||||
|
|
||||||
// For some reason there is already an "Application" object in the QML context,
|
// For some reason there is already an "Application" object in the QML context,
|
||||||
// though I can't find it. Hence, "ApplicationInterface"
|
// though I can't find it. Hence, "ApplicationInterface"
|
||||||
rootContext->setContextProperty("SnapshotUploader", new SnapshotUploader());
|
|
||||||
rootContext->setContextProperty("ApplicationInterface", this);
|
rootContext->setContextProperty("ApplicationInterface", this);
|
||||||
rootContext->setContextProperty("Audio", &AudioScriptingInterface::getInstance());
|
rootContext->setContextProperty("Audio", &AudioScriptingInterface::getInstance());
|
||||||
rootContext->setContextProperty("Controller", DependencyManager::get<controller::ScriptingInterface>().data());
|
rootContext->setContextProperty("Controller", DependencyManager::get<controller::ScriptingInterface>().data());
|
||||||
|
@ -5046,16 +5045,9 @@ void Application::takeSnapshot() {
|
||||||
player->setMedia(QUrl::fromLocalFile(inf.absoluteFilePath()));
|
player->setMedia(QUrl::fromLocalFile(inf.absoluteFilePath()));
|
||||||
player->play();
|
player->play();
|
||||||
|
|
||||||
QString fileName = Snapshot::saveSnapshot(getActiveDisplayPlugin()->getScreenshot());
|
QString path = Snapshot::saveSnapshot(getActiveDisplayPlugin()->getScreenshot());
|
||||||
|
|
||||||
auto accountManager = DependencyManager::get<AccountManager>();
|
emit DependencyManager::get<WindowScriptingInterface>()->snapshotTaken(path);
|
||||||
if (!accountManager->isLoggedIn()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
DependencyManager::get<OffscreenUi>()->load("hifi/dialogs/SnapshotShareDialog.qml", [=](QQmlContext*, QObject* dialog) {
|
|
||||||
dialog->setProperty("source", QUrl::fromLocalFile(fileName));
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
float Application::getRenderResolutionScale() const {
|
float Application::getRenderResolutionScale() const {
|
||||||
|
|
|
@ -1085,6 +1085,15 @@ void Avatar::computeShapeInfo(ShapeInfo& shapeInfo) {
|
||||||
shapeInfo.setOffset(uniformScale * _skeletonModel->getBoundingCapsuleOffset());
|
shapeInfo.setOffset(uniformScale * _skeletonModel->getBoundingCapsuleOffset());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Avatar::getCapsule(glm::vec3& start, glm::vec3& end, float& radius) {
|
||||||
|
ShapeInfo shapeInfo;
|
||||||
|
computeShapeInfo(shapeInfo);
|
||||||
|
glm::vec3 halfExtents = shapeInfo.getHalfExtents(); // x = radius, y = halfHeight
|
||||||
|
start = getPosition() - glm::vec3(0, halfExtents.y, 0) + shapeInfo.getOffset();
|
||||||
|
end = getPosition() + glm::vec3(0, halfExtents.y, 0) + shapeInfo.getOffset();
|
||||||
|
radius = halfExtents.x;
|
||||||
|
}
|
||||||
|
|
||||||
void Avatar::setMotionState(AvatarMotionState* motionState) {
|
void Avatar::setMotionState(AvatarMotionState* motionState) {
|
||||||
_motionState = motionState;
|
_motionState = motionState;
|
||||||
}
|
}
|
||||||
|
|
|
@ -154,6 +154,7 @@ public:
|
||||||
virtual void rebuildCollisionShape();
|
virtual void rebuildCollisionShape();
|
||||||
|
|
||||||
virtual void computeShapeInfo(ShapeInfo& shapeInfo);
|
virtual void computeShapeInfo(ShapeInfo& shapeInfo);
|
||||||
|
void getCapsule(glm::vec3& start, glm::vec3& end, float& radius);
|
||||||
|
|
||||||
AvatarMotionState* getMotionState() { return _motionState; }
|
AvatarMotionState* getMotionState() { return _motionState; }
|
||||||
|
|
||||||
|
|
|
@ -398,3 +398,76 @@ AvatarSharedPointer AvatarManager::getAvatarBySessionID(const QUuid& sessionID)
|
||||||
|
|
||||||
return findAvatar(sessionID);
|
return findAvatar(sessionID);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
RayToAvatarIntersectionResult AvatarManager::findRayIntersection(const PickRay& ray,
|
||||||
|
const QScriptValue& avatarIdsToInclude,
|
||||||
|
const QScriptValue& avatarIdsToDiscard) {
|
||||||
|
RayToAvatarIntersectionResult result;
|
||||||
|
if (QThread::currentThread() != thread()) {
|
||||||
|
QMetaObject::invokeMethod(const_cast<AvatarManager*>(this), "findRayIntersection", Qt::BlockingQueuedConnection,
|
||||||
|
Q_RETURN_ARG(RayToAvatarIntersectionResult, result),
|
||||||
|
Q_ARG(const PickRay&, ray),
|
||||||
|
Q_ARG(const QScriptValue&, avatarIdsToInclude),
|
||||||
|
Q_ARG(const QScriptValue&, avatarIdsToDiscard));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
QVector<EntityItemID> avatarsToInclude = qVectorEntityItemIDFromScriptValue(avatarIdsToInclude);
|
||||||
|
QVector<EntityItemID> avatarsToDiscard = qVectorEntityItemIDFromScriptValue(avatarIdsToDiscard);
|
||||||
|
|
||||||
|
glm::vec3 normDirection = glm::normalize(ray.direction);
|
||||||
|
|
||||||
|
for (auto avatarData : _avatarHash) {
|
||||||
|
auto avatar = std::static_pointer_cast<Avatar>(avatarData);
|
||||||
|
if ((avatarsToInclude.size() > 0 && !avatarsToInclude.contains(avatar->getID())) ||
|
||||||
|
(avatarsToDiscard.size() > 0 && avatarsToDiscard.contains(avatar->getID()))) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
float distance;
|
||||||
|
BoxFace face;
|
||||||
|
glm::vec3 surfaceNormal;
|
||||||
|
|
||||||
|
SkeletonModelPointer avatarModel = avatar->getSkeletonModel();
|
||||||
|
|
||||||
|
// It's better to intersect the ray against the avatar's actual mesh, but this is currently difficult to
|
||||||
|
// do, because the transformed mesh data only exists over in GPU-land. As a compromise, this code
|
||||||
|
// intersects against the avatars capsule and then against the (T-pose) mesh. The end effect is that picking
|
||||||
|
// against the avatar is sort-of right, but you likely wont be able to pick against the arms.
|
||||||
|
|
||||||
|
// TODO -- find a way to extract transformed avatar mesh data from the rendering engine.
|
||||||
|
|
||||||
|
// if we weren't picking against the capsule, we would want to pick against the avatarBounds...
|
||||||
|
// AABox avatarBounds = avatarModel->getRenderableMeshBound();
|
||||||
|
// if (!avatarBounds.findRayIntersection(ray.origin, normDirection, distance, face, surfaceNormal)) {
|
||||||
|
// // ray doesn't intersect avatar's bounding-box
|
||||||
|
// continue;
|
||||||
|
// }
|
||||||
|
|
||||||
|
glm::vec3 start;
|
||||||
|
glm::vec3 end;
|
||||||
|
float radius;
|
||||||
|
avatar->getCapsule(start, end, radius);
|
||||||
|
bool intersects = findRayCapsuleIntersection(ray.origin, normDirection, start, end, radius, distance);
|
||||||
|
if (!intersects) {
|
||||||
|
// ray doesn't intersect avatar's capsule
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString extraInfo;
|
||||||
|
intersects = avatarModel->findRayIntersectionAgainstSubMeshes(ray.origin, normDirection,
|
||||||
|
distance, face, surfaceNormal, extraInfo, true);
|
||||||
|
|
||||||
|
if (intersects && (!result.intersects || distance < result.distance)) {
|
||||||
|
result.intersects = true;
|
||||||
|
result.avatarID = avatar->getID();
|
||||||
|
result.distance = distance;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result.intersects) {
|
||||||
|
result.intersection = ray.origin + normDirection * result.distance;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
|
@ -70,6 +70,10 @@ public:
|
||||||
|
|
||||||
void addAvatarToSimulation(Avatar* avatar);
|
void addAvatarToSimulation(Avatar* avatar);
|
||||||
|
|
||||||
|
Q_INVOKABLE RayToAvatarIntersectionResult findRayIntersection(const PickRay& ray,
|
||||||
|
const QScriptValue& avatarIdsToInclude = QScriptValue(),
|
||||||
|
const QScriptValue& avatarIdsToDiscard = QScriptValue());
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void setShouldShowReceiveStats(bool shouldShowReceiveStats) { _shouldShowReceiveStats = shouldShowReceiveStats; }
|
void setShouldShowReceiveStats(bool shouldShowReceiveStats) { _shouldShowReceiveStats = shouldShowReceiveStats; }
|
||||||
void updateAvatarRenderStatus(bool shouldRenderAvatars);
|
void updateAvatarRenderStatus(bool shouldRenderAvatars);
|
||||||
|
|
|
@ -212,8 +212,8 @@ public:
|
||||||
virtual void clearJointsData() override;
|
virtual void clearJointsData() override;
|
||||||
|
|
||||||
Q_INVOKABLE void useFullAvatarURL(const QUrl& fullAvatarURL, const QString& modelName = QString());
|
Q_INVOKABLE void useFullAvatarURL(const QUrl& fullAvatarURL, const QString& modelName = QString());
|
||||||
Q_INVOKABLE const QUrl& getFullAvatarURLFromPreferences() const { return _fullAvatarURLFromPreferences; }
|
Q_INVOKABLE QUrl getFullAvatarURLFromPreferences() const { return _fullAvatarURLFromPreferences; }
|
||||||
Q_INVOKABLE const QString& getFullAvatarModelName() const { return _fullAvatarModelName; }
|
Q_INVOKABLE QString getFullAvatarModelName() const { return _fullAvatarModelName; }
|
||||||
void resetFullAvatarURL();
|
void resetFullAvatarURL();
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
|
|
||||||
#include <display-plugins/DisplayPlugin.h>
|
#include <display-plugins/DisplayPlugin.h>
|
||||||
#include <display-plugins/CompositorHelper.h>
|
#include <display-plugins/CompositorHelper.h>
|
||||||
|
#include <OffscreenUi.h>
|
||||||
#include <avatar/AvatarManager.h>
|
#include <avatar/AvatarManager.h>
|
||||||
#include "Application.h"
|
#include "Application.h"
|
||||||
|
|
||||||
|
@ -110,13 +111,17 @@ QString HMDScriptingInterface::preferredAudioOutput() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool HMDScriptingInterface::setHandLasers(int hands, bool enabled, const glm::vec4& color, const glm::vec3& direction) const {
|
bool HMDScriptingInterface::setHandLasers(int hands, bool enabled, const glm::vec4& color, const glm::vec3& direction) const {
|
||||||
|
auto offscreenUi = DependencyManager::get<OffscreenUi>();
|
||||||
|
offscreenUi->executeOnUiThread([offscreenUi, enabled] {
|
||||||
|
offscreenUi->getDesktop()->setProperty("hmdHandMouseActive", enabled);
|
||||||
|
});
|
||||||
return qApp->getActiveDisplayPlugin()->setHandLaser(hands,
|
return qApp->getActiveDisplayPlugin()->setHandLaser(hands,
|
||||||
enabled ? DisplayPlugin::HandLaserMode::Overlay : DisplayPlugin::HandLaserMode::None,
|
enabled ? DisplayPlugin::HandLaserMode::Overlay : DisplayPlugin::HandLaserMode::None,
|
||||||
color, direction);
|
color, direction);
|
||||||
}
|
}
|
||||||
|
|
||||||
void HMDScriptingInterface::disableHandLasers(int hands) const {
|
void HMDScriptingInterface::disableHandLasers(int hands) const {
|
||||||
qApp->getActiveDisplayPlugin()->setHandLaser(hands, DisplayPlugin::HandLaserMode::None);
|
setHandLasers(hands, false, vec4(0), vec3(0));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool HMDScriptingInterface::suppressKeyboard() {
|
bool HMDScriptingInterface::suppressKeyboard() {
|
||||||
|
|
|
@ -46,6 +46,7 @@ signals:
|
||||||
void domainChanged(const QString& domainHostname);
|
void domainChanged(const QString& domainHostname);
|
||||||
void svoImportRequested(const QString& url);
|
void svoImportRequested(const QString& url);
|
||||||
void domainConnectionRefused(const QString& reasonMessage, int reasonCode);
|
void domainConnectionRefused(const QString& reasonMessage, int reasonCode);
|
||||||
|
void snapshotTaken(const QString& path);
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
WebWindowClass* doCreateWebWindow(const QString& title, const QString& url, int width, int height);
|
WebWindowClass* doCreateWebWindow(const QString& title, const QString& url, int width, int height);
|
||||||
|
|
|
@ -35,7 +35,7 @@ void setupPreferences() {
|
||||||
MyAvatar* myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar();
|
MyAvatar* myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar();
|
||||||
static const QString AVATAR_BASICS { "Avatar Basics" };
|
static const QString AVATAR_BASICS { "Avatar Basics" };
|
||||||
{
|
{
|
||||||
auto getter = [=]()->QString {return myAvatar->getDisplayName(); };
|
auto getter = [=]()->QString { return myAvatar->getDisplayName(); };
|
||||||
auto setter = [=](const QString& value) { myAvatar->setDisplayName(value); };
|
auto setter = [=](const QString& value) { myAvatar->setDisplayName(value); };
|
||||||
auto preference = new EditPreference(AVATAR_BASICS, "Avatar display name (optional)", getter, setter);
|
auto preference = new EditPreference(AVATAR_BASICS, "Avatar display name (optional)", getter, setter);
|
||||||
preference->setPlaceholderText("Not showing a name");
|
preference->setPlaceholderText("Not showing a name");
|
||||||
|
@ -43,7 +43,7 @@ void setupPreferences() {
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
auto getter = [=]()->QString {return myAvatar->getCollisionSoundURL(); };
|
auto getter = [=]()->QString { return myAvatar->getCollisionSoundURL(); };
|
||||||
auto setter = [=](const QString& value) { myAvatar->setCollisionSoundURL(value); };
|
auto setter = [=](const QString& value) { myAvatar->setCollisionSoundURL(value); };
|
||||||
auto preference = new EditPreference(AVATAR_BASICS, "Avatar collision sound URL (optional)", getter, setter);
|
auto preference = new EditPreference(AVATAR_BASICS, "Avatar collision sound URL (optional)", getter, setter);
|
||||||
preference->setPlaceholderText("Enter the URL of a sound to play when you bump into something");
|
preference->setPlaceholderText("Enter the URL of a sound to play when you bump into something");
|
||||||
|
@ -56,20 +56,24 @@ void setupPreferences() {
|
||||||
auto preference = new AvatarPreference(AVATAR_BASICS, "Appearance", getter, setter);
|
auto preference = new AvatarPreference(AVATAR_BASICS, "Appearance", getter, setter);
|
||||||
preferences->addPreference(preference);
|
preferences->addPreference(preference);
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
auto getter = [=]()->bool {return myAvatar->getSnapTurn(); };
|
auto getter = [=]()->bool { return myAvatar->getSnapTurn(); };
|
||||||
auto setter = [=](bool value) { myAvatar->setSnapTurn(value); };
|
auto setter = [=](bool value) { myAvatar->setSnapTurn(value); };
|
||||||
preferences->addPreference(new CheckPreference(AVATAR_BASICS, "Snap turn when in HMD", getter, setter));
|
preferences->addPreference(new CheckPreference(AVATAR_BASICS, "Snap turn when in HMD", getter, setter));
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
auto getter = [=]()->bool {return myAvatar->getClearOverlayWhenMoving(); };
|
auto getter = [=]()->bool { return myAvatar->getClearOverlayWhenMoving(); };
|
||||||
auto setter = [=](bool value) { myAvatar->setClearOverlayWhenMoving(value); };
|
auto setter = [=](bool value) { myAvatar->setClearOverlayWhenMoving(value); };
|
||||||
preferences->addPreference(new CheckPreference(AVATAR_BASICS, "Clear overlays when moving", getter, setter));
|
preferences->addPreference(new CheckPreference(AVATAR_BASICS, "Clear overlays when moving", getter, setter));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Snapshots
|
||||||
|
static const QString SNAPSHOTS { "Snapshots" };
|
||||||
{
|
{
|
||||||
auto getter = []()->QString { return Snapshot::snapshotsLocation.get(); };
|
auto getter = []()->QString { return Snapshot::snapshotsLocation.get(); };
|
||||||
auto setter = [](const QString& value) { Snapshot::snapshotsLocation.set(value); };
|
auto setter = [](const QString& value) { Snapshot::snapshotsLocation.set(value); };
|
||||||
auto preference = new BrowsePreference("Snapshots", "Put my snapshots here", getter, setter);
|
auto preference = new BrowsePreference(SNAPSHOTS, "Put my snapshots here", getter, setter);
|
||||||
preferences->addPreference(preference);
|
preferences->addPreference(preference);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -85,7 +89,7 @@ void setupPreferences() {
|
||||||
}));
|
}));
|
||||||
|
|
||||||
{
|
{
|
||||||
auto getter = []()->bool {return !Menu::getInstance()->isOptionChecked(MenuOption::DisableActivityLogger); };
|
auto getter = []()->bool { return !Menu::getInstance()->isOptionChecked(MenuOption::DisableActivityLogger); };
|
||||||
auto setter = [](bool value) { Menu::getInstance()->setIsOptionChecked(MenuOption::DisableActivityLogger, !value); };
|
auto setter = [](bool value) { Menu::getInstance()->setIsOptionChecked(MenuOption::DisableActivityLogger, !value); };
|
||||||
preferences->addPreference(new CheckPreference("Privacy", "Send data", getter, setter));
|
preferences->addPreference(new CheckPreference("Privacy", "Send data", getter, setter));
|
||||||
}
|
}
|
||||||
|
@ -184,7 +188,7 @@ void setupPreferences() {
|
||||||
|
|
||||||
static const QString AUDIO("Audio");
|
static const QString AUDIO("Audio");
|
||||||
{
|
{
|
||||||
auto getter = []()->bool {return DependencyManager::get<AudioClient>()->getReceivedAudioStream().getDynamicJitterBuffers(); };
|
auto getter = []()->bool { return DependencyManager::get<AudioClient>()->getReceivedAudioStream().getDynamicJitterBuffers(); };
|
||||||
auto setter = [](bool value) { DependencyManager::get<AudioClient>()->getReceivedAudioStream().setDynamicJitterBuffers(value); };
|
auto setter = [](bool value) { DependencyManager::get<AudioClient>()->getReceivedAudioStream().setDynamicJitterBuffers(value); };
|
||||||
preferences->addPreference(new CheckPreference(AUDIO, "Enable dynamic jitter buffers", getter, setter));
|
preferences->addPreference(new CheckPreference(AUDIO, "Enable dynamic jitter buffers", getter, setter));
|
||||||
}
|
}
|
||||||
|
@ -207,7 +211,7 @@ void setupPreferences() {
|
||||||
preferences->addPreference(preference);
|
preferences->addPreference(preference);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
auto getter = []()->bool {return DependencyManager::get<AudioClient>()->getReceivedAudioStream().getUseStDevForJitterCalc(); };
|
auto getter = []()->bool { return DependencyManager::get<AudioClient>()->getReceivedAudioStream().getUseStDevForJitterCalc(); };
|
||||||
auto setter = [](bool value) { DependencyManager::get<AudioClient>()->getReceivedAudioStream().setUseStDevForJitterCalc(value); };
|
auto setter = [](bool value) { DependencyManager::get<AudioClient>()->getReceivedAudioStream().setUseStDevForJitterCalc(value); };
|
||||||
preferences->addPreference(new CheckPreference(AUDIO, "Use standard deviation for dynamic jitter calc", getter, setter));
|
preferences->addPreference(new CheckPreference(AUDIO, "Use standard deviation for dynamic jitter calc", getter, setter));
|
||||||
}
|
}
|
||||||
|
@ -236,7 +240,7 @@ void setupPreferences() {
|
||||||
preferences->addPreference(preference);
|
preferences->addPreference(preference);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
auto getter = []()->bool {return DependencyManager::get<AudioClient>()->getReceivedAudioStream().getRepetitionWithFade(); };
|
auto getter = []()->bool { return DependencyManager::get<AudioClient>()->getReceivedAudioStream().getRepetitionWithFade(); };
|
||||||
auto setter = [](bool value) { DependencyManager::get<AudioClient>()->getReceivedAudioStream().setRepetitionWithFade(value); };
|
auto setter = [](bool value) { DependencyManager::get<AudioClient>()->getReceivedAudioStream().setRepetitionWithFade(value); };
|
||||||
preferences->addPreference(new CheckPreference(AUDIO, "Repetition with fade", getter, setter));
|
preferences->addPreference(new CheckPreference(AUDIO, "Repetition with fade", getter, setter));
|
||||||
}
|
}
|
||||||
|
@ -250,7 +254,7 @@ void setupPreferences() {
|
||||||
preferences->addPreference(preference);
|
preferences->addPreference(preference);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
auto getter = []()->bool {return DependencyManager::get<AudioClient>()->getOutputStarveDetectionEnabled(); };
|
auto getter = []()->bool { return DependencyManager::get<AudioClient>()->getOutputStarveDetectionEnabled(); };
|
||||||
auto setter = [](bool value) { DependencyManager::get<AudioClient>()->setOutputStarveDetectionEnabled(value); };
|
auto setter = [](bool value) { DependencyManager::get<AudioClient>()->setOutputStarveDetectionEnabled(value); };
|
||||||
auto preference = new CheckPreference(AUDIO, "Output starve detection (automatic buffer size increase)", getter, setter);
|
auto preference = new CheckPreference(AUDIO, "Output starve detection (automatic buffer size increase)", getter, setter);
|
||||||
preferences->addPreference(preference);
|
preferences->addPreference(preference);
|
||||||
|
|
|
@ -44,6 +44,7 @@ const QString URL = "highfidelity_url";
|
||||||
|
|
||||||
Setting::Handle<QString> Snapshot::snapshotsLocation("snapshotsLocation",
|
Setting::Handle<QString> Snapshot::snapshotsLocation("snapshotsLocation",
|
||||||
QStandardPaths::writableLocation(QStandardPaths::DesktopLocation));
|
QStandardPaths::writableLocation(QStandardPaths::DesktopLocation));
|
||||||
|
Setting::Handle<bool> Snapshot::hasSetSnapshotsLocation("hasSetSnapshotsLocation", false);
|
||||||
|
|
||||||
SnapshotMetaData* Snapshot::parseSnapshotData(QString snapshotPath) {
|
SnapshotMetaData* Snapshot::parseSnapshotData(QString snapshotPath) {
|
||||||
|
|
||||||
|
@ -103,7 +104,14 @@ QFile* Snapshot::savedFileForSnapshot(QImage & shot, bool isTemporary) {
|
||||||
const int IMAGE_QUALITY = 100;
|
const int IMAGE_QUALITY = 100;
|
||||||
|
|
||||||
if (!isTemporary) {
|
if (!isTemporary) {
|
||||||
QString snapshotFullPath = snapshotsLocation.get();
|
QString snapshotFullPath;
|
||||||
|
if (!hasSetSnapshotsLocation.get()) {
|
||||||
|
snapshotFullPath = QFileDialog::getExistingDirectory(nullptr, "Choose Snapshots Directory", snapshotsLocation.get());
|
||||||
|
hasSetSnapshotsLocation.set(true);
|
||||||
|
snapshotsLocation.set(snapshotFullPath);
|
||||||
|
} else {
|
||||||
|
snapshotFullPath = snapshotsLocation.get();
|
||||||
|
}
|
||||||
|
|
||||||
if (!snapshotFullPath.endsWith(QDir::separator())) {
|
if (!snapshotFullPath.endsWith(QDir::separator())) {
|
||||||
snapshotFullPath.append(QDir::separator());
|
snapshotFullPath.append(QDir::separator());
|
||||||
|
@ -133,118 +141,3 @@ QFile* Snapshot::savedFileForSnapshot(QImage & shot, bool isTemporary) {
|
||||||
return imageTempFile;
|
return imageTempFile;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const QString FORUM_URL = "https://alphas.highfidelity.io";
|
|
||||||
const QString FORUM_UPLOADS_URL = FORUM_URL + "/uploads";
|
|
||||||
const QString FORUM_POST_URL = FORUM_URL + "/posts";
|
|
||||||
const QString FORUM_REPLY_TO_TOPIC = "244";
|
|
||||||
const QString FORUM_POST_TEMPLATE = "<img src='%1'/><p>%2</p>";
|
|
||||||
const QString SHARE_DEFAULT_ERROR = "The server isn't responding. Please try again in a few minutes.";
|
|
||||||
const QString SUCCESS_LABEL_TEMPLATE = "Success!!! Go check out your image ...<br/><a style='color:#333;text-decoration:none' href='%1'>%1</a>";
|
|
||||||
|
|
||||||
|
|
||||||
QString SnapshotUploader::uploadSnapshot(const QUrl& fileUrl) {
|
|
||||||
auto accountManager = DependencyManager::get<AccountManager>();
|
|
||||||
if (accountManager->getAccountInfo().getDiscourseApiKey().isEmpty()) {
|
|
||||||
OffscreenUi::warning(nullptr, "", "Your Discourse API key is missing, you cannot share snapshots. Please try to relog.");
|
|
||||||
return QString();
|
|
||||||
}
|
|
||||||
|
|
||||||
QHttpPart apiKeyPart;
|
|
||||||
apiKeyPart.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"api_key\""));
|
|
||||||
apiKeyPart.setBody(accountManager->getAccountInfo().getDiscourseApiKey().toLatin1());
|
|
||||||
|
|
||||||
QString filename = fileUrl.toLocalFile();
|
|
||||||
qDebug() << filename;
|
|
||||||
QFile* file = new QFile(filename);
|
|
||||||
Q_ASSERT(file->exists());
|
|
||||||
file->open(QIODevice::ReadOnly);
|
|
||||||
|
|
||||||
QHttpPart imagePart;
|
|
||||||
imagePart.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("image/jpeg"));
|
|
||||||
imagePart.setHeader(QNetworkRequest::ContentDispositionHeader,
|
|
||||||
QVariant("form-data; name=\"file\"; filename=\"" + file->fileName() + "\""));
|
|
||||||
imagePart.setBodyDevice(file);
|
|
||||||
|
|
||||||
QHttpMultiPart* multiPart = new QHttpMultiPart(QHttpMultiPart::FormDataType);
|
|
||||||
file->setParent(multiPart); // we cannot delete the file now, so delete it with the multiPart
|
|
||||||
multiPart->append(apiKeyPart);
|
|
||||||
multiPart->append(imagePart);
|
|
||||||
|
|
||||||
QUrl url(FORUM_UPLOADS_URL);
|
|
||||||
QNetworkRequest request(url);
|
|
||||||
request.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT);
|
|
||||||
|
|
||||||
QString result;
|
|
||||||
QEventLoop loop;
|
|
||||||
|
|
||||||
QSharedPointer<QNetworkReply> reply(NetworkAccessManager::getInstance().post(request, multiPart));
|
|
||||||
QObject::connect(reply.data(), &QNetworkReply::finished, [&] {
|
|
||||||
loop.quit();
|
|
||||||
|
|
||||||
qDebug() << reply->errorString();
|
|
||||||
for (const auto& header : reply->rawHeaderList()) {
|
|
||||||
qDebug() << "Header " << QString(header);
|
|
||||||
}
|
|
||||||
auto replyResult = reply->readAll();
|
|
||||||
qDebug() << QString(replyResult);
|
|
||||||
QJsonDocument jsonResponse = QJsonDocument::fromJson(replyResult);
|
|
||||||
const QJsonObject& responseObject = jsonResponse.object();
|
|
||||||
if (!responseObject.contains("url")) {
|
|
||||||
OffscreenUi::warning(this, "", SHARE_DEFAULT_ERROR);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
result = responseObject["url"].toString();
|
|
||||||
});
|
|
||||||
loop.exec();
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
QString SnapshotUploader::sendForumPost(const QString& snapshotPath, const QString& notes) {
|
|
||||||
// post to Discourse forum
|
|
||||||
QNetworkRequest request;
|
|
||||||
request.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT);
|
|
||||||
QUrl forumUrl(FORUM_POST_URL);
|
|
||||||
|
|
||||||
QUrlQuery query;
|
|
||||||
query.addQueryItem("api_key", DependencyManager::get<AccountManager>()->getAccountInfo().getDiscourseApiKey());
|
|
||||||
query.addQueryItem("topic_id", FORUM_REPLY_TO_TOPIC);
|
|
||||||
query.addQueryItem("raw", FORUM_POST_TEMPLATE.arg(snapshotPath, notes));
|
|
||||||
forumUrl.setQuery(query);
|
|
||||||
|
|
||||||
QByteArray postData = forumUrl.toEncoded(QUrl::RemoveFragment);
|
|
||||||
request.setUrl(forumUrl);
|
|
||||||
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded");
|
|
||||||
|
|
||||||
QNetworkReply* requestReply = NetworkAccessManager::getInstance().post(request, postData);
|
|
||||||
|
|
||||||
QEventLoop loop;
|
|
||||||
QString result;
|
|
||||||
connect(requestReply, &QNetworkReply::finished, [&] {
|
|
||||||
loop.quit();
|
|
||||||
QJsonDocument jsonResponse = QJsonDocument::fromJson(requestReply->readAll());
|
|
||||||
requestReply->deleteLater();
|
|
||||||
const QJsonObject& responseObject = jsonResponse.object();
|
|
||||||
|
|
||||||
if (!responseObject.contains("id")) {
|
|
||||||
QString errorMessage(SHARE_DEFAULT_ERROR);
|
|
||||||
if (responseObject.contains("errors")) {
|
|
||||||
QJsonArray errorArray = responseObject["errors"].toArray();
|
|
||||||
if (!errorArray.first().toString().isEmpty()) {
|
|
||||||
errorMessage = errorArray.first().toString();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
OffscreenUi::warning(this, "", errorMessage);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const QString urlTemplate = "%1/t/%2/%3/%4";
|
|
||||||
result = urlTemplate.arg(FORUM_URL,
|
|
||||||
responseObject["topic_slug"].toString(),
|
|
||||||
QString::number(responseObject["topic_id"].toDouble()),
|
|
||||||
QString::number(responseObject["post_number"].toDouble()));
|
|
||||||
});
|
|
||||||
loop.exec();
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
|
@ -39,16 +39,9 @@ public:
|
||||||
static SnapshotMetaData* parseSnapshotData(QString snapshotPath);
|
static SnapshotMetaData* parseSnapshotData(QString snapshotPath);
|
||||||
|
|
||||||
static Setting::Handle<QString> snapshotsLocation;
|
static Setting::Handle<QString> snapshotsLocation;
|
||||||
|
static Setting::Handle<bool> hasSetSnapshotsLocation;
|
||||||
private:
|
private:
|
||||||
static QFile* savedFileForSnapshot(QImage & image, bool isTemporary);
|
static QFile* savedFileForSnapshot(QImage & image, bool isTemporary);
|
||||||
};
|
};
|
||||||
|
|
||||||
class SnapshotUploader : public QObject{
|
|
||||||
Q_OBJECT
|
|
||||||
public:
|
|
||||||
SnapshotUploader(QObject* parent = nullptr) : QObject(parent) {}
|
|
||||||
Q_INVOKABLE QString uploadSnapshot(const QUrl& fileUrl);
|
|
||||||
Q_INVOKABLE QString sendForumPost(const QString& snapshotPath, const QString& notes);
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif // hifi_Snapshot_h
|
#endif // hifi_Snapshot_h
|
||||||
|
|
|
@ -1,47 +0,0 @@
|
||||||
//
|
|
||||||
// SnapshotShareDialog.cpp
|
|
||||||
// interface/src/ui
|
|
||||||
//
|
|
||||||
// Created by Stojce Slavkovski on 2/16/14.
|
|
||||||
// Copyright 2014 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
|
|
||||||
//
|
|
||||||
|
|
||||||
#if 0
|
|
||||||
|
|
||||||
|
|
||||||
#include <OffscreenUi.h>
|
|
||||||
|
|
||||||
const int NARROW_SNAPSHOT_DIALOG_SIZE = 500;
|
|
||||||
const int WIDE_SNAPSHOT_DIALOG_WIDTH = 650;
|
|
||||||
const int SUCCESS_LABEL_HEIGHT = 140;
|
|
||||||
|
|
||||||
const QString SHARE_BUTTON_STYLE = "border-width:0;border-radius:9px;border-radius:9px;font-family:Arial;font-size:18px;"
|
|
||||||
"font-weight:100;color:#FFFFFF;width: 120px;height: 50px;";
|
|
||||||
const QString SHARE_BUTTON_ENABLED_STYLE = "background-color: #333;";
|
|
||||||
const QString SHARE_BUTTON_DISABLED_STYLE = "background-color: #999;";
|
|
||||||
|
|
||||||
Q_DECLARE_METATYPE(QNetworkAccessManager::Operation)
|
|
||||||
|
|
||||||
SnapshotShareDialog::SnapshotShareDialog(QString fileName, QWidget* parent) :
|
|
||||||
QDialog(parent),
|
|
||||||
_fileName(fileName)
|
|
||||||
{
|
|
||||||
|
|
||||||
|
|
||||||
_ui.snapshotWidget->setPixmap(snaphsotPixmap);
|
|
||||||
_ui.snapshotWidget->adjustSize();
|
|
||||||
}
|
|
||||||
|
|
||||||
void SnapshotShareDialog::accept() {
|
|
||||||
// prevent multiple clicks on share button
|
|
||||||
_ui.shareButton->setEnabled(false);
|
|
||||||
// gray out share button
|
|
||||||
_ui.shareButton->setStyleSheet(SHARE_BUTTON_STYLE + SHARE_BUTTON_DISABLED_STYLE);
|
|
||||||
uploadSnapshot();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -14,30 +14,11 @@
|
||||||
#include <GeometryCache.h>
|
#include <GeometryCache.h>
|
||||||
#include <RegisteredMetaTypes.h>
|
#include <RegisteredMetaTypes.h>
|
||||||
|
|
||||||
|
|
||||||
QString const Circle3DOverlay::TYPE = "circle3d";
|
QString const Circle3DOverlay::TYPE = "circle3d";
|
||||||
|
|
||||||
Circle3DOverlay::Circle3DOverlay() :
|
Circle3DOverlay::Circle3DOverlay() {
|
||||||
_startAt(0.0f),
|
memset(&_minorTickMarksColor, 0, sizeof(_minorTickMarksColor));
|
||||||
_endAt(360.0f),
|
memset(&_majorTickMarksColor, 0, sizeof(_majorTickMarksColor));
|
||||||
_outerRadius(1.0f),
|
|
||||||
_innerRadius(0.0f),
|
|
||||||
_hasTickMarks(false),
|
|
||||||
_majorTickMarksAngle(0.0f),
|
|
||||||
_minorTickMarksAngle(0.0f),
|
|
||||||
_majorTickMarksLength(0.0f),
|
|
||||||
_minorTickMarksLength(0.0f),
|
|
||||||
_quadVerticesID(GeometryCache::UNKNOWN_ID),
|
|
||||||
_lineVerticesID(GeometryCache::UNKNOWN_ID),
|
|
||||||
_majorTicksVerticesID(GeometryCache::UNKNOWN_ID),
|
|
||||||
_minorTicksVerticesID(GeometryCache::UNKNOWN_ID),
|
|
||||||
_lastStartAt(-1.0f),
|
|
||||||
_lastEndAt(-1.0f),
|
|
||||||
_lastOuterRadius(-1.0f),
|
|
||||||
_lastInnerRadius(-1.0f)
|
|
||||||
{
|
|
||||||
_majorTickMarksColor.red = _majorTickMarksColor.green = _majorTickMarksColor.blue = (unsigned char)0;
|
|
||||||
_minorTickMarksColor.red = _minorTickMarksColor.green = _minorTickMarksColor.blue = (unsigned char)0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Circle3DOverlay::Circle3DOverlay(const Circle3DOverlay* circle3DOverlay) :
|
Circle3DOverlay::Circle3DOverlay(const Circle3DOverlay* circle3DOverlay) :
|
||||||
|
@ -56,11 +37,7 @@ Circle3DOverlay::Circle3DOverlay(const Circle3DOverlay* circle3DOverlay) :
|
||||||
_quadVerticesID(GeometryCache::UNKNOWN_ID),
|
_quadVerticesID(GeometryCache::UNKNOWN_ID),
|
||||||
_lineVerticesID(GeometryCache::UNKNOWN_ID),
|
_lineVerticesID(GeometryCache::UNKNOWN_ID),
|
||||||
_majorTicksVerticesID(GeometryCache::UNKNOWN_ID),
|
_majorTicksVerticesID(GeometryCache::UNKNOWN_ID),
|
||||||
_minorTicksVerticesID(GeometryCache::UNKNOWN_ID),
|
_minorTicksVerticesID(GeometryCache::UNKNOWN_ID)
|
||||||
_lastStartAt(-1.0f),
|
|
||||||
_lastEndAt(-1.0f),
|
|
||||||
_lastOuterRadius(-1.0f),
|
|
||||||
_lastInnerRadius(-1.0f)
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -70,36 +47,25 @@ void Circle3DOverlay::render(RenderArgs* args) {
|
||||||
}
|
}
|
||||||
|
|
||||||
float alpha = getAlpha();
|
float alpha = getAlpha();
|
||||||
|
|
||||||
if (alpha == 0.0f) {
|
if (alpha == 0.0f) {
|
||||||
return; // do nothing if our alpha is 0, we're not visible
|
return; // do nothing if our alpha is 0, we're not visible
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create the circle in the coordinates origin
|
bool geometryChanged = _dirty;
|
||||||
float outerRadius = getOuterRadius();
|
_dirty = false;
|
||||||
float innerRadius = getInnerRadius(); // only used in solid case
|
|
||||||
float startAt = getStartAt();
|
|
||||||
float endAt = getEndAt();
|
|
||||||
|
|
||||||
bool geometryChanged = (startAt != _lastStartAt || endAt != _lastEndAt ||
|
|
||||||
innerRadius != _lastInnerRadius || outerRadius != _lastOuterRadius);
|
|
||||||
|
|
||||||
|
|
||||||
const float FULL_CIRCLE = 360.0f;
|
const float FULL_CIRCLE = 360.0f;
|
||||||
const float SLICES = 180.0f; // The amount of segment to create the circle
|
const float SLICES = 180.0f; // The amount of segment to create the circle
|
||||||
const float SLICE_ANGLE = FULL_CIRCLE / SLICES;
|
const float SLICE_ANGLE = FULL_CIRCLE / SLICES;
|
||||||
|
|
||||||
xColor colorX = getColor();
|
|
||||||
const float MAX_COLOR = 255.0f;
|
const float MAX_COLOR = 255.0f;
|
||||||
glm::vec4 color(colorX.red / MAX_COLOR, colorX.green / MAX_COLOR, colorX.blue / MAX_COLOR, alpha);
|
|
||||||
|
|
||||||
bool colorChanged = colorX.red != _lastColor.red || colorX.green != _lastColor.green || colorX.blue != _lastColor.blue;
|
|
||||||
_lastColor = colorX;
|
|
||||||
|
|
||||||
auto geometryCache = DependencyManager::get<GeometryCache>();
|
auto geometryCache = DependencyManager::get<GeometryCache>();
|
||||||
|
|
||||||
Q_ASSERT(args->_batch);
|
Q_ASSERT(args->_batch);
|
||||||
auto& batch = *args->_batch;
|
auto& batch = *args->_batch;
|
||||||
|
if (args->_pipeline) {
|
||||||
|
batch.setPipeline(args->_pipeline->pipeline);
|
||||||
|
}
|
||||||
|
|
||||||
// FIXME: THe line width of _lineWidth is not supported anymore, we ll need a workaround
|
// FIXME: THe line width of _lineWidth is not supported anymore, we ll need a workaround
|
||||||
|
|
||||||
|
@ -110,81 +76,89 @@ void Circle3DOverlay::render(RenderArgs* args) {
|
||||||
// for our overlay, is solid means we draw a ring between the inner and outer radius of the circle, otherwise
|
// for our overlay, is solid means we draw a ring between the inner and outer radius of the circle, otherwise
|
||||||
// we just draw a line...
|
// we just draw a line...
|
||||||
if (getIsSolid()) {
|
if (getIsSolid()) {
|
||||||
if (_quadVerticesID == GeometryCache::UNKNOWN_ID) {
|
if (!_quadVerticesID) {
|
||||||
_quadVerticesID = geometryCache->allocateID();
|
_quadVerticesID = geometryCache->allocateID();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (geometryChanged || colorChanged) {
|
if (geometryChanged) {
|
||||||
|
|
||||||
QVector<glm::vec2> points;
|
QVector<glm::vec2> points;
|
||||||
|
QVector<glm::vec4> colors;
|
||||||
float angle = startAt;
|
|
||||||
float angleInRadians = glm::radians(angle);
|
|
||||||
glm::vec2 mostRecentInnerPoint(cosf(angleInRadians) * innerRadius, sinf(angleInRadians) * innerRadius);
|
|
||||||
glm::vec2 mostRecentOuterPoint(cosf(angleInRadians) * outerRadius, sinf(angleInRadians) * outerRadius);
|
|
||||||
|
|
||||||
while (angle < endAt) {
|
|
||||||
angleInRadians = glm::radians(angle);
|
|
||||||
glm::vec2 thisInnerPoint(cosf(angleInRadians) * innerRadius, sinf(angleInRadians) * innerRadius);
|
|
||||||
glm::vec2 thisOuterPoint(cosf(angleInRadians) * outerRadius, sinf(angleInRadians) * outerRadius);
|
|
||||||
|
|
||||||
points << mostRecentInnerPoint << mostRecentOuterPoint << thisOuterPoint; // first triangle
|
|
||||||
points << mostRecentInnerPoint << thisInnerPoint << thisOuterPoint; // second triangle
|
|
||||||
|
|
||||||
angle += SLICE_ANGLE;
|
|
||||||
|
|
||||||
mostRecentInnerPoint = thisInnerPoint;
|
float pulseLevel = updatePulse();
|
||||||
mostRecentOuterPoint = thisOuterPoint;
|
vec4 pulseModifier = vec4(1);
|
||||||
|
if (_alphaPulse != 0.0f) {
|
||||||
|
pulseModifier.a = (_alphaPulse >= 0.0f) ? pulseLevel : (1.0f - pulseLevel);
|
||||||
}
|
}
|
||||||
|
if (_colorPulse != 0.0f) {
|
||||||
// get the last slice portion....
|
float pulseValue = (_colorPulse >= 0.0f) ? pulseLevel : (1.0f - pulseLevel);
|
||||||
angle = endAt;
|
pulseModifier = vec4(vec3(pulseValue), pulseModifier.a);
|
||||||
angleInRadians = glm::radians(angle);
|
}
|
||||||
glm::vec2 lastInnerPoint(cosf(angleInRadians) * innerRadius, sinf(angleInRadians) * innerRadius);
|
vec4 innerStartColor = vec4(toGlm(_innerStartColor), _innerStartAlpha) * pulseModifier;
|
||||||
glm::vec2 lastOuterPoint(cosf(angleInRadians) * outerRadius, sinf(angleInRadians) * outerRadius);
|
vec4 outerStartColor = vec4(toGlm(_outerStartColor), _outerStartAlpha) * pulseModifier;
|
||||||
|
vec4 innerEndColor = vec4(toGlm(_innerEndColor), _innerEndAlpha) * pulseModifier;
|
||||||
|
vec4 outerEndColor = vec4(toGlm(_outerEndColor), _outerEndAlpha) * pulseModifier;
|
||||||
|
|
||||||
points << mostRecentInnerPoint << mostRecentOuterPoint << lastOuterPoint; // first triangle
|
if (_innerRadius <= 0) {
|
||||||
points << mostRecentInnerPoint << lastInnerPoint << lastOuterPoint; // second triangle
|
_solidPrimitive = gpu::TRIANGLE_FAN;
|
||||||
|
points << vec2();
|
||||||
geometryCache->updateVertices(_quadVerticesID, points, color);
|
colors << innerStartColor;
|
||||||
|
for (float angle = _startAt; angle <= _endAt; angle += SLICE_ANGLE) {
|
||||||
|
float range = (angle - _startAt) / (_endAt - _startAt);
|
||||||
|
float angleRadians = glm::radians(angle);
|
||||||
|
points << glm::vec2(cosf(angleRadians) * _outerRadius, sinf(angleRadians) * _outerRadius);
|
||||||
|
colors << glm::mix(outerStartColor, outerEndColor, range);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
_solidPrimitive = gpu::TRIANGLE_STRIP;
|
||||||
|
for (float angle = _startAt; angle <= _endAt; angle += SLICE_ANGLE) {
|
||||||
|
float range = (angle - _startAt) / (_endAt - _startAt);
|
||||||
|
|
||||||
|
float angleRadians = glm::radians(angle);
|
||||||
|
points << glm::vec2(cosf(angleRadians) * _innerRadius, sinf(angleRadians) * _innerRadius);
|
||||||
|
colors << glm::mix(innerStartColor, innerEndColor, range);
|
||||||
|
|
||||||
|
points << glm::vec2(cosf(angleRadians) * _outerRadius, sinf(angleRadians) * _outerRadius);
|
||||||
|
colors << glm::mix(outerStartColor, outerEndColor, range);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
geometryCache->updateVertices(_quadVerticesID, points, colors);
|
||||||
}
|
}
|
||||||
|
|
||||||
geometryCache->renderVertices(batch, gpu::TRIANGLES, _quadVerticesID);
|
geometryCache->renderVertices(batch, _solidPrimitive, _quadVerticesID);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
if (_lineVerticesID == GeometryCache::UNKNOWN_ID) {
|
if (!_lineVerticesID) {
|
||||||
_lineVerticesID = geometryCache->allocateID();
|
_lineVerticesID = geometryCache->allocateID();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (geometryChanged || colorChanged) {
|
if (geometryChanged) {
|
||||||
QVector<glm::vec2> points;
|
QVector<glm::vec2> points;
|
||||||
|
|
||||||
float angle = startAt;
|
float angle = _startAt;
|
||||||
float angleInRadians = glm::radians(angle);
|
float angleInRadians = glm::radians(angle);
|
||||||
glm::vec2 firstPoint(cosf(angleInRadians) * outerRadius, sinf(angleInRadians) * outerRadius);
|
glm::vec2 firstPoint(cosf(angleInRadians) * _outerRadius, sinf(angleInRadians) * _outerRadius);
|
||||||
points << firstPoint;
|
points << firstPoint;
|
||||||
|
|
||||||
while (angle < endAt) {
|
while (angle < _endAt) {
|
||||||
angle += SLICE_ANGLE;
|
angle += SLICE_ANGLE;
|
||||||
angleInRadians = glm::radians(angle);
|
angleInRadians = glm::radians(angle);
|
||||||
glm::vec2 thisPoint(cosf(angleInRadians) * outerRadius, sinf(angleInRadians) * outerRadius);
|
glm::vec2 thisPoint(cosf(angleInRadians) * _outerRadius, sinf(angleInRadians) * _outerRadius);
|
||||||
points << thisPoint;
|
points << thisPoint;
|
||||||
|
|
||||||
if (getIsDashedLine()) {
|
if (getIsDashedLine()) {
|
||||||
angle += SLICE_ANGLE / 2.0f; // short gap
|
angle += SLICE_ANGLE / 2.0f; // short gap
|
||||||
angleInRadians = glm::radians(angle);
|
angleInRadians = glm::radians(angle);
|
||||||
glm::vec2 dashStartPoint(cosf(angleInRadians) * outerRadius, sinf(angleInRadians) * outerRadius);
|
glm::vec2 dashStartPoint(cosf(angleInRadians) * _outerRadius, sinf(angleInRadians) * _outerRadius);
|
||||||
points << dashStartPoint;
|
points << dashStartPoint;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// get the last slice portion....
|
// get the last slice portion....
|
||||||
angle = endAt;
|
angle = _endAt;
|
||||||
angleInRadians = glm::radians(angle);
|
angleInRadians = glm::radians(angle);
|
||||||
glm::vec2 lastPoint(cosf(angleInRadians) * outerRadius, sinf(angleInRadians) * outerRadius);
|
glm::vec2 lastPoint(cosf(angleInRadians) * _outerRadius, sinf(angleInRadians) * _outerRadius);
|
||||||
points << lastPoint;
|
points << lastPoint;
|
||||||
|
geometryCache->updateVertices(_lineVerticesID, points, vec4(toGlm(getColor()), getAlpha()));
|
||||||
geometryCache->updateVertices(_lineVerticesID, points, color);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (getIsDashedLine()) {
|
if (getIsDashedLine()) {
|
||||||
|
@ -214,13 +188,13 @@ void Circle3DOverlay::render(RenderArgs* args) {
|
||||||
if (getMajorTickMarksAngle() > 0.0f && getMajorTickMarksLength() != 0.0f) {
|
if (getMajorTickMarksAngle() > 0.0f && getMajorTickMarksLength() != 0.0f) {
|
||||||
|
|
||||||
float tickMarkAngle = getMajorTickMarksAngle();
|
float tickMarkAngle = getMajorTickMarksAngle();
|
||||||
float angle = startAt - fmodf(startAt, tickMarkAngle) + tickMarkAngle;
|
float angle = _startAt - fmodf(_startAt, tickMarkAngle) + tickMarkAngle;
|
||||||
float angleInRadians = glm::radians(angle);
|
float angleInRadians = glm::radians(angle);
|
||||||
float tickMarkLength = getMajorTickMarksLength();
|
float tickMarkLength = getMajorTickMarksLength();
|
||||||
float startRadius = (tickMarkLength > 0.0f) ? innerRadius : outerRadius;
|
float startRadius = (tickMarkLength > 0.0f) ? _innerRadius : _outerRadius;
|
||||||
float endRadius = startRadius + tickMarkLength;
|
float endRadius = startRadius + tickMarkLength;
|
||||||
|
|
||||||
while (angle <= endAt) {
|
while (angle <= _endAt) {
|
||||||
angleInRadians = glm::radians(angle);
|
angleInRadians = glm::radians(angle);
|
||||||
|
|
||||||
glm::vec2 thisPointA(cosf(angleInRadians) * startRadius, sinf(angleInRadians) * startRadius);
|
glm::vec2 thisPointA(cosf(angleInRadians) * startRadius, sinf(angleInRadians) * startRadius);
|
||||||
|
@ -236,13 +210,13 @@ void Circle3DOverlay::render(RenderArgs* args) {
|
||||||
if (getMinorTickMarksAngle() > 0.0f && getMinorTickMarksLength() != 0.0f) {
|
if (getMinorTickMarksAngle() > 0.0f && getMinorTickMarksLength() != 0.0f) {
|
||||||
|
|
||||||
float tickMarkAngle = getMinorTickMarksAngle();
|
float tickMarkAngle = getMinorTickMarksAngle();
|
||||||
float angle = startAt - fmodf(startAt, tickMarkAngle) + tickMarkAngle;
|
float angle = _startAt - fmodf(_startAt, tickMarkAngle) + tickMarkAngle;
|
||||||
float angleInRadians = glm::radians(angle);
|
float angleInRadians = glm::radians(angle);
|
||||||
float tickMarkLength = getMinorTickMarksLength();
|
float tickMarkLength = getMinorTickMarksLength();
|
||||||
float startRadius = (tickMarkLength > 0.0f) ? innerRadius : outerRadius;
|
float startRadius = (tickMarkLength > 0.0f) ? _innerRadius : _outerRadius;
|
||||||
float endRadius = startRadius + tickMarkLength;
|
float endRadius = startRadius + tickMarkLength;
|
||||||
|
|
||||||
while (angle <= endAt) {
|
while (angle <= _endAt) {
|
||||||
angleInRadians = glm::radians(angle);
|
angleInRadians = glm::radians(angle);
|
||||||
|
|
||||||
glm::vec2 thisPointA(cosf(angleInRadians) * startRadius, sinf(angleInRadians) * startRadius);
|
glm::vec2 thisPointA(cosf(angleInRadians) * startRadius, sinf(angleInRadians) * startRadius);
|
||||||
|
@ -269,17 +243,10 @@ void Circle3DOverlay::render(RenderArgs* args) {
|
||||||
|
|
||||||
geometryCache->renderVertices(batch, gpu::LINES, _minorTicksVerticesID);
|
geometryCache->renderVertices(batch, gpu::LINES, _minorTicksVerticesID);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (geometryChanged) {
|
|
||||||
_lastStartAt = startAt;
|
|
||||||
_lastEndAt = endAt;
|
|
||||||
_lastInnerRadius = innerRadius;
|
|
||||||
_lastOuterRadius = outerRadius;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const render::ShapeKey Circle3DOverlay::getShapeKey() {
|
const render::ShapeKey Circle3DOverlay::getShapeKey() {
|
||||||
auto builder = render::ShapeKey::Builder().withoutCullFace();
|
auto builder = render::ShapeKey::Builder().withoutCullFace().withUnlit();
|
||||||
if (getAlpha() != 1.0f) {
|
if (getAlpha() != 1.0f) {
|
||||||
builder.withTranslucent();
|
builder.withTranslucent();
|
||||||
}
|
}
|
||||||
|
@ -289,72 +256,102 @@ const render::ShapeKey Circle3DOverlay::getShapeKey() {
|
||||||
return builder.build();
|
return builder.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<typename T> T fromVariant(const QVariant& v, bool& valid) {
|
||||||
|
valid = v.isValid();
|
||||||
|
return qvariant_cast<T>(v);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<> xColor fromVariant(const QVariant& v, bool& valid) {
|
||||||
|
return xColorFromVariant(v, valid);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
bool updateIfValid(const QVariantMap& properties, const char* key, T& output) {
|
||||||
|
bool valid;
|
||||||
|
T result = fromVariant<T>(properties[key], valid);
|
||||||
|
if (!valid) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Don't signal updates if the value was already set
|
||||||
|
if (result == output) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
output = result;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Multicast, many outputs
|
||||||
|
template<typename T>
|
||||||
|
bool updateIfValid(const QVariantMap& properties, const char* key, std::initializer_list<std::reference_wrapper<T>> outputs) {
|
||||||
|
bool valid;
|
||||||
|
T value = fromVariant<T>(properties[key], valid);
|
||||||
|
if (!valid) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
bool updated = false;
|
||||||
|
for (T& output : outputs) {
|
||||||
|
if (output != value) {
|
||||||
|
output = value;
|
||||||
|
updated = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return updated;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Multicast, multiple possible inputs, in order of preference
|
||||||
|
template<typename T>
|
||||||
|
bool updateIfValid(const QVariantMap& properties, const std::initializer_list<const char*> keys, T& output) {
|
||||||
|
for (const char* key : keys) {
|
||||||
|
if (updateIfValid<T>(properties, key, output)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void Circle3DOverlay::setProperties(const QVariantMap& properties) {
|
void Circle3DOverlay::setProperties(const QVariantMap& properties) {
|
||||||
Planar3DOverlay::setProperties(properties);
|
Planar3DOverlay::setProperties(properties);
|
||||||
|
_dirty |= updateIfValid<float>(properties, "alpha", { _innerStartAlpha, _innerEndAlpha, _outerStartAlpha, _outerEndAlpha });
|
||||||
|
_dirty |= updateIfValid<float>(properties, "Alpha", { _innerStartAlpha, _innerEndAlpha, _outerStartAlpha, _outerEndAlpha });
|
||||||
|
_dirty |= updateIfValid<float>(properties, "startAlpha", { _innerStartAlpha, _outerStartAlpha });
|
||||||
|
_dirty |= updateIfValid<float>(properties, "endAlpha", { _innerEndAlpha, _outerEndAlpha });
|
||||||
|
_dirty |= updateIfValid<float>(properties, "innerAlpha", { _innerStartAlpha, _innerEndAlpha });
|
||||||
|
_dirty |= updateIfValid<float>(properties, "outerAlpha", { _outerStartAlpha, _outerEndAlpha });
|
||||||
|
_dirty |= updateIfValid(properties, "innerStartAlpha", _innerStartAlpha);
|
||||||
|
_dirty |= updateIfValid(properties, "innerEndAlpha", _innerEndAlpha);
|
||||||
|
_dirty |= updateIfValid(properties, "outerStartAlpha", _outerStartAlpha);
|
||||||
|
_dirty |= updateIfValid(properties, "outerEndAlpha", _outerEndAlpha);
|
||||||
|
|
||||||
QVariant startAt = properties["startAt"];
|
_dirty |= updateIfValid<xColor>(properties, "color", { _innerStartColor, _innerEndColor, _outerStartColor, _outerEndColor });
|
||||||
if (startAt.isValid()) {
|
_dirty |= updateIfValid<xColor>(properties, "startColor", { _innerStartColor, _outerStartColor } );
|
||||||
setStartAt(startAt.toFloat());
|
_dirty |= updateIfValid<xColor>(properties, "endColor", { _innerEndColor, _outerEndColor } );
|
||||||
}
|
_dirty |= updateIfValid<xColor>(properties, "innerColor", { _innerStartColor, _innerEndColor } );
|
||||||
|
_dirty |= updateIfValid<xColor>(properties, "outerColor", { _outerStartColor, _outerEndColor } );
|
||||||
|
_dirty |= updateIfValid(properties, "innerStartColor", _innerStartColor);
|
||||||
|
_dirty |= updateIfValid(properties, "innerEndColor", _innerEndColor);
|
||||||
|
_dirty |= updateIfValid(properties, "outerStartColor", _outerStartColor);
|
||||||
|
_dirty |= updateIfValid(properties, "outerEndColor", _outerEndColor);
|
||||||
|
|
||||||
QVariant endAt = properties["endAt"];
|
_dirty |= updateIfValid(properties, "startAt", _startAt);
|
||||||
if (endAt.isValid()) {
|
_dirty |= updateIfValid(properties, "endAt", _endAt);
|
||||||
setEndAt(endAt.toFloat());
|
|
||||||
}
|
|
||||||
|
|
||||||
QVariant outerRadius = properties["radius"];
|
_dirty |= updateIfValid(properties, { "radius", "outerRadius" }, _outerRadius);
|
||||||
if (!outerRadius.isValid()) {
|
_dirty |= updateIfValid(properties, "innerRadius", _innerRadius);
|
||||||
outerRadius = properties["outerRadius"];
|
_dirty |= updateIfValid(properties, "hasTickMarks", _hasTickMarks);
|
||||||
}
|
_dirty |= updateIfValid(properties, "majorTickMarksAngle", _majorTickMarksAngle);
|
||||||
if (outerRadius.isValid()) {
|
_dirty |= updateIfValid(properties, "minorTickMarksAngle", _minorTickMarksAngle);
|
||||||
setOuterRadius(outerRadius.toFloat());
|
_dirty |= updateIfValid(properties, "majorTickMarksLength", _majorTickMarksLength);
|
||||||
}
|
_dirty |= updateIfValid(properties, "minorTickMarksLength", _minorTickMarksLength);
|
||||||
|
_dirty |= updateIfValid(properties, "majorTickMarksColor", _majorTickMarksColor);
|
||||||
|
_dirty |= updateIfValid(properties, "minorTickMarksColor", _minorTickMarksColor);
|
||||||
|
|
||||||
QVariant innerRadius = properties["innerRadius"];
|
if (_innerStartAlpha < 1.0f || _innerEndAlpha < 1.0f || _outerStartAlpha < 1.0f || _outerEndAlpha < 1.0f) {
|
||||||
if (innerRadius.isValid()) {
|
// Force the alpha to 0.5, since we'll ignore it in the presence of these other values, but we need
|
||||||
setInnerRadius(innerRadius.toFloat());
|
// it to be non-1 in order to get the right pipeline and non-0 in order to render at all.
|
||||||
}
|
_alpha = 0.5f;
|
||||||
|
|
||||||
QVariant hasTickMarks = properties["hasTickMarks"];
|
|
||||||
if (hasTickMarks.isValid()) {
|
|
||||||
setHasTickMarks(hasTickMarks.toBool());
|
|
||||||
}
|
|
||||||
|
|
||||||
QVariant majorTickMarksAngle = properties["majorTickMarksAngle"];
|
|
||||||
if (majorTickMarksAngle.isValid()) {
|
|
||||||
setMajorTickMarksAngle(majorTickMarksAngle.toFloat());
|
|
||||||
}
|
|
||||||
|
|
||||||
QVariant minorTickMarksAngle = properties["minorTickMarksAngle"];
|
|
||||||
if (minorTickMarksAngle.isValid()) {
|
|
||||||
setMinorTickMarksAngle(minorTickMarksAngle.toFloat());
|
|
||||||
}
|
|
||||||
|
|
||||||
QVariant majorTickMarksLength = properties["majorTickMarksLength"];
|
|
||||||
if (majorTickMarksLength.isValid()) {
|
|
||||||
setMajorTickMarksLength(majorTickMarksLength.toFloat());
|
|
||||||
}
|
|
||||||
|
|
||||||
QVariant minorTickMarksLength = properties["minorTickMarksLength"];
|
|
||||||
if (minorTickMarksLength.isValid()) {
|
|
||||||
setMinorTickMarksLength(minorTickMarksLength.toFloat());
|
|
||||||
}
|
|
||||||
|
|
||||||
bool valid;
|
|
||||||
auto majorTickMarksColor = properties["majorTickMarksColor"];
|
|
||||||
if (majorTickMarksColor.isValid()) {
|
|
||||||
auto color = xColorFromVariant(majorTickMarksColor, valid);
|
|
||||||
if (valid) {
|
|
||||||
_majorTickMarksColor = color;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
auto minorTickMarksColor = properties["minorTickMarksColor"];
|
|
||||||
if (minorTickMarksColor.isValid()) {
|
|
||||||
auto color = xColorFromVariant(majorTickMarksColor, valid);
|
|
||||||
if (valid) {
|
|
||||||
_minorTickMarksColor = color;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -59,28 +59,34 @@ public:
|
||||||
virtual Circle3DOverlay* createClone() const override;
|
virtual Circle3DOverlay* createClone() const override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
float _startAt;
|
float _startAt { 0 };
|
||||||
float _endAt;
|
float _endAt { 360 };
|
||||||
float _outerRadius;
|
float _outerRadius { 1 };
|
||||||
float _innerRadius;
|
float _innerRadius { 0 };
|
||||||
bool _hasTickMarks;
|
|
||||||
float _majorTickMarksAngle;
|
xColor _innerStartColor;
|
||||||
float _minorTickMarksAngle;
|
xColor _innerEndColor;
|
||||||
float _majorTickMarksLength;
|
xColor _outerStartColor;
|
||||||
float _minorTickMarksLength;
|
xColor _outerEndColor;
|
||||||
|
float _innerStartAlpha;
|
||||||
|
float _innerEndAlpha;
|
||||||
|
float _outerStartAlpha;
|
||||||
|
float _outerEndAlpha;
|
||||||
|
|
||||||
|
bool _hasTickMarks { false };
|
||||||
|
float _majorTickMarksAngle { 0 };
|
||||||
|
float _minorTickMarksAngle { 0 };
|
||||||
|
float _majorTickMarksLength { 0 };
|
||||||
|
float _minorTickMarksLength { 0 };
|
||||||
xColor _majorTickMarksColor;
|
xColor _majorTickMarksColor;
|
||||||
xColor _minorTickMarksColor;
|
xColor _minorTickMarksColor;
|
||||||
|
gpu::Primitive _solidPrimitive { gpu::TRIANGLE_FAN };
|
||||||
int _quadVerticesID;
|
int _quadVerticesID { 0 };
|
||||||
int _lineVerticesID;
|
int _lineVerticesID { 0 };
|
||||||
int _majorTicksVerticesID;
|
int _majorTicksVerticesID { 0 };
|
||||||
int _minorTicksVerticesID;
|
int _minorTicksVerticesID { 0 };
|
||||||
|
|
||||||
xColor _lastColor;
|
bool _dirty { true };
|
||||||
float _lastStartAt;
|
|
||||||
float _lastEndAt;
|
|
||||||
float _lastOuterRadius;
|
|
||||||
float _lastInnerRadius;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -34,6 +34,7 @@
|
||||||
#include <StreamUtils.h>
|
#include <StreamUtils.h>
|
||||||
#include <UUID.h>
|
#include <UUID.h>
|
||||||
#include <shared/JSONHelpers.h>
|
#include <shared/JSONHelpers.h>
|
||||||
|
#include <ShapeInfo.h>
|
||||||
|
|
||||||
#include "AvatarLogging.h"
|
#include "AvatarLogging.h"
|
||||||
|
|
||||||
|
@ -1681,3 +1682,25 @@ AvatarEntityIDs AvatarData::getAndClearRecentlyDetachedIDs() {
|
||||||
});
|
});
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QScriptValue RayToAvatarIntersectionResultToScriptValue(QScriptEngine* engine, const RayToAvatarIntersectionResult& value) {
|
||||||
|
QScriptValue obj = engine->newObject();
|
||||||
|
obj.setProperty("intersects", value.intersects);
|
||||||
|
QScriptValue avatarIDValue = quuidToScriptValue(engine, value.avatarID);
|
||||||
|
obj.setProperty("avatarID", avatarIDValue);
|
||||||
|
obj.setProperty("distance", value.distance);
|
||||||
|
QScriptValue intersection = vec3toScriptValue(engine, value.intersection);
|
||||||
|
obj.setProperty("intersection", intersection);
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
void RayToAvatarIntersectionResultFromScriptValue(const QScriptValue& object, RayToAvatarIntersectionResult& value) {
|
||||||
|
value.intersects = object.property("intersects").toVariant().toBool();
|
||||||
|
QScriptValue avatarIDValue = object.property("avatarID");
|
||||||
|
quuidFromScriptValue(avatarIDValue, value.avatarID);
|
||||||
|
value.distance = object.property("distance").toVariant().toFloat();
|
||||||
|
QScriptValue intersection = object.property("intersection");
|
||||||
|
if (intersection.isValid()) {
|
||||||
|
vec3FromScriptValue(intersection, value.intersection);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -495,4 +495,19 @@ public:
|
||||||
|
|
||||||
void registerAvatarTypes(QScriptEngine* engine);
|
void registerAvatarTypes(QScriptEngine* engine);
|
||||||
|
|
||||||
|
class RayToAvatarIntersectionResult {
|
||||||
|
public:
|
||||||
|
RayToAvatarIntersectionResult() : intersects(false), avatarID(), distance(0) {}
|
||||||
|
bool intersects;
|
||||||
|
QUuid avatarID;
|
||||||
|
float distance;
|
||||||
|
glm::vec3 intersection;
|
||||||
|
};
|
||||||
|
|
||||||
|
Q_DECLARE_METATYPE(RayToAvatarIntersectionResult)
|
||||||
|
|
||||||
|
QScriptValue RayToAvatarIntersectionResultToScriptValue(QScriptEngine* engine, const RayToAvatarIntersectionResult& results);
|
||||||
|
void RayToAvatarIntersectionResultFromScriptValue(const QScriptValue& object, RayToAvatarIntersectionResult& results);
|
||||||
|
|
||||||
|
|
||||||
#endif // hifi_AvatarData_h
|
#endif // hifi_AvatarData_h
|
||||||
|
|
|
@ -12,6 +12,8 @@
|
||||||
#include <glm/gtx/intersect.hpp>
|
#include <glm/gtx/intersect.hpp>
|
||||||
|
|
||||||
#include <QtCore/QLoggingCategory>
|
#include <QtCore/QLoggingCategory>
|
||||||
|
#include <QtCore/QFileInfo>
|
||||||
|
#include <QtCore/QDateTime>
|
||||||
#include <QtWidgets/QApplication>
|
#include <QtWidgets/QApplication>
|
||||||
#include <QtWidgets/QWidget>
|
#include <QtWidgets/QWidget>
|
||||||
|
|
||||||
|
@ -34,6 +36,9 @@ static const QString REPROJECTION = "Allow Reprojection";
|
||||||
static const QString FRAMERATE = DisplayPlugin::MENU_PATH() + ">Framerate";
|
static const QString FRAMERATE = DisplayPlugin::MENU_PATH() + ">Framerate";
|
||||||
static const QString DEVELOPER_MENU_PATH = "Developer>" + DisplayPlugin::MENU_PATH();
|
static const QString DEVELOPER_MENU_PATH = "Developer>" + DisplayPlugin::MENU_PATH();
|
||||||
static const bool DEFAULT_MONO_VIEW = true;
|
static const bool DEFAULT_MONO_VIEW = true;
|
||||||
|
static const int NUMBER_OF_HANDS = 2;
|
||||||
|
static const glm::mat4 IDENTITY_MATRIX;
|
||||||
|
|
||||||
|
|
||||||
glm::uvec2 HmdDisplayPlugin::getRecommendedUiSize() const {
|
glm::uvec2 HmdDisplayPlugin::getRecommendedUiSize() const {
|
||||||
return CompositorHelper::VIRTUAL_SCREEN_SIZE;
|
return CompositorHelper::VIRTUAL_SCREEN_SIZE;
|
||||||
|
@ -90,170 +95,6 @@ void HmdDisplayPlugin::internalDeactivate() {
|
||||||
Parent::internalDeactivate();
|
Parent::internalDeactivate();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static const char * REPROJECTION_VS = R"VS(#version 410 core
|
|
||||||
in vec3 Position;
|
|
||||||
in vec2 TexCoord;
|
|
||||||
|
|
||||||
out vec3 vPosition;
|
|
||||||
out vec2 vTexCoord;
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
gl_Position = vec4(Position, 1);
|
|
||||||
vTexCoord = TexCoord;
|
|
||||||
vPosition = Position;
|
|
||||||
}
|
|
||||||
|
|
||||||
)VS";
|
|
||||||
|
|
||||||
static GLint REPROJECTION_MATRIX_LOCATION = -1;
|
|
||||||
static GLint INVERSE_PROJECTION_MATRIX_LOCATION = -1;
|
|
||||||
static GLint PROJECTION_MATRIX_LOCATION = -1;
|
|
||||||
static const char * REPROJECTION_FS = R"FS(#version 410 core
|
|
||||||
|
|
||||||
uniform sampler2D sampler;
|
|
||||||
uniform mat3 reprojection = mat3(1);
|
|
||||||
uniform mat4 inverseProjections[2];
|
|
||||||
uniform mat4 projections[2];
|
|
||||||
|
|
||||||
in vec2 vTexCoord;
|
|
||||||
in vec3 vPosition;
|
|
||||||
|
|
||||||
out vec4 FragColor;
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
vec2 uv = vTexCoord;
|
|
||||||
|
|
||||||
mat4 eyeInverseProjection;
|
|
||||||
mat4 eyeProjection;
|
|
||||||
|
|
||||||
float xoffset = 1.0;
|
|
||||||
vec2 uvmin = vec2(0.0);
|
|
||||||
vec2 uvmax = vec2(1.0);
|
|
||||||
// determine the correct projection and inverse projection to use.
|
|
||||||
if (vTexCoord.x < 0.5) {
|
|
||||||
uvmax.x = 0.5;
|
|
||||||
eyeInverseProjection = inverseProjections[0];
|
|
||||||
eyeProjection = projections[0];
|
|
||||||
} else {
|
|
||||||
xoffset = -1.0;
|
|
||||||
uvmin.x = 0.5;
|
|
||||||
uvmax.x = 1.0;
|
|
||||||
eyeInverseProjection = inverseProjections[1];
|
|
||||||
eyeProjection = projections[1];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Account for stereo in calculating the per-eye NDC coordinates
|
|
||||||
vec4 ndcSpace = vec4(vPosition, 1.0);
|
|
||||||
ndcSpace.x *= 2.0;
|
|
||||||
ndcSpace.x += xoffset;
|
|
||||||
|
|
||||||
// Convert from NDC to eyespace
|
|
||||||
vec4 eyeSpace = eyeInverseProjection * ndcSpace;
|
|
||||||
eyeSpace /= eyeSpace.w;
|
|
||||||
|
|
||||||
// Convert to a noramlized ray
|
|
||||||
vec3 ray = eyeSpace.xyz;
|
|
||||||
ray = normalize(ray);
|
|
||||||
|
|
||||||
// Adjust the ray by the rotation
|
|
||||||
ray = reprojection * ray;
|
|
||||||
|
|
||||||
// Project back on to the texture plane
|
|
||||||
ray *= eyeSpace.z / ray.z;
|
|
||||||
|
|
||||||
// Update the eyespace vector
|
|
||||||
eyeSpace.xyz = ray;
|
|
||||||
|
|
||||||
// Reproject back into NDC
|
|
||||||
ndcSpace = eyeProjection * eyeSpace;
|
|
||||||
ndcSpace /= ndcSpace.w;
|
|
||||||
ndcSpace.x -= xoffset;
|
|
||||||
ndcSpace.x /= 2.0;
|
|
||||||
|
|
||||||
// Calculate the new UV coordinates
|
|
||||||
uv = (ndcSpace.xy / 2.0) + 0.5;
|
|
||||||
if (any(greaterThan(uv, uvmax)) || any(lessThan(uv, uvmin))) {
|
|
||||||
FragColor = vec4(0.0, 0.0, 0.0, 1.0);
|
|
||||||
} else {
|
|
||||||
FragColor = texture(sampler, uv);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)FS";
|
|
||||||
|
|
||||||
#ifdef DEBUG_REPROJECTION_SHADER
|
|
||||||
#include <QtCore/QFile>
|
|
||||||
#include <QtCore/QFileInfo>
|
|
||||||
#include <QtCore/QDateTime>
|
|
||||||
#include <PathUtils.h>
|
|
||||||
|
|
||||||
static const QString REPROJECTION_FS_FILE = "c:/Users/bdavis/Git/hifi/interface/resources/shaders/reproject.frag";
|
|
||||||
|
|
||||||
static ProgramPtr getReprojectionProgram() {
|
|
||||||
static ProgramPtr _currentProgram;
|
|
||||||
uint64_t now = usecTimestampNow();
|
|
||||||
static uint64_t _lastFileCheck = now;
|
|
||||||
|
|
||||||
bool modified = false;
|
|
||||||
if ((now - _lastFileCheck) > USECS_PER_MSEC * 100) {
|
|
||||||
QFileInfo info(REPROJECTION_FS_FILE);
|
|
||||||
QDateTime lastModified = info.lastModified();
|
|
||||||
static QDateTime _lastModified = lastModified;
|
|
||||||
qDebug() << lastModified.toTime_t();
|
|
||||||
qDebug() << _lastModified.toTime_t();
|
|
||||||
if (lastModified > _lastModified) {
|
|
||||||
_lastModified = lastModified;
|
|
||||||
modified = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!_currentProgram || modified) {
|
|
||||||
_currentProgram.reset();
|
|
||||||
try {
|
|
||||||
QFile shaderFile(REPROJECTION_FS_FILE);
|
|
||||||
shaderFile.open(QIODevice::ReadOnly);
|
|
||||||
QString fragment = shaderFile.readAll();
|
|
||||||
compileProgram(_currentProgram, REPROJECTION_VS, fragment.toLocal8Bit().data());
|
|
||||||
} catch (const std::runtime_error& error) {
|
|
||||||
qDebug() << "Failed to build: " << error.what();
|
|
||||||
}
|
|
||||||
if (!_currentProgram) {
|
|
||||||
_currentProgram = loadDefaultShader();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return _currentProgram;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
static GLint PREVIEW_TEXTURE_LOCATION = -1;
|
|
||||||
|
|
||||||
static const char * LASER_VS = R"VS(#version 410 core
|
|
||||||
uniform mat4 mvp = mat4(1);
|
|
||||||
|
|
||||||
in vec3 Position;
|
|
||||||
|
|
||||||
out vec3 vPosition;
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
gl_Position = mvp * vec4(Position, 1);
|
|
||||||
vPosition = Position;
|
|
||||||
}
|
|
||||||
|
|
||||||
)VS";
|
|
||||||
|
|
||||||
static const char * LASER_FS = R"FS(#version 410 core
|
|
||||||
|
|
||||||
uniform vec4 color = vec4(1.0, 1.0, 1.0, 1.0);
|
|
||||||
in vec3 vPosition;
|
|
||||||
|
|
||||||
out vec4 FragColor;
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
FragColor = color;
|
|
||||||
}
|
|
||||||
|
|
||||||
)FS";
|
|
||||||
|
|
||||||
void HmdDisplayPlugin::customizeContext() {
|
void HmdDisplayPlugin::customizeContext() {
|
||||||
Parent::customizeContext();
|
Parent::customizeContext();
|
||||||
// Only enable mirroring if we know vsync is disabled
|
// Only enable mirroring if we know vsync is disabled
|
||||||
|
@ -263,24 +104,147 @@ void HmdDisplayPlugin::customizeContext() {
|
||||||
#endif
|
#endif
|
||||||
_enablePreview = !isVsyncEnabled();
|
_enablePreview = !isVsyncEnabled();
|
||||||
_sphereSection = loadSphereSection(_program, CompositorHelper::VIRTUAL_UI_TARGET_FOV.y, CompositorHelper::VIRTUAL_UI_ASPECT_RATIO);
|
_sphereSection = loadSphereSection(_program, CompositorHelper::VIRTUAL_UI_TARGET_FOV.y, CompositorHelper::VIRTUAL_UI_ASPECT_RATIO);
|
||||||
|
|
||||||
using namespace oglplus;
|
using namespace oglplus;
|
||||||
if (!_enablePreview) {
|
if (!_enablePreview) {
|
||||||
const std::string version("#version 410 core\n");
|
const std::string version("#version 410 core\n");
|
||||||
compileProgram(_previewProgram, version + DrawUnitQuadTexcoord_vert, version + DrawTexture_frag);
|
compileProgram(_previewProgram, version + DrawUnitQuadTexcoord_vert, version + DrawTexture_frag);
|
||||||
PREVIEW_TEXTURE_LOCATION = Uniform<int>(*_previewProgram, "colorMap").Location();
|
_previewUniforms.previewTexture = Uniform<int>(*_previewProgram, "colorMap").Location();
|
||||||
}
|
}
|
||||||
|
|
||||||
compileProgram(_laserProgram, LASER_VS, LASER_FS);
|
updateReprojectionProgram();
|
||||||
_laserGeometry = loadLaser(_laserProgram);
|
updateOverlayProgram();
|
||||||
|
updateLaserProgram();
|
||||||
|
|
||||||
compileProgram(_reprojectionProgram, REPROJECTION_VS, REPROJECTION_FS);
|
_laserGeometry = loadLaser(_laserProgram);
|
||||||
REPROJECTION_MATRIX_LOCATION = Uniform<glm::mat3>(*_reprojectionProgram, "reprojection").Location();
|
}
|
||||||
INVERSE_PROJECTION_MATRIX_LOCATION = Uniform<glm::mat4>(*_reprojectionProgram, "inverseProjections").Location();
|
//#define LIVE_SHADER_RELOAD 1
|
||||||
PROJECTION_MATRIX_LOCATION = Uniform<glm::mat4>(*_reprojectionProgram, "projections").Location();
|
|
||||||
|
static QString readFile(const QString& filename) {
|
||||||
|
QFile file(filename);
|
||||||
|
file.open(QFile::Text | QFile::ReadOnly);
|
||||||
|
QString result;
|
||||||
|
result.append(QTextStream(&file).readAll());
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void HmdDisplayPlugin::updateReprojectionProgram() {
|
||||||
|
static const QString vsFile = PathUtils::resourcesPath() + "/shaders/hmd_reproject.vert";
|
||||||
|
static const QString fsFile = PathUtils::resourcesPath() + "/shaders/hmd_reproject.frag";
|
||||||
|
#if LIVE_SHADER_RELOAD
|
||||||
|
static qint64 vsBuiltAge = 0;
|
||||||
|
static qint64 fsBuiltAge = 0;
|
||||||
|
QFileInfo vsInfo(vsFile);
|
||||||
|
QFileInfo fsInfo(fsFile);
|
||||||
|
auto vsAge = vsInfo.lastModified().toMSecsSinceEpoch();
|
||||||
|
auto fsAge = fsInfo.lastModified().toMSecsSinceEpoch();
|
||||||
|
if (!_reprojectionProgram || vsAge > vsBuiltAge || fsAge > fsBuiltAge) {
|
||||||
|
vsBuiltAge = vsAge;
|
||||||
|
fsBuiltAge = fsAge;
|
||||||
|
#else
|
||||||
|
if (!_reprojectionProgram) {
|
||||||
|
#endif
|
||||||
|
QString vsSource = readFile(vsFile);
|
||||||
|
QString fsSource = readFile(fsFile);
|
||||||
|
ProgramPtr program;
|
||||||
|
try {
|
||||||
|
compileProgram(program, vsSource.toLocal8Bit().toStdString(), fsSource.toLocal8Bit().toStdString());
|
||||||
|
if (program) {
|
||||||
|
using namespace oglplus;
|
||||||
|
_reprojectionUniforms.reprojectionMatrix = Uniform<glm::mat3>(*program, "reprojection").Location();
|
||||||
|
_reprojectionUniforms.inverseProjectionMatrix = Uniform<glm::mat4>(*program, "inverseProjections").Location();
|
||||||
|
_reprojectionUniforms.projectionMatrix = Uniform<glm::mat4>(*program, "projections").Location();
|
||||||
|
_reprojectionProgram = program;
|
||||||
|
}
|
||||||
|
} catch (std::runtime_error& error) {
|
||||||
|
qWarning() << "Error building reprojection shader " << error.what();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void HmdDisplayPlugin::updateLaserProgram() {
|
||||||
|
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 fsFile = PathUtils::resourcesPath() + "/shaders/hmd_hand_lasers.frag";
|
||||||
|
|
||||||
|
#if LIVE_SHADER_RELOAD
|
||||||
|
static qint64 vsBuiltAge = 0;
|
||||||
|
static qint64 gsBuiltAge = 0;
|
||||||
|
static qint64 fsBuiltAge = 0;
|
||||||
|
QFileInfo vsInfo(vsFile);
|
||||||
|
QFileInfo fsInfo(fsFile);
|
||||||
|
QFileInfo gsInfo(fsFile);
|
||||||
|
auto vsAge = vsInfo.lastModified().toMSecsSinceEpoch();
|
||||||
|
auto fsAge = fsInfo.lastModified().toMSecsSinceEpoch();
|
||||||
|
auto gsAge = gsInfo.lastModified().toMSecsSinceEpoch();
|
||||||
|
if (!_laserProgram || vsAge > vsBuiltAge || fsAge > fsBuiltAge || gsAge > gsBuiltAge) {
|
||||||
|
vsBuiltAge = vsAge;
|
||||||
|
gsBuiltAge = gsAge;
|
||||||
|
fsBuiltAge = fsAge;
|
||||||
|
#else
|
||||||
|
if (!_laserProgram) {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
QString vsSource = readFile(vsFile);
|
||||||
|
QString fsSource = readFile(fsFile);
|
||||||
|
QString gsSource = readFile(gsFile);
|
||||||
|
ProgramPtr program;
|
||||||
|
try {
|
||||||
|
compileProgram(program, vsSource.toLocal8Bit().toStdString(), gsSource.toLocal8Bit().toStdString(), fsSource.toLocal8Bit().toStdString());
|
||||||
|
if (program) {
|
||||||
|
using namespace oglplus;
|
||||||
|
_laserUniforms.color = Uniform<glm::vec4>(*program, "color").Location();
|
||||||
|
_laserUniforms.mvp = Uniform<glm::mat4>(*program, "mvp").Location();
|
||||||
|
_laserProgram = program;
|
||||||
|
}
|
||||||
|
} catch (std::runtime_error& error) {
|
||||||
|
qWarning() << "Error building hand laser composite shader " << error.what();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void HmdDisplayPlugin::updateOverlayProgram() {
|
||||||
|
static const QString vsFile = PathUtils::resourcesPath() + "/shaders/hmd_ui_glow.vert";
|
||||||
|
static const QString fsFile = PathUtils::resourcesPath() + "/shaders/hmd_ui_glow.frag";
|
||||||
|
|
||||||
|
#if LIVE_SHADER_RELOAD
|
||||||
|
static qint64 vsBuiltAge = 0;
|
||||||
|
static qint64 fsBuiltAge = 0;
|
||||||
|
QFileInfo vsInfo(vsFile);
|
||||||
|
QFileInfo fsInfo(fsFile);
|
||||||
|
auto vsAge = vsInfo.lastModified().toMSecsSinceEpoch();
|
||||||
|
auto fsAge = fsInfo.lastModified().toMSecsSinceEpoch();
|
||||||
|
if (!_overlayProgram || vsAge > vsBuiltAge || fsAge > fsBuiltAge) {
|
||||||
|
vsBuiltAge = vsAge;
|
||||||
|
fsBuiltAge = fsAge;
|
||||||
|
#else
|
||||||
|
if (!_overlayProgram) {
|
||||||
|
#endif
|
||||||
|
QString vsSource = readFile(vsFile);
|
||||||
|
QString fsSource = readFile(fsFile);
|
||||||
|
ProgramPtr program;
|
||||||
|
try {
|
||||||
|
compileProgram(program, vsSource.toLocal8Bit().toStdString(), fsSource.toLocal8Bit().toStdString());
|
||||||
|
if (program) {
|
||||||
|
using namespace oglplus;
|
||||||
|
_overlayUniforms.mvp = Uniform<glm::mat4>(*program, "mvp").Location();
|
||||||
|
_overlayUniforms.alpha = Uniform<float>(*program, "alpha").Location();
|
||||||
|
_overlayUniforms.glowColors = Uniform<glm::vec4>(*program, "glowColors").Location();
|
||||||
|
_overlayUniforms.glowPoints = Uniform<glm::vec4>(*program, "glowPoints").Location();
|
||||||
|
_overlayUniforms.resolution = Uniform<glm::vec2>(*program, "resolution").Location();
|
||||||
|
_overlayUniforms.radius = Uniform<float>(*program, "radius").Location();
|
||||||
|
_overlayProgram = program;
|
||||||
|
useProgram(_overlayProgram);
|
||||||
|
Uniform<glm::vec2>(*_overlayProgram, _overlayUniforms.resolution).Set(CompositorHelper::VIRTUAL_SCREEN_SIZE);
|
||||||
|
}
|
||||||
|
} catch (std::runtime_error& error) {
|
||||||
|
qWarning() << "Error building overlay composite shader " << error.what();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void HmdDisplayPlugin::uncustomizeContext() {
|
void HmdDisplayPlugin::uncustomizeContext() {
|
||||||
|
_overlayProgram.reset();
|
||||||
_sphereSection.reset();
|
_sphereSection.reset();
|
||||||
_compositeFramebuffer.reset();
|
_compositeFramebuffer.reset();
|
||||||
_previewProgram.reset();
|
_previewProgram.reset();
|
||||||
|
@ -312,12 +276,12 @@ void HmdDisplayPlugin::compositeScene() {
|
||||||
using namespace oglplus;
|
using namespace oglplus;
|
||||||
Texture::MinFilter(TextureTarget::_2D, TextureMinFilter::Linear);
|
Texture::MinFilter(TextureTarget::_2D, TextureMinFilter::Linear);
|
||||||
Texture::MagFilter(TextureTarget::_2D, TextureMagFilter::Linear);
|
Texture::MagFilter(TextureTarget::_2D, TextureMagFilter::Linear);
|
||||||
Uniform<glm::mat3>(*_reprojectionProgram, REPROJECTION_MATRIX_LOCATION).Set(_currentPresentFrameInfo.presentReprojection);
|
Uniform<glm::mat3>(*_reprojectionProgram, _reprojectionUniforms.reprojectionMatrix).Set(_currentPresentFrameInfo.presentReprojection);
|
||||||
//Uniform<glm::mat4>(*_reprojectionProgram, PROJECTION_MATRIX_LOCATION).Set(_eyeProjections);
|
//Uniform<glm::mat4>(*_reprojectionProgram, PROJECTION_MATRIX_LOCATION).Set(_eyeProjections);
|
||||||
//Uniform<glm::mat4>(*_reprojectionProgram, INVERSE_PROJECTION_MATRIX_LOCATION).Set(_eyeInverseProjections);
|
//Uniform<glm::mat4>(*_reprojectionProgram, INVERSE_PROJECTION_MATRIX_LOCATION).Set(_eyeInverseProjections);
|
||||||
// FIXME what's the right oglplus mechanism to do this? It's not that ^^^ ... better yet, switch to a uniform buffer
|
// FIXME what's the right oglplus mechanism to do this? It's not that ^^^ ... better yet, switch to a uniform buffer
|
||||||
glUniformMatrix4fv(INVERSE_PROJECTION_MATRIX_LOCATION, 2, GL_FALSE, &(_eyeInverseProjections[0][0][0]));
|
glUniformMatrix4fv(_reprojectionUniforms.inverseProjectionMatrix, 2, GL_FALSE, &(_eyeInverseProjections[0][0][0]));
|
||||||
glUniformMatrix4fv(PROJECTION_MATRIX_LOCATION, 2, GL_FALSE, &(_eyeProjections[0][0][0]));
|
glUniformMatrix4fv(_reprojectionUniforms.projectionMatrix, 2, GL_FALSE, &(_eyeProjections[0][0][0]));
|
||||||
_plane->UseInProgram(*_reprojectionProgram);
|
_plane->UseInProgram(*_reprojectionProgram);
|
||||||
_plane->Draw();
|
_plane->Draw();
|
||||||
}
|
}
|
||||||
|
@ -327,19 +291,93 @@ void HmdDisplayPlugin::compositeOverlay() {
|
||||||
auto compositorHelper = DependencyManager::get<CompositorHelper>();
|
auto compositorHelper = DependencyManager::get<CompositorHelper>();
|
||||||
glm::mat4 modelMat = compositorHelper->getModelTransform().getMatrix();
|
glm::mat4 modelMat = compositorHelper->getModelTransform().getMatrix();
|
||||||
|
|
||||||
useProgram(_program);
|
withPresentThreadLock([&] {
|
||||||
// set the alpha
|
_presentHandLasers = _handLasers;
|
||||||
Uniform<float>(*_program, _alphaUniform).Set(_compositeOverlayAlpha);
|
_presentHandPoses = _handPoses;
|
||||||
|
_presentUiModelTransform = _uiModelTransform;
|
||||||
|
});
|
||||||
|
std::array<vec2, NUMBER_OF_HANDS> handGlowPoints { { vec2(-1), vec2(-1) } };
|
||||||
|
|
||||||
|
// compute the glow point interesections
|
||||||
|
for (int i = 0; i < NUMBER_OF_HANDS; ++i) {
|
||||||
|
if (_presentHandPoses[i] == IDENTITY_MATRIX) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const auto& handLaser = _presentHandLasers[i];
|
||||||
|
if (!handLaser.valid()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto& laserDirection = handLaser.direction;
|
||||||
|
auto model = _presentHandPoses[i];
|
||||||
|
auto castDirection = glm::quat_cast(model) * laserDirection;
|
||||||
|
if (glm::abs(glm::length2(castDirection) - 1.0f) > EPSILON) {
|
||||||
|
castDirection = glm::normalize(castDirection);
|
||||||
|
castDirection = glm::inverse(_presentUiModelTransform.getRotation()) * castDirection;
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME fetch the actual UI radius from... somewhere?
|
||||||
|
float uiRadius = 1.0f;
|
||||||
|
|
||||||
|
// Find the intersection of the laser with he UI and use it to scale the model matrix
|
||||||
|
float distance;
|
||||||
|
if (!glm::intersectRaySphere(vec3(_presentHandPoses[i][3]), castDirection, _presentUiModelTransform.getTranslation(), uiRadius * uiRadius, distance)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
vec3 intersectionPosition = vec3(_presentHandPoses[i][3]) + (castDirection * distance) - _presentUiModelTransform.getTranslation();
|
||||||
|
intersectionPosition = glm::inverse(_presentUiModelTransform.getRotation()) * intersectionPosition;
|
||||||
|
|
||||||
|
// Take the interesection normal and convert it to a texture coordinate
|
||||||
|
vec2 yawPitch;
|
||||||
|
{
|
||||||
|
vec2 xdir = glm::normalize(vec2(intersectionPosition.x, -intersectionPosition.z));
|
||||||
|
yawPitch.x = glm::atan(xdir.x, xdir.y);
|
||||||
|
yawPitch.y = (acosf(intersectionPosition.y) * -1.0f) + M_PI_2;
|
||||||
|
}
|
||||||
|
vec2 halfFov = CompositorHelper::VIRTUAL_UI_TARGET_FOV / 2.0f;
|
||||||
|
|
||||||
|
// Are we out of range
|
||||||
|
if (glm::any(glm::greaterThan(glm::abs(yawPitch), halfFov))) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
yawPitch /= CompositorHelper::VIRTUAL_UI_TARGET_FOV;
|
||||||
|
yawPitch += 0.5f;
|
||||||
|
handGlowPoints[i] = yawPitch;
|
||||||
|
}
|
||||||
|
|
||||||
|
updateOverlayProgram();
|
||||||
|
if (!_overlayProgram) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
useProgram(_overlayProgram);
|
||||||
|
// Setup the uniforms
|
||||||
|
{
|
||||||
|
if (_overlayUniforms.alpha >= 0) {
|
||||||
|
Uniform<float>(*_overlayProgram, _overlayUniforms.alpha).Set(_compositeOverlayAlpha);
|
||||||
|
}
|
||||||
|
if (_overlayUniforms.glowPoints >= 0) {
|
||||||
|
vec4 glowPoints(handGlowPoints[0], handGlowPoints[1]);
|
||||||
|
Uniform<glm::vec4>(*_overlayProgram, _overlayUniforms.glowPoints).Set(glowPoints);
|
||||||
|
}
|
||||||
|
if (_overlayUniforms.glowColors >= 0) {
|
||||||
|
std::array<glm::vec4, NUMBER_OF_HANDS> glowColors;
|
||||||
|
glowColors[0] = _presentHandLasers[0].color;
|
||||||
|
glowColors[1] = _presentHandLasers[1].color;
|
||||||
|
glProgramUniform4fv(GetName(*_overlayProgram), _overlayUniforms.glowColors, 2, &glowColors[0].r);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
_sphereSection->Use();
|
_sphereSection->Use();
|
||||||
for_each_eye([&](Eye eye) {
|
for_each_eye([&](Eye eye) {
|
||||||
eyeViewport(eye);
|
eyeViewport(eye);
|
||||||
auto modelView = glm::inverse(_currentPresentFrameInfo.presentPose * getEyeToHeadTransform(eye)) * modelMat;
|
auto modelView = glm::inverse(_currentPresentFrameInfo.presentPose * getEyeToHeadTransform(eye)) * modelMat;
|
||||||
auto mvp = _eyeProjections[eye] * modelView;
|
auto mvp = _eyeProjections[eye] * modelView;
|
||||||
Uniform<glm::mat4>(*_program, _mvpUniform).Set(mvp);
|
Uniform<glm::mat4>(*_overlayProgram, _overlayUniforms.mvp).Set(mvp);
|
||||||
_sphereSection->Draw();
|
_sphereSection->Draw();
|
||||||
});
|
});
|
||||||
// restore the alpha
|
|
||||||
Uniform<float>(*_program, _alphaUniform).Set(1.0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void HmdDisplayPlugin::compositePointer() {
|
void HmdDisplayPlugin::compositePointer() {
|
||||||
|
@ -423,7 +461,7 @@ void HmdDisplayPlugin::internalPresent() {
|
||||||
glClearColor(0, 0, 0, 1);
|
glClearColor(0, 0, 0, 1);
|
||||||
glClear(GL_COLOR_BUFFER_BIT);
|
glClear(GL_COLOR_BUFFER_BIT);
|
||||||
glViewport(targetViewportPosition.x, targetViewportPosition.y, targetViewportSize.x, targetViewportSize.y);
|
glViewport(targetViewportPosition.x, targetViewportPosition.y, targetViewportSize.x, targetViewportSize.y);
|
||||||
glUniform1i(PREVIEW_TEXTURE_LOCATION, 0);
|
glUniform1i(_previewUniforms.previewTexture, 0);
|
||||||
glActiveTexture(GL_TEXTURE0);
|
glActiveTexture(GL_TEXTURE0);
|
||||||
glBindTexture(GL_TEXTURE_2D, _previewTextureID);
|
glBindTexture(GL_TEXTURE_2D, _previewTextureID);
|
||||||
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
|
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
|
||||||
|
@ -478,26 +516,17 @@ bool HmdDisplayPlugin::setHandLaser(uint32_t hands, HandLaserMode mode, const ve
|
||||||
}
|
}
|
||||||
|
|
||||||
void HmdDisplayPlugin::compositeExtra() {
|
void HmdDisplayPlugin::compositeExtra() {
|
||||||
const int NUMBER_OF_HANDS = 2;
|
|
||||||
std::array<HandLaserInfo, NUMBER_OF_HANDS> handLasers;
|
|
||||||
std::array<mat4, NUMBER_OF_HANDS> renderHandPoses;
|
|
||||||
Transform uiModelTransform;
|
|
||||||
withPresentThreadLock([&] {
|
|
||||||
handLasers = _handLasers;
|
|
||||||
renderHandPoses = _handPoses;
|
|
||||||
uiModelTransform = _uiModelTransform;
|
|
||||||
});
|
|
||||||
|
|
||||||
// If neither hand laser is activated, exit
|
// If neither hand laser is activated, exit
|
||||||
if (!handLasers[0].valid() && !handLasers[1].valid()) {
|
if (!_presentHandLasers[0].valid() && !_presentHandLasers[1].valid()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const glm::mat4 identity;
|
if (_presentHandPoses[0] == IDENTITY_MATRIX && _presentHandPoses[1] == IDENTITY_MATRIX) {
|
||||||
if (renderHandPoses[0] == identity && renderHandPoses[1] == identity) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
updateLaserProgram();
|
||||||
|
|
||||||
// Render hand lasers
|
// Render hand lasers
|
||||||
using namespace oglplus;
|
using namespace oglplus;
|
||||||
useProgram(_laserProgram);
|
useProgram(_laserProgram);
|
||||||
|
@ -505,16 +534,16 @@ void HmdDisplayPlugin::compositeExtra() {
|
||||||
std::array<mat4, NUMBER_OF_HANDS> handLaserModelMatrices;
|
std::array<mat4, NUMBER_OF_HANDS> handLaserModelMatrices;
|
||||||
|
|
||||||
for (int i = 0; i < NUMBER_OF_HANDS; ++i) {
|
for (int i = 0; i < NUMBER_OF_HANDS; ++i) {
|
||||||
if (renderHandPoses[i] == identity) {
|
if (_presentHandPoses[i] == IDENTITY_MATRIX) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
const auto& handLaser = handLasers[i];
|
const auto& handLaser = _presentHandLasers[i];
|
||||||
if (!handLaser.valid()) {
|
if (!handLaser.valid()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto& laserDirection = handLaser.direction;
|
const auto& laserDirection = handLaser.direction;
|
||||||
auto model = renderHandPoses[i];
|
auto model = _presentHandPoses[i];
|
||||||
auto castDirection = glm::quat_cast(model) * laserDirection;
|
auto castDirection = glm::quat_cast(model) * laserDirection;
|
||||||
if (glm::abs(glm::length2(castDirection) - 1.0f) > EPSILON) {
|
if (glm::abs(glm::length2(castDirection) - 1.0f) > EPSILON) {
|
||||||
castDirection = glm::normalize(castDirection);
|
castDirection = glm::normalize(castDirection);
|
||||||
|
@ -525,7 +554,7 @@ void HmdDisplayPlugin::compositeExtra() {
|
||||||
|
|
||||||
// Find the intersection of the laser with he UI and use it to scale the model matrix
|
// Find the intersection of the laser with he UI and use it to scale the model matrix
|
||||||
float distance;
|
float distance;
|
||||||
if (!glm::intersectRaySphere(vec3(renderHandPoses[i][3]), castDirection, uiModelTransform.getTranslation(), uiRadius * uiRadius, distance)) {
|
if (!glm::intersectRaySphere(vec3(_presentHandPoses[i][3]), castDirection, _presentUiModelTransform.getTranslation(), uiRadius * uiRadius, distance)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -539,19 +568,20 @@ void HmdDisplayPlugin::compositeExtra() {
|
||||||
handLaserModelMatrices[i] = model;
|
handLaserModelMatrices[i] = model;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
glEnable(GL_BLEND);
|
||||||
for_each_eye([&](Eye eye) {
|
for_each_eye([&](Eye eye) {
|
||||||
eyeViewport(eye);
|
eyeViewport(eye);
|
||||||
auto eyePose = _currentPresentFrameInfo.presentPose * getEyeToHeadTransform(eye);
|
auto eyePose = _currentPresentFrameInfo.presentPose * getEyeToHeadTransform(eye);
|
||||||
auto view = glm::inverse(eyePose);
|
auto view = glm::inverse(eyePose);
|
||||||
const auto& projection = _eyeProjections[eye];
|
const auto& projection = _eyeProjections[eye];
|
||||||
for (int i = 0; i < NUMBER_OF_HANDS; ++i) {
|
for (int i = 0; i < NUMBER_OF_HANDS; ++i) {
|
||||||
if (handLaserModelMatrices[i] == identity) {
|
if (handLaserModelMatrices[i] == IDENTITY_MATRIX) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
Uniform<glm::mat4>(*_laserProgram, "mvp").Set(projection * view * handLaserModelMatrices[i]);
|
Uniform<glm::mat4>(*_laserProgram, "mvp").Set(projection * view * handLaserModelMatrices[i]);
|
||||||
Uniform<glm::vec4>(*_laserProgram, "color").Set(handLasers[i].color);
|
Uniform<glm::vec4>(*_laserProgram, "color").Set(_presentHandLasers[i].color);
|
||||||
_laserGeometry->Draw();
|
_laserGeometry->Draw();
|
||||||
// TODO render some kind of visual indicator at the intersection point with the UI.
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
glDisable(GL_BLEND);
|
||||||
}
|
}
|
||||||
|
|
|
@ -64,6 +64,11 @@ protected:
|
||||||
Transform _uiModelTransform;
|
Transform _uiModelTransform;
|
||||||
std::array<HandLaserInfo, 2> _handLasers;
|
std::array<HandLaserInfo, 2> _handLasers;
|
||||||
std::array<glm::mat4, 2> _handPoses;
|
std::array<glm::mat4, 2> _handPoses;
|
||||||
|
|
||||||
|
Transform _presentUiModelTransform;
|
||||||
|
std::array<HandLaserInfo, 2> _presentHandLasers;
|
||||||
|
std::array<mat4, 2> _presentHandPoses;
|
||||||
|
|
||||||
std::array<glm::mat4, 2> _eyeOffsets;
|
std::array<glm::mat4, 2> _eyeOffsets;
|
||||||
std::array<glm::mat4, 2> _eyeProjections;
|
std::array<glm::mat4, 2> _eyeProjections;
|
||||||
std::array<glm::mat4, 2> _eyeInverseProjections;
|
std::array<glm::mat4, 2> _eyeInverseProjections;
|
||||||
|
@ -87,21 +92,49 @@ protected:
|
||||||
FrameInfo _currentRenderFrameInfo;
|
FrameInfo _currentRenderFrameInfo;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
void updateOverlayProgram();
|
||||||
|
void updateLaserProgram();
|
||||||
|
void updateReprojectionProgram();
|
||||||
|
|
||||||
bool _enablePreview { false };
|
bool _enablePreview { false };
|
||||||
bool _monoPreview { true };
|
bool _monoPreview { true };
|
||||||
bool _enableReprojection { true };
|
bool _enableReprojection { true };
|
||||||
bool _firstPreview { true };
|
bool _firstPreview { true };
|
||||||
|
|
||||||
|
ProgramPtr _overlayProgram;
|
||||||
|
struct OverlayUniforms {
|
||||||
|
int32_t mvp { -1 };
|
||||||
|
int32_t alpha { -1 };
|
||||||
|
int32_t glowColors { -1 };
|
||||||
|
int32_t glowPoints { -1 };
|
||||||
|
int32_t resolution { -1 };
|
||||||
|
int32_t radius { -1 };
|
||||||
|
} _overlayUniforms;
|
||||||
|
|
||||||
ProgramPtr _previewProgram;
|
ProgramPtr _previewProgram;
|
||||||
|
struct PreviewUniforms {
|
||||||
|
int32_t previewTexture { -1 };
|
||||||
|
} _previewUniforms;
|
||||||
|
|
||||||
float _previewAspect { 0 };
|
float _previewAspect { 0 };
|
||||||
GLuint _previewTextureID { 0 };
|
GLuint _previewTextureID { 0 };
|
||||||
glm::uvec2 _prevWindowSize { 0, 0 };
|
glm::uvec2 _prevWindowSize { 0, 0 };
|
||||||
qreal _prevDevicePixelRatio { 0 };
|
qreal _prevDevicePixelRatio { 0 };
|
||||||
|
|
||||||
ProgramPtr _reprojectionProgram;
|
ProgramPtr _reprojectionProgram;
|
||||||
|
struct ReprojectionUniforms {
|
||||||
|
int32_t reprojectionMatrix { -1 };
|
||||||
|
int32_t inverseProjectionMatrix { -1 };
|
||||||
|
int32_t projectionMatrix { -1 };
|
||||||
|
} _reprojectionUniforms;
|
||||||
|
|
||||||
ShapeWrapperPtr _sphereSection;
|
ShapeWrapperPtr _sphereSection;
|
||||||
|
|
||||||
ProgramPtr _laserProgram;
|
ProgramPtr _laserProgram;
|
||||||
|
struct LaserUniforms {
|
||||||
|
int32_t mvp { -1 };
|
||||||
|
int32_t color { -1 };
|
||||||
|
} _laserUniforms;
|
||||||
ShapeWrapperPtr _laserGeometry;
|
ShapeWrapperPtr _laserGeometry;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
//
|
//
|
||||||
|
|
||||||
#include <glm/gtx/quaternion.hpp>
|
#include <glm/gtx/quaternion.hpp>
|
||||||
|
#include <set>
|
||||||
|
|
||||||
#include <QJsonDocument>
|
#include <QJsonDocument>
|
||||||
#include <QtCore/QThread>
|
#include <QtCore/QThread>
|
||||||
|
@ -690,8 +691,8 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& info) {
|
||||||
glm::vec3 scaleToFit = dimensions / _model->getFBXGeometry().getUnscaledMeshExtents().size();
|
glm::vec3 scaleToFit = dimensions / _model->getFBXGeometry().getUnscaledMeshExtents().size();
|
||||||
// multiply each point by scale before handing the point-set off to the physics engine.
|
// multiply each point by scale before handing the point-set off to the physics engine.
|
||||||
// also determine the extents of the collision model.
|
// also determine the extents of the collision model.
|
||||||
for (int i = 0; i < pointCollection.size(); i++) {
|
for (int32_t i = 0; i < pointCollection.size(); i++) {
|
||||||
for (int j = 0; j < pointCollection[i].size(); j++) {
|
for (int32_t j = 0; j < pointCollection[i].size(); j++) {
|
||||||
// compensate for registration
|
// compensate for registration
|
||||||
pointCollection[i][j] += _model->getOffset();
|
pointCollection[i][j] += _model->getOffset();
|
||||||
// scale so the collision points match the model points
|
// scale so the collision points match the model points
|
||||||
|
@ -708,9 +709,9 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& info) {
|
||||||
// compute meshPart local transforms
|
// compute meshPart local transforms
|
||||||
QVector<glm::mat4> localTransforms;
|
QVector<glm::mat4> localTransforms;
|
||||||
const FBXGeometry& fbxGeometry = _model->getFBXGeometry();
|
const FBXGeometry& fbxGeometry = _model->getFBXGeometry();
|
||||||
int numberOfMeshes = fbxGeometry.meshes.size();
|
int32_t numMeshes = (int32_t)fbxGeometry.meshes.size();
|
||||||
int totalNumVertices = 0;
|
int32_t totalNumVertices = 0;
|
||||||
for (int i = 0; i < numberOfMeshes; i++) {
|
for (int32_t i = 0; i < numMeshes; i++) {
|
||||||
const FBXMesh& mesh = fbxGeometry.meshes.at(i);
|
const FBXMesh& mesh = fbxGeometry.meshes.at(i);
|
||||||
if (mesh.clusters.size() > 0) {
|
if (mesh.clusters.size() > 0) {
|
||||||
const FBXCluster& cluster = mesh.clusters.at(0);
|
const FBXCluster& cluster = mesh.clusters.at(0);
|
||||||
|
@ -722,7 +723,7 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& info) {
|
||||||
}
|
}
|
||||||
totalNumVertices += mesh.vertices.size();
|
totalNumVertices += mesh.vertices.size();
|
||||||
}
|
}
|
||||||
const int MAX_VERTICES_PER_STATIC_MESH = 1e6;
|
const int32_t MAX_VERTICES_PER_STATIC_MESH = 1e6;
|
||||||
if (totalNumVertices > MAX_VERTICES_PER_STATIC_MESH) {
|
if (totalNumVertices > MAX_VERTICES_PER_STATIC_MESH) {
|
||||||
qWarning() << "model" << getModelURL() << "has too many vertices" << totalNumVertices << "and will collide as a box.";
|
qWarning() << "model" << getModelURL() << "has too many vertices" << totalNumVertices << "and will collide as a box.";
|
||||||
info.setParams(SHAPE_TYPE_BOX, 0.5f * dimensions);
|
info.setParams(SHAPE_TYPE_BOX, 0.5f * dimensions);
|
||||||
|
@ -730,7 +731,9 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& info) {
|
||||||
}
|
}
|
||||||
|
|
||||||
auto& meshes = _model->getGeometry()->getGeometry()->getMeshes();
|
auto& meshes = _model->getGeometry()->getGeometry()->getMeshes();
|
||||||
int32_t numMeshes = (int32_t)(meshes.size());
|
|
||||||
|
// the render geometry's mesh count should match that of the FBXGeometry
|
||||||
|
assert(numMeshes == (int32_t)(meshes.size()));
|
||||||
|
|
||||||
ShapeInfo::PointCollection& pointCollection = info.getPointCollection();
|
ShapeInfo::PointCollection& pointCollection = info.getPointCollection();
|
||||||
pointCollection.clear();
|
pointCollection.clear();
|
||||||
|
@ -741,8 +744,8 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& info) {
|
||||||
}
|
}
|
||||||
|
|
||||||
Extents extents;
|
Extents extents;
|
||||||
int meshCount = 0;
|
int32_t meshCount = 0;
|
||||||
int pointListIndex = 0;
|
int32_t pointListIndex = 0;
|
||||||
for (auto& mesh : meshes) {
|
for (auto& mesh : meshes) {
|
||||||
const gpu::BufferView& vertices = mesh->getVertexBuffer();
|
const gpu::BufferView& vertices = mesh->getVertexBuffer();
|
||||||
const gpu::BufferView& indices = mesh->getIndexBuffer();
|
const gpu::BufferView& indices = mesh->getIndexBuffer();
|
||||||
|
@ -775,6 +778,7 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& info) {
|
||||||
if (type == SHAPE_TYPE_STATIC_MESH) {
|
if (type == SHAPE_TYPE_STATIC_MESH) {
|
||||||
// copy into triangleIndices
|
// copy into triangleIndices
|
||||||
ShapeInfo::TriangleIndices& triangleIndices = info.getTriangleIndices();
|
ShapeInfo::TriangleIndices& triangleIndices = info.getTriangleIndices();
|
||||||
|
triangleIndices.clear();
|
||||||
triangleIndices.reserve((int32_t)((gpu::Size)(triangleIndices.size()) + indices.getNumElements()));
|
triangleIndices.reserve((int32_t)((gpu::Size)(triangleIndices.size()) + indices.getNumElements()));
|
||||||
gpu::BufferView::Iterator<const model::Mesh::Part> partItr = parts.cbegin<const model::Mesh::Part>();
|
gpu::BufferView::Iterator<const model::Mesh::Part> partItr = parts.cbegin<const model::Mesh::Part>();
|
||||||
while (partItr != parts.cend<const model::Mesh::Part>()) {
|
while (partItr != parts.cend<const model::Mesh::Part>()) {
|
||||||
|
@ -823,6 +827,64 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& info) {
|
||||||
}
|
}
|
||||||
++partItr;
|
++partItr;
|
||||||
}
|
}
|
||||||
|
} else if (type == SHAPE_TYPE_SIMPLE_COMPOUND) {
|
||||||
|
// for each mesh copy unique part indices, separated by special bogus (flag) index values
|
||||||
|
ShapeInfo::TriangleIndices& triangleIndices = info.getTriangleIndices();
|
||||||
|
triangleIndices.clear();
|
||||||
|
gpu::BufferView::Iterator<const model::Mesh::Part> partItr = parts.cbegin<const model::Mesh::Part>();
|
||||||
|
while (partItr != parts.cend<const model::Mesh::Part>()) {
|
||||||
|
// collect unique list of indices for this part
|
||||||
|
std::set<int32_t> uniqueIndices;
|
||||||
|
if (partItr->_topology == model::Mesh::TRIANGLES) {
|
||||||
|
assert(partItr->_numIndices % 3 == 0);
|
||||||
|
auto indexItr = indices.cbegin<const gpu::BufferView::Index>() + partItr->_startIndex;
|
||||||
|
auto indexEnd = indexItr + partItr->_numIndices;
|
||||||
|
while (indexItr != indexEnd) {
|
||||||
|
uniqueIndices.insert(*indexItr);
|
||||||
|
++indexItr;
|
||||||
|
}
|
||||||
|
} else if (partItr->_topology == model::Mesh::TRIANGLE_STRIP) {
|
||||||
|
assert(partItr->_numIndices > 2);
|
||||||
|
auto indexItr = indices.cbegin<const gpu::BufferView::Index>() + partItr->_startIndex;
|
||||||
|
auto indexEnd = indexItr + (partItr->_numIndices - 2);
|
||||||
|
|
||||||
|
// first triangle uses the first three indices
|
||||||
|
uniqueIndices.insert(*(indexItr++));
|
||||||
|
uniqueIndices.insert(*(indexItr++));
|
||||||
|
uniqueIndices.insert(*(indexItr++));
|
||||||
|
|
||||||
|
// the rest use previous and next index
|
||||||
|
uint32_t triangleCount = 1;
|
||||||
|
while (indexItr != indexEnd) {
|
||||||
|
if ((*indexItr) != model::Mesh::PRIMITIVE_RESTART_INDEX) {
|
||||||
|
if (triangleCount % 2 == 0) {
|
||||||
|
// even triangles use first two indices in order
|
||||||
|
uniqueIndices.insert(*(indexItr - 2));
|
||||||
|
uniqueIndices.insert(*(indexItr - 1));
|
||||||
|
} else {
|
||||||
|
// odd triangles swap order of first two indices
|
||||||
|
uniqueIndices.insert(*(indexItr - 1));
|
||||||
|
uniqueIndices.insert(*(indexItr - 2));
|
||||||
|
}
|
||||||
|
uniqueIndices.insert(*indexItr);
|
||||||
|
++triangleCount;
|
||||||
|
}
|
||||||
|
++indexItr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// store uniqueIndices in triangleIndices
|
||||||
|
triangleIndices.reserve(triangleIndices.size() + (int32_t)uniqueIndices.size());
|
||||||
|
for (auto index : uniqueIndices) {
|
||||||
|
triangleIndices.push_back(index);
|
||||||
|
}
|
||||||
|
// flag end of part
|
||||||
|
triangleIndices.push_back(END_OF_MESH_PART);
|
||||||
|
|
||||||
|
++partItr;
|
||||||
|
}
|
||||||
|
// flag end of mesh
|
||||||
|
triangleIndices.push_back(END_OF_MESH);
|
||||||
}
|
}
|
||||||
++meshCount;
|
++meshCount;
|
||||||
}
|
}
|
||||||
|
@ -830,13 +892,13 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& info) {
|
||||||
// scale and shift
|
// scale and shift
|
||||||
glm::vec3 extentsSize = extents.size();
|
glm::vec3 extentsSize = extents.size();
|
||||||
glm::vec3 scaleToFit = dimensions / extentsSize;
|
glm::vec3 scaleToFit = dimensions / extentsSize;
|
||||||
for (int i = 0; i < 3; ++i) {
|
for (int32_t i = 0; i < 3; ++i) {
|
||||||
if (extentsSize[i] < 1.0e-6f) {
|
if (extentsSize[i] < 1.0e-6f) {
|
||||||
scaleToFit[i] = 1.0f;
|
scaleToFit[i] = 1.0f;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (auto points : pointCollection) {
|
for (auto points : pointCollection) {
|
||||||
for (int i = 0; i < points.size(); ++i) {
|
for (int32_t i = 0; i < points.size(); ++i) {
|
||||||
points[i] = (points[i] * scaleToFit);
|
points[i] = (points[i] * scaleToFit);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -87,6 +87,36 @@ ProgramPtr loadCubemapShader() {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void compileProgram(ProgramPtr & result, const std::string& vs, const std::string& gs, const std::string& fs) {
|
||||||
|
using namespace oglplus;
|
||||||
|
try {
|
||||||
|
result = std::make_shared<Program>();
|
||||||
|
// attach the shaders to the program
|
||||||
|
result->AttachShader(
|
||||||
|
VertexShader()
|
||||||
|
.Source(GLSLSource(vs))
|
||||||
|
.Compile()
|
||||||
|
);
|
||||||
|
result->AttachShader(
|
||||||
|
GeometryShader()
|
||||||
|
.Source(GLSLSource(gs))
|
||||||
|
.Compile()
|
||||||
|
);
|
||||||
|
result->AttachShader(
|
||||||
|
FragmentShader()
|
||||||
|
.Source(GLSLSource(fs))
|
||||||
|
.Compile()
|
||||||
|
);
|
||||||
|
result->Link();
|
||||||
|
} catch (ProgramBuildError& err) {
|
||||||
|
Q_UNUSED(err);
|
||||||
|
qWarning() << err.Log().c_str();
|
||||||
|
Q_ASSERT_X(false, "compileProgram", "Failed to build shader program");
|
||||||
|
qFatal("%s", (const char*)err.Message);
|
||||||
|
result.reset();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void compileProgram(ProgramPtr & result, const std::string& vs, const std::string& fs) {
|
void compileProgram(ProgramPtr & result, const std::string& vs, const std::string& fs) {
|
||||||
using namespace oglplus;
|
using namespace oglplus;
|
||||||
try {
|
try {
|
||||||
|
|
|
@ -62,6 +62,8 @@ using Mat4Uniform = oglplus::Uniform<mat4>;
|
||||||
ProgramPtr loadDefaultShader();
|
ProgramPtr loadDefaultShader();
|
||||||
ProgramPtr loadCubemapShader();
|
ProgramPtr loadCubemapShader();
|
||||||
void compileProgram(ProgramPtr & result, const std::string& vs, const std::string& fs);
|
void compileProgram(ProgramPtr & result, const std::string& vs, const std::string& fs);
|
||||||
|
void compileProgram(ProgramPtr & result, const std::string& vs, const std::string& gs, const std::string& fs);
|
||||||
|
|
||||||
ShapeWrapperPtr loadSkybox(ProgramPtr program);
|
ShapeWrapperPtr loadSkybox(ProgramPtr program);
|
||||||
ShapeWrapperPtr loadPlane(ProgramPtr program, float aspect = 1.0f);
|
ShapeWrapperPtr loadPlane(ProgramPtr program, float aspect = 1.0f);
|
||||||
ShapeWrapperPtr loadSphereSection(ProgramPtr program, float fov = PI / 3.0f * 2.0f, float aspect = 16.0f / 9.0f, int slices = 128, int stacks = 128);
|
ShapeWrapperPtr loadSphereSection(ProgramPtr program, float fov = PI / 3.0f * 2.0f, float aspect = 16.0f / 9.0f, int slices = 128, int stacks = 128);
|
||||||
|
|
|
@ -50,9 +50,11 @@ void KeyboardMouseDevice::InputDevice::focusOutEvent() {
|
||||||
|
|
||||||
void KeyboardMouseDevice::keyPressEvent(QKeyEvent* event) {
|
void KeyboardMouseDevice::keyPressEvent(QKeyEvent* event) {
|
||||||
auto input = _inputDevice->makeInput((Qt::Key) event->key());
|
auto input = _inputDevice->makeInput((Qt::Key) event->key());
|
||||||
auto result = _inputDevice->_buttonPressedMap.insert(input.getChannel());
|
if (!(event->modifiers() & Qt::KeyboardModifier::ControlModifier)) {
|
||||||
if (!result.second) {
|
auto result = _inputDevice->_buttonPressedMap.insert(input.getChannel());
|
||||||
// key pressed again ? without catching the release event ?
|
if (result.second) {
|
||||||
|
// key pressed again ? without catching the release event ?
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -564,10 +564,10 @@ bool AddressManager::handleViewpoint(const QString& viewpointString, bool should
|
||||||
if (viewpointString[positionRegex.matchedLength() - 1] == QChar('/')
|
if (viewpointString[positionRegex.matchedLength() - 1] == QChar('/')
|
||||||
&& orientationRegex.indexIn(viewpointString, positionRegex.matchedLength() - 1) != -1) {
|
&& orientationRegex.indexIn(viewpointString, positionRegex.matchedLength() - 1) != -1) {
|
||||||
|
|
||||||
glm::quat newOrientation = glm::normalize(glm::quat(orientationRegex.cap(4).toFloat(),
|
newOrientation = glm::normalize(glm::quat(orientationRegex.cap(4).toFloat(),
|
||||||
orientationRegex.cap(1).toFloat(),
|
orientationRegex.cap(1).toFloat(),
|
||||||
orientationRegex.cap(2).toFloat(),
|
orientationRegex.cap(2).toFloat(),
|
||||||
orientationRegex.cap(3).toFloat()));
|
orientationRegex.cap(3).toFloat()));
|
||||||
|
|
||||||
if (!isNaN(newOrientation.x) && !isNaN(newOrientation.y) && !isNaN(newOrientation.z)
|
if (!isNaN(newOrientation.x) && !isNaN(newOrientation.y) && !isNaN(newOrientation.z)
|
||||||
&& !isNaN(newOrientation.w)) {
|
&& !isNaN(newOrientation.w)) {
|
||||||
|
|
|
@ -69,6 +69,7 @@ static const btVector3 _unitSphereDirections[NUM_UNIT_SPHERE_DIRECTIONS] = {
|
||||||
|
|
||||||
// util method
|
// util method
|
||||||
btConvexHullShape* createConvexHull(const ShapeInfo::PointList& points) {
|
btConvexHullShape* createConvexHull(const ShapeInfo::PointList& points) {
|
||||||
|
//std::cout << "adebug createConvexHull() points.size() = " << points.size() << std::endl; // adebug
|
||||||
assert(points.size() > 0);
|
assert(points.size() > 0);
|
||||||
|
|
||||||
btConvexHullShape* hull = new btConvexHullShape();
|
btConvexHullShape* hull = new btConvexHullShape();
|
||||||
|
@ -240,6 +241,7 @@ void deleteStaticMeshArray(btTriangleIndexVertexArray* dataArray) {
|
||||||
btCollisionShape* ShapeFactory::createShapeFromInfo(const ShapeInfo& info) {
|
btCollisionShape* ShapeFactory::createShapeFromInfo(const ShapeInfo& info) {
|
||||||
btCollisionShape* shape = NULL;
|
btCollisionShape* shape = NULL;
|
||||||
int type = info.getType();
|
int type = info.getType();
|
||||||
|
//std::cout << "adebug createShapeFromInfo() type = " << type << std::endl; // adebug
|
||||||
switch(type) {
|
switch(type) {
|
||||||
case SHAPE_TYPE_BOX: {
|
case SHAPE_TYPE_BOX: {
|
||||||
shape = new btBoxShape(glmToBullet(info.getHalfExtents()));
|
shape = new btBoxShape(glmToBullet(info.getHalfExtents()));
|
||||||
|
@ -258,8 +260,7 @@ btCollisionShape* ShapeFactory::createShapeFromInfo(const ShapeInfo& info) {
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case SHAPE_TYPE_COMPOUND:
|
case SHAPE_TYPE_COMPOUND:
|
||||||
case SHAPE_TYPE_SIMPLE_HULL:
|
case SHAPE_TYPE_SIMPLE_HULL: {
|
||||||
case SHAPE_TYPE_SIMPLE_COMPOUND: {
|
|
||||||
const ShapeInfo::PointCollection& pointCollection = info.getPointCollection();
|
const ShapeInfo::PointCollection& pointCollection = info.getPointCollection();
|
||||||
uint32_t numSubShapes = info.getNumSubShapes();
|
uint32_t numSubShapes = info.getNumSubShapes();
|
||||||
if (numSubShapes == 1) {
|
if (numSubShapes == 1) {
|
||||||
|
@ -270,12 +271,63 @@ btCollisionShape* ShapeFactory::createShapeFromInfo(const ShapeInfo& info) {
|
||||||
trans.setIdentity();
|
trans.setIdentity();
|
||||||
foreach (const ShapeInfo::PointList& hullPoints, pointCollection) {
|
foreach (const ShapeInfo::PointList& hullPoints, pointCollection) {
|
||||||
btConvexHullShape* hull = createConvexHull(hullPoints);
|
btConvexHullShape* hull = createConvexHull(hullPoints);
|
||||||
compound->addChildShape (trans, hull);
|
compound->addChildShape(trans, hull);
|
||||||
}
|
}
|
||||||
shape = compound;
|
shape = compound;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case SHAPE_TYPE_SIMPLE_COMPOUND: {
|
||||||
|
const ShapeInfo::PointCollection& pointCollection = info.getPointCollection();
|
||||||
|
const ShapeInfo::TriangleIndices& triangleIndices = info.getTriangleIndices();
|
||||||
|
uint32_t numIndices = triangleIndices.size();
|
||||||
|
uint32_t numMeshes = info.getNumSubShapes();
|
||||||
|
const uint32_t MIN_NUM_SIMPLE_COMPOUND_INDICES = 2; // END_OF_MESH_PART + END_OF_MESH
|
||||||
|
if (numMeshes > 0 && numIndices > MIN_NUM_SIMPLE_COMPOUND_INDICES) {
|
||||||
|
uint32_t i = 0;
|
||||||
|
std::vector<btConvexHullShape*> hulls;
|
||||||
|
for (auto& points : pointCollection) {
|
||||||
|
// build a hull around each part
|
||||||
|
while (i < numIndices) {
|
||||||
|
ShapeInfo::PointList hullPoints;
|
||||||
|
hullPoints.reserve(points.size());
|
||||||
|
while (i < numIndices) {
|
||||||
|
int32_t j = triangleIndices[i];
|
||||||
|
++i;
|
||||||
|
if (j == END_OF_MESH_PART) {
|
||||||
|
// end of part
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
hullPoints.push_back(points[j]);
|
||||||
|
}
|
||||||
|
if (hullPoints.size() > 0) {
|
||||||
|
btConvexHullShape* hull = createConvexHull(hullPoints);
|
||||||
|
hulls.push_back(hull);
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(i < numIndices);
|
||||||
|
if (triangleIndices[i] == END_OF_MESH) {
|
||||||
|
// end of mesh
|
||||||
|
++i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
uint32_t numHulls = (uint32_t)hulls.size();
|
||||||
|
if (numHulls == 1) {
|
||||||
|
shape = hulls[0];
|
||||||
|
} else {
|
||||||
|
auto compound = new btCompoundShape();
|
||||||
|
btTransform trans;
|
||||||
|
trans.setIdentity();
|
||||||
|
for (auto hull : hulls) {
|
||||||
|
compound->addChildShape(trans, hull);
|
||||||
|
}
|
||||||
|
shape = compound;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
case SHAPE_TYPE_STATIC_MESH: {
|
case SHAPE_TYPE_STATIC_MESH: {
|
||||||
btTriangleIndexVertexArray* dataArray = createStaticMeshArray(info);
|
btTriangleIndexVertexArray* dataArray = createStaticMeshArray(info);
|
||||||
shape = new StaticMeshShape(dataArray);
|
shape = new StaticMeshShape(dataArray);
|
||||||
|
|
|
@ -432,7 +432,7 @@ void GeometryCache::renderGrid(gpu::Batch& batch, const glm::vec2& minCorner, co
|
||||||
renderQuad(batch, minCorner, maxCorner, MIN_TEX_COORD, MAX_TEX_COORD, color, id);
|
renderQuad(batch, minCorner, maxCorner, MIN_TEX_COORD, MAX_TEX_COORD, color, id);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GeometryCache::updateVertices(int id, const QVector<glm::vec2>& points, const glm::vec4& color) {
|
void GeometryCache::updateVertices(int id, const QVector<glm::vec2>& points, const QVector<glm::vec4>& colors) {
|
||||||
BatchItemDetails& details = _registeredVertices[id];
|
BatchItemDetails& details = _registeredVertices[id];
|
||||||
|
|
||||||
if (details.isCreated) {
|
if (details.isCreated) {
|
||||||
|
@ -469,11 +469,6 @@ void GeometryCache::updateVertices(int id, const QVector<glm::vec2>& points, con
|
||||||
details.vertices = points.size();
|
details.vertices = points.size();
|
||||||
details.vertexSize = FLOATS_PER_VERTEX;
|
details.vertexSize = FLOATS_PER_VERTEX;
|
||||||
|
|
||||||
int compactColor = ((int(color.x * 255.0f) & 0xFF)) |
|
|
||||||
((int(color.y * 255.0f) & 0xFF) << 8) |
|
|
||||||
((int(color.z * 255.0f) & 0xFF) << 16) |
|
|
||||||
((int(color.w * 255.0f) & 0xFF) << 24);
|
|
||||||
|
|
||||||
float* vertexData = new float[details.vertices * FLOATS_PER_VERTEX];
|
float* vertexData = new float[details.vertices * FLOATS_PER_VERTEX];
|
||||||
float* vertex = vertexData;
|
float* vertex = vertexData;
|
||||||
|
|
||||||
|
@ -481,16 +476,25 @@ void GeometryCache::updateVertices(int id, const QVector<glm::vec2>& points, con
|
||||||
int* colorDataAt = colorData;
|
int* colorDataAt = colorData;
|
||||||
|
|
||||||
const glm::vec3 NORMAL(0.0f, 0.0f, 1.0f);
|
const glm::vec3 NORMAL(0.0f, 0.0f, 1.0f);
|
||||||
foreach(const glm::vec2& point, points) {
|
auto pointCount = points.size();
|
||||||
|
auto colorCount = colors.size();
|
||||||
|
int compactColor = 0;
|
||||||
|
for (auto i = 0; i < pointCount; ++i) {
|
||||||
|
const auto& point = points[i];
|
||||||
*(vertex++) = point.x;
|
*(vertex++) = point.x;
|
||||||
*(vertex++) = point.y;
|
*(vertex++) = point.y;
|
||||||
*(vertex++) = NORMAL.x;
|
*(vertex++) = NORMAL.x;
|
||||||
*(vertex++) = NORMAL.y;
|
*(vertex++) = NORMAL.y;
|
||||||
*(vertex++) = NORMAL.z;
|
*(vertex++) = NORMAL.z;
|
||||||
|
if (i < colorCount) {
|
||||||
|
const auto& color = colors[i];
|
||||||
|
compactColor = ((int(color.x * 255.0f) & 0xFF)) |
|
||||||
|
((int(color.y * 255.0f) & 0xFF) << 8) |
|
||||||
|
((int(color.z * 255.0f) & 0xFF) << 16) |
|
||||||
|
((int(color.w * 255.0f) & 0xFF) << 24);
|
||||||
|
}
|
||||||
*(colorDataAt++) = compactColor;
|
*(colorDataAt++) = compactColor;
|
||||||
}
|
}
|
||||||
|
|
||||||
details.verticesBuffer->append(sizeof(float) * FLOATS_PER_VERTEX * details.vertices, (gpu::Byte*) vertexData);
|
details.verticesBuffer->append(sizeof(float) * FLOATS_PER_VERTEX * details.vertices, (gpu::Byte*) vertexData);
|
||||||
details.colorBuffer->append(sizeof(int) * details.vertices, (gpu::Byte*) colorData);
|
details.colorBuffer->append(sizeof(int) * details.vertices, (gpu::Byte*) colorData);
|
||||||
delete[] vertexData;
|
delete[] vertexData;
|
||||||
|
@ -501,7 +505,11 @@ void GeometryCache::updateVertices(int id, const QVector<glm::vec2>& points, con
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void GeometryCache::updateVertices(int id, const QVector<glm::vec3>& points, const glm::vec4& color) {
|
void GeometryCache::updateVertices(int id, const QVector<glm::vec2>& points, const glm::vec4& color) {
|
||||||
|
updateVertices(id, points, QVector<glm::vec4>({ color }));
|
||||||
|
}
|
||||||
|
|
||||||
|
void GeometryCache::updateVertices(int id, const QVector<glm::vec3>& points, const QVector<glm::vec4>& colors) {
|
||||||
BatchItemDetails& details = _registeredVertices[id];
|
BatchItemDetails& details = _registeredVertices[id];
|
||||||
if (details.isCreated) {
|
if (details.isCreated) {
|
||||||
details.clear();
|
details.clear();
|
||||||
|
@ -537,11 +545,8 @@ void GeometryCache::updateVertices(int id, const QVector<glm::vec3>& points, con
|
||||||
details.vertices = points.size();
|
details.vertices = points.size();
|
||||||
details.vertexSize = FLOATS_PER_VERTEX;
|
details.vertexSize = FLOATS_PER_VERTEX;
|
||||||
|
|
||||||
int compactColor = ((int(color.x * 255.0f) & 0xFF)) |
|
// Default to white
|
||||||
((int(color.y * 255.0f) & 0xFF) << 8) |
|
int compactColor = 0xFFFFFFFF;
|
||||||
((int(color.z * 255.0f) & 0xFF) << 16) |
|
|
||||||
((int(color.w * 255.0f) & 0xFF) << 24);
|
|
||||||
|
|
||||||
float* vertexData = new float[details.vertices * FLOATS_PER_VERTEX];
|
float* vertexData = new float[details.vertices * FLOATS_PER_VERTEX];
|
||||||
float* vertex = vertexData;
|
float* vertex = vertexData;
|
||||||
|
|
||||||
|
@ -549,14 +554,23 @@ void GeometryCache::updateVertices(int id, const QVector<glm::vec3>& points, con
|
||||||
int* colorDataAt = colorData;
|
int* colorDataAt = colorData;
|
||||||
|
|
||||||
const glm::vec3 NORMAL(0.0f, 0.0f, 1.0f);
|
const glm::vec3 NORMAL(0.0f, 0.0f, 1.0f);
|
||||||
foreach(const glm::vec3& point, points) {
|
auto pointCount = points.size();
|
||||||
|
auto colorCount = colors.size();
|
||||||
|
for (auto i = 0; i < pointCount; ++i) {
|
||||||
|
const glm::vec3& point = points[i];
|
||||||
|
if (i < colorCount) {
|
||||||
|
const glm::vec4& color = colors[i];
|
||||||
|
compactColor = ((int(color.x * 255.0f) & 0xFF)) |
|
||||||
|
((int(color.y * 255.0f) & 0xFF) << 8) |
|
||||||
|
((int(color.z * 255.0f) & 0xFF) << 16) |
|
||||||
|
((int(color.w * 255.0f) & 0xFF) << 24);
|
||||||
|
}
|
||||||
*(vertex++) = point.x;
|
*(vertex++) = point.x;
|
||||||
*(vertex++) = point.y;
|
*(vertex++) = point.y;
|
||||||
*(vertex++) = point.z;
|
*(vertex++) = point.z;
|
||||||
*(vertex++) = NORMAL.x;
|
*(vertex++) = NORMAL.x;
|
||||||
*(vertex++) = NORMAL.y;
|
*(vertex++) = NORMAL.y;
|
||||||
*(vertex++) = NORMAL.z;
|
*(vertex++) = NORMAL.z;
|
||||||
|
|
||||||
*(colorDataAt++) = compactColor;
|
*(colorDataAt++) = compactColor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -570,6 +584,10 @@ void GeometryCache::updateVertices(int id, const QVector<glm::vec3>& points, con
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GeometryCache::updateVertices(int id, const QVector<glm::vec3>& points, const glm::vec4& color) {
|
||||||
|
updateVertices(id, points, QVector<glm::vec4>({ color }));
|
||||||
|
}
|
||||||
|
|
||||||
void GeometryCache::updateVertices(int id, const QVector<glm::vec3>& points, const QVector<glm::vec2>& texCoords, const glm::vec4& color) {
|
void GeometryCache::updateVertices(int id, const QVector<glm::vec3>& points, const QVector<glm::vec2>& texCoords, const glm::vec4& color) {
|
||||||
BatchItemDetails& details = _registeredVertices[id];
|
BatchItemDetails& details = _registeredVertices[id];
|
||||||
|
|
||||||
|
|
|
@ -283,7 +283,9 @@ public:
|
||||||
const glm::vec4& color1, const glm::vec4& color2, int id = UNKNOWN_ID);
|
const glm::vec4& color1, const glm::vec4& color2, int id = UNKNOWN_ID);
|
||||||
|
|
||||||
void updateVertices(int id, const QVector<glm::vec2>& points, const glm::vec4& color);
|
void updateVertices(int id, const QVector<glm::vec2>& points, const glm::vec4& color);
|
||||||
|
void updateVertices(int id, const QVector<glm::vec2>& points, const QVector<glm::vec4>& colors);
|
||||||
void updateVertices(int id, const QVector<glm::vec3>& points, const glm::vec4& color);
|
void updateVertices(int id, const QVector<glm::vec3>& points, const glm::vec4& color);
|
||||||
|
void updateVertices(int id, const QVector<glm::vec3>& points, const QVector<glm::vec4>& colors);
|
||||||
void updateVertices(int id, const QVector<glm::vec3>& points, const QVector<glm::vec2>& texCoords, const glm::vec4& color);
|
void updateVertices(int id, const QVector<glm::vec3>& points, const QVector<glm::vec2>& texCoords, const glm::vec4& color);
|
||||||
void renderVertices(gpu::Batch& batch, gpu::Primitive primitiveType, int id);
|
void renderVertices(gpu::Batch& batch, gpu::Primitive primitiveType, int id);
|
||||||
|
|
||||||
|
@ -360,7 +362,7 @@ private:
|
||||||
|
|
||||||
QHash<IntPair, VerticesIndices> _coneVBOs;
|
QHash<IntPair, VerticesIndices> _coneVBOs;
|
||||||
|
|
||||||
int _nextID{ 0 };
|
int _nextID{ 1 };
|
||||||
|
|
||||||
QHash<int, Vec3PairVec4Pair> _lastRegisteredQuad3DTexture;
|
QHash<int, Vec3PairVec4Pair> _lastRegisteredQuad3DTexture;
|
||||||
QHash<Vec3PairVec4Pair, BatchItemDetails> _quad3DTextures;
|
QHash<Vec3PairVec4Pair, BatchItemDetails> _quad3DTextures;
|
||||||
|
|
|
@ -20,6 +20,7 @@
|
||||||
#include <PathUtils.h>
|
#include <PathUtils.h>
|
||||||
#include <PerfStat.h>
|
#include <PerfStat.h>
|
||||||
#include <ViewFrustum.h>
|
#include <ViewFrustum.h>
|
||||||
|
#include <GLMHelpers.h>
|
||||||
|
|
||||||
#include "AbstractViewStateInterface.h"
|
#include "AbstractViewStateInterface.h"
|
||||||
#include "MeshPartPayload.h"
|
#include "MeshPartPayload.h"
|
||||||
|
|
|
@ -465,6 +465,7 @@ void ScriptEngine::init() {
|
||||||
qScriptRegisterMetaType(this, EntityItemPropertiesToScriptValue, EntityItemPropertiesFromScriptValueHonorReadOnly);
|
qScriptRegisterMetaType(this, EntityItemPropertiesToScriptValue, EntityItemPropertiesFromScriptValueHonorReadOnly);
|
||||||
qScriptRegisterMetaType(this, EntityItemIDtoScriptValue, EntityItemIDfromScriptValue);
|
qScriptRegisterMetaType(this, EntityItemIDtoScriptValue, EntityItemIDfromScriptValue);
|
||||||
qScriptRegisterMetaType(this, RayToEntityIntersectionResultToScriptValue, RayToEntityIntersectionResultFromScriptValue);
|
qScriptRegisterMetaType(this, RayToEntityIntersectionResultToScriptValue, RayToEntityIntersectionResultFromScriptValue);
|
||||||
|
qScriptRegisterMetaType(this, RayToAvatarIntersectionResultToScriptValue, RayToAvatarIntersectionResultFromScriptValue);
|
||||||
qScriptRegisterSequenceMetaType<QVector<QUuid>>(this);
|
qScriptRegisterSequenceMetaType<QVector<QUuid>>(this);
|
||||||
qScriptRegisterSequenceMetaType<QVector<EntityItemID>>(this);
|
qScriptRegisterSequenceMetaType<QVector<EntityItemID>>(this);
|
||||||
|
|
||||||
|
|
|
@ -26,6 +26,10 @@ const float MIN_SHAPE_OFFSET = 0.001f; // offsets less than 1mm will be ignored
|
||||||
// trim convex hulls with many points down to only 42 points.
|
// trim convex hulls with many points down to only 42 points.
|
||||||
const int MAX_HULL_POINTS = 42;
|
const int MAX_HULL_POINTS = 42;
|
||||||
|
|
||||||
|
|
||||||
|
const int32_t END_OF_MESH_PART = -1; // bogus vertex index at end of mesh part
|
||||||
|
const int32_t END_OF_MESH = -2; // bogus vertex index at end of mesh
|
||||||
|
|
||||||
enum ShapeType {
|
enum ShapeType {
|
||||||
SHAPE_TYPE_NONE,
|
SHAPE_TYPE_NONE,
|
||||||
SHAPE_TYPE_BOX,
|
SHAPE_TYPE_BOX,
|
||||||
|
@ -50,7 +54,7 @@ public:
|
||||||
|
|
||||||
using PointList = QVector<glm::vec3>;
|
using PointList = QVector<glm::vec3>;
|
||||||
using PointCollection = QVector<PointList>;
|
using PointCollection = QVector<PointList>;
|
||||||
using TriangleIndices = QVector<uint32_t>;
|
using TriangleIndices = QVector<int32_t>;
|
||||||
|
|
||||||
void clear();
|
void clear();
|
||||||
|
|
||||||
|
|
|
@ -1984,10 +1984,24 @@ function MyController(hand) {
|
||||||
|
|
||||||
var noVelocity = false;
|
var noVelocity = false;
|
||||||
if (this.grabbedEntity !== null) {
|
if (this.grabbedEntity !== null) {
|
||||||
|
|
||||||
|
// If this looks like the release after adjusting something still held in the other hand, print the position
|
||||||
|
// and rotation of the held thing to help content creators set the userData.
|
||||||
|
var grabData = getEntityCustomData(GRAB_USER_DATA_KEY, this.grabbedEntity, {});
|
||||||
|
if (grabData.refCount > 1) {
|
||||||
|
grabbedProperties = Entities.getEntityProperties(this.grabbedEntity, ["localPosition", "localRotation"]);
|
||||||
|
if (grabbedProperties && grabbedProperties.localPosition && grabbedProperties.localRotation) {
|
||||||
|
print((this.hand === RIGHT_HAND ? '"LeftHand"' : '"RightHand"') + ":" +
|
||||||
|
'[{"x":' + grabbedProperties.localPosition.x + ', "y":' + grabbedProperties.localPosition.y +
|
||||||
|
', "z":' + grabbedProperties.localPosition.z + '}, {"x":' + grabbedProperties.localRotation.x +
|
||||||
|
', "y":' + grabbedProperties.localRotation.y + ', "z":' + grabbedProperties.localRotation.z +
|
||||||
|
', "w":' + grabbedProperties.localRotation.w + '}]');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (this.actionID !== null) {
|
if (this.actionID !== null) {
|
||||||
Entities.deleteAction(this.grabbedEntity, this.actionID);
|
Entities.deleteAction(this.grabbedEntity, this.actionID);
|
||||||
// sometimes we want things to stay right where they are when we let go.
|
// sometimes we want things to stay right where they are when we let go.
|
||||||
var grabData = getEntityCustomData(GRAB_USER_DATA_KEY, this.grabbedEntity, {});
|
|
||||||
var releaseVelocityData = getEntityCustomData(GRABBABLE_DATA_KEY, this.grabbedEntity, DEFAULT_GRABBABLE_DATA);
|
var releaseVelocityData = getEntityCustomData(GRABBABLE_DATA_KEY, this.grabbedEntity, DEFAULT_GRABBABLE_DATA);
|
||||||
if (releaseVelocityData.disableReleaseVelocity === true ||
|
if (releaseVelocityData.disableReleaseVelocity === true ||
|
||||||
// this next line allowed both:
|
// this next line allowed both:
|
||||||
|
|
|
@ -49,10 +49,10 @@
|
||||||
// 2. Declare a text string.
|
// 2. Declare a text string.
|
||||||
// 3. Call createNotifications(text, NotificationType) parsing the text.
|
// 3. Call createNotifications(text, NotificationType) parsing the text.
|
||||||
// example:
|
// example:
|
||||||
// if (key.text === "s") {
|
// if (key.text === "o") {
|
||||||
// if (ctrlIsPressed === true) {
|
// if (ctrlIsPressed === true) {
|
||||||
// noteString = "Snapshot taken.";
|
// noteString = "Open script";
|
||||||
// createNotification(noteString, NotificationType.SNAPSHOT);
|
// createNotification(noteString, NotificationType.OPEN_SCRIPT);
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
|
|
||||||
|
@ -233,8 +233,9 @@ function calculate3DOverlayPositions(noticeWidth, noticeHeight, y) {
|
||||||
// Pushes data to each array and sets up data for 2nd dimension array
|
// Pushes data to each array and sets up data for 2nd dimension array
|
||||||
// to handle auxiliary data not carried by the overlay class
|
// to handle auxiliary data not carried by the overlay class
|
||||||
// specifically notification "heights", "times" of creation, and .
|
// specifically notification "heights", "times" of creation, and .
|
||||||
function notify(notice, button, height) {
|
function notify(notice, button, height, imageProperties, image) {
|
||||||
var noticeWidth,
|
var notificationText,
|
||||||
|
noticeWidth,
|
||||||
noticeHeight,
|
noticeHeight,
|
||||||
positions,
|
positions,
|
||||||
last;
|
last;
|
||||||
|
@ -246,20 +247,27 @@ function notify(notice, button, height) {
|
||||||
noticeHeight = notice.height * NOTIFICATION_3D_SCALE;
|
noticeHeight = notice.height * NOTIFICATION_3D_SCALE;
|
||||||
|
|
||||||
notice.size = { x: noticeWidth, y: noticeHeight };
|
notice.size = { x: noticeWidth, y: noticeHeight };
|
||||||
notice.topMargin = 0.75 * notice.topMargin * NOTIFICATION_3D_SCALE;
|
|
||||||
notice.leftMargin = 2 * notice.leftMargin * NOTIFICATION_3D_SCALE;
|
positions = calculate3DOverlayPositions(noticeWidth, noticeHeight, notice.y);
|
||||||
notice.bottomMargin = 0;
|
|
||||||
notice.rightMargin = 0;
|
if (!image) {
|
||||||
notice.lineHeight = 10.0 * (fontSize / 12.0) * NOTIFICATION_3D_SCALE;
|
notice.topMargin = 0.75 * notice.topMargin * NOTIFICATION_3D_SCALE;
|
||||||
notice.isFacingAvatar = false;
|
notice.leftMargin = 2 * notice.leftMargin * NOTIFICATION_3D_SCALE;
|
||||||
|
notice.bottomMargin = 0;
|
||||||
|
notice.rightMargin = 0;
|
||||||
|
notice.lineHeight = 10.0 * (fontSize / 12.0) * NOTIFICATION_3D_SCALE;
|
||||||
|
notice.isFacingAvatar = false;
|
||||||
|
|
||||||
|
notificationText = Overlays.addOverlay("text3d", notice);
|
||||||
|
notifications.push(notificationText);
|
||||||
|
} else {
|
||||||
|
notifications.push(Overlays.addOverlay("image3d", notice));
|
||||||
|
}
|
||||||
|
|
||||||
button.url = button.imageURL;
|
button.url = button.imageURL;
|
||||||
button.scale = button.width * NOTIFICATION_3D_SCALE;
|
button.scale = button.width * NOTIFICATION_3D_SCALE;
|
||||||
button.isFacingAvatar = false;
|
button.isFacingAvatar = false;
|
||||||
|
|
||||||
positions = calculate3DOverlayPositions(noticeWidth, noticeHeight, notice.y);
|
|
||||||
|
|
||||||
notifications.push((Overlays.addOverlay("text3d", notice)));
|
|
||||||
buttons.push((Overlays.addOverlay("image3d", button)));
|
buttons.push((Overlays.addOverlay("image3d", button)));
|
||||||
overlay3DDetails.push({
|
overlay3DDetails.push({
|
||||||
notificationOrientation: positions.notificationOrientation,
|
notificationOrientation: positions.notificationOrientation,
|
||||||
|
@ -269,23 +277,44 @@ function notify(notice, button, height) {
|
||||||
height: noticeHeight
|
height: noticeHeight
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
var notificationText = Overlays.addOverlay("text", notice);
|
if (!image) {
|
||||||
notifications.push((notificationText));
|
notificationText = Overlays.addOverlay("text", notice);
|
||||||
buttons.push((Overlays.addOverlay("image", button)));
|
notifications.push((notificationText));
|
||||||
|
} else {
|
||||||
|
notifications.push(Overlays.addOverlay("image", notice));
|
||||||
|
}
|
||||||
|
buttons.push(Overlays.addOverlay("image", button));
|
||||||
}
|
}
|
||||||
|
|
||||||
height = height + 1.0;
|
height = height + 1.0;
|
||||||
heights.push(height);
|
heights.push(height);
|
||||||
times.push(new Date().getTime() / 1000);
|
times.push(new Date().getTime() / 1000);
|
||||||
myAlpha.push(0);
|
|
||||||
last = notifications.length - 1;
|
last = notifications.length - 1;
|
||||||
|
myAlpha.push(notifications[last].alpha);
|
||||||
createArrays(notifications[last], buttons[last], times[last], heights[last], myAlpha[last]);
|
createArrays(notifications[last], buttons[last], times[last], heights[last], myAlpha[last]);
|
||||||
fadeIn(notifications[last], buttons[last]);
|
fadeIn(notifications[last], buttons[last]);
|
||||||
|
|
||||||
|
if (imageProperties && !image) {
|
||||||
|
var imageHeight = notice.width / imageProperties.aspectRatio;
|
||||||
|
notice = {
|
||||||
|
x: notice.x,
|
||||||
|
y: notice.y + height,
|
||||||
|
width: notice.width,
|
||||||
|
height: imageHeight,
|
||||||
|
subImage: { x: 0, y: 0 },
|
||||||
|
color: { red: 255, green: 255, blue: 255},
|
||||||
|
visible: true,
|
||||||
|
imageURL: imageProperties.path,
|
||||||
|
alpha: backgroundAlpha
|
||||||
|
};
|
||||||
|
notify(notice, button, imageHeight, imageProperties, true);
|
||||||
|
}
|
||||||
|
|
||||||
return notificationText;
|
return notificationText;
|
||||||
}
|
}
|
||||||
|
|
||||||
// This function creates and sizes the overlays
|
// This function creates and sizes the overlays
|
||||||
function createNotification(text, notificationType) {
|
function createNotification(text, notificationType, imageProperties) {
|
||||||
var count = (text.match(/\n/g) || []).length,
|
var count = (text.match(/\n/g) || []).length,
|
||||||
breakPoint = 43.0, // length when new line is added
|
breakPoint = 43.0, // length when new line is added
|
||||||
extraLine = 0,
|
extraLine = 0,
|
||||||
|
@ -308,6 +337,7 @@ function createNotification(text, notificationType) {
|
||||||
|
|
||||||
level = (stack + 20.0);
|
level = (stack + 20.0);
|
||||||
height = height + extraLine;
|
height = height + extraLine;
|
||||||
|
|
||||||
noticeProperties = {
|
noticeProperties = {
|
||||||
x: overlayLocationX,
|
x: overlayLocationX,
|
||||||
y: level,
|
y: level,
|
||||||
|
@ -336,12 +366,11 @@ function createNotification(text, notificationType) {
|
||||||
};
|
};
|
||||||
|
|
||||||
if (Menu.isOptionChecked(PLAY_NOTIFICATION_SOUNDS_MENU_ITEM) &&
|
if (Menu.isOptionChecked(PLAY_NOTIFICATION_SOUNDS_MENU_ITEM) &&
|
||||||
Menu.isOptionChecked(NotificationType.getMenuString(notificationType)))
|
Menu.isOptionChecked(NotificationType.getMenuString(notificationType))) {
|
||||||
{
|
|
||||||
randomSounds.playRandom();
|
randomSounds.playRandom();
|
||||||
}
|
}
|
||||||
|
|
||||||
return notify(noticeProperties, buttonProperties, height);
|
return notify(noticeProperties, buttonProperties, height, imageProperties);
|
||||||
}
|
}
|
||||||
|
|
||||||
function deleteNotification(index) {
|
function deleteNotification(index) {
|
||||||
|
@ -362,21 +391,12 @@ function deleteNotification(index) {
|
||||||
|
|
||||||
// wraps whole word to newline
|
// wraps whole word to newline
|
||||||
function stringDivider(str, slotWidth, spaceReplacer) {
|
function stringDivider(str, slotWidth, spaceReplacer) {
|
||||||
var p,
|
var left, right;
|
||||||
left,
|
|
||||||
right;
|
|
||||||
|
|
||||||
if (str.length > slotWidth) {
|
if (str.length > slotWidth && slotWidth > 0) {
|
||||||
p = slotWidth;
|
left = str.substring(0, slotWidth);
|
||||||
while (p > 0 && str[p] !== ' ') {
|
right = str.substring(slotWidth);
|
||||||
p -= 1;
|
return left + spaceReplacer + stringDivider(right, slotWidth, spaceReplacer);
|
||||||
}
|
|
||||||
|
|
||||||
if (p > 0) {
|
|
||||||
left = str.substring(0, p);
|
|
||||||
right = str.substring(p + 1);
|
|
||||||
return left + spaceReplacer + stringDivider(right, slotWidth, spaceReplacer);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return str;
|
return str;
|
||||||
}
|
}
|
||||||
|
@ -504,7 +524,15 @@ function onMuteStateChanged() {
|
||||||
}
|
}
|
||||||
|
|
||||||
function onDomainConnectionRefused(reason) {
|
function onDomainConnectionRefused(reason) {
|
||||||
createNotification("Connection refused: " + reason, NotificationType.CONNECTION_REFUSED );
|
createNotification("Connection refused: " + reason, NotificationType.CONNECTION_REFUSED);
|
||||||
|
}
|
||||||
|
|
||||||
|
function onSnapshotTaken(path) {
|
||||||
|
var imageProperties = {
|
||||||
|
path: Script.resolvePath("file:///" + path),
|
||||||
|
aspectRatio: Window.innerWidth / Window.innerHeight
|
||||||
|
}
|
||||||
|
createNotification(wordWrap("Snapshot saved to " + path), NotificationType.SNAPSHOT, imageProperties);
|
||||||
}
|
}
|
||||||
|
|
||||||
// handles mouse clicks on buttons
|
// handles mouse clicks on buttons
|
||||||
|
@ -541,13 +569,6 @@ function keyPressEvent(key) {
|
||||||
if (key.key === 16777249) {
|
if (key.key === 16777249) {
|
||||||
ctrlIsPressed = true;
|
ctrlIsPressed = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (key.text === "s") {
|
|
||||||
if (ctrlIsPressed === true) {
|
|
||||||
noteString = "Snapshot taken.";
|
|
||||||
createNotification(noteString, NotificationType.SNAPSHOT);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function setup() {
|
function setup() {
|
||||||
|
@ -615,5 +636,6 @@ Script.update.connect(update);
|
||||||
Script.scriptEnding.connect(scriptEnding);
|
Script.scriptEnding.connect(scriptEnding);
|
||||||
Menu.menuItemEvent.connect(menuItemEvent);
|
Menu.menuItemEvent.connect(menuItemEvent);
|
||||||
Window.domainConnectionRefused.connect(onDomainConnectionRefused);
|
Window.domainConnectionRefused.connect(onDomainConnectionRefused);
|
||||||
|
Window.snapshotTaken.connect(onSnapshotTaken);
|
||||||
|
|
||||||
setup();
|
setup();
|
||||||
|
|
|
@ -28,6 +28,16 @@ ApplicationWindow {
|
||||||
property var toolbar;
|
property var toolbar;
|
||||||
property var lastButton;
|
property var lastButton;
|
||||||
|
|
||||||
|
Button {
|
||||||
|
text: HMD.active ? "Disable HMD" : "Enable HMD"
|
||||||
|
onClicked: HMD.active = !HMD.active
|
||||||
|
}
|
||||||
|
|
||||||
|
Button {
|
||||||
|
text: desktop.hmdHandMouseActive ? "Disable HMD HandMouse" : "Enable HMD HandMouse"
|
||||||
|
onClicked: desktop.hmdHandMouseActive = !desktop.hmdHandMouseActive
|
||||||
|
}
|
||||||
|
|
||||||
// Window visibility
|
// Window visibility
|
||||||
Button {
|
Button {
|
||||||
text: "toggle desktop"
|
text: "toggle desktop"
|
||||||
|
@ -340,13 +350,13 @@ ApplicationWindow {
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
|
||||||
Window {
|
Window {
|
||||||
id: blue
|
id: blue
|
||||||
closable: true
|
closable: true
|
||||||
visible: true
|
visible: true
|
||||||
resizable: true
|
resizable: true
|
||||||
destroyOnHidden: false
|
destroyOnHidden: false
|
||||||
|
title: "Blue"
|
||||||
|
|
||||||
width: 100; height: 100
|
width: 100; height: 100
|
||||||
x: 1280 / 2; y: 720 / 2
|
x: 1280 / 2; y: 720 / 2
|
||||||
|
@ -366,7 +376,33 @@ ApplicationWindow {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Window {
|
||||||
|
id: green
|
||||||
|
closable: true
|
||||||
|
visible: true
|
||||||
|
resizable: true
|
||||||
|
title: "Green"
|
||||||
|
destroyOnHidden: false
|
||||||
|
|
||||||
|
width: 100; height: 100
|
||||||
|
x: 1280 / 2; y: 720 / 2
|
||||||
|
Settings {
|
||||||
|
category: "TestWindow.Green"
|
||||||
|
property alias x: green.x
|
||||||
|
property alias y: green.y
|
||||||
|
property alias width: green.width
|
||||||
|
property alias height: green.height
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
anchors.fill: parent
|
||||||
|
visible: true
|
||||||
|
color: "green"
|
||||||
|
Component.onDestruction: console.log("Green destroyed")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
Rectangle { width: 100; height: 100; x: 100; y: 100; color: "#00f" }
|
Rectangle { width: 100; height: 100; x: 100; y: 100; color: "#00f" }
|
||||||
|
|
||||||
Window {
|
Window {
|
||||||
|
|
Loading…
Reference in a new issue