mirror of
https://github.com/overte-org/overte.git
synced 2025-08-09 17:01:18 +02:00
Merge branch 'master' of https://github.com/highfidelity/hifi into tuneAvatarInfo
This commit is contained in:
commit
c884b276e2
30 changed files with 944 additions and 357 deletions
|
@ -3,4 +3,4 @@ Please read the [general build guide](BUILD.md) for information on dependencies
|
||||||
###Qt5 Dependencies
|
###Qt5 Dependencies
|
||||||
Should you choose not to install Qt5 via a package manager that handles dependencies for you, you may be missing some Qt5 dependencies. On Ubuntu, for example, the following additional packages are required:
|
Should you choose not to install Qt5 via a package manager that handles dependencies for you, you may be missing some Qt5 dependencies. On Ubuntu, for example, the following additional packages are required:
|
||||||
|
|
||||||
libasound2 libxmu-dev libxi-dev freeglut3-dev libasound2-dev libjack0 libjack-dev libxrandr-dev libudev-dev
|
libasound2 libxmu-dev libxi-dev freeglut3-dev libasound2-dev libjack0 libjack-dev libxrandr-dev libudev-dev libssl-dev
|
||||||
|
|
|
@ -16,7 +16,6 @@ import "../styles-uit"
|
||||||
|
|
||||||
Original.CheckBox {
|
Original.CheckBox {
|
||||||
id: checkBox
|
id: checkBox
|
||||||
HifiConstants { id: hifi }
|
|
||||||
|
|
||||||
property int colorScheme: hifi.colorSchemes.light
|
property int colorScheme: hifi.colorSchemes.light
|
||||||
readonly property bool isLightColorScheme: colorScheme == hifi.colorSchemes.light
|
readonly property bool isLightColorScheme: colorScheme == hifi.colorSchemes.light
|
||||||
|
|
|
@ -109,35 +109,46 @@ TableView {
|
||||||
|
|
||||||
handle: Item {
|
handle: Item {
|
||||||
id: scrollbarHandle
|
id: scrollbarHandle
|
||||||
implicitWidth: 6
|
implicitWidth: hifi.dimensions.scrollbarHandleWidth
|
||||||
Rectangle {
|
Rectangle {
|
||||||
anchors {
|
anchors {
|
||||||
fill: parent
|
fill: parent
|
||||||
topMargin: 3
|
topMargin: 3
|
||||||
bottomMargin: 3 // ""
|
bottomMargin: 3 // ""
|
||||||
leftMargin: 2 // Move it right
|
leftMargin: 1 // Move it right
|
||||||
rightMargin: -2 // ""
|
rightMargin: -1 // ""
|
||||||
}
|
}
|
||||||
radius: 3
|
radius: hifi.dimensions.scrollbarHandleWidth/2
|
||||||
color: isLightColorScheme ? hifi.colors.tableScrollHandleLight : hifi.colors.tableScrollHandleDark
|
color: isLightColorScheme ? hifi.colors.tableScrollHandleLight : hifi.colors.tableScrollHandleDark
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
scrollBarBackground: Item {
|
scrollBarBackground: Item {
|
||||||
implicitWidth: 9
|
implicitWidth: hifi.dimensions.scrollbarBackgroundWidth
|
||||||
Rectangle {
|
Rectangle {
|
||||||
anchors {
|
anchors {
|
||||||
fill: parent
|
fill: parent
|
||||||
margins: -1 // Expand
|
margins: -1 // Expand
|
||||||
topMargin: headerVisible ? -hifi.dimensions.tableHeaderHeight : -1
|
topMargin: -1
|
||||||
|
}
|
||||||
|
color: isLightColorScheme ? hifi.colors.tableScrollBackgroundLight : hifi.colors.tableScrollBackgroundDark
|
||||||
|
|
||||||
|
// Extend header color above scrollbar background
|
||||||
|
Rectangle {
|
||||||
|
anchors {
|
||||||
|
top: parent.top
|
||||||
|
topMargin: -hifi.dimensions.tableHeaderHeight
|
||||||
|
left: parent.left
|
||||||
|
right: parent.right
|
||||||
|
}
|
||||||
|
height: hifi.dimensions.tableHeaderHeight
|
||||||
|
color: tableView.isLightColorScheme ? hifi.colors.tableBackgroundLight : hifi.colors.tableBackgroundDark
|
||||||
|
visible: headerVisible
|
||||||
}
|
}
|
||||||
color: isLightColorScheme ? hifi.colors.tableBackgroundLight : hifi.colors.tableBackgroundDark
|
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
// Extend header bottom border
|
// Extend header bottom border
|
||||||
anchors {
|
anchors {
|
||||||
top: parent.top
|
top: parent.top
|
||||||
topMargin: hifi.dimensions.tableHeaderHeight - 1
|
|
||||||
left: parent.left
|
left: parent.left
|
||||||
right: parent.right
|
right: parent.right
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,7 +15,7 @@ import "../styles-uit"
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
property alias text: popupText.text
|
property alias text: popupText.text
|
||||||
property real radius: hifi.dimensions.borderRadius
|
property real popupRadius: hifi.dimensions.borderRadius
|
||||||
visible: false
|
visible: false
|
||||||
id: letterbox
|
id: letterbox
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
|
@ -23,13 +23,13 @@ Item {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
color: "black"
|
color: "black"
|
||||||
opacity: 0.5
|
opacity: 0.5
|
||||||
radius: radius
|
radius: popupRadius
|
||||||
}
|
}
|
||||||
Rectangle {
|
Rectangle {
|
||||||
width: Math.max(parent.width * 0.75, 400)
|
width: Math.max(parent.width * 0.75, 400)
|
||||||
height: popupText.contentHeight*1.5
|
height: popupText.contentHeight*1.5
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
radius: radius
|
radius: popupRadius
|
||||||
color: "white"
|
color: "white"
|
||||||
FiraSansSemiBold {
|
FiraSansSemiBold {
|
||||||
id: popupText
|
id: popupText
|
||||||
|
|
|
@ -9,7 +9,6 @@
|
||||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
//
|
//
|
||||||
|
|
||||||
import Hifi 1.0 as Hifi
|
|
||||||
import QtQuick 2.5
|
import QtQuick 2.5
|
||||||
import QtGraphicalEffects 1.0
|
import QtGraphicalEffects 1.0
|
||||||
import "../styles-uit"
|
import "../styles-uit"
|
||||||
|
@ -68,6 +67,8 @@ Row {
|
||||||
size: thisNameCard.displayTextHeight
|
size: thisNameCard.displayTextHeight
|
||||||
// Text Positioning
|
// Text Positioning
|
||||||
verticalAlignment: Text.AlignVCenter
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
// Style
|
||||||
|
color: hifi.colors.darkGray
|
||||||
}
|
}
|
||||||
|
|
||||||
// UserName Text
|
// UserName Text
|
||||||
|
@ -83,6 +84,8 @@ Row {
|
||||||
size: thisNameCard.usernameTextHeight
|
size: thisNameCard.usernameTextHeight
|
||||||
// Text Positioning
|
// Text Positioning
|
||||||
verticalAlignment: Text.AlignVCenter
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
// Style
|
||||||
|
color: hifi.colors.baseGray
|
||||||
}
|
}
|
||||||
|
|
||||||
// Spacer
|
// Spacer
|
||||||
|
@ -105,7 +108,7 @@ Row {
|
||||||
// Anchors
|
// Anchors
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
// Style
|
// Style
|
||||||
color: "#dbdbdb" // Very appropriate hex value here
|
color: "#c5c5c5"
|
||||||
radius: parent.radius
|
radius: parent.radius
|
||||||
}
|
}
|
||||||
// Rectangle for the VU meter audio level
|
// Rectangle for the VU meter audio level
|
||||||
|
@ -114,7 +117,7 @@ Row {
|
||||||
// Size
|
// Size
|
||||||
width: (thisNameCard.audioLevel) * parent.width
|
width: (thisNameCard.audioLevel) * parent.width
|
||||||
// Style
|
// Style
|
||||||
color: "#dbdbdb" // Very appropriate hex value here
|
color: "#c5c5c5"
|
||||||
radius: parent.radius
|
radius: parent.radius
|
||||||
// Anchors
|
// Anchors
|
||||||
anchors.bottom: parent.bottom
|
anchors.bottom: parent.bottom
|
||||||
|
@ -128,9 +131,10 @@ Row {
|
||||||
start: Qt.point(0, 0)
|
start: Qt.point(0, 0)
|
||||||
end: Qt.point(parent.width, 0)
|
end: Qt.point(parent.width, 0)
|
||||||
gradient: Gradient {
|
gradient: Gradient {
|
||||||
GradientStop { position: 0.05; color: "#00CFEF" }
|
GradientStop { position: 0.0; color: "#2c8e72" }
|
||||||
GradientStop { position: 0.5; color: "#9450A5" }
|
GradientStop { position: 0.9; color: "#1fc6a6" }
|
||||||
GradientStop { position: 0.95; color: "#EA4C5F" }
|
GradientStop { position: 0.91; color: "#ea4c5f" }
|
||||||
|
GradientStop { position: 1.0; color: "#ea4c5f" }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,29 +16,46 @@ import QtQuick.Controls 1.4
|
||||||
import "../styles-uit"
|
import "../styles-uit"
|
||||||
import "../controls-uit" as HifiControls
|
import "../controls-uit" as HifiControls
|
||||||
|
|
||||||
Item {
|
Rectangle {
|
||||||
id: pal
|
id: pal
|
||||||
// Size
|
// Size
|
||||||
width: parent.width
|
width: parent.width
|
||||||
height: parent.height
|
height: parent.height
|
||||||
|
// Style
|
||||||
|
color: "#E3E3E3"
|
||||||
// Properties
|
// Properties
|
||||||
property int myCardHeight: 70
|
property int myCardHeight: 70
|
||||||
property int rowHeight: 70
|
property int rowHeight: 70
|
||||||
property int actionButtonWidth: 75
|
property int actionButtonWidth: 75
|
||||||
property int nameCardWidth: width - actionButtonWidth*(iAmAdmin ? 4 : 2) - 4
|
property int nameCardWidth: palContainer.width - actionButtonWidth*(iAmAdmin ? 4 : 2) - 4 - hifi.dimensions.scrollbarBackgroundWidth
|
||||||
property var myData: ({displayName: "", userName: "", audioLevel: 0.0}) // valid dummy until set
|
property var myData: ({displayName: "", userName: "", audioLevel: 0.0}) // valid dummy until set
|
||||||
property var ignored: ({}); // Keep a local list of ignored avatars & their data. Necessary because HashMap is slow to respond after ignoring.
|
property var ignored: ({}); // Keep a local list of ignored avatars & their data. Necessary because HashMap is slow to respond after ignoring.
|
||||||
property var userModelData: [] // This simple list is essentially a mirror of the userModel listModel without all the extra complexities.
|
property var userModelData: [] // This simple list is essentially a mirror of the userModel listModel without all the extra complexities.
|
||||||
property bool iAmAdmin: false
|
property bool iAmAdmin: false
|
||||||
|
|
||||||
|
// This is the container for the PAL
|
||||||
|
Rectangle {
|
||||||
|
id: palContainer
|
||||||
|
// Size
|
||||||
|
width: pal.width - 50
|
||||||
|
height: pal.height - 50
|
||||||
|
// Style
|
||||||
|
color: pal.color
|
||||||
|
// Anchors
|
||||||
|
anchors.centerIn: pal
|
||||||
|
// Properties
|
||||||
|
radius: hifi.dimensions.borderRadius
|
||||||
|
|
||||||
// This contains the current user's NameCard and will contain other information in the future
|
// This contains the current user's NameCard and will contain other information in the future
|
||||||
Rectangle {
|
Rectangle {
|
||||||
id: myInfo
|
id: myInfo
|
||||||
// Size
|
// Size
|
||||||
width: pal.width
|
width: palContainer.width
|
||||||
height: myCardHeight + 20
|
height: myCardHeight + 20
|
||||||
|
// Style
|
||||||
|
color: pal.color
|
||||||
// Anchors
|
// Anchors
|
||||||
anchors.top: pal.top
|
anchors.top: palContainer.top
|
||||||
// Properties
|
// Properties
|
||||||
radius: hifi.dimensions.borderRadius
|
radius: hifi.dimensions.borderRadius
|
||||||
// This NameCard refers to the current user's NameCard (the one above the table)
|
// This NameCard refers to the current user's NameCard (the one above the table)
|
||||||
|
@ -57,15 +74,15 @@ Item {
|
||||||
}
|
}
|
||||||
// Rectangles used to cover up rounded edges on bottom of MyInfo Rectangle
|
// Rectangles used to cover up rounded edges on bottom of MyInfo Rectangle
|
||||||
Rectangle {
|
Rectangle {
|
||||||
color: "#FFFFFF"
|
color: pal.color
|
||||||
width: pal.width
|
width: palContainer.width
|
||||||
height: 10
|
height: 10
|
||||||
anchors.top: myInfo.bottom
|
anchors.top: myInfo.bottom
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
}
|
}
|
||||||
Rectangle {
|
Rectangle {
|
||||||
color: "#FFFFFF"
|
color: pal.color
|
||||||
width: pal.width
|
width: palContainer.width
|
||||||
height: 10
|
height: 10
|
||||||
anchors.bottom: table.top
|
anchors.bottom: table.top
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
|
@ -74,7 +91,7 @@ Item {
|
||||||
Rectangle {
|
Rectangle {
|
||||||
id: adminTab
|
id: adminTab
|
||||||
// Size
|
// Size
|
||||||
width: actionButtonWidth * 2 + 2
|
width: 2*actionButtonWidth + hifi.dimensions.scrollbarBackgroundWidth + 2
|
||||||
height: 40
|
height: 40
|
||||||
// Anchors
|
// Anchors
|
||||||
anchors.bottom: myInfo.bottom
|
anchors.bottom: myInfo.bottom
|
||||||
|
@ -98,6 +115,7 @@ Item {
|
||||||
anchors.topMargin: 8
|
anchors.topMargin: 8
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
|
anchors.rightMargin: hifi.dimensions.scrollbarBackgroundWidth
|
||||||
// Style
|
// Style
|
||||||
font.capitalization: Font.AllUppercase
|
font.capitalization: Font.AllUppercase
|
||||||
color: hifi.colors.redHighlight
|
color: hifi.colors.redHighlight
|
||||||
|
@ -110,8 +128,8 @@ Item {
|
||||||
HifiControls.Table {
|
HifiControls.Table {
|
||||||
id: table
|
id: table
|
||||||
// Size
|
// Size
|
||||||
height: pal.height - myInfo.height - 4
|
height: palContainer.height - myInfo.height - 4
|
||||||
width: pal.width - 4
|
width: palContainer.width - 4
|
||||||
// Anchors
|
// Anchors
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
anchors.top: myInfo.bottom
|
anchors.top: myInfo.bottom
|
||||||
|
@ -154,6 +172,8 @@ Item {
|
||||||
TableViewColumn {
|
TableViewColumn {
|
||||||
visible: iAmAdmin
|
visible: iAmAdmin
|
||||||
role: "kick"
|
role: "kick"
|
||||||
|
// The hacky spaces used to center text over the button, since I don't know how to apply a margin
|
||||||
|
// to column header text.
|
||||||
title: "BAN"
|
title: "BAN"
|
||||||
width: actionButtonWidth
|
width: actionButtonWidth
|
||||||
movable: false
|
movable: false
|
||||||
|
@ -168,7 +188,7 @@ Item {
|
||||||
// Size
|
// Size
|
||||||
height: rowHeight
|
height: rowHeight
|
||||||
color: styleData.selected
|
color: styleData.selected
|
||||||
? "#afafaf"
|
? hifi.colors.orangeHighlight
|
||||||
: styleData.alternate ? hifi.colors.tableRowLightEven : hifi.colors.tableRowLightOdd
|
: styleData.alternate ? hifi.colors.tableRowLightEven : hifi.colors.tableRowLightOdd
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -353,7 +373,7 @@ Item {
|
||||||
width: 20
|
width: 20
|
||||||
height: 28
|
height: 28
|
||||||
anchors.right: adminTab.right
|
anchors.right: adminTab.right
|
||||||
anchors.rightMargin: 31
|
anchors.rightMargin: 31 + hifi.dimensions.scrollbarBackgroundWidth
|
||||||
anchors.top: adminTab.top
|
anchors.top: adminTab.top
|
||||||
anchors.topMargin: 2
|
anchors.topMargin: 2
|
||||||
RalewayRegular {
|
RalewayRegular {
|
||||||
|
@ -379,6 +399,7 @@ Item {
|
||||||
LetterboxMessage {
|
LetterboxMessage {
|
||||||
id: letterboxMessage
|
id: letterboxMessage
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function findSessionIndex(sessionId, optionalData) { // no findIndex in .qml
|
function findSessionIndex(sessionId, optionalData) { // no findIndex in .qml
|
||||||
var data = optionalData || userModelData, length = data.length;
|
var data = optionalData || userModelData, length = data.length;
|
||||||
|
@ -425,6 +446,7 @@ Item {
|
||||||
if (selected) {
|
if (selected) {
|
||||||
table.selection.clear(); // for now, no multi-select
|
table.selection.clear(); // for now, no multi-select
|
||||||
table.selection.select(userIndex);
|
table.selection.select(userIndex);
|
||||||
|
table.positionViewAtRow(userIndex, ListView.Visible);
|
||||||
} else {
|
} else {
|
||||||
table.selection.deselect(userIndex);
|
table.selection.deselect(userIndex);
|
||||||
}
|
}
|
||||||
|
|
|
@ -59,11 +59,17 @@ Item {
|
||||||
readonly property color faintGray: "#e3e3e3"
|
readonly property color faintGray: "#e3e3e3"
|
||||||
readonly property color primaryHighlight: "#00b4ef"
|
readonly property color primaryHighlight: "#00b4ef"
|
||||||
readonly property color blueHighlight: "#00b4ef"
|
readonly property color blueHighlight: "#00b4ef"
|
||||||
readonly property color blueAccent: "#1080b8"
|
readonly property color blueAccent: "#0093C5"
|
||||||
readonly property color redHighlight: "#e2334d"
|
readonly property color redHighlight: "#EA4C5F"
|
||||||
readonly property color redAccent: "#b70a37"
|
readonly property color redAccent: "#C62147"
|
||||||
readonly property color greenHighlight: "#1ac567"
|
readonly property color greenHighlight: "#1ac567"
|
||||||
readonly property color greenShadow: "#2c8e72"
|
readonly property color greenShadow: "#359D85"
|
||||||
|
readonly property color orangeHighlight: "#FFC49C"
|
||||||
|
readonly property color orangeAccent: "#FF6309"
|
||||||
|
readonly property color indigoHighlight: "#C0D2FF"
|
||||||
|
readonly property color indigoAccent: "#9495FF"
|
||||||
|
readonly property color magentaHighlight: "#EF93D1"
|
||||||
|
readonly property color magentaAccent: "#A2277C"
|
||||||
// Semitransparent
|
// Semitransparent
|
||||||
readonly property color darkGray30: "#4d121212"
|
readonly property color darkGray30: "#4d121212"
|
||||||
readonly property color darkGray0: "#00121212"
|
readonly property color darkGray0: "#00121212"
|
||||||
|
@ -95,9 +101,9 @@ Item {
|
||||||
readonly property color tableRowDarkEven: "#1c1c1c" // Equivalent to "#a6181818" over #404040 background
|
readonly property color tableRowDarkEven: "#1c1c1c" // Equivalent to "#a6181818" over #404040 background
|
||||||
readonly property color tableBackgroundLight: tableRowLightEven
|
readonly property color tableBackgroundLight: tableRowLightEven
|
||||||
readonly property color tableBackgroundDark: tableRowDarkEven
|
readonly property color tableBackgroundDark: tableRowDarkEven
|
||||||
readonly property color tableScrollHandleLight: "#8F8F8F"
|
readonly property color tableScrollHandleLight: "#DDDDDD"
|
||||||
readonly property color tableScrollHandleDark: "#707070"
|
readonly property color tableScrollHandleDark: "#707070"
|
||||||
readonly property color tableScrollBackgroundLight: tableRowLightEven
|
readonly property color tableScrollBackgroundLight: tableRowLightOdd
|
||||||
readonly property color tableScrollBackgroundDark: "#323232"
|
readonly property color tableScrollBackgroundDark: "#323232"
|
||||||
readonly property color checkboxLightStart: "#ffffff"
|
readonly property color checkboxLightStart: "#ffffff"
|
||||||
readonly property color checkboxLightFinish: "#afafaf"
|
readonly property color checkboxLightFinish: "#afafaf"
|
||||||
|
@ -151,6 +157,8 @@ Item {
|
||||||
readonly property real controlLineHeight: 28 // Height of spinbox control on 1920 x 1080 monitor
|
readonly property real controlLineHeight: 28 // Height of spinbox control on 1920 x 1080 monitor
|
||||||
readonly property real controlInterlineHeight: 21 // 75% of controlLineHeight
|
readonly property real controlInterlineHeight: 21 // 75% of controlLineHeight
|
||||||
readonly property vector2d menuPadding: Qt.vector2d(14, 12)
|
readonly property vector2d menuPadding: Qt.vector2d(14, 12)
|
||||||
|
readonly property real scrollbarBackgroundWidth: 18
|
||||||
|
readonly property real scrollbarHandleWidth: scrollbarBackgroundWidth - 2
|
||||||
}
|
}
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
|
|
|
@ -89,6 +89,7 @@
|
||||||
#include <OctalCode.h>
|
#include <OctalCode.h>
|
||||||
#include <OctreeSceneStats.h>
|
#include <OctreeSceneStats.h>
|
||||||
#include <OffscreenUi.h>
|
#include <OffscreenUi.h>
|
||||||
|
#include <gl/OffscreenQmlSurfaceCache.h>
|
||||||
#include <gl/OffscreenGLCanvas.h>
|
#include <gl/OffscreenGLCanvas.h>
|
||||||
#include <PathUtils.h>
|
#include <PathUtils.h>
|
||||||
#include <PerfStat.h>
|
#include <PerfStat.h>
|
||||||
|
@ -196,7 +197,7 @@ static const int MAX_CONCURRENT_RESOURCE_DOWNLOADS = 16;
|
||||||
// For processing on QThreadPool, we target a number of threads after reserving some
|
// For processing on QThreadPool, we target a number of threads after reserving some
|
||||||
// based on how many are being consumed by the application and the display plugin. However,
|
// based on how many are being consumed by the application and the display plugin. However,
|
||||||
// we will never drop below the 'min' value
|
// we will never drop below the 'min' value
|
||||||
static const int MIN_PROCESSING_THREAD_POOL_SIZE = 2;
|
static const int MIN_PROCESSING_THREAD_POOL_SIZE = 1;
|
||||||
|
|
||||||
static const QString SNAPSHOT_EXTENSION = ".jpg";
|
static const QString SNAPSHOT_EXTENSION = ".jpg";
|
||||||
static const QString SVO_EXTENSION = ".svo";
|
static const QString SVO_EXTENSION = ".svo";
|
||||||
|
@ -512,6 +513,7 @@ bool setupEssentials(int& argc, char** argv) {
|
||||||
DependencyManager::set<InterfaceParentFinder>();
|
DependencyManager::set<InterfaceParentFinder>();
|
||||||
DependencyManager::set<EntityTreeRenderer>(true, qApp, qApp);
|
DependencyManager::set<EntityTreeRenderer>(true, qApp, qApp);
|
||||||
DependencyManager::set<CompositorHelper>();
|
DependencyManager::set<CompositorHelper>();
|
||||||
|
DependencyManager::set<OffscreenQmlSurfaceCache>();
|
||||||
return previousSessionCrashed;
|
return previousSessionCrashed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2003,6 +2005,10 @@ void Application::initializeUi() {
|
||||||
showCursor(compositorHelper->getAllowMouseCapture() ? Qt::BlankCursor : Qt::ArrowCursor);
|
showCursor(compositorHelper->getAllowMouseCapture() ? Qt::BlankCursor : Qt::ArrowCursor);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Pre-create a couple of Web3D overlays to speed up tablet UI
|
||||||
|
auto offscreenSurfaceCache = DependencyManager::get<OffscreenQmlSurfaceCache>();
|
||||||
|
offscreenSurfaceCache->reserve(Web3DOverlay::QML, 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Application::paintGL() {
|
void Application::paintGL() {
|
||||||
|
@ -3298,6 +3304,66 @@ bool Application::shouldPaint(float nsecsElapsed) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef Q_OS_WIN
|
||||||
|
#include <Windows.h>
|
||||||
|
#include <TCHAR.h>
|
||||||
|
#include <pdh.h>
|
||||||
|
#pragma comment(lib, "pdh.lib")
|
||||||
|
|
||||||
|
static ULARGE_INTEGER lastCPU, lastSysCPU, lastUserCPU;
|
||||||
|
static int numProcessors;
|
||||||
|
static HANDLE self;
|
||||||
|
static PDH_HQUERY cpuQuery;
|
||||||
|
static PDH_HCOUNTER cpuTotal;
|
||||||
|
|
||||||
|
void initCpuUsage() {
|
||||||
|
SYSTEM_INFO sysInfo;
|
||||||
|
FILETIME ftime, fsys, fuser;
|
||||||
|
|
||||||
|
GetSystemInfo(&sysInfo);
|
||||||
|
numProcessors = sysInfo.dwNumberOfProcessors;
|
||||||
|
|
||||||
|
GetSystemTimeAsFileTime(&ftime);
|
||||||
|
memcpy(&lastCPU, &ftime, sizeof(FILETIME));
|
||||||
|
|
||||||
|
self = GetCurrentProcess();
|
||||||
|
GetProcessTimes(self, &ftime, &ftime, &fsys, &fuser);
|
||||||
|
memcpy(&lastSysCPU, &fsys, sizeof(FILETIME));
|
||||||
|
memcpy(&lastUserCPU, &fuser, sizeof(FILETIME));
|
||||||
|
|
||||||
|
PdhOpenQuery(NULL, NULL, &cpuQuery);
|
||||||
|
PdhAddCounter(cpuQuery, "\\Processor(_Total)\\% Processor Time", NULL, &cpuTotal);
|
||||||
|
PdhCollectQueryData(cpuQuery);
|
||||||
|
}
|
||||||
|
|
||||||
|
void getCpuUsage(vec3& systemAndUser) {
|
||||||
|
FILETIME ftime, fsys, fuser;
|
||||||
|
ULARGE_INTEGER now, sys, user;
|
||||||
|
|
||||||
|
GetSystemTimeAsFileTime(&ftime);
|
||||||
|
memcpy(&now, &ftime, sizeof(FILETIME));
|
||||||
|
|
||||||
|
GetProcessTimes(self, &ftime, &ftime, &fsys, &fuser);
|
||||||
|
memcpy(&sys, &fsys, sizeof(FILETIME));
|
||||||
|
memcpy(&user, &fuser, sizeof(FILETIME));
|
||||||
|
systemAndUser.x = (sys.QuadPart - lastSysCPU.QuadPart);
|
||||||
|
systemAndUser.y = (user.QuadPart - lastUserCPU.QuadPart);
|
||||||
|
systemAndUser /= (float)(now.QuadPart - lastCPU.QuadPart);
|
||||||
|
systemAndUser /= (float)numProcessors;
|
||||||
|
systemAndUser *= 100.0f;
|
||||||
|
lastCPU = now;
|
||||||
|
lastUserCPU = user;
|
||||||
|
lastSysCPU = sys;
|
||||||
|
|
||||||
|
PDH_FMT_COUNTERVALUE counterVal;
|
||||||
|
PdhCollectQueryData(cpuQuery);
|
||||||
|
PdhGetFormattedCounterValue(cpuTotal, PDH_FMT_DOUBLE, NULL, &counterVal);
|
||||||
|
systemAndUser.z = (float)counterVal.doubleValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
void Application::idle(float nsecsElapsed) {
|
void Application::idle(float nsecsElapsed) {
|
||||||
PerformanceTimer perfTimer("idle");
|
PerformanceTimer perfTimer("idle");
|
||||||
|
|
||||||
|
@ -3314,6 +3380,18 @@ void Application::idle(float nsecsElapsed) {
|
||||||
connect(offscreenUi.data(), &OffscreenUi::showDesktop, this, &Application::showDesktop);
|
connect(offscreenUi.data(), &OffscreenUi::showDesktop, this, &Application::showDesktop);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef Q_OS_WIN
|
||||||
|
static std::once_flag once;
|
||||||
|
std::call_once(once, [] {
|
||||||
|
initCpuUsage();
|
||||||
|
});
|
||||||
|
|
||||||
|
vec3 kernelUserAndSystem;
|
||||||
|
getCpuUsage(kernelUserAndSystem);
|
||||||
|
PROFILE_COUNTER(app, "cpuProcess", { { "system", kernelUserAndSystem.x }, { "user", kernelUserAndSystem.y } });
|
||||||
|
PROFILE_COUNTER(app, "cpuSystem", { { "system", kernelUserAndSystem.z } });
|
||||||
|
#endif
|
||||||
|
|
||||||
auto displayPlugin = getActiveDisplayPlugin();
|
auto displayPlugin = getActiveDisplayPlugin();
|
||||||
if (displayPlugin) {
|
if (displayPlugin) {
|
||||||
PROFILE_COUNTER_IF_CHANGED(app, "present", float, displayPlugin->presentRate());
|
PROFILE_COUNTER_IF_CHANGED(app, "present", float, displayPlugin->presentRate());
|
||||||
|
@ -3324,6 +3402,9 @@ void Application::idle(float nsecsElapsed) {
|
||||||
PROFILE_COUNTER_IF_CHANGED(app, "currentProcessing", int, DependencyManager::get<StatTracker>()->getStat("Processing").toInt());
|
PROFILE_COUNTER_IF_CHANGED(app, "currentProcessing", int, DependencyManager::get<StatTracker>()->getStat("Processing").toInt());
|
||||||
PROFILE_COUNTER_IF_CHANGED(app, "pendingProcessing", int, DependencyManager::get<StatTracker>()->getStat("PendingProcessing").toInt());
|
PROFILE_COUNTER_IF_CHANGED(app, "pendingProcessing", int, DependencyManager::get<StatTracker>()->getStat("PendingProcessing").toInt());
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
PROFILE_RANGE(app, __FUNCTION__);
|
PROFILE_RANGE(app, __FUNCTION__);
|
||||||
|
|
||||||
if (auto steamClient = PluginManager::getInstance()->getSteamClientPlugin()) {
|
if (auto steamClient = PluginManager::getInstance()->getSteamClientPlugin()) {
|
||||||
|
@ -5644,18 +5725,18 @@ void Application::showAssetServerWidget(QString filePath) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Application::addAssetToWorldFromURL(QString url) {
|
void Application::addAssetToWorldFromURL(QString url) {
|
||||||
qInfo(interfaceapp) << "Download asset and add to world from" << url;
|
qInfo(interfaceapp) << "Download model and add to world from" << url;
|
||||||
|
|
||||||
QString filename = url.section("filename=", 1, 1); // Filename is in "?filename=" parameter at end of URL.
|
QString filename = url.section("filename=", 1, 1); // Filename is in "?filename=" parameter at end of URL.
|
||||||
|
|
||||||
if (!DependencyManager::get<NodeList>()->getThisNodeCanWriteAssets()) {
|
if (!DependencyManager::get<NodeList>()->getThisNodeCanWriteAssets()) {
|
||||||
QString errorInfo = "You do not have permissions to write to the Asset Server.";
|
QString errorInfo = "You do not have permissions to write to the Asset Server.";
|
||||||
qWarning(interfaceapp) << "Error downloading asset: " + errorInfo;
|
qWarning(interfaceapp) << "Error downloading model: " + errorInfo;
|
||||||
addAssetToWorldError(filename, errorInfo);
|
addAssetToWorldError(filename, errorInfo);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
addAssetToWorldInfo(filename, "Downloading asset file " + filename + ".");
|
addAssetToWorldInfo(filename, "Downloading model file " + filename + ".");
|
||||||
|
|
||||||
auto request = ResourceManager::createResourceRequest(nullptr, QUrl(url));
|
auto request = ResourceManager::createResourceRequest(nullptr, QUrl(url));
|
||||||
connect(request, &ResourceRequest::finished, this, &Application::addAssetToWorldFromURLRequestFinished);
|
connect(request, &ResourceRequest::finished, this, &Application::addAssetToWorldFromURLRequestFinished);
|
||||||
|
@ -5670,7 +5751,7 @@ void Application::addAssetToWorldFromURLRequestFinished() {
|
||||||
QString filename = url.section("filename=", 1, 1); // Filename from trailing "?filename=" URL parameter.
|
QString filename = url.section("filename=", 1, 1); // Filename from trailing "?filename=" URL parameter.
|
||||||
|
|
||||||
if (result == ResourceRequest::Success) {
|
if (result == ResourceRequest::Success) {
|
||||||
qInfo(interfaceapp) << "Downloaded asset from" << url;
|
qInfo(interfaceapp) << "Downloaded model from" << url;
|
||||||
QTemporaryDir temporaryDir;
|
QTemporaryDir temporaryDir;
|
||||||
temporaryDir.setAutoRemove(false);
|
temporaryDir.setAutoRemove(false);
|
||||||
if (temporaryDir.isValid()) {
|
if (temporaryDir.isValid()) {
|
||||||
|
@ -5722,7 +5803,7 @@ void Application::addAssetToWorld(QString filePath) {
|
||||||
// Test repeated because possibly different code paths.
|
// Test repeated because possibly different code paths.
|
||||||
if (!DependencyManager::get<NodeList>()->getThisNodeCanWriteAssets()) {
|
if (!DependencyManager::get<NodeList>()->getThisNodeCanWriteAssets()) {
|
||||||
QString errorInfo = "You do not have permissions to write to the Asset Server.";
|
QString errorInfo = "You do not have permissions to write to the Asset Server.";
|
||||||
qWarning(interfaceapp) << "Error downloading asset: " + errorInfo;
|
qWarning(interfaceapp) << "Error downloading model: " + errorInfo;
|
||||||
addAssetToWorldError(filename, errorInfo);
|
addAssetToWorldError(filename, errorInfo);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -5743,7 +5824,7 @@ void Application::addAssetToWorldWithNewMapping(QString filePath, QString mappin
|
||||||
} else if (result != GetMappingRequest::NoError) {
|
} else if (result != GetMappingRequest::NoError) {
|
||||||
QString errorInfo = "Could not map asset name: "
|
QString errorInfo = "Could not map asset name: "
|
||||||
+ mapping.left(mapping.length() - QString::number(copy).length() - 1);
|
+ mapping.left(mapping.length() - QString::number(copy).length() - 1);
|
||||||
qWarning(interfaceapp) << "Error downloading asset: " + errorInfo;
|
qWarning(interfaceapp) << "Error downloading model: " + errorInfo;
|
||||||
addAssetToWorldError(filenameFromPath(filePath), errorInfo);
|
addAssetToWorldError(filenameFromPath(filePath), errorInfo);
|
||||||
} else if (copy < MAX_COPY_COUNT - 1) {
|
} else if (copy < MAX_COPY_COUNT - 1) {
|
||||||
if (copy > 0) {
|
if (copy > 0) {
|
||||||
|
@ -5755,7 +5836,7 @@ void Application::addAssetToWorldWithNewMapping(QString filePath, QString mappin
|
||||||
} else {
|
} else {
|
||||||
QString errorInfo = "Too many copies of asset name: "
|
QString errorInfo = "Too many copies of asset name: "
|
||||||
+ mapping.left(mapping.length() - QString::number(copy).length() - 1);
|
+ mapping.left(mapping.length() - QString::number(copy).length() - 1);
|
||||||
qWarning(interfaceapp) << "Error downloading asset: " + errorInfo;
|
qWarning(interfaceapp) << "Error downloading model: " + errorInfo;
|
||||||
addAssetToWorldError(filenameFromPath(filePath), errorInfo);
|
addAssetToWorldError(filenameFromPath(filePath), errorInfo);
|
||||||
}
|
}
|
||||||
request->deleteLater();
|
request->deleteLater();
|
||||||
|
@ -5769,8 +5850,8 @@ void Application::addAssetToWorldUpload(QString filePath, QString mapping) {
|
||||||
auto upload = DependencyManager::get<AssetClient>()->createUpload(filePath);
|
auto upload = DependencyManager::get<AssetClient>()->createUpload(filePath);
|
||||||
QObject::connect(upload, &AssetUpload::finished, this, [=](AssetUpload* upload, const QString& hash) mutable {
|
QObject::connect(upload, &AssetUpload::finished, this, [=](AssetUpload* upload, const QString& hash) mutable {
|
||||||
if (upload->getError() != AssetUpload::NoError) {
|
if (upload->getError() != AssetUpload::NoError) {
|
||||||
QString errorInfo = "Could not upload asset to the Asset Server.";
|
QString errorInfo = "Could not upload model to the Asset Server.";
|
||||||
qWarning(interfaceapp) << "Error downloading asset: " + errorInfo;
|
qWarning(interfaceapp) << "Error downloading model: " + errorInfo;
|
||||||
addAssetToWorldError(filenameFromPath(filePath), errorInfo);
|
addAssetToWorldError(filenameFromPath(filePath), errorInfo);
|
||||||
} else {
|
} else {
|
||||||
addAssetToWorldSetMapping(filePath, mapping, hash);
|
addAssetToWorldSetMapping(filePath, mapping, hash);
|
||||||
|
@ -5795,7 +5876,7 @@ void Application::addAssetToWorldSetMapping(QString filePath, QString mapping, Q
|
||||||
connect(request, &SetMappingRequest::finished, this, [=](SetMappingRequest* request) mutable {
|
connect(request, &SetMappingRequest::finished, this, [=](SetMappingRequest* request) mutable {
|
||||||
if (request->getError() != SetMappingRequest::NoError) {
|
if (request->getError() != SetMappingRequest::NoError) {
|
||||||
QString errorInfo = "Could not set asset mapping.";
|
QString errorInfo = "Could not set asset mapping.";
|
||||||
qWarning(interfaceapp) << "Error downloading asset: " + errorInfo;
|
qWarning(interfaceapp) << "Error downloading model: " + errorInfo;
|
||||||
addAssetToWorldError(filenameFromPath(filePath), errorInfo);
|
addAssetToWorldError(filenameFromPath(filePath), errorInfo);
|
||||||
} else {
|
} else {
|
||||||
addAssetToWorldAddEntity(filePath, mapping);
|
addAssetToWorldAddEntity(filePath, mapping);
|
||||||
|
@ -5822,8 +5903,8 @@ void Application::addAssetToWorldAddEntity(QString filePath, QString mapping) {
|
||||||
// on. But FBX dimensions may be in cm, so we monitor for the dimension change and rescale again if warranted.
|
// on. But FBX dimensions may be in cm, so we monitor for the dimension change and rescale again if warranted.
|
||||||
|
|
||||||
if (entityID == QUuid()) {
|
if (entityID == QUuid()) {
|
||||||
QString errorInfo = "Could not add asset " + mapping + " to world.";
|
QString errorInfo = "Could not add model " + mapping + " to world.";
|
||||||
qWarning(interfaceapp) << "Could not add asset to world: " + errorInfo;
|
qWarning(interfaceapp) << "Could not add model to world: " + errorInfo;
|
||||||
addAssetToWorldError(filenameFromPath(filePath), errorInfo);
|
addAssetToWorldError(filenameFromPath(filePath), errorInfo);
|
||||||
} else {
|
} else {
|
||||||
// Monitor when asset is rendered in world so that can resize if necessary.
|
// Monitor when asset is rendered in world so that can resize if necessary.
|
||||||
|
@ -5866,7 +5947,7 @@ void Application::addAssetToWorldCheckModelSize() {
|
||||||
auto scale = std::min(MAXIMUM_DIMENSION / dimensions.x, std::min(MAXIMUM_DIMENSION / dimensions.y,
|
auto scale = std::min(MAXIMUM_DIMENSION / dimensions.x, std::min(MAXIMUM_DIMENSION / dimensions.y,
|
||||||
MAXIMUM_DIMENSION / dimensions.z));
|
MAXIMUM_DIMENSION / dimensions.z));
|
||||||
dimensions *= scale;
|
dimensions *= scale;
|
||||||
qInfo(interfaceapp) << "Asset" << name << "auto-resized from" << previousDimensions << " to " << dimensions;
|
qInfo(interfaceapp) << "Model" << name << "auto-resized from" << previousDimensions << " to " << dimensions;
|
||||||
doResize = true;
|
doResize = true;
|
||||||
|
|
||||||
item = _addAssetToWorldResizeList.erase(item); // Finished with this entity; advance to next.
|
item = _addAssetToWorldResizeList.erase(item); // Finished with this entity; advance to next.
|
||||||
|
@ -5881,7 +5962,7 @@ void Application::addAssetToWorldCheckModelSize() {
|
||||||
// Rescale all dimensions.
|
// Rescale all dimensions.
|
||||||
const glm::vec3 UNIT_DIMENSIONS = glm::vec3(1.0f, 1.0f, 1.0f);
|
const glm::vec3 UNIT_DIMENSIONS = glm::vec3(1.0f, 1.0f, 1.0f);
|
||||||
dimensions = UNIT_DIMENSIONS;
|
dimensions = UNIT_DIMENSIONS;
|
||||||
qInfo(interfaceapp) << "Asset" << name << "auto-resize timed out; resized to " << dimensions;
|
qInfo(interfaceapp) << "Model" << name << "auto-resize timed out; resized to " << dimensions;
|
||||||
doResize = true;
|
doResize = true;
|
||||||
|
|
||||||
item = _addAssetToWorldResizeList.erase(item); // Finished with this entity; advance to next.
|
item = _addAssetToWorldResizeList.erase(item); // Finished with this entity; advance to next.
|
||||||
|
@ -5934,7 +6015,7 @@ void Application::addAssetToWorldInfo(QString modelName, QString infoText) {
|
||||||
if (!_addAssetToWorldErrorTimer.isActive()) {
|
if (!_addAssetToWorldErrorTimer.isActive()) {
|
||||||
if (!_addAssetToWorldMessageBox) {
|
if (!_addAssetToWorldMessageBox) {
|
||||||
_addAssetToWorldMessageBox = DependencyManager::get<OffscreenUi>()->createMessageBox(OffscreenUi::ICON_INFORMATION,
|
_addAssetToWorldMessageBox = DependencyManager::get<OffscreenUi>()->createMessageBox(OffscreenUi::ICON_INFORMATION,
|
||||||
"Downloading Asset", "", QMessageBox::NoButton, QMessageBox::NoButton);
|
"Downloading Model", "", QMessageBox::NoButton, QMessageBox::NoButton);
|
||||||
connect(_addAssetToWorldMessageBox, SIGNAL(destroyed()), this, SLOT(onAssetToWorldMessageBoxClosed()));
|
connect(_addAssetToWorldMessageBox, SIGNAL(destroyed()), this, SLOT(onAssetToWorldMessageBoxClosed()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5999,7 +6080,6 @@ void Application::addAssetToWorldInfoTimeout() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void Application::addAssetToWorldError(QString modelName, QString errorText) {
|
void Application::addAssetToWorldError(QString modelName, QString errorText) {
|
||||||
// Displays the most recent error message for a few seconds.
|
// Displays the most recent error message for a few seconds.
|
||||||
|
|
||||||
|
@ -6018,7 +6098,7 @@ void Application::addAssetToWorldError(QString modelName, QString errorText) {
|
||||||
|
|
||||||
if (!_addAssetToWorldMessageBox) {
|
if (!_addAssetToWorldMessageBox) {
|
||||||
_addAssetToWorldMessageBox = DependencyManager::get<OffscreenUi>()->createMessageBox(OffscreenUi::ICON_INFORMATION,
|
_addAssetToWorldMessageBox = DependencyManager::get<OffscreenUi>()->createMessageBox(OffscreenUi::ICON_INFORMATION,
|
||||||
"Downloading Asset", "", QMessageBox::NoButton, QMessageBox::NoButton);
|
"Downloading Model", "", QMessageBox::NoButton, QMessageBox::NoButton);
|
||||||
connect(_addAssetToWorldMessageBox, SIGNAL(destroyed()), this, SLOT(onAssetToWorldMessageBoxClosed()));
|
connect(_addAssetToWorldMessageBox, SIGNAL(destroyed()), this, SLOT(onAssetToWorldMessageBoxClosed()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -37,7 +37,7 @@ int SoftAttachmentModel::getJointIndexOverride(int i) const {
|
||||||
|
|
||||||
// virtual
|
// virtual
|
||||||
// use the _rigOverride matrices instead of the Model::_rig
|
// use the _rigOverride matrices instead of the Model::_rig
|
||||||
void SoftAttachmentModel::updateClusterMatrices(glm::vec3 modelPosition, glm::quat modelOrientation) {
|
void SoftAttachmentModel::updateClusterMatrices() {
|
||||||
if (!_needsUpdateClusterMatrices) {
|
if (!_needsUpdateClusterMatrices) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -45,7 +45,6 @@ void SoftAttachmentModel::updateClusterMatrices(glm::vec3 modelPosition, glm::qu
|
||||||
|
|
||||||
const FBXGeometry& geometry = getFBXGeometry();
|
const FBXGeometry& geometry = getFBXGeometry();
|
||||||
|
|
||||||
glm::mat4 modelToWorld = glm::mat4_cast(modelOrientation);
|
|
||||||
for (int i = 0; i < _meshStates.size(); i++) {
|
for (int i = 0; i < _meshStates.size(); i++) {
|
||||||
MeshState& state = _meshStates[i];
|
MeshState& state = _meshStates[i];
|
||||||
const FBXMesh& mesh = geometry.meshes.at(i);
|
const FBXMesh& mesh = geometry.meshes.at(i);
|
||||||
|
@ -53,7 +52,7 @@ void SoftAttachmentModel::updateClusterMatrices(glm::vec3 modelPosition, glm::qu
|
||||||
for (int j = 0; j < mesh.clusters.size(); j++) {
|
for (int j = 0; j < mesh.clusters.size(); j++) {
|
||||||
const FBXCluster& cluster = mesh.clusters.at(j);
|
const FBXCluster& cluster = mesh.clusters.at(j);
|
||||||
|
|
||||||
// TODO: cache these look ups as an optimization
|
// TODO: cache these look-ups as an optimization
|
||||||
int jointIndexOverride = getJointIndexOverride(cluster.jointIndex);
|
int jointIndexOverride = getJointIndexOverride(cluster.jointIndex);
|
||||||
glm::mat4 jointMatrix;
|
glm::mat4 jointMatrix;
|
||||||
if (jointIndexOverride >= 0 && jointIndexOverride < _rigOverride->getJointStateCount()) {
|
if (jointIndexOverride >= 0 && jointIndexOverride < _rigOverride->getJointStateCount()) {
|
||||||
|
@ -61,7 +60,13 @@ void SoftAttachmentModel::updateClusterMatrices(glm::vec3 modelPosition, glm::qu
|
||||||
} else {
|
} else {
|
||||||
jointMatrix = _rig->getJointTransform(cluster.jointIndex);
|
jointMatrix = _rig->getJointTransform(cluster.jointIndex);
|
||||||
}
|
}
|
||||||
state.clusterMatrices[j] = modelToWorld * jointMatrix * cluster.inverseBindMatrix;
|
#if GLM_ARCH & GLM_ARCH_SSE2
|
||||||
|
glm::mat4 out, inverseBindMatrix = cluster.inverseBindMatrix;
|
||||||
|
glm_mat4_mul((glm_vec4*)&jointMatrix, (glm_vec4*)&inverseBindMatrix, (glm_vec4*)&out);
|
||||||
|
state.clusterMatrices[j] = out;
|
||||||
|
#else
|
||||||
|
state.clusterMatrices[j] = jointMatrix * cluster.inverseBindMatrix;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
// Once computed the cluster matrices, update the buffer(s)
|
// Once computed the cluster matrices, update the buffer(s)
|
||||||
|
|
|
@ -31,7 +31,7 @@ public:
|
||||||
~SoftAttachmentModel();
|
~SoftAttachmentModel();
|
||||||
|
|
||||||
virtual void updateRig(float deltaTime, glm::mat4 parentTransform) override;
|
virtual void updateRig(float deltaTime, glm::mat4 parentTransform) override;
|
||||||
virtual void updateClusterMatrices(glm::vec3 modelPosition, glm::quat modelOrientation) override;
|
virtual void updateClusterMatrices() override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
int getJointIndexOverride(int i) const;
|
int getJointIndexOverride(int i) const;
|
||||||
|
|
|
@ -60,6 +60,19 @@ WindowScriptingInterface::WindowScriptingInterface() {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
WindowScriptingInterface::~WindowScriptingInterface() {
|
||||||
|
QHashIterator<int, QQuickItem*> i(_messageBoxes);
|
||||||
|
while (i.hasNext()) {
|
||||||
|
i.next();
|
||||||
|
auto messageBox = i.value();
|
||||||
|
disconnect(messageBox);
|
||||||
|
messageBox->setVisible(false);
|
||||||
|
messageBox->deleteLater();
|
||||||
|
}
|
||||||
|
|
||||||
|
_messageBoxes.clear();
|
||||||
|
}
|
||||||
|
|
||||||
QScriptValue WindowScriptingInterface::hasFocus() {
|
QScriptValue WindowScriptingInterface::hasFocus() {
|
||||||
return qApp->hasFocus();
|
return qApp->hasFocus();
|
||||||
}
|
}
|
||||||
|
@ -210,3 +223,62 @@ void WindowScriptingInterface::shareSnapshot(const QString& path, const QUrl& hr
|
||||||
bool WindowScriptingInterface::isPhysicsEnabled() {
|
bool WindowScriptingInterface::isPhysicsEnabled() {
|
||||||
return qApp->isPhysicsEnabled();
|
return qApp->isPhysicsEnabled();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int WindowScriptingInterface::openMessageBox(QString title, QString text, int buttons, int defaultButton) {
|
||||||
|
if (QThread::currentThread() != thread()) {
|
||||||
|
int result;
|
||||||
|
QMetaObject::invokeMethod(this, "openMessageBox", Qt::BlockingQueuedConnection,
|
||||||
|
Q_RETURN_ARG(int, result),
|
||||||
|
Q_ARG(QString, title),
|
||||||
|
Q_ARG(QString, text),
|
||||||
|
Q_ARG(int, buttons),
|
||||||
|
Q_ARG(int, defaultButton));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
return createMessageBox(title, text, buttons, defaultButton);
|
||||||
|
}
|
||||||
|
|
||||||
|
int WindowScriptingInterface::createMessageBox(QString title, QString text, int buttons, int defaultButton) {
|
||||||
|
auto messageBox = DependencyManager::get<OffscreenUi>()->createMessageBox(OffscreenUi::ICON_INFORMATION, title, text,
|
||||||
|
static_cast<QFlags<QMessageBox::StandardButton>>(buttons), static_cast<QMessageBox::StandardButton>(defaultButton));
|
||||||
|
connect(messageBox, SIGNAL(selected(int)), this, SLOT(onMessageBoxSelected(int)));
|
||||||
|
|
||||||
|
_lastMessageBoxID += 1;
|
||||||
|
_messageBoxes.insert(_lastMessageBoxID, messageBox);
|
||||||
|
|
||||||
|
return _lastMessageBoxID;
|
||||||
|
}
|
||||||
|
|
||||||
|
void WindowScriptingInterface::updateMessageBox(int id, QString title, QString text, int buttons, int defaultButton) {
|
||||||
|
auto messageBox = _messageBoxes.value(id);
|
||||||
|
if (messageBox) {
|
||||||
|
messageBox->setProperty("title", title);
|
||||||
|
messageBox->setProperty("text", text);
|
||||||
|
messageBox->setProperty("buttons", buttons);
|
||||||
|
messageBox->setProperty("defaultButton", defaultButton);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void WindowScriptingInterface::closeMessageBox(int id) {
|
||||||
|
auto messageBox = _messageBoxes.value(id);
|
||||||
|
if (messageBox) {
|
||||||
|
disconnect(messageBox);
|
||||||
|
messageBox->setVisible(false);
|
||||||
|
messageBox->deleteLater();
|
||||||
|
_messageBoxes.remove(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void WindowScriptingInterface::onMessageBoxSelected(int button) {
|
||||||
|
auto messageBox = qobject_cast<QQuickItem*>(sender());
|
||||||
|
auto keys = _messageBoxes.keys(messageBox);
|
||||||
|
if (keys.length() > 0) {
|
||||||
|
auto id = keys[0]; // Should be just one message box.
|
||||||
|
emit messageBoxClosed(id, button);
|
||||||
|
disconnect(messageBox);
|
||||||
|
messageBox->setVisible(false);
|
||||||
|
messageBox->deleteLater();
|
||||||
|
_messageBoxes.remove(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -14,7 +14,9 @@
|
||||||
|
|
||||||
#include <QtCore/QObject>
|
#include <QtCore/QObject>
|
||||||
#include <QtCore/QString>
|
#include <QtCore/QString>
|
||||||
|
#include <QtQuick/QQuickItem>
|
||||||
#include <QtScript/QScriptValue>
|
#include <QtScript/QScriptValue>
|
||||||
|
#include <QtWidgets/QMessageBox>
|
||||||
|
|
||||||
class CustomPromptResult {
|
class CustomPromptResult {
|
||||||
public:
|
public:
|
||||||
|
@ -35,6 +37,7 @@ class WindowScriptingInterface : public QObject, public Dependency {
|
||||||
Q_PROPERTY(int y READ getY)
|
Q_PROPERTY(int y READ getY)
|
||||||
public:
|
public:
|
||||||
WindowScriptingInterface();
|
WindowScriptingInterface();
|
||||||
|
~WindowScriptingInterface();
|
||||||
int getInnerWidth();
|
int getInnerWidth();
|
||||||
int getInnerHeight();
|
int getInnerHeight();
|
||||||
int getX();
|
int getX();
|
||||||
|
@ -56,6 +59,13 @@ public slots:
|
||||||
void shareSnapshot(const QString& path, const QUrl& href = QUrl(""));
|
void shareSnapshot(const QString& path, const QUrl& href = QUrl(""));
|
||||||
bool isPhysicsEnabled();
|
bool isPhysicsEnabled();
|
||||||
|
|
||||||
|
int openMessageBox(QString title, QString text, int buttons, int defaultButton);
|
||||||
|
void updateMessageBox(int id, QString title, QString text, int buttons, int defaultButton);
|
||||||
|
void closeMessageBox(int id);
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void onMessageBoxSelected(int button);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void domainChanged(const QString& domainHostname);
|
void domainChanged(const QString& domainHostname);
|
||||||
void svoImportRequested(const QString& url);
|
void svoImportRequested(const QString& url);
|
||||||
|
@ -64,9 +74,15 @@ signals:
|
||||||
void snapshotShared(const QString& error);
|
void snapshotShared(const QString& error);
|
||||||
void processingGif();
|
void processingGif();
|
||||||
|
|
||||||
|
void messageBoxClosed(int id, int button);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QString getPreviousBrowseLocation() const;
|
QString getPreviousBrowseLocation() const;
|
||||||
void setPreviousBrowseLocation(const QString& location);
|
void setPreviousBrowseLocation(const QString& location);
|
||||||
|
|
||||||
|
int createMessageBox(QString title, QString text, int buttons, int defaultButton);
|
||||||
|
QHash<int, QQuickItem*> _messageBoxes;
|
||||||
|
int _lastMessageBoxID{ -1 };
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // hifi_WindowScriptingInterface_h
|
#endif // hifi_WindowScriptingInterface_h
|
||||||
|
|
|
@ -28,14 +28,15 @@
|
||||||
#include <AbstractViewStateInterface.h>
|
#include <AbstractViewStateInterface.h>
|
||||||
|
|
||||||
#include <gl/OffscreenQmlSurface.h>
|
#include <gl/OffscreenQmlSurface.h>
|
||||||
|
#include <gl/OffscreenQmlSurfaceCache.h>
|
||||||
|
|
||||||
static const float DPI = 30.47f;
|
static const float DPI = 30.47f;
|
||||||
static const float INCHES_TO_METERS = 1.0f / 39.3701f;
|
static const float INCHES_TO_METERS = 1.0f / 39.3701f;
|
||||||
static const float METERS_TO_INCHES = 39.3701f;
|
static const float METERS_TO_INCHES = 39.3701f;
|
||||||
static const float OPAQUE_ALPHA_THRESHOLD = 0.99f;
|
static const float OPAQUE_ALPHA_THRESHOLD = 0.99f;
|
||||||
|
|
||||||
QString const Web3DOverlay::TYPE = "web3d";
|
const QString Web3DOverlay::TYPE = "web3d";
|
||||||
|
const QString Web3DOverlay::QML = "Web3DOverlay.qml";
|
||||||
Web3DOverlay::Web3DOverlay() : _dpi(DPI) {
|
Web3DOverlay::Web3DOverlay() : _dpi(DPI) {
|
||||||
_touchDevice.setCapabilities(QTouchDevice::Position);
|
_touchDevice.setCapabilities(QTouchDevice::Position);
|
||||||
_touchDevice.setType(QTouchDevice::TouchScreen);
|
_touchDevice.setType(QTouchDevice::TouchScreen);
|
||||||
|
@ -80,8 +81,9 @@ Web3DOverlay::~Web3DOverlay() {
|
||||||
// is no longer valid
|
// is no longer valid
|
||||||
auto webSurface = _webSurface;
|
auto webSurface = _webSurface;
|
||||||
AbstractViewStateInterface::instance()->postLambdaEvent([webSurface] {
|
AbstractViewStateInterface::instance()->postLambdaEvent([webSurface] {
|
||||||
webSurface->deleteLater();
|
DependencyManager::get<OffscreenQmlSurfaceCache>()->release(QML, webSurface);
|
||||||
});
|
});
|
||||||
|
_webSurface.reset();
|
||||||
}
|
}
|
||||||
auto geometryCache = DependencyManager::get<GeometryCache>();
|
auto geometryCache = DependencyManager::get<GeometryCache>();
|
||||||
if (geometryCache) {
|
if (geometryCache) {
|
||||||
|
@ -109,23 +111,14 @@ void Web3DOverlay::render(RenderArgs* args) {
|
||||||
QOpenGLContext * currentContext = QOpenGLContext::currentContext();
|
QOpenGLContext * currentContext = QOpenGLContext::currentContext();
|
||||||
QSurface * currentSurface = currentContext->surface();
|
QSurface * currentSurface = currentContext->surface();
|
||||||
if (!_webSurface) {
|
if (!_webSurface) {
|
||||||
auto deleter = [](OffscreenQmlSurface* webSurface) {
|
_webSurface = DependencyManager::get<OffscreenQmlSurfaceCache>()->acquire(QML);
|
||||||
AbstractViewStateInterface::instance()->postLambdaEvent([webSurface] {
|
_webSurface->setMaxFps(10);
|
||||||
webSurface->deleteLater();
|
|
||||||
});
|
|
||||||
};
|
|
||||||
_webSurface = QSharedPointer<OffscreenQmlSurface>(new OffscreenQmlSurface(), deleter);
|
|
||||||
// FIXME, the max FPS could be better managed by being dynamic (based on the number of current surfaces
|
// FIXME, the max FPS could be better managed by being dynamic (based on the number of current surfaces
|
||||||
// and the current rendering load)
|
// and the current rendering load)
|
||||||
_webSurface->setMaxFps(10);
|
|
||||||
_webSurface->create(currentContext);
|
|
||||||
_webSurface->setBaseUrl(QUrl::fromLocalFile(PathUtils::resourcesPath() + "/qml/"));
|
|
||||||
_webSurface->load("Web3DOverlay.qml");
|
|
||||||
_webSurface->resume();
|
_webSurface->resume();
|
||||||
|
_webSurface->resize(QSize(_resolution.x, _resolution.y));
|
||||||
_webSurface->getRootItem()->setProperty("url", _url);
|
_webSurface->getRootItem()->setProperty("url", _url);
|
||||||
_webSurface->getRootItem()->setProperty("scriptURL", _scriptURL);
|
_webSurface->getRootItem()->setProperty("scriptURL", _scriptURL);
|
||||||
_webSurface->getRootContext()->setContextProperty("ApplicationInterface", qApp);
|
|
||||||
_webSurface->resize(QSize(_resolution.x, _resolution.y));
|
|
||||||
currentContext->makeCurrent(currentSurface);
|
currentContext->makeCurrent(currentSurface);
|
||||||
|
|
||||||
auto forwardPointerEvent = [=](unsigned int overlayID, const PointerEvent& event) {
|
auto forwardPointerEvent = [=](unsigned int overlayID, const PointerEvent& event) {
|
||||||
|
|
|
@ -21,6 +21,7 @@ class Web3DOverlay : public Billboard3DOverlay {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
static const QString QML;
|
||||||
static QString const TYPE;
|
static QString const TYPE;
|
||||||
virtual QString getType() const override { return TYPE; }
|
virtual QString getType() const override { return TYPE; }
|
||||||
|
|
||||||
|
|
|
@ -424,35 +424,25 @@ bool adjustedFormatForAudioDevice(const QAudioDeviceInfo& audioDevice,
|
||||||
adjustedAudioFormat = desiredAudioFormat;
|
adjustedAudioFormat = desiredAudioFormat;
|
||||||
|
|
||||||
//
|
//
|
||||||
// Attempt the device sample rate in decreasing order of preference.
|
// Attempt the device sample rate and channel count in decreasing order of preference.
|
||||||
//
|
//
|
||||||
if (audioDevice.supportedSampleRates().contains(48000)) {
|
const int sampleRates[] = { 48000, 44100, 32000, 24000, 16000, 96000, 192000, 88200, 176400 };
|
||||||
adjustedAudioFormat.setSampleRate(48000);
|
const int inputChannels[] = { 1, 2, 4, 6, 8 }; // prefer mono
|
||||||
} else if (audioDevice.supportedSampleRates().contains(44100)) {
|
const int outputChannels[] = { 2, 4, 6, 8, 1 }; // prefer stereo, downmix as last resort
|
||||||
adjustedAudioFormat.setSampleRate(44100);
|
|
||||||
} else if (audioDevice.supportedSampleRates().contains(32000)) {
|
for (int channelCount : (desiredAudioFormat.channelCount() == 1 ? inputChannels : outputChannels)) {
|
||||||
adjustedAudioFormat.setSampleRate(32000);
|
for (int sampleRate : sampleRates) {
|
||||||
} else if (audioDevice.supportedSampleRates().contains(24000)) {
|
|
||||||
adjustedAudioFormat.setSampleRate(24000);
|
adjustedAudioFormat.setChannelCount(channelCount);
|
||||||
} else if (audioDevice.supportedSampleRates().contains(16000)) {
|
adjustedAudioFormat.setSampleRate(sampleRate);
|
||||||
adjustedAudioFormat.setSampleRate(16000);
|
|
||||||
} else if (audioDevice.supportedSampleRates().contains(96000)) {
|
if (audioDevice.isFormatSupported(adjustedAudioFormat)) {
|
||||||
adjustedAudioFormat.setSampleRate(96000);
|
return true;
|
||||||
} else if (audioDevice.supportedSampleRates().contains(192000)) {
|
}
|
||||||
adjustedAudioFormat.setSampleRate(192000);
|
}
|
||||||
} else if (audioDevice.supportedSampleRates().contains(88200)) {
|
|
||||||
adjustedAudioFormat.setSampleRate(88200);
|
|
||||||
} else if (audioDevice.supportedSampleRates().contains(176400)) {
|
|
||||||
adjustedAudioFormat.setSampleRate(176400);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (adjustedAudioFormat != desiredAudioFormat) {
|
return false; // a supported format could not be found
|
||||||
// return the nearest in case it needs 2 channels
|
|
||||||
adjustedAudioFormat = audioDevice.nearestFormat(adjustedAudioFormat);
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool sampleChannelConversion(const int16_t* sourceSamples, int16_t* destinationSamples, unsigned int numSourceSamples,
|
bool sampleChannelConversion(const int16_t* sourceSamples, int16_t* destinationSamples, unsigned int numSourceSamples,
|
||||||
|
|
|
@ -114,10 +114,10 @@ protected:
|
||||||
bool _vsyncEnabled { true };
|
bool _vsyncEnabled { true };
|
||||||
QThread* _presentThread{ nullptr };
|
QThread* _presentThread{ nullptr };
|
||||||
std::queue<gpu::FramePointer> _newFrameQueue;
|
std::queue<gpu::FramePointer> _newFrameQueue;
|
||||||
RateCounter<> _droppedFrameRate;
|
RateCounter<100> _droppedFrameRate;
|
||||||
RateCounter<> _newFrameRate;
|
RateCounter<100> _newFrameRate;
|
||||||
RateCounter<> _presentRate;
|
RateCounter<100> _presentRate;
|
||||||
RateCounter<> _renderRate;
|
RateCounter<100> _renderRate;
|
||||||
|
|
||||||
gpu::FramePointer _currentFrame;
|
gpu::FramePointer _currentFrame;
|
||||||
gpu::Frame* _lastFrame { nullptr };
|
gpu::Frame* _lastFrame { nullptr };
|
||||||
|
|
|
@ -555,8 +555,9 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS
|
||||||
}
|
}
|
||||||
} else if (subobject.name == "Properties70") {
|
} else if (subobject.name == "Properties70") {
|
||||||
foreach (const FBXNode& subsubobject, subobject.children) {
|
foreach (const FBXNode& subsubobject, subobject.children) {
|
||||||
|
static const QVariant APPLICATION_NAME = QVariant(QByteArray("Original|ApplicationName"));
|
||||||
if (subsubobject.name == "P" && subsubobject.properties.size() >= 5 &&
|
if (subsubobject.name == "P" && subsubobject.properties.size() >= 5 &&
|
||||||
subsubobject.properties.at(0) == "Original|ApplicationName") {
|
subsubobject.properties.at(0) == APPLICATION_NAME) {
|
||||||
geometry.applicationName = subsubobject.properties.at(4).toString();
|
geometry.applicationName = subsubobject.properties.at(4).toString();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -571,10 +572,12 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS
|
||||||
int index = 4;
|
int index = 4;
|
||||||
foreach (const FBXNode& subobject, object.children) {
|
foreach (const FBXNode& subobject, object.children) {
|
||||||
if (subobject.name == propertyName) {
|
if (subobject.name == propertyName) {
|
||||||
QString subpropName = subobject.properties.at(0).toString();
|
static const QVariant UNIT_SCALE_FACTOR = QByteArray("UnitScaleFactor");
|
||||||
if (subpropName == "UnitScaleFactor") {
|
static const QVariant AMBIENT_COLOR = QByteArray("AmbientColor");
|
||||||
|
const auto& subpropName = subobject.properties.at(0);
|
||||||
|
if (subpropName == UNIT_SCALE_FACTOR) {
|
||||||
unitScaleFactor = subobject.properties.at(index).toFloat();
|
unitScaleFactor = subobject.properties.at(index).toFloat();
|
||||||
} else if (subpropName == "AmbientColor") {
|
} else if (subpropName == AMBIENT_COLOR) {
|
||||||
ambientColor = getVec3(subobject.properties, index);
|
ambientColor = getVec3(subobject.properties, index);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -672,66 +675,87 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS
|
||||||
index = 4;
|
index = 4;
|
||||||
}
|
}
|
||||||
if (properties) {
|
if (properties) {
|
||||||
foreach (const FBXNode& property, subobject.children) {
|
static const QVariant GEOMETRIC_TRANSLATION = QByteArray("GeometricTranslation");
|
||||||
|
static const QVariant GEOMETRIC_ROTATION = QByteArray("GeometricRotation");
|
||||||
|
static const QVariant GEOMETRIC_SCALING = QByteArray("GeometricScaling");
|
||||||
|
static const QVariant LCL_TRANSLATION = QByteArray("Lcl Translation");
|
||||||
|
static const QVariant LCL_ROTATION = QByteArray("Lcl Rotation");
|
||||||
|
static const QVariant LCL_SCALING = QByteArray("Lcl Scaling");
|
||||||
|
static const QVariant ROTATION_MAX = QByteArray("RotationMax");
|
||||||
|
static const QVariant ROTATION_MAX_X = QByteArray("RotationMaxX");
|
||||||
|
static const QVariant ROTATION_MAX_Y = QByteArray("RotationMaxY");
|
||||||
|
static const QVariant ROTATION_MAX_Z = QByteArray("RotationMaxZ");
|
||||||
|
static const QVariant ROTATION_MIN = QByteArray("RotationMin");
|
||||||
|
static const QVariant ROTATION_MIN_X = QByteArray("RotationMinX");
|
||||||
|
static const QVariant ROTATION_MIN_Y = QByteArray("RotationMinY");
|
||||||
|
static const QVariant ROTATION_MIN_Z = QByteArray("RotationMinZ");
|
||||||
|
static const QVariant ROTATION_OFFSET = QByteArray("RotationOffset");
|
||||||
|
static const QVariant ROTATION_PIVOT = QByteArray("RotationPivot");
|
||||||
|
static const QVariant SCALING_OFFSET = QByteArray("ScalingOffset");
|
||||||
|
static const QVariant SCALING_PIVOT = QByteArray("ScalingPivot");
|
||||||
|
static const QVariant PRE_ROTATION = QByteArray("PreRotation");
|
||||||
|
static const QVariant POST_ROTATION = QByteArray("PostRotation");
|
||||||
|
foreach(const FBXNode& property, subobject.children) {
|
||||||
|
const auto& childProperty = property.properties.at(0);
|
||||||
if (property.name == propertyName) {
|
if (property.name == propertyName) {
|
||||||
if (property.properties.at(0) == "Lcl Translation") {
|
if (childProperty == LCL_TRANSLATION) {
|
||||||
translation = getVec3(property.properties, index);
|
translation = getVec3(property.properties, index);
|
||||||
|
|
||||||
} else if (property.properties.at(0) == "RotationOffset") {
|
} else if (childProperty == ROTATION_OFFSET) {
|
||||||
rotationOffset = getVec3(property.properties, index);
|
rotationOffset = getVec3(property.properties, index);
|
||||||
|
|
||||||
} else if (property.properties.at(0) == "RotationPivot") {
|
} else if (childProperty == ROTATION_PIVOT) {
|
||||||
rotationPivot = getVec3(property.properties, index);
|
rotationPivot = getVec3(property.properties, index);
|
||||||
|
|
||||||
} else if (property.properties.at(0) == "PreRotation") {
|
} else if (childProperty == PRE_ROTATION) {
|
||||||
preRotation = getVec3(property.properties, index);
|
preRotation = getVec3(property.properties, index);
|
||||||
|
|
||||||
} else if (property.properties.at(0) == "Lcl Rotation") {
|
} else if (childProperty == LCL_ROTATION) {
|
||||||
rotation = getVec3(property.properties, index);
|
rotation = getVec3(property.properties, index);
|
||||||
|
|
||||||
} else if (property.properties.at(0) == "PostRotation") {
|
} else if (childProperty == POST_ROTATION) {
|
||||||
postRotation = getVec3(property.properties, index);
|
postRotation = getVec3(property.properties, index);
|
||||||
|
|
||||||
} else if (property.properties.at(0) == "ScalingPivot") {
|
} else if (childProperty == SCALING_PIVOT) {
|
||||||
scalePivot = getVec3(property.properties, index);
|
scalePivot = getVec3(property.properties, index);
|
||||||
|
|
||||||
} else if (property.properties.at(0) == "Lcl Scaling") {
|
} else if (childProperty == LCL_SCALING) {
|
||||||
scale = getVec3(property.properties, index);
|
scale = getVec3(property.properties, index);
|
||||||
|
|
||||||
} else if (property.properties.at(0) == "ScalingOffset") {
|
} else if (childProperty == SCALING_OFFSET) {
|
||||||
scaleOffset = getVec3(property.properties, index);
|
scaleOffset = getVec3(property.properties, index);
|
||||||
|
|
||||||
// NOTE: these rotation limits are stored in degrees (NOT radians)
|
// NOTE: these rotation limits are stored in degrees (NOT radians)
|
||||||
} else if (property.properties.at(0) == "RotationMin") {
|
} else if (childProperty == ROTATION_MIN) {
|
||||||
rotationMin = getVec3(property.properties, index);
|
rotationMin = getVec3(property.properties, index);
|
||||||
|
|
||||||
} else if (property.properties.at(0) == "RotationMax") {
|
} else if (childProperty == ROTATION_MAX) {
|
||||||
rotationMax = getVec3(property.properties, index);
|
rotationMax = getVec3(property.properties, index);
|
||||||
|
|
||||||
} else if (property.properties.at(0) == "RotationMinX") {
|
} else if (childProperty == ROTATION_MIN_X) {
|
||||||
rotationMinX = property.properties.at(index).toBool();
|
rotationMinX = property.properties.at(index).toBool();
|
||||||
|
|
||||||
} else if (property.properties.at(0) == "RotationMinY") {
|
} else if (childProperty == ROTATION_MIN_Y) {
|
||||||
rotationMinY = property.properties.at(index).toBool();
|
rotationMinY = property.properties.at(index).toBool();
|
||||||
|
|
||||||
} else if (property.properties.at(0) == "RotationMinZ") {
|
} else if (childProperty == ROTATION_MIN_Z) {
|
||||||
rotationMinZ = property.properties.at(index).toBool();
|
rotationMinZ = property.properties.at(index).toBool();
|
||||||
|
|
||||||
} else if (property.properties.at(0) == "RotationMaxX") {
|
} else if (childProperty == ROTATION_MAX_X) {
|
||||||
rotationMaxX = property.properties.at(index).toBool();
|
rotationMaxX = property.properties.at(index).toBool();
|
||||||
|
|
||||||
} else if (property.properties.at(0) == "RotationMaxY") {
|
} else if (childProperty == ROTATION_MAX_Y) {
|
||||||
rotationMaxY = property.properties.at(index).toBool();
|
rotationMaxY = property.properties.at(index).toBool();
|
||||||
|
|
||||||
} else if (property.properties.at(0) == "RotationMaxZ") {
|
} else if (childProperty == ROTATION_MAX_Z) {
|
||||||
rotationMaxZ = property.properties.at(index).toBool();
|
rotationMaxZ = property.properties.at(index).toBool();
|
||||||
} else if (property.properties.at(0) == "GeometricTranslation") {
|
} else if (childProperty == GEOMETRIC_TRANSLATION) {
|
||||||
geometricTranslation = getVec3(property.properties, index);
|
geometricTranslation = getVec3(property.properties, index);
|
||||||
hasGeometricOffset = true;
|
hasGeometricOffset = true;
|
||||||
} else if (property.properties.at(0) == "GeometricRotation") {
|
} else if (childProperty == GEOMETRIC_ROTATION) {
|
||||||
geometricRotation = getVec3(property.properties, index);
|
geometricRotation = getVec3(property.properties, index);
|
||||||
hasGeometricOffset = true;
|
hasGeometricOffset = true;
|
||||||
} else if (property.properties.at(0) == "GeometricScaling") {
|
} else if (childProperty == GEOMETRIC_SCALING) {
|
||||||
geometricScaling = getVec3(property.properties, index);
|
geometricScaling = getVec3(property.properties, index);
|
||||||
hasGeometricOffset = true;
|
hasGeometricOffset = true;
|
||||||
}
|
}
|
||||||
|
@ -842,20 +866,26 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS
|
||||||
propertyName = "P";
|
propertyName = "P";
|
||||||
index = 4;
|
index = 4;
|
||||||
foreach (const FBXNode& property, subobject.children) {
|
foreach (const FBXNode& property, subobject.children) {
|
||||||
|
static const QVariant UV_SET = QByteArray("UVSet");
|
||||||
|
static const QVariant CURRENT_TEXTURE_BLEND_MODE = QByteArray("CurrentTextureBlendMode");
|
||||||
|
static const QVariant USE_MATERIAL = QByteArray("UseMaterial");
|
||||||
|
static const QVariant TRANSLATION = QByteArray("Translation");
|
||||||
|
static const QVariant ROTATION = QByteArray("Rotation");
|
||||||
|
static const QVariant SCALING = QByteArray("Scaling");
|
||||||
if (property.name == propertyName) {
|
if (property.name == propertyName) {
|
||||||
QString v = property.properties.at(0).toString();
|
QString v = property.properties.at(0).toString();
|
||||||
if (property.properties.at(0) == "UVSet") {
|
if (property.properties.at(0) == UV_SET) {
|
||||||
std::string uvName = property.properties.at(index).toString().toStdString();
|
std::string uvName = property.properties.at(index).toString().toStdString();
|
||||||
tex.assign(tex.UVSet, property.properties.at(index).toString());
|
tex.assign(tex.UVSet, property.properties.at(index).toString());
|
||||||
} else if (property.properties.at(0) == "CurrentTextureBlendMode") {
|
} else if (property.properties.at(0) == CURRENT_TEXTURE_BLEND_MODE) {
|
||||||
tex.assign<uint8_t>(tex.currentTextureBlendMode, property.properties.at(index).value<int>());
|
tex.assign<uint8_t>(tex.currentTextureBlendMode, property.properties.at(index).value<int>());
|
||||||
} else if (property.properties.at(0) == "UseMaterial") {
|
} else if (property.properties.at(0) == USE_MATERIAL) {
|
||||||
tex.assign<bool>(tex.useMaterial, property.properties.at(index).value<int>());
|
tex.assign<bool>(tex.useMaterial, property.properties.at(index).value<int>());
|
||||||
} else if (property.properties.at(0) == "Translation") {
|
} else if (property.properties.at(0) == TRANSLATION) {
|
||||||
tex.assign(tex.translation, getVec3(property.properties, index));
|
tex.assign(tex.translation, getVec3(property.properties, index));
|
||||||
} else if (property.properties.at(0) == "Rotation") {
|
} else if (property.properties.at(0) == ROTATION) {
|
||||||
tex.assign(tex.rotation, getVec3(property.properties, index));
|
tex.assign(tex.rotation, getVec3(property.properties, index));
|
||||||
} else if (property.properties.at(0) == "Scaling") {
|
} else if (property.properties.at(0) == SCALING) {
|
||||||
tex.assign(tex.scaling, getVec3(property.properties, index));
|
tex.assign(tex.scaling, getVec3(property.properties, index));
|
||||||
if (tex.scaling.x == 0.0f) {
|
if (tex.scaling.x == 0.0f) {
|
||||||
tex.scaling.x = 1.0f;
|
tex.scaling.x = 1.0f;
|
||||||
|
@ -931,87 +961,114 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS
|
||||||
|
|
||||||
if (properties) {
|
if (properties) {
|
||||||
std::vector<std::string> unknowns;
|
std::vector<std::string> unknowns;
|
||||||
|
static const QVariant DIFFUSE_COLOR = QByteArray("DiffuseColor");
|
||||||
|
static const QVariant DIFFUSE_FACTOR = QByteArray("DiffuseFactor");
|
||||||
|
static const QVariant DIFFUSE = QByteArray("Diffuse");
|
||||||
|
static const QVariant SPECULAR_COLOR = QByteArray("SpecularColor");
|
||||||
|
static const QVariant SPECULAR_FACTOR = QByteArray("SpecularFactor");
|
||||||
|
static const QVariant SPECULAR = QByteArray("Specular");
|
||||||
|
static const QVariant EMISSIVE_COLOR = QByteArray("EmissiveColor");
|
||||||
|
static const QVariant EMISSIVE_FACTOR = QByteArray("EmissiveFactor");
|
||||||
|
static const QVariant EMISSIVE = QByteArray("Emissive");
|
||||||
|
static const QVariant AMBIENT_FACTOR = QByteArray("AmbientFactor");
|
||||||
|
static const QVariant SHININESS = QByteArray("Shininess");
|
||||||
|
static const QVariant OPACITY = QByteArray("Shininess");
|
||||||
|
static const QVariant MAYA_USE_NORMAL_MAP = QByteArray("Maya|use_normal_map");
|
||||||
|
static const QVariant MAYA_BASE_COLOR = QByteArray("Maya|base_color");
|
||||||
|
static const QVariant MAYA_USE_COLOR_MAP = QByteArray("Maya|use_color_map");
|
||||||
|
static const QVariant MAYA_ROUGHNESS = QByteArray("Maya|roughness");
|
||||||
|
static const QVariant MAYA_USE_ROUGHNESS_MAP = QByteArray("Maya|use_roughness_map");
|
||||||
|
static const QVariant MAYA_METALLIC = QByteArray("Maya|metallic");
|
||||||
|
static const QVariant MAYA_USE_METALLIC_MAP = QByteArray("Maya|use_metallic_map");
|
||||||
|
static const QVariant MAYA_EMISSIVE = QByteArray("Maya|emissive");
|
||||||
|
static const QVariant MAYA_EMISSIVE_INTENSITY = QByteArray("Maya|emissive_intensity");
|
||||||
|
static const QVariant MAYA_USE_EMISSIVE_MAP = QByteArray("Maya|use_emissive_map");
|
||||||
|
static const QVariant MAYA_USE_AO_MAP = QByteArray("Maya|use_ao_map");
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
foreach(const FBXNode& property, subobject.children) {
|
foreach(const FBXNode& property, subobject.children) {
|
||||||
if (property.name == propertyName) {
|
if (property.name == propertyName) {
|
||||||
if (property.properties.at(0) == "DiffuseColor") {
|
if (property.properties.at(0) == DIFFUSE_COLOR) {
|
||||||
material.diffuseColor = getVec3(property.properties, index);
|
material.diffuseColor = getVec3(property.properties, index);
|
||||||
} else if (property.properties.at(0) == "DiffuseFactor") {
|
} else if (property.properties.at(0) == DIFFUSE_FACTOR) {
|
||||||
material.diffuseFactor = property.properties.at(index).value<double>();
|
material.diffuseFactor = property.properties.at(index).value<double>();
|
||||||
} else if (property.properties.at(0) == "Diffuse") {
|
} else if (property.properties.at(0) == DIFFUSE) {
|
||||||
// NOTE: this is uneeded but keep it for now for debug
|
// NOTE: this is uneeded but keep it for now for debug
|
||||||
// material.diffuseColor = getVec3(property.properties, index);
|
// material.diffuseColor = getVec3(property.properties, index);
|
||||||
// material.diffuseFactor = 1.0;
|
// material.diffuseFactor = 1.0;
|
||||||
|
|
||||||
} else if (property.properties.at(0) == "SpecularColor") {
|
} else if (property.properties.at(0) == SPECULAR_COLOR) {
|
||||||
material.specularColor = getVec3(property.properties, index);
|
material.specularColor = getVec3(property.properties, index);
|
||||||
} else if (property.properties.at(0) == "SpecularFactor") {
|
} else if (property.properties.at(0) == SPECULAR_FACTOR) {
|
||||||
material.specularFactor = property.properties.at(index).value<double>();
|
material.specularFactor = property.properties.at(index).value<double>();
|
||||||
} else if (property.properties.at(0) == "Specular") {
|
} else if (property.properties.at(0) == SPECULAR) {
|
||||||
// NOTE: this is uneeded but keep it for now for debug
|
// NOTE: this is uneeded but keep it for now for debug
|
||||||
// material.specularColor = getVec3(property.properties, index);
|
// material.specularColor = getVec3(property.properties, index);
|
||||||
// material.specularFactor = 1.0;
|
// material.specularFactor = 1.0;
|
||||||
|
|
||||||
} else if (property.properties.at(0) == "EmissiveColor") {
|
} else if (property.properties.at(0) == EMISSIVE_COLOR) {
|
||||||
material.emissiveColor = getVec3(property.properties, index);
|
material.emissiveColor = getVec3(property.properties, index);
|
||||||
} else if (property.properties.at(0) == "EmissiveFactor") {
|
} else if (property.properties.at(0) == EMISSIVE_FACTOR) {
|
||||||
material.emissiveFactor = property.properties.at(index).value<double>();
|
material.emissiveFactor = property.properties.at(index).value<double>();
|
||||||
} else if (property.properties.at(0) == "Emissive") {
|
} else if (property.properties.at(0) == EMISSIVE) {
|
||||||
// NOTE: this is uneeded but keep it for now for debug
|
// NOTE: this is uneeded but keep it for now for debug
|
||||||
// material.emissiveColor = getVec3(property.properties, index);
|
// material.emissiveColor = getVec3(property.properties, index);
|
||||||
// material.emissiveFactor = 1.0;
|
// material.emissiveFactor = 1.0;
|
||||||
|
|
||||||
} else if (property.properties.at(0) == "AmbientFactor") {
|
} else if (property.properties.at(0) == AMBIENT_FACTOR) {
|
||||||
material.ambientFactor = property.properties.at(index).value<double>();
|
material.ambientFactor = property.properties.at(index).value<double>();
|
||||||
// Detected just for BLender AO vs lightmap
|
// Detected just for BLender AO vs lightmap
|
||||||
} else if (property.properties.at(0) == "Shininess") {
|
} else if (property.properties.at(0) == SHININESS) {
|
||||||
material.shininess = property.properties.at(index).value<double>();
|
material.shininess = property.properties.at(index).value<double>();
|
||||||
|
|
||||||
} else if (property.properties.at(0) == "Opacity") {
|
} else if (property.properties.at(0) == OPACITY) {
|
||||||
material.opacity = property.properties.at(index).value<double>();
|
material.opacity = property.properties.at(index).value<double>();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sting Ray Material Properties!!!!
|
// Sting Ray Material Properties!!!!
|
||||||
else if (property.properties.at(0) == "Maya|use_normal_map") {
|
else if (property.properties.at(0) == MAYA_USE_NORMAL_MAP) {
|
||||||
material.isPBSMaterial = true;
|
material.isPBSMaterial = true;
|
||||||
material.useNormalMap = (bool)property.properties.at(index).value<double>();
|
material.useNormalMap = (bool)property.properties.at(index).value<double>();
|
||||||
|
|
||||||
} else if (property.properties.at(0) == "Maya|base_color") {
|
} else if (property.properties.at(0) == MAYA_BASE_COLOR) {
|
||||||
material.isPBSMaterial = true;
|
material.isPBSMaterial = true;
|
||||||
material.diffuseColor = getVec3(property.properties, index);
|
material.diffuseColor = getVec3(property.properties, index);
|
||||||
|
|
||||||
} else if (property.properties.at(0) == "Maya|use_color_map") {
|
} else if (property.properties.at(0) == MAYA_USE_COLOR_MAP) {
|
||||||
material.isPBSMaterial = true;
|
material.isPBSMaterial = true;
|
||||||
material.useAlbedoMap = (bool) property.properties.at(index).value<double>();
|
material.useAlbedoMap = (bool) property.properties.at(index).value<double>();
|
||||||
|
|
||||||
} else if (property.properties.at(0) == "Maya|roughness") {
|
} else if (property.properties.at(0) == MAYA_ROUGHNESS) {
|
||||||
material.isPBSMaterial = true;
|
material.isPBSMaterial = true;
|
||||||
material.roughness = property.properties.at(index).value<double>();
|
material.roughness = property.properties.at(index).value<double>();
|
||||||
|
|
||||||
} else if (property.properties.at(0) == "Maya|use_roughness_map") {
|
} else if (property.properties.at(0) == MAYA_USE_ROUGHNESS_MAP) {
|
||||||
material.isPBSMaterial = true;
|
material.isPBSMaterial = true;
|
||||||
material.useRoughnessMap = (bool)property.properties.at(index).value<double>();
|
material.useRoughnessMap = (bool)property.properties.at(index).value<double>();
|
||||||
|
|
||||||
} else if (property.properties.at(0) == "Maya|metallic") {
|
} else if (property.properties.at(0) == MAYA_METALLIC) {
|
||||||
material.isPBSMaterial = true;
|
material.isPBSMaterial = true;
|
||||||
material.metallic = property.properties.at(index).value<double>();
|
material.metallic = property.properties.at(index).value<double>();
|
||||||
|
|
||||||
} else if (property.properties.at(0) == "Maya|use_metallic_map") {
|
} else if (property.properties.at(0) == MAYA_USE_METALLIC_MAP) {
|
||||||
material.isPBSMaterial = true;
|
material.isPBSMaterial = true;
|
||||||
material.useMetallicMap = (bool)property.properties.at(index).value<double>();
|
material.useMetallicMap = (bool)property.properties.at(index).value<double>();
|
||||||
|
|
||||||
} else if (property.properties.at(0) == "Maya|emissive") {
|
} else if (property.properties.at(0) == MAYA_EMISSIVE) {
|
||||||
material.isPBSMaterial = true;
|
material.isPBSMaterial = true;
|
||||||
material.emissiveColor = getVec3(property.properties, index);
|
material.emissiveColor = getVec3(property.properties, index);
|
||||||
|
|
||||||
} else if (property.properties.at(0) == "Maya|emissive_intensity") {
|
} else if (property.properties.at(0) == MAYA_EMISSIVE_INTENSITY) {
|
||||||
material.isPBSMaterial = true;
|
material.isPBSMaterial = true;
|
||||||
material.emissiveIntensity = property.properties.at(index).value<double>();
|
material.emissiveIntensity = property.properties.at(index).value<double>();
|
||||||
|
|
||||||
} else if (property.properties.at(0) == "Maya|use_emissive_map") {
|
} else if (property.properties.at(0) == MAYA_USE_EMISSIVE_MAP) {
|
||||||
material.isPBSMaterial = true;
|
material.isPBSMaterial = true;
|
||||||
material.useEmissiveMap = (bool)property.properties.at(index).value<double>();
|
material.useEmissiveMap = (bool)property.properties.at(index).value<double>();
|
||||||
|
|
||||||
} else if (property.properties.at(0) == "Maya|use_ao_map") {
|
} else if (property.properties.at(0) == MAYA_USE_AO_MAP) {
|
||||||
material.isPBSMaterial = true;
|
material.isPBSMaterial = true;
|
||||||
material.useOcclusionMap = (bool)property.properties.at(index).value<double>();
|
material.useOcclusionMap = (bool)property.properties.at(index).value<double>();
|
||||||
|
|
||||||
|
@ -1116,9 +1173,11 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
} else if (child.name == "Connections") {
|
} else if (child.name == "Connections") {
|
||||||
|
static const QVariant OO = QByteArray("OO");
|
||||||
|
static const QVariant OP = QByteArray("OP");
|
||||||
foreach (const FBXNode& connection, child.children) {
|
foreach (const FBXNode& connection, child.children) {
|
||||||
if (connection.name == "C" || connection.name == "Connect") {
|
if (connection.name == "C" || connection.name == "Connect") {
|
||||||
if (connection.properties.at(0) == "OO") {
|
if (connection.properties.at(0) == OO) {
|
||||||
QString childID = getID(connection.properties, 1);
|
QString childID = getID(connection.properties, 1);
|
||||||
QString parentID = getID(connection.properties, 2);
|
QString parentID = getID(connection.properties, 2);
|
||||||
ooChildToParent.insert(childID, parentID);
|
ooChildToParent.insert(childID, parentID);
|
||||||
|
@ -1132,8 +1191,7 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS
|
||||||
_lightmapOffset = glm::clamp((*lightIt).second.color.x, 0.f, 1.f);
|
_lightmapOffset = glm::clamp((*lightIt).second.color.x, 0.f, 1.f);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
} else if (connection.properties.at(0) == OP) {
|
||||||
if (connection.properties.at(0) == "OP") {
|
|
||||||
int counter = 0;
|
int counter = 0;
|
||||||
QByteArray type = connection.properties.at(3).toByteArray().toLower();
|
QByteArray type = connection.properties.at(3).toByteArray().toLower();
|
||||||
if (type.contains("DiffuseFactor")) {
|
if (type.contains("DiffuseFactor")) {
|
||||||
|
|
|
@ -171,7 +171,8 @@ ExtractedMesh FBXReader::extractMesh(const FBXNode& object, unsigned int& meshIn
|
||||||
QVector<int> materials;
|
QVector<int> materials;
|
||||||
QVector<int> textures;
|
QVector<int> textures;
|
||||||
bool isMaterialPerPolygon = false;
|
bool isMaterialPerPolygon = false;
|
||||||
|
static const QVariant BY_VERTICE = QByteArray("ByVertice");
|
||||||
|
static const QVariant INDEX_TO_DIRECT = QByteArray("IndexToDirect");
|
||||||
foreach (const FBXNode& child, object.children) {
|
foreach (const FBXNode& child, object.children) {
|
||||||
if (child.name == "Vertices") {
|
if (child.name == "Vertices") {
|
||||||
data.vertices = createVec3Vector(getDoubleVector(child));
|
data.vertices = createVec3Vector(getDoubleVector(child));
|
||||||
|
@ -189,10 +190,10 @@ ExtractedMesh FBXReader::extractMesh(const FBXNode& object, unsigned int& meshIn
|
||||||
} else if (subdata.name == "NormalsIndex") {
|
} else if (subdata.name == "NormalsIndex") {
|
||||||
data.normalIndices = getIntVector(subdata);
|
data.normalIndices = getIntVector(subdata);
|
||||||
|
|
||||||
} else if (subdata.name == "MappingInformationType" && subdata.properties.at(0) == "ByVertice") {
|
} else if (subdata.name == "MappingInformationType" && subdata.properties.at(0) == BY_VERTICE) {
|
||||||
data.normalsByVertex = true;
|
data.normalsByVertex = true;
|
||||||
|
|
||||||
} else if (subdata.name == "ReferenceInformationType" && subdata.properties.at(0) == "IndexToDirect") {
|
} else if (subdata.name == "ReferenceInformationType" && subdata.properties.at(0) == INDEX_TO_DIRECT) {
|
||||||
indexToDirect = true;
|
indexToDirect = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -209,10 +210,10 @@ ExtractedMesh FBXReader::extractMesh(const FBXNode& object, unsigned int& meshIn
|
||||||
} else if (subdata.name == "ColorsIndex") {
|
} else if (subdata.name == "ColorsIndex") {
|
||||||
data.colorIndices = getIntVector(subdata);
|
data.colorIndices = getIntVector(subdata);
|
||||||
|
|
||||||
} else if (subdata.name == "MappingInformationType" && subdata.properties.at(0) == "ByVertice") {
|
} else if (subdata.name == "MappingInformationType" && subdata.properties.at(0) == BY_VERTICE) {
|
||||||
data.colorsByVertex = true;
|
data.colorsByVertex = true;
|
||||||
|
|
||||||
} else if (subdata.name == "ReferenceInformationType" && subdata.properties.at(0) == "IndexToDirect") {
|
} else if (subdata.name == "ReferenceInformationType" && subdata.properties.at(0) == INDEX_TO_DIRECT) {
|
||||||
indexToDirect = true;
|
indexToDirect = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -298,11 +299,12 @@ ExtractedMesh FBXReader::extractMesh(const FBXNode& object, unsigned int& meshIn
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (child.name == "LayerElementMaterial") {
|
} else if (child.name == "LayerElementMaterial") {
|
||||||
|
static const QVariant BY_POLYGON = QByteArray("ByPolygon");
|
||||||
foreach (const FBXNode& subdata, child.children) {
|
foreach (const FBXNode& subdata, child.children) {
|
||||||
if (subdata.name == "Materials") {
|
if (subdata.name == "Materials") {
|
||||||
materials = getIntVector(subdata);
|
materials = getIntVector(subdata);
|
||||||
} else if (subdata.name == "MappingInformationType") {
|
} else if (subdata.name == "MappingInformationType") {
|
||||||
if (subdata.properties.at(0) == "ByPolygon")
|
if (subdata.properties.at(0) == BY_POLYGON)
|
||||||
isMaterialPerPolygon = true;
|
isMaterialPerPolygon = true;
|
||||||
} else {
|
} else {
|
||||||
isMaterialPerPolygon = false;
|
isMaterialPerPolygon = false;
|
||||||
|
|
|
@ -43,31 +43,54 @@ template<class T> QVariant readBinaryArray(QDataStream& in, int& position) {
|
||||||
position += sizeof(quint32) * 3;
|
position += sizeof(quint32) * 3;
|
||||||
|
|
||||||
QVector<T> values;
|
QVector<T> values;
|
||||||
const unsigned int DEFLATE_ENCODING = 1;
|
if ((int)QSysInfo::ByteOrder == (int)in.byteOrder()) {
|
||||||
if (encoding == DEFLATE_ENCODING) {
|
values.resize(arrayLength);
|
||||||
// preface encoded data with uncompressed length
|
const unsigned int DEFLATE_ENCODING = 1;
|
||||||
QByteArray compressed(sizeof(quint32) + compressedLength, 0);
|
QByteArray arrayData;
|
||||||
*((quint32*)compressed.data()) = qToBigEndian<quint32>(arrayLength * sizeof(T));
|
if (encoding == DEFLATE_ENCODING) {
|
||||||
in.readRawData(compressed.data() + sizeof(quint32), compressedLength);
|
// preface encoded data with uncompressed length
|
||||||
position += compressedLength;
|
QByteArray compressed(sizeof(quint32) + compressedLength, 0);
|
||||||
QByteArray uncompressed = qUncompress(compressed);
|
*((quint32*)compressed.data()) = qToBigEndian<quint32>(arrayLength * sizeof(T));
|
||||||
if (uncompressed.isEmpty()) { // answers empty byte array if corrupt
|
in.readRawData(compressed.data() + sizeof(quint32), compressedLength);
|
||||||
throw QString("corrupt fbx file");
|
position += compressedLength;
|
||||||
}
|
arrayData = qUncompress(compressed);
|
||||||
QDataStream uncompressedIn(uncompressed);
|
if (arrayData.isEmpty() || arrayData.size() != (sizeof(T) * arrayLength)) { // answers empty byte array if corrupt
|
||||||
uncompressedIn.setByteOrder(QDataStream::LittleEndian);
|
throw QString("corrupt fbx file");
|
||||||
uncompressedIn.setVersion(QDataStream::Qt_4_5); // for single/double precision switch
|
}
|
||||||
for (quint32 i = 0; i < arrayLength; i++) {
|
} else {
|
||||||
T value;
|
arrayData.resize(sizeof(T) * arrayLength);
|
||||||
uncompressedIn >> value;
|
position += sizeof(T) * arrayLength;
|
||||||
values.append(value);
|
in.readRawData(arrayData.data(), arrayData.size());
|
||||||
}
|
}
|
||||||
|
memcpy(&values[0], arrayData.constData(), arrayData.size());
|
||||||
} else {
|
} else {
|
||||||
for (quint32 i = 0; i < arrayLength; i++) {
|
values.reserve(arrayLength);
|
||||||
T value;
|
const unsigned int DEFLATE_ENCODING = 1;
|
||||||
in >> value;
|
if (encoding == DEFLATE_ENCODING) {
|
||||||
position += streamSize<T>();
|
// preface encoded data with uncompressed length
|
||||||
values.append(value);
|
QByteArray compressed(sizeof(quint32) + compressedLength, 0);
|
||||||
|
*((quint32*)compressed.data()) = qToBigEndian<quint32>(arrayLength * sizeof(T));
|
||||||
|
in.readRawData(compressed.data() + sizeof(quint32), compressedLength);
|
||||||
|
position += compressedLength;
|
||||||
|
QByteArray uncompressed = qUncompress(compressed);
|
||||||
|
if (uncompressed.isEmpty()) { // answers empty byte array if corrupt
|
||||||
|
throw QString("corrupt fbx file");
|
||||||
|
}
|
||||||
|
QDataStream uncompressedIn(uncompressed);
|
||||||
|
uncompressedIn.setByteOrder(QDataStream::LittleEndian);
|
||||||
|
uncompressedIn.setVersion(QDataStream::Qt_4_5); // for single/double precision switch
|
||||||
|
for (quint32 i = 0; i < arrayLength; i++) {
|
||||||
|
T value;
|
||||||
|
uncompressedIn >> value;
|
||||||
|
values.append(value);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (quint32 i = 0; i < arrayLength; i++) {
|
||||||
|
T value;
|
||||||
|
in >> value;
|
||||||
|
position += streamSize<T>();
|
||||||
|
values.append(value);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return QVariant::fromValue(values);
|
return QVariant::fromValue(values);
|
||||||
|
|
57
libraries/gl/src/gl/OffscreenQmlSurfaceCache.cpp
Normal file
57
libraries/gl/src/gl/OffscreenQmlSurfaceCache.cpp
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
//
|
||||||
|
// Created by Bradley Austin Davis on 2017-01-11
|
||||||
|
// Copyright 2013-2017 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
#include "OffscreenQmlSurfaceCache.h"
|
||||||
|
|
||||||
|
#include <QtGui/QOpenGLContext>
|
||||||
|
#include <QtQml/QQmlContext>
|
||||||
|
|
||||||
|
#include <PathUtils.h>
|
||||||
|
#include "OffscreenQmlSurface.h"
|
||||||
|
|
||||||
|
OffscreenQmlSurfaceCache::OffscreenQmlSurfaceCache() {
|
||||||
|
}
|
||||||
|
|
||||||
|
OffscreenQmlSurfaceCache::~OffscreenQmlSurfaceCache() {
|
||||||
|
_cache.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
QSharedPointer<OffscreenQmlSurface> OffscreenQmlSurfaceCache::acquire(const QString& rootSource) {
|
||||||
|
auto& list = _cache[rootSource];
|
||||||
|
if (list.empty()) {
|
||||||
|
list.push_back(buildSurface(rootSource));
|
||||||
|
}
|
||||||
|
auto result = list.front();
|
||||||
|
list.pop_front();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void OffscreenQmlSurfaceCache::reserve(const QString& rootSource, int count) {
|
||||||
|
auto& list = _cache[rootSource];
|
||||||
|
while (list.size() < count) {
|
||||||
|
list.push_back(buildSurface(rootSource));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void OffscreenQmlSurfaceCache::release(const QString& rootSource, const QSharedPointer<OffscreenQmlSurface>& surface) {
|
||||||
|
surface->pause();
|
||||||
|
_cache[rootSource].push_back(surface);
|
||||||
|
}
|
||||||
|
|
||||||
|
QSharedPointer<OffscreenQmlSurface> OffscreenQmlSurfaceCache::buildSurface(const QString& rootSource) {
|
||||||
|
auto surface = QSharedPointer<OffscreenQmlSurface>(new OffscreenQmlSurface());
|
||||||
|
QOpenGLContext* currentContext = QOpenGLContext::currentContext();
|
||||||
|
QSurface* currentSurface = currentContext->surface();
|
||||||
|
surface->create(currentContext);
|
||||||
|
surface->setBaseUrl(QUrl::fromLocalFile(PathUtils::resourcesPath() + "/qml/"));
|
||||||
|
surface->load(rootSource);
|
||||||
|
surface->getRootContext()->setContextProperty("ApplicationInterface", qApp);
|
||||||
|
surface->resize(QSize(100, 100));
|
||||||
|
currentContext->makeCurrent(currentSurface);
|
||||||
|
return surface;
|
||||||
|
}
|
||||||
|
|
34
libraries/gl/src/gl/OffscreenQmlSurfaceCache.h
Normal file
34
libraries/gl/src/gl/OffscreenQmlSurfaceCache.h
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
//
|
||||||
|
// Created by Bradley Austin Davis on 2017-01-11
|
||||||
|
// Copyright 2013-2017 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
#pragma once
|
||||||
|
#ifndef hifi_OffscreenQmlSurfaceCache_h
|
||||||
|
#define hifi_OffscreenQmlSurfaceCahce_h
|
||||||
|
|
||||||
|
#include "DependencyManager.h"
|
||||||
|
|
||||||
|
#include <QtCore/QSharedPointer>
|
||||||
|
|
||||||
|
class OffscreenQmlSurface;
|
||||||
|
|
||||||
|
class OffscreenQmlSurfaceCache : public Dependency {
|
||||||
|
SINGLETON_DEPENDENCY
|
||||||
|
|
||||||
|
public:
|
||||||
|
OffscreenQmlSurfaceCache();
|
||||||
|
virtual ~OffscreenQmlSurfaceCache();
|
||||||
|
|
||||||
|
QSharedPointer<OffscreenQmlSurface> acquire(const QString& rootSource);
|
||||||
|
void release(const QString& rootSource, const QSharedPointer<OffscreenQmlSurface>& surface);
|
||||||
|
void reserve(const QString& rootSource, int count = 1);
|
||||||
|
|
||||||
|
private:
|
||||||
|
QSharedPointer<OffscreenQmlSurface> buildSurface(const QString& rootSource);
|
||||||
|
QHash<QString, QList<QSharedPointer<OffscreenQmlSurface>>> _cache;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
|
@ -88,8 +88,6 @@ SendQueue::SendQueue(Socket* socket, HifiSockAddr dest) :
|
||||||
_socket(socket),
|
_socket(socket),
|
||||||
_destination(dest)
|
_destination(dest)
|
||||||
{
|
{
|
||||||
PROFILE_ASYNC_BEGIN(network, "SendQueue", _destination.toString());
|
|
||||||
|
|
||||||
// setup psuedo-random number generation for all instances of SendQueue
|
// setup psuedo-random number generation for all instances of SendQueue
|
||||||
static std::random_device rd;
|
static std::random_device rd;
|
||||||
static std::mt19937 generator(rd());
|
static std::mt19937 generator(rd());
|
||||||
|
@ -108,7 +106,6 @@ SendQueue::SendQueue(Socket* socket, HifiSockAddr dest) :
|
||||||
}
|
}
|
||||||
|
|
||||||
SendQueue::~SendQueue() {
|
SendQueue::~SendQueue() {
|
||||||
PROFILE_ASYNC_END(network, "SendQueue", _destination.toString());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void SendQueue::queuePacket(std::unique_ptr<Packet> packet) {
|
void SendQueue::queuePacket(std::unique_ptr<Packet> packet) {
|
||||||
|
@ -229,8 +226,6 @@ void SendQueue::sendHandshake() {
|
||||||
if (!_hasReceivedHandshakeACK) {
|
if (!_hasReceivedHandshakeACK) {
|
||||||
// we haven't received a handshake ACK from the client, send another now
|
// we haven't received a handshake ACK from the client, send another now
|
||||||
auto handshakePacket = ControlPacket::create(ControlPacket::Handshake, sizeof(SequenceNumber));
|
auto handshakePacket = ControlPacket::create(ControlPacket::Handshake, sizeof(SequenceNumber));
|
||||||
PROFILE_ASYNC_BEGIN(network, "SendQueue:Handshake", _destination.toString());
|
|
||||||
|
|
||||||
handshakePacket->writePrimitive(_initialSequenceNumber);
|
handshakePacket->writePrimitive(_initialSequenceNumber);
|
||||||
_socket->writeBasePacket(*handshakePacket, _destination);
|
_socket->writeBasePacket(*handshakePacket, _destination);
|
||||||
|
|
||||||
|
@ -246,8 +241,6 @@ void SendQueue::handshakeACK(SequenceNumber initialSequenceNumber) {
|
||||||
std::lock_guard<std::mutex> locker { _handshakeMutex };
|
std::lock_guard<std::mutex> locker { _handshakeMutex };
|
||||||
_hasReceivedHandshakeACK = true;
|
_hasReceivedHandshakeACK = true;
|
||||||
}
|
}
|
||||||
PROFILE_ASYNC_END(network, "SendQueue:Handshake", _destination.toString());
|
|
||||||
|
|
||||||
// Notify on the handshake ACK condition
|
// Notify on the handshake ACK condition
|
||||||
_handshakeACKCondition.notify_one();
|
_handshakeACKCondition.notify_one();
|
||||||
}
|
}
|
||||||
|
|
|
@ -63,8 +63,7 @@ void MeshPartPayload::updateMeshPart(const std::shared_ptr<const model::Mesh>& d
|
||||||
|
|
||||||
void MeshPartPayload::updateTransform(const Transform& transform, const Transform& offsetTransform) {
|
void MeshPartPayload::updateTransform(const Transform& transform, const Transform& offsetTransform) {
|
||||||
_transform = transform;
|
_transform = transform;
|
||||||
_offsetTransform = offsetTransform;
|
Transform::mult(_drawTransform, _transform, offsetTransform);
|
||||||
Transform::mult(_drawTransform, _transform, _offsetTransform);
|
|
||||||
_worldBound = _localBound;
|
_worldBound = _localBound;
|
||||||
_worldBound.transform(_drawTransform);
|
_worldBound.transform(_drawTransform);
|
||||||
}
|
}
|
||||||
|
@ -360,8 +359,8 @@ void ModelMeshPartPayload::notifyLocationChanged() {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ModelMeshPartPayload::updateTransformForSkinnedMesh(const Transform& transform, const Transform& offsetTransform, const QVector<glm::mat4>& clusterMatrices) {
|
void ModelMeshPartPayload::updateTransformForSkinnedMesh(const Transform& transform, const QVector<glm::mat4>& clusterMatrices) {
|
||||||
ModelMeshPartPayload::updateTransform(transform, offsetTransform);
|
_transform = transform;
|
||||||
|
|
||||||
if (clusterMatrices.size() > 0) {
|
if (clusterMatrices.size() > 0) {
|
||||||
_worldBound = AABox();
|
_worldBound = AABox();
|
||||||
|
@ -371,8 +370,10 @@ void ModelMeshPartPayload::updateTransformForSkinnedMesh(const Transform& transf
|
||||||
_worldBound += clusterBound;
|
_worldBound += clusterBound;
|
||||||
}
|
}
|
||||||
|
|
||||||
// clusterMatrix has world rotation but not world translation.
|
_worldBound.transform(transform);
|
||||||
_worldBound.translate(transform.getTranslation());
|
if (clusterMatrices.size() == 1) {
|
||||||
|
_transform = _transform.worldTransform(Transform(clusterMatrices[0]));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -520,23 +521,15 @@ void ModelMeshPartPayload::bindTransform(gpu::Batch& batch, const ShapePipeline:
|
||||||
// Still relying on the raw data from the model
|
// Still relying on the raw data from the model
|
||||||
const Model::MeshState& state = _model->_meshStates.at(_meshIndex);
|
const Model::MeshState& state = _model->_meshStates.at(_meshIndex);
|
||||||
|
|
||||||
Transform transform;
|
|
||||||
if (state.clusterBuffer) {
|
if (state.clusterBuffer) {
|
||||||
if (canCauterize && _model->getCauterizeBones()) {
|
if (canCauterize && _model->getCauterizeBones()) {
|
||||||
batch.setUniformBuffer(ShapePipeline::Slot::BUFFER::SKINNING, state.cauterizedClusterBuffer);
|
batch.setUniformBuffer(ShapePipeline::Slot::BUFFER::SKINNING, state.cauterizedClusterBuffer);
|
||||||
} else {
|
} else {
|
||||||
batch.setUniformBuffer(ShapePipeline::Slot::BUFFER::SKINNING, state.clusterBuffer);
|
batch.setUniformBuffer(ShapePipeline::Slot::BUFFER::SKINNING, state.clusterBuffer);
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
if (canCauterize && _model->getCauterizeBones()) {
|
|
||||||
transform = Transform(state.cauterizedClusterMatrices[0]);
|
|
||||||
} else {
|
|
||||||
transform = Transform(state.clusterMatrices[0]);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
transform.preTranslate(_transform.getTranslation());
|
batch.setModelTransform(_transform);
|
||||||
batch.setModelTransform(transform);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ModelMeshPartPayload::startFade() {
|
void ModelMeshPartPayload::startFade() {
|
||||||
|
@ -569,8 +562,7 @@ void ModelMeshPartPayload::render(RenderArgs* args) const {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// When an individual mesh parts like this finishes its fade, we will mark the Model as
|
||||||
// When an individual mesh parts like this finishes its fade, we will mark the Model as
|
|
||||||
// having render items that need updating
|
// having render items that need updating
|
||||||
bool nextIsFading = _isFading ? isStillFading() : false;
|
bool nextIsFading = _isFading ? isStillFading() : false;
|
||||||
bool startFading = !_isFading && !_hasFinishedFade && _hasStartedFade;
|
bool startFading = !_isFading && !_hasFinishedFade && _hasStartedFade;
|
||||||
|
@ -592,7 +584,7 @@ void ModelMeshPartPayload::render(RenderArgs* args) const {
|
||||||
|
|
||||||
// Bind the model transform and the skinCLusterMatrices if needed
|
// Bind the model transform and the skinCLusterMatrices if needed
|
||||||
bool canCauterize = args->_renderMode != RenderArgs::SHADOW_RENDER_MODE;
|
bool canCauterize = args->_renderMode != RenderArgs::SHADOW_RENDER_MODE;
|
||||||
_model->updateClusterMatrices(_transform.getTranslation(), _transform.getRotation());
|
_model->updateClusterMatrices();
|
||||||
bindTransform(batch, locations, canCauterize);
|
bindTransform(batch, locations, canCauterize);
|
||||||
|
|
||||||
//Bind the index buffer and vertex buffer and Blend shapes if needed
|
//Bind the index buffer and vertex buffer and Blend shapes if needed
|
||||||
|
|
|
@ -34,7 +34,7 @@ public:
|
||||||
virtual void updateMeshPart(const std::shared_ptr<const model::Mesh>& drawMesh, int partIndex);
|
virtual void updateMeshPart(const std::shared_ptr<const model::Mesh>& drawMesh, int partIndex);
|
||||||
|
|
||||||
virtual void notifyLocationChanged() {}
|
virtual void notifyLocationChanged() {}
|
||||||
virtual void updateTransform(const Transform& transform, const Transform& offsetTransform);
|
void updateTransform(const Transform& transform, const Transform& offsetTransform);
|
||||||
|
|
||||||
virtual void updateMaterial(model::MaterialPointer drawMaterial);
|
virtual void updateMaterial(model::MaterialPointer drawMaterial);
|
||||||
|
|
||||||
|
@ -56,13 +56,12 @@ public:
|
||||||
model::Mesh::Part _drawPart;
|
model::Mesh::Part _drawPart;
|
||||||
|
|
||||||
std::shared_ptr<const model::Material> _drawMaterial;
|
std::shared_ptr<const model::Material> _drawMaterial;
|
||||||
|
|
||||||
model::Box _localBound;
|
model::Box _localBound;
|
||||||
Transform _drawTransform;
|
Transform _drawTransform;
|
||||||
Transform _transform;
|
Transform _transform;
|
||||||
Transform _offsetTransform;
|
|
||||||
mutable model::Box _worldBound;
|
mutable model::Box _worldBound;
|
||||||
|
|
||||||
bool _hasColorAttrib = false;
|
bool _hasColorAttrib = false;
|
||||||
|
|
||||||
size_t getVerticesCount() const { return _drawMesh ? _drawMesh->getNumVertices() : 0; }
|
size_t getVerticesCount() const { return _drawMesh ? _drawMesh->getNumVertices() : 0; }
|
||||||
|
@ -86,7 +85,7 @@ public:
|
||||||
typedef Payload::DataPointer Pointer;
|
typedef Payload::DataPointer Pointer;
|
||||||
|
|
||||||
void notifyLocationChanged() override;
|
void notifyLocationChanged() override;
|
||||||
void updateTransformForSkinnedMesh(const Transform& transform, const Transform& offsetTransform, const QVector<glm::mat4>& clusterMatrices);
|
void updateTransformForSkinnedMesh(const Transform& transform, const QVector<glm::mat4>& clusterMatrices);
|
||||||
|
|
||||||
// Entity fade in
|
// Entity fade in
|
||||||
void startFade();
|
void startFade();
|
||||||
|
@ -101,7 +100,7 @@ public:
|
||||||
|
|
||||||
// ModelMeshPartPayload functions to perform render
|
// ModelMeshPartPayload functions to perform render
|
||||||
void bindMesh(gpu::Batch& batch) const override;
|
void bindMesh(gpu::Batch& batch) const override;
|
||||||
void bindTransform(gpu::Batch& batch, const render::ShapePipeline::LocationsPointer locations, bool canCauterize) const override;
|
void bindTransform(gpu::Batch& batch, const render::ShapePipeline::LocationsPointer locations, bool canCauterize = true) const override;
|
||||||
|
|
||||||
void initCache();
|
void initCache();
|
||||||
|
|
||||||
|
|
|
@ -92,7 +92,6 @@ Model::Model(RigPointer rig, QObject* parent) :
|
||||||
_snapModelToRegistrationPoint(false),
|
_snapModelToRegistrationPoint(false),
|
||||||
_snappedToRegistrationPoint(false),
|
_snappedToRegistrationPoint(false),
|
||||||
_cauterizeBones(false),
|
_cauterizeBones(false),
|
||||||
_pupilDilation(0.0f),
|
|
||||||
_url(HTTP_INVALID_COM),
|
_url(HTTP_INVALID_COM),
|
||||||
_isVisible(true),
|
_isVisible(true),
|
||||||
_blendNumber(0),
|
_blendNumber(0),
|
||||||
|
@ -217,23 +216,17 @@ void Model::updateRenderItems() {
|
||||||
render::ScenePointer scene = AbstractViewStateInterface::instance()->getMain3DScene();
|
render::ScenePointer scene = AbstractViewStateInterface::instance()->getMain3DScene();
|
||||||
|
|
||||||
Transform modelTransform;
|
Transform modelTransform;
|
||||||
modelTransform.setScale(scale);
|
|
||||||
modelTransform.setTranslation(self->_translation);
|
modelTransform.setTranslation(self->_translation);
|
||||||
modelTransform.setRotation(self->_rotation);
|
modelTransform.setRotation(self->_rotation);
|
||||||
|
|
||||||
Transform modelMeshOffset;
|
Transform scaledModelTransform(modelTransform);
|
||||||
if (self->isLoaded()) {
|
scaledModelTransform.setScale(scale);
|
||||||
// includes model offset and unitScale.
|
|
||||||
modelMeshOffset = Transform(self->_rig->getGeometryToRigTransform());
|
|
||||||
} else {
|
|
||||||
modelMeshOffset.postTranslate(self->_offset);
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t deleteGeometryCounter = self->_deleteGeometryCounter;
|
uint32_t deleteGeometryCounter = self->_deleteGeometryCounter;
|
||||||
|
|
||||||
render::PendingChanges pendingChanges;
|
render::PendingChanges pendingChanges;
|
||||||
foreach (auto itemID, self->_modelMeshRenderItems.keys()) {
|
foreach (auto itemID, self->_modelMeshRenderItems.keys()) {
|
||||||
pendingChanges.updateItem<ModelMeshPartPayload>(itemID, [modelTransform, modelMeshOffset, deleteGeometryCounter](ModelMeshPartPayload& data) {
|
pendingChanges.updateItem<ModelMeshPartPayload>(itemID, [modelTransform, deleteGeometryCounter](ModelMeshPartPayload& data) {
|
||||||
if (data._model && data._model->isLoaded()) {
|
if (data._model && data._model->isLoaded()) {
|
||||||
if (!data.hasStartedFade() && data._model->getGeometry()->areTexturesLoaded()) {
|
if (!data.hasStartedFade() && data._model->getGeometry()->areTexturesLoaded()) {
|
||||||
data.startFade();
|
data.startFade();
|
||||||
|
@ -241,11 +234,11 @@ void Model::updateRenderItems() {
|
||||||
// Ensure the model geometry was not reset between frames
|
// Ensure the model geometry was not reset between frames
|
||||||
if (deleteGeometryCounter == data._model->_deleteGeometryCounter) {
|
if (deleteGeometryCounter == data._model->_deleteGeometryCounter) {
|
||||||
// lazy update of cluster matrices used for rendering. We need to update them here, so we can correctly update the bounding box.
|
// lazy update of cluster matrices used for rendering. We need to update them here, so we can correctly update the bounding box.
|
||||||
data._model->updateClusterMatrices(modelTransform.getTranslation(), modelTransform.getRotation());
|
data._model->updateClusterMatrices();
|
||||||
|
|
||||||
// update the model transform and bounding box for this render item.
|
// update the model transform and bounding box for this render item.
|
||||||
const Model::MeshState& state = data._model->_meshStates.at(data._meshIndex);
|
const Model::MeshState& state = data._model->_meshStates.at(data._meshIndex);
|
||||||
data.updateTransformForSkinnedMesh(modelTransform, modelMeshOffset, state.clusterMatrices);
|
data.updateTransformForSkinnedMesh(modelTransform, state.clusterMatrices);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -255,9 +248,9 @@ void Model::updateRenderItems() {
|
||||||
Transform collisionMeshOffset;
|
Transform collisionMeshOffset;
|
||||||
collisionMeshOffset.setIdentity();
|
collisionMeshOffset.setIdentity();
|
||||||
foreach (auto itemID, self->_collisionRenderItems.keys()) {
|
foreach (auto itemID, self->_collisionRenderItems.keys()) {
|
||||||
pendingChanges.updateItem<MeshPartPayload>(itemID, [modelTransform, collisionMeshOffset](MeshPartPayload& data) {
|
pendingChanges.updateItem<MeshPartPayload>(itemID, [scaledModelTransform, collisionMeshOffset](MeshPartPayload& data) {
|
||||||
// update the model transform for this render item.
|
// update the model transform for this render item.
|
||||||
data.updateTransform(modelTransform, collisionMeshOffset);
|
data.updateTransform(scaledModelTransform, collisionMeshOffset);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -295,6 +288,7 @@ bool Model::updateGeometry() {
|
||||||
|
|
||||||
if (_rig->jointStatesEmpty() && getFBXGeometry().joints.size() > 0) {
|
if (_rig->jointStatesEmpty() && getFBXGeometry().joints.size() > 0) {
|
||||||
initJointStates();
|
initJointStates();
|
||||||
|
assert(_meshStates.empty());
|
||||||
|
|
||||||
const FBXGeometry& fbxGeometry = getFBXGeometry();
|
const FBXGeometry& fbxGeometry = getFBXGeometry();
|
||||||
foreach (const FBXMesh& mesh, fbxGeometry.meshes) {
|
foreach (const FBXMesh& mesh, fbxGeometry.meshes) {
|
||||||
|
@ -304,6 +298,9 @@ bool Model::updateGeometry() {
|
||||||
|
|
||||||
_meshStates.append(state);
|
_meshStates.append(state);
|
||||||
|
|
||||||
|
// Note: we add empty buffers for meshes that lack blendshapes so we can access the buffers by index
|
||||||
|
// later in ModelMeshPayload, however the vast majority of meshes will not have them.
|
||||||
|
// TODO? make _blendedVertexBuffers a map instead of vector and only add for meshes with blendshapes?
|
||||||
auto buffer = std::make_shared<gpu::Buffer>();
|
auto buffer = std::make_shared<gpu::Buffer>();
|
||||||
if (!mesh.blendshapes.isEmpty()) {
|
if (!mesh.blendshapes.isEmpty()) {
|
||||||
buffer->resize((mesh.vertices.size() + mesh.normals.size()) * sizeof(glm::vec3));
|
buffer->resize((mesh.vertices.size() + mesh.normals.size()) * sizeof(glm::vec3));
|
||||||
|
@ -1152,7 +1149,7 @@ void Model::simulateInternal(float deltaTime) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// virtual
|
// virtual
|
||||||
void Model::updateClusterMatrices(glm::vec3 modelPosition, glm::quat modelOrientation) {
|
void Model::updateClusterMatrices() {
|
||||||
PerformanceTimer perfTimer("Model::updateClusterMatrices");
|
PerformanceTimer perfTimer("Model::updateClusterMatrices");
|
||||||
|
|
||||||
if (!_needsUpdateClusterMatrices || !isLoaded()) {
|
if (!_needsUpdateClusterMatrices || !isLoaded()) {
|
||||||
|
@ -1167,7 +1164,6 @@ void Model::updateClusterMatrices(glm::vec3 modelPosition, glm::quat modelOrient
|
||||||
glm::vec4(0.0f, 0.0f, 0.0f, 1.0f));
|
glm::vec4(0.0f, 0.0f, 0.0f, 1.0f));
|
||||||
auto cauterizeMatrix = _rig->getJointTransform(geometry.neckJointIndex) * zeroScale;
|
auto cauterizeMatrix = _rig->getJointTransform(geometry.neckJointIndex) * zeroScale;
|
||||||
|
|
||||||
glm::mat4 modelToWorld = glm::mat4_cast(modelOrientation);
|
|
||||||
for (int i = 0; i < _meshStates.size(); i++) {
|
for (int i = 0; i < _meshStates.size(); i++) {
|
||||||
MeshState& state = _meshStates[i];
|
MeshState& state = _meshStates[i];
|
||||||
const FBXMesh& mesh = geometry.meshes.at(i);
|
const FBXMesh& mesh = geometry.meshes.at(i);
|
||||||
|
@ -1175,12 +1171,11 @@ void Model::updateClusterMatrices(glm::vec3 modelPosition, glm::quat modelOrient
|
||||||
const FBXCluster& cluster = mesh.clusters.at(j);
|
const FBXCluster& cluster = mesh.clusters.at(j);
|
||||||
auto jointMatrix = _rig->getJointTransform(cluster.jointIndex);
|
auto jointMatrix = _rig->getJointTransform(cluster.jointIndex);
|
||||||
#if GLM_ARCH & GLM_ARCH_SSE2
|
#if GLM_ARCH & GLM_ARCH_SSE2
|
||||||
glm::mat4 temp, out, inverseBindMatrix = cluster.inverseBindMatrix;
|
glm::mat4 out, inverseBindMatrix = cluster.inverseBindMatrix;
|
||||||
glm_mat4_mul((glm_vec4*)&modelToWorld, (glm_vec4*)&jointMatrix, (glm_vec4*)&temp);
|
glm_mat4_mul((glm_vec4*)&jointMatrix, (glm_vec4*)&inverseBindMatrix, (glm_vec4*)&out);
|
||||||
glm_mat4_mul((glm_vec4*)&temp, (glm_vec4*)&inverseBindMatrix, (glm_vec4*)&out);
|
|
||||||
state.clusterMatrices[j] = out;
|
state.clusterMatrices[j] = out;
|
||||||
#else
|
#else
|
||||||
state.clusterMatrices[j] = modelToWorld * jointMatrix * cluster.inverseBindMatrix;
|
state.clusterMatrices[j] = jointMatrix * cluster.inverseBindMatrix;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// as an optimization, don't build cautrizedClusterMatrices if the boneSet is empty.
|
// as an optimization, don't build cautrizedClusterMatrices if the boneSet is empty.
|
||||||
|
@ -1188,7 +1183,13 @@ void Model::updateClusterMatrices(glm::vec3 modelPosition, glm::quat modelOrient
|
||||||
if (_cauterizeBoneSet.find(cluster.jointIndex) != _cauterizeBoneSet.end()) {
|
if (_cauterizeBoneSet.find(cluster.jointIndex) != _cauterizeBoneSet.end()) {
|
||||||
jointMatrix = cauterizeMatrix;
|
jointMatrix = cauterizeMatrix;
|
||||||
}
|
}
|
||||||
state.cauterizedClusterMatrices[j] = modelToWorld * jointMatrix * cluster.inverseBindMatrix;
|
#if GLM_ARCH & GLM_ARCH_SSE2
|
||||||
|
glm::mat4 out, inverseBindMatrix = cluster.inverseBindMatrix;
|
||||||
|
glm_mat4_mul((glm_vec4*)&jointMatrix, (glm_vec4*)&inverseBindMatrix, (glm_vec4*)&out);
|
||||||
|
state.cauterizedClusterMatrices[j] = out;
|
||||||
|
#else
|
||||||
|
state.cauterizedClusterMatrices[j] = jointMatrix * cluster.inverseBindMatrix;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -126,7 +126,7 @@ public:
|
||||||
bool getSnapModelToRegistrationPoint() { return _snapModelToRegistrationPoint; }
|
bool getSnapModelToRegistrationPoint() { return _snapModelToRegistrationPoint; }
|
||||||
|
|
||||||
virtual void simulate(float deltaTime, bool fullUpdate = true);
|
virtual void simulate(float deltaTime, bool fullUpdate = true);
|
||||||
virtual void updateClusterMatrices(glm::vec3 modelPosition, glm::quat modelOrientation);
|
virtual void updateClusterMatrices();
|
||||||
|
|
||||||
/// Returns a reference to the shared geometry.
|
/// Returns a reference to the shared geometry.
|
||||||
const Geometry::Pointer& getGeometry() const { return _renderGeometry; }
|
const Geometry::Pointer& getGeometry() const { return _renderGeometry; }
|
||||||
|
@ -251,9 +251,6 @@ signals:
|
||||||
protected:
|
protected:
|
||||||
bool addedToScene() const { return _addedToScene; }
|
bool addedToScene() const { return _addedToScene; }
|
||||||
|
|
||||||
void setPupilDilation(float dilation) { _pupilDilation = dilation; }
|
|
||||||
float getPupilDilation() const { return _pupilDilation; }
|
|
||||||
|
|
||||||
void setBlendshapeCoefficients(const QVector<float>& coefficients) { _blendshapeCoefficients = coefficients; }
|
void setBlendshapeCoefficients(const QVector<float>& coefficients) { _blendshapeCoefficients = coefficients; }
|
||||||
const QVector<float>& getBlendshapeCoefficients() const { return _blendshapeCoefficients; }
|
const QVector<float>& getBlendshapeCoefficients() const { return _blendshapeCoefficients; }
|
||||||
|
|
||||||
|
@ -347,7 +344,6 @@ protected:
|
||||||
void deleteGeometry();
|
void deleteGeometry();
|
||||||
void initJointTransforms();
|
void initJointTransforms();
|
||||||
|
|
||||||
float _pupilDilation;
|
|
||||||
QVector<float> _blendshapeCoefficients;
|
QVector<float> _blendshapeCoefficients;
|
||||||
|
|
||||||
QUrl _url;
|
QUrl _url;
|
||||||
|
|
|
@ -20,9 +20,10 @@
|
||||||
// When partially squeezing over a HUD element, a laser or the reticle is shown where the active hand
|
// When partially squeezing over a HUD element, a laser or the reticle is shown where the active hand
|
||||||
// controller beam intersects the HUD.
|
// controller beam intersects the HUD.
|
||||||
|
|
||||||
var systemLaserOn = false;
|
var activeTrigger;
|
||||||
|
function isLaserOn() {
|
||||||
|
return activeTrigger.partial();
|
||||||
|
}
|
||||||
Script.include("../libraries/controllers.js");
|
Script.include("../libraries/controllers.js");
|
||||||
|
|
||||||
// UTILITIES -------------
|
// UTILITIES -------------
|
||||||
|
@ -124,12 +125,6 @@ function ignoreMouseActivity() {
|
||||||
if (!Reticle.allowMouseCapture) {
|
if (!Reticle.allowMouseCapture) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// if the lasers are on, then reticle/mouse should be hidden and we can ignore it for seeking or depth updating
|
|
||||||
if (systemLaserOn) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
var pos = Reticle.position;
|
var pos = Reticle.position;
|
||||||
if (!pos || (pos.x == -1 && pos.y == -1)) {
|
if (!pos || (pos.x == -1 && pos.y == -1)) {
|
||||||
return true;
|
return true;
|
||||||
|
@ -270,12 +265,6 @@ var ONE_MINUS_WEIGHTING = 1 - WEIGHTING;
|
||||||
var AVERAGE_MOUSE_VELOCITY_FOR_SEEK_TO = 20;
|
var AVERAGE_MOUSE_VELOCITY_FOR_SEEK_TO = 20;
|
||||||
function isShakingMouse() { // True if the person is waving the mouse around trying to find it.
|
function isShakingMouse() { // True if the person is waving the mouse around trying to find it.
|
||||||
var now = Date.now(), mouse = Reticle.position, isShaking = false;
|
var now = Date.now(), mouse = Reticle.position, isShaking = false;
|
||||||
|
|
||||||
// if the lasers are on, then we ignore mouse shaking
|
|
||||||
if (systemLaserOn) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (lastIntegration && (lastIntegration !== now)) {
|
if (lastIntegration && (lastIntegration !== now)) {
|
||||||
var velocity = Vec3.length(Vec3.subtract(mouse, lastMouse)) / (now - lastIntegration);
|
var velocity = Vec3.length(Vec3.subtract(mouse, lastMouse)) / (now - lastIntegration);
|
||||||
averageMouseVelocity = (ONE_MINUS_WEIGHTING * averageMouseVelocity) + (WEIGHTING * velocity);
|
averageMouseVelocity = (ONE_MINUS_WEIGHTING * averageMouseVelocity) + (WEIGHTING * velocity);
|
||||||
|
@ -290,16 +279,8 @@ function isShakingMouse() { // True if the person is waving the mouse around try
|
||||||
var NON_LINEAR_DIVISOR = 2;
|
var NON_LINEAR_DIVISOR = 2;
|
||||||
var MINIMUM_SEEK_DISTANCE = 0.1;
|
var MINIMUM_SEEK_DISTANCE = 0.1;
|
||||||
function updateSeeking(doNotStartSeeking) {
|
function updateSeeking(doNotStartSeeking) {
|
||||||
// if the lasers are on, then we never do seeking
|
if (!doNotStartSeeking && !isLaserOn() && (!Reticle.visible || isShakingMouse())) {
|
||||||
if (systemLaserOn) {
|
isSeeking = true;
|
||||||
isSeeking = false;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!doNotStartSeeking && (!Reticle.visible || isShakingMouse())) {
|
|
||||||
if (!isSeeking) {
|
|
||||||
isSeeking = true;
|
|
||||||
}
|
|
||||||
} // e.g., if we're about to turn it on with first movement.
|
} // e.g., if we're about to turn it on with first movement.
|
||||||
if (!isSeeking) {
|
if (!isSeeking) {
|
||||||
return;
|
return;
|
||||||
|
@ -340,7 +321,7 @@ function updateMouseActivity(isClick) {
|
||||||
return;
|
return;
|
||||||
} // Bug: mouse clicks should keep going. Just not hand controller clicks
|
} // Bug: mouse clicks should keep going. Just not hand controller clicks
|
||||||
handControllerLockOut.update(now);
|
handControllerLockOut.update(now);
|
||||||
Reticle.visible = !systemLaserOn;
|
Reticle.visible = true;
|
||||||
}
|
}
|
||||||
function expireMouseCursor(now) {
|
function expireMouseCursor(now) {
|
||||||
if (!isPointingAtOverlay() && mouseCursorActivity.expired(now)) {
|
if (!isPointingAtOverlay() && mouseCursorActivity.expired(now)) {
|
||||||
|
@ -392,7 +373,7 @@ setupHandler(Controller.mouseDoublePressEvent, onMouseClick);
|
||||||
|
|
||||||
var leftTrigger = new Trigger('left');
|
var leftTrigger = new Trigger('left');
|
||||||
var rightTrigger = new Trigger('right');
|
var rightTrigger = new Trigger('right');
|
||||||
var activeTrigger = rightTrigger;
|
activeTrigger = rightTrigger;
|
||||||
var activeHand = Controller.Standard.RightHand;
|
var activeHand = Controller.Standard.RightHand;
|
||||||
var LEFT_HUD_LASER = 1;
|
var LEFT_HUD_LASER = 1;
|
||||||
var RIGHT_HUD_LASER = 2;
|
var RIGHT_HUD_LASER = 2;
|
||||||
|
@ -492,6 +473,7 @@ var LASER_ALPHA = 0.5;
|
||||||
var LASER_SEARCH_COLOR_XYZW = {x: 10 / 255, y: 10 / 255, z: 255 / 255, w: LASER_ALPHA};
|
var LASER_SEARCH_COLOR_XYZW = {x: 10 / 255, y: 10 / 255, z: 255 / 255, w: LASER_ALPHA};
|
||||||
var LASER_TRIGGER_COLOR_XYZW = {x: 250 / 255, y: 10 / 255, z: 10 / 255, w: LASER_ALPHA};
|
var LASER_TRIGGER_COLOR_XYZW = {x: 250 / 255, y: 10 / 255, z: 10 / 255, w: LASER_ALPHA};
|
||||||
var SYSTEM_LASER_DIRECTION = {x: 0, y: 0, z: -1};
|
var SYSTEM_LASER_DIRECTION = {x: 0, y: 0, z: -1};
|
||||||
|
var systemLaserOn = false;
|
||||||
function clearSystemLaser() {
|
function clearSystemLaser() {
|
||||||
if (!systemLaserOn) {
|
if (!systemLaserOn) {
|
||||||
return;
|
return;
|
||||||
|
@ -500,7 +482,6 @@ function clearSystemLaser() {
|
||||||
HMD.disableExtraLaser();
|
HMD.disableExtraLaser();
|
||||||
systemLaserOn = false;
|
systemLaserOn = false;
|
||||||
weMovedReticle = true;
|
weMovedReticle = true;
|
||||||
Reticle.position = { x: -1, y: -1 };
|
|
||||||
}
|
}
|
||||||
function setColoredLaser() { // answer trigger state if lasers supported, else falsey.
|
function setColoredLaser() { // answer trigger state if lasers supported, else falsey.
|
||||||
var color = (activeTrigger.state === 'full') ? LASER_TRIGGER_COLOR_XYZW : LASER_SEARCH_COLOR_XYZW;
|
var color = (activeTrigger.state === 'full') ? LASER_TRIGGER_COLOR_XYZW : LASER_SEARCH_COLOR_XYZW;
|
||||||
|
@ -584,12 +565,6 @@ function update() {
|
||||||
Reticle.visible = false;
|
Reticle.visible = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
var BASIC_TIMER_INTERVAL = 20; // 20ms = 50hz good enough
|
|
||||||
var updateIntervalTimer = Script.setInterval(function(){
|
|
||||||
update();
|
|
||||||
}, BASIC_TIMER_INTERVAL);
|
|
||||||
|
|
||||||
|
|
||||||
// Check periodically for changes to setup.
|
// Check periodically for changes to setup.
|
||||||
var SETTINGS_CHANGE_RECHECK_INTERVAL = 10 * 1000; // 10 seconds
|
var SETTINGS_CHANGE_RECHECK_INTERVAL = 10 * 1000; // 10 seconds
|
||||||
function checkSettings() {
|
function checkSettings() {
|
||||||
|
@ -599,9 +574,10 @@ function checkSettings() {
|
||||||
checkSettings();
|
checkSettings();
|
||||||
|
|
||||||
var settingsChecker = Script.setInterval(checkSettings, SETTINGS_CHANGE_RECHECK_INTERVAL);
|
var settingsChecker = Script.setInterval(checkSettings, SETTINGS_CHANGE_RECHECK_INTERVAL);
|
||||||
|
Script.update.connect(update);
|
||||||
Script.scriptEnding.connect(function () {
|
Script.scriptEnding.connect(function () {
|
||||||
Script.clearInterval(settingsChecker);
|
Script.clearInterval(settingsChecker);
|
||||||
Script.clearInterval(updateIntervalTimer);
|
Script.update.disconnect(update);
|
||||||
OffscreenFlags.navigationFocusDisabled = false;
|
OffscreenFlags.navigationFocusDisabled = false;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -14,12 +14,17 @@
|
||||||
|
|
||||||
// Event bridge messages.
|
// Event bridge messages.
|
||||||
var CLARA_IO_DOWNLOAD = "CLARA.IO DOWNLOAD";
|
var CLARA_IO_DOWNLOAD = "CLARA.IO DOWNLOAD";
|
||||||
|
var CLARA_IO_STATUS = "CLARA.IO STATUS";
|
||||||
|
var CLARA_IO_CANCEL_DOWNLOAD = "CLARA.IO CANCEL DOWNLOAD";
|
||||||
|
var CLARA_IO_CANCELLED_DOWNLOAD = "CLARA.IO CANCELLED DOWNLOAD";
|
||||||
var GOTO_DIRECTORY = "GOTO_DIRECTORY";
|
var GOTO_DIRECTORY = "GOTO_DIRECTORY";
|
||||||
var QUERY_CAN_WRITE_ASSETS = "QUERY_CAN_WRITE_ASSETS";
|
var QUERY_CAN_WRITE_ASSETS = "QUERY_CAN_WRITE_ASSETS";
|
||||||
var CAN_WRITE_ASSETS = "CAN_WRITE_ASSETS";
|
var CAN_WRITE_ASSETS = "CAN_WRITE_ASSETS";
|
||||||
var WARN_USER_NO_PERMISSIONS = "WARN_USER_NO_PERMISSIONS";
|
var WARN_USER_NO_PERMISSIONS = "WARN_USER_NO_PERMISSIONS";
|
||||||
|
|
||||||
var canWriteAssets = false;
|
var canWriteAssets = false;
|
||||||
|
var xmlHttpRequest = null;
|
||||||
|
var isDownloading = false; // Explicitly track download request status.
|
||||||
|
|
||||||
function injectCommonCode(isDirectoryPage) {
|
function injectCommonCode(isDirectoryPage) {
|
||||||
|
|
||||||
|
@ -75,10 +80,10 @@
|
||||||
|
|
||||||
// Add button links.
|
// Add button links.
|
||||||
$('#exploreClaraMarketplace').on('click', function () {
|
$('#exploreClaraMarketplace').on('click', function () {
|
||||||
window.location = "https://clara.io/library?gameCheck=true&public=true"
|
window.location = "https://clara.io/library?gameCheck=true&public=true";
|
||||||
});
|
});
|
||||||
$('#exploreHifiMarketplace').on('click', function () {
|
$('#exploreHifiMarketplace').on('click', function () {
|
||||||
window.location = "http://www.highfidelity.com/marketplace"
|
window.location = "http://www.highfidelity.com/marketplace";
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -111,61 +116,147 @@
|
||||||
element.setAttribute("href", href + parameters);
|
element.setAttribute("href", href + parameters);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Replace download options with a single, "Download to High Fidelity" option.
|
// Remove unwanted buttons and replace download options with a single "Download to High Fidelity" button.
|
||||||
var buttons = $("a.embed-button").parent("div");
|
var buttons = $("a.embed-button").parent("div");
|
||||||
if (buttons.length > 0) {
|
var downloadFBX;
|
||||||
var downloadFBX = buttons.find("a[data-extension=\'fbx\']")[0];
|
if (buttons.find("div.btn-group").length > 0) {
|
||||||
downloadFBX.addEventListener("click", startAutoDownload);
|
buttons.children(".btn-primary, .btn-group , .embed-button").each(function () { this.remove(); });
|
||||||
var firstButton = buttons.children(":first-child")[0];
|
if ($("#hifi-download-container").length === 0) { // Button hasn't been moved already.
|
||||||
buttons[0].insertBefore(downloadFBX, firstButton);
|
downloadFBX = $('<a class="btn btn-primary"><i class=\'glyphicon glyphicon-download-alt\'></i> Download to High Fidelity</a>');
|
||||||
downloadFBX.setAttribute("class", "btn btn-primary download");
|
buttons.prepend(downloadFBX);
|
||||||
downloadFBX.innerHTML = "<i class=\'glyphicon glyphicon-download-alt\'></i> Download to High Fidelity";
|
downloadFBX[0].addEventListener("click", startAutoDownload);
|
||||||
buttons.children(":nth-child(2), .btn-group , .embed-button").each(function () { this.remove(); });
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Move the "Download to High Fidelity" button to be more visible on tablet.
|
// Move the "Download to High Fidelity" button to be more visible on tablet.
|
||||||
if ($("#hifi-download-container").length === 0 && window.innerWidth < 700) {
|
if ($("#hifi-download-container").length === 0 && window.innerWidth < 700) {
|
||||||
// Moving the button stops the Clara.io download from starting so instead, make a visual copy in the right place
|
|
||||||
// and wire its click event to click the original button.
|
|
||||||
var downloadContainer = $('<div id="hifi-download-container"></div>');
|
var downloadContainer = $('<div id="hifi-download-container"></div>');
|
||||||
$(".top-title .col-sm-4").append(downloadContainer);
|
$(".top-title .col-sm-4").append(downloadContainer);
|
||||||
var downloadButton = $("a[data-extension=\'fbx\']").clone();
|
downloadContainer.append(downloadFBX);
|
||||||
downloadButton[0].addEventListener("click", function () { downloadFBX.click(); });
|
|
||||||
downloadContainer.append(downloadButton);
|
|
||||||
downloadFBX.style.visibility = "hidden";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Automatic download to High Fidelity.
|
// Automatic download to High Fidelity.
|
||||||
var downloadTimer;
|
function startAutoDownload() {
|
||||||
function startAutoDownload(event) {
|
|
||||||
if (!canWriteAssets) {
|
// One file request at a time.
|
||||||
console.log("Clara.io FBX file download cancelled because no permissions to write to Asset Server");
|
if (isDownloading) {
|
||||||
EventBridge.emitWebEvent(WARN_USER_NO_PERMISSIONS);
|
console.log("WARNIKNG: Clara.io FBX: Prepare only one download at a time");
|
||||||
event.stopPropagation();
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
window.scrollTo(0, 0); // Scroll to top ready for history.back().
|
// User must be able to write to Asset Server.
|
||||||
if (!downloadTimer) {
|
if (!canWriteAssets) {
|
||||||
downloadTimer = setInterval(autoDownload, 1000);
|
console.log("ERROR: Clara.io FBX: File download cancelled because no permissions to write to Asset Server");
|
||||||
|
EventBridge.emitWebEvent(WARN_USER_NO_PERMISSIONS);
|
||||||
|
event.stopPropagation();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
function autoDownload() {
|
// User must be logged in.
|
||||||
if ($("div.download-body").length !== 0) {
|
var loginButton = $("#topnav a[href='/signup']");
|
||||||
var downloadButton = $("div.download-body a.download-file");
|
if (loginButton.length > 0) {
|
||||||
if (downloadButton.length > 0) {
|
loginButton[0].click();
|
||||||
clearInterval(downloadTimer);
|
return;
|
||||||
downloadTimer = null;
|
|
||||||
var href = downloadButton[0].href;
|
|
||||||
EventBridge.emitWebEvent(CLARA_IO_DOWNLOAD + " " + href);
|
|
||||||
console.log("Clara.io FBX file download initiated for " + href);
|
|
||||||
$("a.btn.cancel").click();
|
|
||||||
history.back(); // Remove history item created by clicking "download".
|
|
||||||
};
|
|
||||||
} else if ($("div#view-signup_login_dialog").length === 0) {
|
|
||||||
// Don't stop checking for button if user is asked to log in.
|
|
||||||
clearInterval(downloadTimer);
|
|
||||||
downloadTimer = null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Obtain zip file to download for requested asset.
|
||||||
|
// Reference: https://clara.io/learn/sdk/api/export
|
||||||
|
|
||||||
|
//var XMLHTTPREQUEST_URL = "https://clara.io/api/scenes/{uuid}/export/fbx?zip=true¢erScene=true&alignSceneGround=true&fbxUnit=Meter&fbxVersion=7&fbxEmbedTextures=true&imageFormat=WebGL";
|
||||||
|
// 13 Jan 2017: Specify FBX version 5 and remove some options in order to make Clara.io site more likely to
|
||||||
|
// be successful in generating zip files.
|
||||||
|
var XMLHTTPREQUEST_URL = "https://clara.io/api/scenes/{uuid}/export/fbx?fbxUnit=Meter&fbxVersion=5&fbxEmbedTextures=true&imageFormat=WebGL";
|
||||||
|
|
||||||
|
var uuid = location.href.match(/\/view\/([a-z0-9\-]*)/)[1];
|
||||||
|
var url = XMLHTTPREQUEST_URL.replace("{uuid}", uuid);
|
||||||
|
|
||||||
|
xmlHttpRequest = new XMLHttpRequest();
|
||||||
|
var responseTextIndex = 0;
|
||||||
|
var zipFileURL = "";
|
||||||
|
|
||||||
|
xmlHttpRequest.onreadystatechange = function () {
|
||||||
|
// Messages are appended to responseText; process the new ones.
|
||||||
|
var message = this.responseText.slice(responseTextIndex);
|
||||||
|
var statusMessage = "";
|
||||||
|
|
||||||
|
if (isDownloading) { // Ignore messages in flight after finished/cancelled.
|
||||||
|
var lines = message.split(/[\n\r]+/);
|
||||||
|
|
||||||
|
for (var i = 0, length = lines.length; i < length; i++) {
|
||||||
|
if (lines[i].slice(0, 5) === "data:") {
|
||||||
|
// Parse line.
|
||||||
|
var data;
|
||||||
|
try {
|
||||||
|
data = JSON.parse(lines[i].slice(5));
|
||||||
|
}
|
||||||
|
catch (e) {
|
||||||
|
data = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract status message.
|
||||||
|
if (data.hasOwnProperty("message") && data.message !== null) {
|
||||||
|
statusMessage = data.message;
|
||||||
|
console.log("Clara.io FBX: " + statusMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract zip file URL.
|
||||||
|
if (data.hasOwnProperty("files") && data.files.length > 0) {
|
||||||
|
zipFileURL = data.files[0].url;
|
||||||
|
if (zipFileURL.slice(-4) !== ".zip") {
|
||||||
|
console.log(JSON.stringify(data)); // Data for debugging.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (statusMessage !== "") {
|
||||||
|
// Update the UI with the most recent status message.
|
||||||
|
EventBridge.emitWebEvent(CLARA_IO_STATUS + " " + statusMessage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
responseTextIndex = this.responseText.length;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Note: onprogress doesn't have computable total length so can't use it to determine % complete.
|
||||||
|
|
||||||
|
xmlHttpRequest.onload = function () {
|
||||||
|
var statusMessage = "";
|
||||||
|
|
||||||
|
if (!isDownloading) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var HTTP_OK = 200;
|
||||||
|
if (this.status !== HTTP_OK) {
|
||||||
|
statusMessage = "Zip file request terminated with " + this.status + " " + this.statusText;
|
||||||
|
console.log("ERROR: Clara.io FBX: " + statusMessage);
|
||||||
|
EventBridge.emitWebEvent(CLARA_IO_STATUS + " " + statusMessage);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (zipFileURL.slice(-4) !== ".zip") {
|
||||||
|
statusMessage = "Error creating zip file for download.";
|
||||||
|
console.log("ERROR: Clara.io FBX: " + statusMessage + ": " + zipFileURL);
|
||||||
|
EventBridge.emitWebEvent(CLARA_IO_STATUS + " " + statusMessage);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
EventBridge.emitWebEvent(CLARA_IO_DOWNLOAD + " " + zipFileURL);
|
||||||
|
console.log("Clara.io FBX: File download initiated for " + zipFileURL);
|
||||||
|
|
||||||
|
xmlHttpRequest = null;
|
||||||
|
isDownloading = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
isDownloading = true;
|
||||||
|
|
||||||
|
console.log("Clara.io FBX: Request zip file for " + uuid);
|
||||||
|
EventBridge.emitWebEvent(CLARA_IO_STATUS + " Initiating download");
|
||||||
|
|
||||||
|
xmlHttpRequest.open("POST", url, true);
|
||||||
|
xmlHttpRequest.setRequestHeader("Accept", "text/event-stream");
|
||||||
|
xmlHttpRequest.send();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -210,12 +301,27 @@
|
||||||
EventBridge.emitWebEvent(QUERY_CAN_WRITE_ASSETS);
|
EventBridge.emitWebEvent(QUERY_CAN_WRITE_ASSETS);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function cancelClaraDownload() {
|
||||||
|
isDownloading = false;
|
||||||
|
|
||||||
|
if (xmlHttpRequest) {
|
||||||
|
xmlHttpRequest.abort();
|
||||||
|
xmlHttpRequest = null;
|
||||||
|
console.log("Clara.io FBX: File download cancelled");
|
||||||
|
EventBridge.emitWebEvent(CLARA_IO_CANCELLED_DOWNLOAD);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function onLoad() {
|
function onLoad() {
|
||||||
|
|
||||||
EventBridge.scriptEventReceived.connect(function (message) {
|
EventBridge.scriptEventReceived.connect(function (message) {
|
||||||
if (message.slice(0, CAN_WRITE_ASSETS.length) === CAN_WRITE_ASSETS) {
|
if (message.slice(0, CAN_WRITE_ASSETS.length) === CAN_WRITE_ASSETS) {
|
||||||
canWriteAssets = message.slice(-4) === "true";
|
canWriteAssets = message.slice(-4) === "true";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (message.slice(0, CLARA_IO_CANCEL_DOWNLOAD.length) === CLARA_IO_CANCEL_DOWNLOAD) {
|
||||||
|
cancelClaraDownload();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
var DIRECTORY = 0;
|
var DIRECTORY = 0;
|
||||||
|
|
|
@ -22,10 +22,21 @@ var MARKETPLACES_INJECT_SCRIPT_URL = Script.resolvePath("../html/js/marketplaces
|
||||||
|
|
||||||
// Event bridge messages.
|
// Event bridge messages.
|
||||||
var CLARA_IO_DOWNLOAD = "CLARA.IO DOWNLOAD";
|
var CLARA_IO_DOWNLOAD = "CLARA.IO DOWNLOAD";
|
||||||
|
var CLARA_IO_STATUS = "CLARA.IO STATUS";
|
||||||
|
var CLARA_IO_CANCEL_DOWNLOAD = "CLARA.IO CANCEL DOWNLOAD";
|
||||||
|
var CLARA_IO_CANCELLED_DOWNLOAD = "CLARA.IO CANCELLED DOWNLOAD";
|
||||||
var GOTO_DIRECTORY = "GOTO_DIRECTORY";
|
var GOTO_DIRECTORY = "GOTO_DIRECTORY";
|
||||||
var QUERY_CAN_WRITE_ASSETS = "QUERY_CAN_WRITE_ASSETS";
|
var QUERY_CAN_WRITE_ASSETS = "QUERY_CAN_WRITE_ASSETS";
|
||||||
var CAN_WRITE_ASSETS = "CAN_WRITE_ASSETS";
|
var CAN_WRITE_ASSETS = "CAN_WRITE_ASSETS";
|
||||||
var WARN_USER_NO_PERMISSIONS = "WARN_USER_NO_PERMISSIONS";
|
var WARN_USER_NO_PERMISSIONS = "WARN_USER_NO_PERMISSIONS";
|
||||||
|
|
||||||
|
var CLARA_DOWNLOAD_TITLE = "Preparing Download";
|
||||||
|
var messageBox = null;
|
||||||
|
var isDownloadBeingCancelled = false;
|
||||||
|
|
||||||
|
var CANCEL_BUTTON = 4194304; // QMessageBox::Cancel
|
||||||
|
var NO_BUTTON = 0; // QMessageBox::NoButton
|
||||||
|
|
||||||
var NO_PERMISSIONS_ERROR_MESSAGE = "Cannot download model because you can't write to \nthe domain's Asset Server.";
|
var NO_PERMISSIONS_ERROR_MESSAGE = "Cannot download model because you can't write to \nthe domain's Asset Server.";
|
||||||
|
|
||||||
var marketplaceWindow = new OverlayWebWindow({
|
var marketplaceWindow = new OverlayWebWindow({
|
||||||
|
@ -36,17 +47,71 @@ var marketplaceWindow = new OverlayWebWindow({
|
||||||
visible: false
|
visible: false
|
||||||
});
|
});
|
||||||
marketplaceWindow.setScriptURL(MARKETPLACES_INJECT_SCRIPT_URL);
|
marketplaceWindow.setScriptURL(MARKETPLACES_INJECT_SCRIPT_URL);
|
||||||
marketplaceWindow.webEventReceived.connect(function (message) {
|
|
||||||
|
function onWebEventReceived(message) {
|
||||||
if (message === GOTO_DIRECTORY) {
|
if (message === GOTO_DIRECTORY) {
|
||||||
marketplaceWindow.setURL(MARKETPLACES_URL);
|
var url = MARKETPLACES_URL;
|
||||||
|
if (marketplaceWindow.visible) {
|
||||||
|
marketplaceWindow.setURL(url);
|
||||||
|
}
|
||||||
|
if (marketplaceWebTablet) {
|
||||||
|
marketplaceWebTablet.setURL(url);
|
||||||
|
}
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
if (message === QUERY_CAN_WRITE_ASSETS) {
|
if (message === QUERY_CAN_WRITE_ASSETS) {
|
||||||
marketplaceWindow.emitScriptEvent(CAN_WRITE_ASSETS + " " + Entities.canWriteAssets());
|
var canWriteAssets = CAN_WRITE_ASSETS + " " + Entities.canWriteAssets();
|
||||||
|
if (marketplaceWindow.visible) {
|
||||||
|
marketplaceWindow.emitScriptEvent(canWriteAssets);
|
||||||
|
}
|
||||||
|
if (marketplaceWebTablet) {
|
||||||
|
marketplaceWebTablet.getOverlayObject().emitScriptEvent(canWriteAssets);
|
||||||
|
}
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
if (message === WARN_USER_NO_PERMISSIONS) {
|
if (message === WARN_USER_NO_PERMISSIONS) {
|
||||||
Window.alert(NO_PERMISSIONS_ERROR_MESSAGE);
|
Window.alert(NO_PERMISSIONS_ERROR_MESSAGE);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
if (message.slice(0, CLARA_IO_STATUS.length) === CLARA_IO_STATUS) {
|
||||||
|
if (isDownloadBeingCancelled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var text = message.slice(CLARA_IO_STATUS.length);
|
||||||
|
if (messageBox === null) {
|
||||||
|
messageBox = Window.openMessageBox(CLARA_DOWNLOAD_TITLE, text, CANCEL_BUTTON, NO_BUTTON);
|
||||||
|
} else {
|
||||||
|
Window.updateMessageBox(messageBox, CLARA_DOWNLOAD_TITLE, text, CANCEL_BUTTON, NO_BUTTON);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (message.slice(0, CLARA_IO_DOWNLOAD.length) === CLARA_IO_DOWNLOAD) {
|
||||||
|
if (messageBox !== null) {
|
||||||
|
Window.closeMessageBox(messageBox);
|
||||||
|
messageBox = null;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (message === CLARA_IO_CANCELLED_DOWNLOAD) {
|
||||||
|
isDownloadBeingCancelled = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
marketplaceWindow.webEventReceived.connect(onWebEventReceived);
|
||||||
|
|
||||||
|
function onMessageBoxClosed(id, button) {
|
||||||
|
if (id === messageBox && button === CANCEL_BUTTON) {
|
||||||
|
isDownloadBeingCancelled = true;
|
||||||
|
messageBox = null;
|
||||||
|
marketplaceWindow.emitScriptEvent(CLARA_IO_CANCEL_DOWNLOAD);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Window.messageBoxClosed.connect(onMessageBoxClosed);
|
||||||
|
|
||||||
var toolHeight = 50;
|
var toolHeight = 50;
|
||||||
var toolWidth = 50;
|
var toolWidth = 50;
|
||||||
|
@ -71,17 +136,7 @@ function showMarketplace() {
|
||||||
marketplaceWebTablet = new WebTablet(MARKETPLACE_URL_INITIAL, null, null, true);
|
marketplaceWebTablet = new WebTablet(MARKETPLACE_URL_INITIAL, null, null, true);
|
||||||
Settings.setValue(persistenceKey, marketplaceWebTablet.pickle());
|
Settings.setValue(persistenceKey, marketplaceWebTablet.pickle());
|
||||||
marketplaceWebTablet.setScriptURL(MARKETPLACES_INJECT_SCRIPT_URL);
|
marketplaceWebTablet.setScriptURL(MARKETPLACES_INJECT_SCRIPT_URL);
|
||||||
marketplaceWebTablet.getOverlayObject().webEventReceived.connect(function (message) {
|
marketplaceWebTablet.getOverlayObject().webEventReceived.connect(onWebEventReceived);
|
||||||
if (message === GOTO_DIRECTORY) {
|
|
||||||
marketplaceWebTablet.setURL(MARKETPLACES_URL);
|
|
||||||
}
|
|
||||||
if (message === QUERY_CAN_WRITE_ASSETS) {
|
|
||||||
marketplaceWebTablet.getOverlayObject().emitScriptEvent(CAN_WRITE_ASSETS + " " + Entities.canWriteAssets());
|
|
||||||
}
|
|
||||||
if (message === WARN_USER_NO_PERMISSIONS) {
|
|
||||||
Window.alert(NO_PERMISSIONS_ERROR_MESSAGE);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else {
|
} else {
|
||||||
marketplaceWindow.setURL(MARKETPLACE_URL_INITIAL);
|
marketplaceWindow.setURL(MARKETPLACE_URL_INITIAL);
|
||||||
marketplaceWindow.setVisible(true);
|
marketplaceWindow.setVisible(true);
|
||||||
|
|
|
@ -19,9 +19,13 @@ const UNSELECTED_TEXTURES = {"idle-D": Script.resolvePath("./assets/models/Avata
|
||||||
const SELECTED_TEXTURES = { "idle-D": Script.resolvePath("./assets/models/Avatar-Overlay-v1.fbx/Avatar-Overlay-v1.fbm/avatar-overlay-selected.png"),
|
const SELECTED_TEXTURES = { "idle-D": Script.resolvePath("./assets/models/Avatar-Overlay-v1.fbx/Avatar-Overlay-v1.fbm/avatar-overlay-selected.png"),
|
||||||
"idle-E": Script.resolvePath("./assets/models/Avatar-Overlay-v1.fbx/Avatar-Overlay-v1.fbm/avatar-overlay-selected.png")
|
"idle-E": Script.resolvePath("./assets/models/Avatar-Overlay-v1.fbx/Avatar-Overlay-v1.fbm/avatar-overlay-selected.png")
|
||||||
};
|
};
|
||||||
|
const HOVER_TEXTURES = { "idle-D": Script.resolvePath("./assets/models/Avatar-Overlay-v1.fbx/Avatar-Overlay-v1.fbm/avatar-overlay-hover.png"),
|
||||||
|
"idle-E": Script.resolvePath("./assets/models/Avatar-Overlay-v1.fbx/Avatar-Overlay-v1.fbm/avatar-overlay-hover.png")
|
||||||
|
};
|
||||||
|
|
||||||
const UNSELECTED_COLOR = { red: 0x1F, green: 0xC6, blue: 0xA6};
|
const UNSELECTED_COLOR = { red: 0x1F, green: 0xC6, blue: 0xA6};
|
||||||
const SELECTED_COLOR = {red: 0xf3, green: 0x91, blue: 0x29};
|
const SELECTED_COLOR = {red: 0xF3, green: 0x91, blue: 0x29};
|
||||||
|
const HOVER_COLOR = {red: 0xD0, green: 0xD0, blue: 0xD0}; // almost white for now
|
||||||
|
|
||||||
(function() { // BEGIN LOCAL_SCOPE
|
(function() { // BEGIN LOCAL_SCOPE
|
||||||
|
|
||||||
|
@ -46,6 +50,7 @@ function ExtendedOverlay(key, type, properties, selected, hasModel) { // A wrapp
|
||||||
}
|
}
|
||||||
this.key = key;
|
this.key = key;
|
||||||
this.selected = selected || false; // not undefined
|
this.selected = selected || false; // not undefined
|
||||||
|
this.hovering = false;
|
||||||
this.activeOverlay = Overlays.addOverlay(type, properties); // We could use different overlays for (un)selected...
|
this.activeOverlay = Overlays.addOverlay(type, properties); // We could use different overlays for (un)selected...
|
||||||
}
|
}
|
||||||
// Instance methods:
|
// Instance methods:
|
||||||
|
@ -58,8 +63,8 @@ ExtendedOverlay.prototype.editOverlay = function (properties) { // change displa
|
||||||
Overlays.editOverlay(this.activeOverlay, properties);
|
Overlays.editOverlay(this.activeOverlay, properties);
|
||||||
};
|
};
|
||||||
|
|
||||||
function color(selected, level) {
|
function color(selected, hovering, level) {
|
||||||
var base = selected ? SELECTED_COLOR : UNSELECTED_COLOR;
|
var base = hovering ? HOVER_COLOR : selected ? SELECTED_COLOR : UNSELECTED_COLOR;
|
||||||
function scale(component) {
|
function scale(component) {
|
||||||
var delta = 0xFF - component;
|
var delta = 0xFF - component;
|
||||||
return component + (delta * level);
|
return component + (delta * level);
|
||||||
|
@ -67,16 +72,38 @@ function color(selected, level) {
|
||||||
return {red: scale(base.red), green: scale(base.green), blue: scale(base.blue)};
|
return {red: scale(base.red), green: scale(base.green), blue: scale(base.blue)};
|
||||||
}
|
}
|
||||||
|
|
||||||
function textures(selected) {
|
function textures(selected, hovering) {
|
||||||
return selected ? SELECTED_TEXTURES : UNSELECTED_TEXTURES;
|
return hovering ? HOVER_TEXTURES : selected ? SELECTED_TEXTURES : UNSELECTED_TEXTURES;
|
||||||
|
}
|
||||||
|
// so we don't have to traverse the overlays to get the last one
|
||||||
|
var lastHoveringId = 0;
|
||||||
|
ExtendedOverlay.prototype.hover = function (hovering) {
|
||||||
|
this.hovering = hovering;
|
||||||
|
if (this.key === lastHoveringId) {
|
||||||
|
if (hovering) {
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
lastHoveringId = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.editOverlay({color: color(this.selected, hovering, this.audioLevel)});
|
||||||
|
if (this.model) {
|
||||||
|
this.model.editOverlay({textures: textures(this.selected, hovering)});
|
||||||
|
}
|
||||||
|
if (hovering) {
|
||||||
|
// un-hover the last hovering overlay
|
||||||
|
if (lastHoveringId && lastHoveringId != this.key) {
|
||||||
|
ExtendedOverlay.get(lastHoveringId).hover(false);
|
||||||
|
}
|
||||||
|
lastHoveringId = this.key;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ExtendedOverlay.prototype.select = function (selected) {
|
ExtendedOverlay.prototype.select = function (selected) {
|
||||||
if (this.selected === selected) {
|
if (this.selected === selected) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.editOverlay({color: color(selected, this.audioLevel)});
|
this.editOverlay({color: color(selected, this.hovering, this.audioLevel)});
|
||||||
if (this.model) {
|
if (this.model) {
|
||||||
this.model.editOverlay({textures: textures(selected)});
|
this.model.editOverlay({textures: textures(selected)});
|
||||||
}
|
}
|
||||||
|
@ -98,14 +125,25 @@ ExtendedOverlay.some = function (iterator) { // Bails early as soon as iterator
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
ExtendedOverlay.applyPickRay = function (pickRay, cb) { // cb(overlay) on the one overlay intersected by pickRay, if any.
|
ExtendedOverlay.unHover = function () { // calls hover(false) on lastHoveringId (if any)
|
||||||
|
if (lastHoveringId) {
|
||||||
|
ExtendedOverlay.get(lastHoveringId).hover(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// hit(overlay) on the one overlay intersected by pickRay, if any.
|
||||||
|
// noHit() if no ExtendedOverlay was intersected (helps with hover)
|
||||||
|
ExtendedOverlay.applyPickRay = function (pickRay, hit, noHit) {
|
||||||
var pickedOverlay = Overlays.findRayIntersection(pickRay); // Depends on nearer coverOverlays to extend closer to us than farther ones.
|
var pickedOverlay = Overlays.findRayIntersection(pickRay); // Depends on nearer coverOverlays to extend closer to us than farther ones.
|
||||||
if (!pickedOverlay.intersects) {
|
if (!pickedOverlay.intersects) {
|
||||||
|
if (noHit) {
|
||||||
|
return noHit();
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
ExtendedOverlay.some(function (overlay) { // See if pickedOverlay is one of ours.
|
ExtendedOverlay.some(function (overlay) { // See if pickedOverlay is one of ours.
|
||||||
if ((overlay.activeOverlay) === pickedOverlay.overlayID) {
|
if ((overlay.activeOverlay) === pickedOverlay.overlayID) {
|
||||||
cb(overlay);
|
hit(overlay);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -209,7 +247,7 @@ function addAvatarNode(id) {
|
||||||
drawInFront: true,
|
drawInFront: true,
|
||||||
solid: true,
|
solid: true,
|
||||||
alpha: 0.8,
|
alpha: 0.8,
|
||||||
color: color(selected, 0.0),
|
color: color(selected, false, 0.0),
|
||||||
ignoreRayIntersection: false}, selected, true);
|
ignoreRayIntersection: false}, selected, true);
|
||||||
}
|
}
|
||||||
function populateUserList() {
|
function populateUserList() {
|
||||||
|
@ -293,7 +331,7 @@ function updateOverlays() {
|
||||||
|
|
||||||
overlay.ping = pingPong;
|
overlay.ping = pingPong;
|
||||||
overlay.editOverlay({
|
overlay.editOverlay({
|
||||||
color: color(ExtendedOverlay.isSelected(id), overlay.audioLevel),
|
color: color(ExtendedOverlay.isSelected(id), overlay.hovering, overlay.audioLevel),
|
||||||
position: target,
|
position: target,
|
||||||
dimensions: 0.032 * distance
|
dimensions: 0.032 * distance
|
||||||
});
|
});
|
||||||
|
@ -317,6 +355,7 @@ function updateOverlays() {
|
||||||
}
|
}
|
||||||
function removeOverlays() {
|
function removeOverlays() {
|
||||||
selectedIds = [];
|
selectedIds = [];
|
||||||
|
lastHoveringId = 0;
|
||||||
HighlightedEntity.clearOverlays();
|
HighlightedEntity.clearOverlays();
|
||||||
ExtendedOverlay.some(function (overlay) { overlay.deleteOverlay(); });
|
ExtendedOverlay.some(function (overlay) { overlay.deleteOverlay(); });
|
||||||
}
|
}
|
||||||
|
@ -338,9 +377,54 @@ function handleMouseEvent(mousePressEvent) { // handleClick if we get one.
|
||||||
}
|
}
|
||||||
handleClick(Camera.computePickRay(mousePressEvent.x, mousePressEvent.y));
|
handleClick(Camera.computePickRay(mousePressEvent.x, mousePressEvent.y));
|
||||||
}
|
}
|
||||||
|
function handleMouseMove(pickRay) { // given the pickRay, just do the hover logic
|
||||||
|
ExtendedOverlay.applyPickRay(pickRay, function (overlay) {
|
||||||
|
overlay.hover(true);
|
||||||
|
}, function () {
|
||||||
|
ExtendedOverlay.unHover();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// handy global to keep track of which hand is the mouse (if any)
|
||||||
|
var currentHandPressed = 0;
|
||||||
|
const TRIGGER_CLICK_THRESHOLD = 0.85;
|
||||||
|
const TRIGGER_PRESS_THRESHOLD = 0.05;
|
||||||
|
|
||||||
|
function handleMouseMoveEvent(event) { // find out which overlay (if any) is over the mouse position
|
||||||
|
if (HMD.active) {
|
||||||
|
if (currentHandPressed != 0) {
|
||||||
|
pickRay = controllerComputePickRay(currentHandPressed);
|
||||||
|
} else {
|
||||||
|
// nothing should hover, so
|
||||||
|
ExtendedOverlay.unHover();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
pickRay = Camera.computePickRay(event.x, event.y);
|
||||||
|
}
|
||||||
|
handleMouseMove(pickRay);
|
||||||
|
}
|
||||||
|
function handleTriggerPressed(hand, value) {
|
||||||
|
// The idea is if you press one trigger, it is the one
|
||||||
|
// we will consider the mouse. Even if the other is pressed,
|
||||||
|
// we ignore it until this one is no longer pressed.
|
||||||
|
isPressed = value > TRIGGER_PRESS_THRESHOLD;
|
||||||
|
if (currentHandPressed == 0) {
|
||||||
|
currentHandPressed = isPressed ? hand : 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (currentHandPressed == hand) {
|
||||||
|
currentHandPressed = isPressed ? hand : 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// otherwise, the other hand is still triggered
|
||||||
|
// so do nothing.
|
||||||
|
}
|
||||||
|
|
||||||
// We get mouseMoveEvents from the handControllers, via handControllerPointer.
|
// We get mouseMoveEvents from the handControllers, via handControllerPointer.
|
||||||
// But we don't get mousePressEvents.
|
// But we don't get mousePressEvents.
|
||||||
var triggerMapping = Controller.newMapping(Script.resolvePath('') + '-click');
|
var triggerMapping = Controller.newMapping(Script.resolvePath('') + '-click');
|
||||||
|
var triggerPressMapping = Controller.newMapping(Script.resolvePath('') + '-press');
|
||||||
function controllerComputePickRay(hand) {
|
function controllerComputePickRay(hand) {
|
||||||
var controllerPose = getControllerWorldLocation(hand, true);
|
var controllerPose = getControllerWorldLocation(hand, true);
|
||||||
if (controllerPose.valid) {
|
if (controllerPose.valid) {
|
||||||
|
@ -349,15 +433,21 @@ function controllerComputePickRay(hand) {
|
||||||
}
|
}
|
||||||
function makeClickHandler(hand) {
|
function makeClickHandler(hand) {
|
||||||
return function (clicked) {
|
return function (clicked) {
|
||||||
if (clicked > 0.85) {
|
if (clicked > TRIGGER_CLICK_THRESHOLD) {
|
||||||
var pickRay = controllerComputePickRay(hand);
|
var pickRay = controllerComputePickRay(hand);
|
||||||
handleClick(pickRay);
|
handleClick(pickRay);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
function makePressHandler(hand) {
|
||||||
|
return function (value) {
|
||||||
|
handleTriggerPressed(hand, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
triggerMapping.from(Controller.Standard.RTClick).peek().to(makeClickHandler(Controller.Standard.RightHand));
|
triggerMapping.from(Controller.Standard.RTClick).peek().to(makeClickHandler(Controller.Standard.RightHand));
|
||||||
triggerMapping.from(Controller.Standard.LTClick).peek().to(makeClickHandler(Controller.Standard.LeftHand));
|
triggerMapping.from(Controller.Standard.LTClick).peek().to(makeClickHandler(Controller.Standard.LeftHand));
|
||||||
|
triggerPressMapping.from(Controller.Standard.RT).peek().to(makePressHandler(Controller.Standard.RightHand));
|
||||||
|
triggerPressMapping.from(Controller.Standard.LT).peek().to(makePressHandler(Controller.Standard.LeftHand));
|
||||||
//
|
//
|
||||||
// Manage the connection between the button and the window.
|
// Manage the connection between the button and the window.
|
||||||
//
|
//
|
||||||
|
@ -377,9 +467,11 @@ function off() {
|
||||||
if (isWired) { // It is not ok to disconnect these twice, hence guard.
|
if (isWired) { // It is not ok to disconnect these twice, hence guard.
|
||||||
Script.update.disconnect(updateOverlays);
|
Script.update.disconnect(updateOverlays);
|
||||||
Controller.mousePressEvent.disconnect(handleMouseEvent);
|
Controller.mousePressEvent.disconnect(handleMouseEvent);
|
||||||
|
Controller.mouseMoveEvent.disconnect(handleMouseMoveEvent);
|
||||||
isWired = false;
|
isWired = false;
|
||||||
}
|
}
|
||||||
triggerMapping.disable(); // It's ok if we disable twice.
|
triggerMapping.disable(); // It's ok if we disable twice.
|
||||||
|
triggerPressMapping.disable(); // see above
|
||||||
removeOverlays();
|
removeOverlays();
|
||||||
Users.requestsDomainListData = false;
|
Users.requestsDomainListData = false;
|
||||||
}
|
}
|
||||||
|
@ -391,7 +483,9 @@ function onClicked() {
|
||||||
isWired = true;
|
isWired = true;
|
||||||
Script.update.connect(updateOverlays);
|
Script.update.connect(updateOverlays);
|
||||||
Controller.mousePressEvent.connect(handleMouseEvent);
|
Controller.mousePressEvent.connect(handleMouseEvent);
|
||||||
|
Controller.mouseMoveEvent.connect(handleMouseMoveEvent);
|
||||||
triggerMapping.enable();
|
triggerMapping.enable();
|
||||||
|
triggerPressMapping.enable();
|
||||||
} else {
|
} else {
|
||||||
off();
|
off();
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue