mirror of
https://github.com/overte-org/overte.git
synced 2025-08-10 12:42:58 +02:00
Merge branch 'master' of github.com:highfidelity/hifi into fix-edit-js-restart
This commit is contained in:
commit
ef709c09d0
79 changed files with 2524 additions and 1979 deletions
|
@ -153,7 +153,7 @@ void OctreeInboundPacketProcessor::processPacket(QSharedPointer<ReceivedMessage>
|
||||||
qDebug() << " maxSize=" << maxSize;
|
qDebug() << " maxSize=" << maxSize;
|
||||||
qDebug("OctreeInboundPacketProcessor::processPacket() %hhu "
|
qDebug("OctreeInboundPacketProcessor::processPacket() %hhu "
|
||||||
"payload=%p payloadLength=%lld editData=%p payloadPosition=%lld maxSize=%d",
|
"payload=%p payloadLength=%lld editData=%p payloadPosition=%lld maxSize=%d",
|
||||||
packetType, message->getRawMessage(), message->getSize(), editData,
|
(unsigned char)packetType, message->getRawMessage(), message->getSize(), editData,
|
||||||
message->getPosition(), maxSize);
|
message->getPosition(), maxSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -191,7 +191,7 @@ void OctreeInboundPacketProcessor::processPacket(QSharedPointer<ReceivedMessage>
|
||||||
if (debugProcessPacket) {
|
if (debugProcessPacket) {
|
||||||
qDebug("OctreeInboundPacketProcessor::processPacket() DONE LOOPING FOR %hhu "
|
qDebug("OctreeInboundPacketProcessor::processPacket() DONE LOOPING FOR %hhu "
|
||||||
"payload=%p payloadLength=%lld editData=%p payloadPosition=%lld",
|
"payload=%p payloadLength=%lld editData=%p payloadPosition=%lld",
|
||||||
packetType, message->getRawMessage(), message->getSize(), editData, message->getPosition());
|
(unsigned char)packetType, message->getRawMessage(), message->getSize(), editData, message->getPosition());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make sure our Node and NodeList knows we've heard from this node.
|
// Make sure our Node and NodeList knows we've heard from this node.
|
||||||
|
@ -208,7 +208,7 @@ void OctreeInboundPacketProcessor::processPacket(QSharedPointer<ReceivedMessage>
|
||||||
}
|
}
|
||||||
trackInboundPacket(nodeUUID, sequence, transitTime, editsInPacket, processTime, lockWaitTime);
|
trackInboundPacket(nodeUUID, sequence, transitTime, editsInPacket, processTime, lockWaitTime);
|
||||||
} else {
|
} else {
|
||||||
qDebug("unknown packet ignored... packetType=%hhu", packetType);
|
qDebug("unknown packet ignored... packetType=%hhu", (unsigned char)packetType);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
BIN
interface/resources/fonts/FiraSans-Regular.ttf
Normal file
BIN
interface/resources/fonts/FiraSans-Regular.ttf
Normal file
Binary file not shown.
Binary file not shown.
|
@ -21,7 +21,6 @@ import "../hifi/models"
|
||||||
TableView {
|
TableView {
|
||||||
id: tableView
|
id: tableView
|
||||||
|
|
||||||
// property var tableModel: ListModel { }
|
|
||||||
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
|
||||||
|
|
||||||
|
@ -46,7 +45,7 @@ TableView {
|
||||||
|
|
||||||
RalewayRegular {
|
RalewayRegular {
|
||||||
id: textHeader
|
id: textHeader
|
||||||
size: hifi.fontSizes.tableText
|
size: hifi.fontSizes.tableHeading
|
||||||
color: hifi.colors.lightGrayText
|
color: hifi.colors.lightGrayText
|
||||||
text: styleData.value
|
text: styleData.value
|
||||||
anchors {
|
anchors {
|
||||||
|
@ -87,7 +86,7 @@ TableView {
|
||||||
bottomMargin: 3 // ""
|
bottomMargin: 3 // ""
|
||||||
}
|
}
|
||||||
radius: 3
|
radius: 3
|
||||||
color: hifi.colors.tableScrollHandle
|
color: hifi.colors.tableScrollHandleDark
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -107,7 +106,7 @@ TableView {
|
||||||
margins: 1 // Shrink
|
margins: 1 // Shrink
|
||||||
}
|
}
|
||||||
radius: 4
|
radius: 4
|
||||||
color: hifi.colors.tableScrollBackground
|
color: hifi.colors.tableScrollBackgroundDark
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -91,7 +91,7 @@ FocusScope {
|
||||||
HiFiGlyphs {
|
HiFiGlyphs {
|
||||||
anchors {
|
anchors {
|
||||||
top: parent.top
|
top: parent.top
|
||||||
topMargin: -8
|
topMargin: -11
|
||||||
horizontalCenter: parent.horizontalCenter
|
horizontalCenter: parent.horizontalCenter
|
||||||
}
|
}
|
||||||
size: hifi.dimensions.spinnerSize
|
size: hifi.dimensions.spinnerSize
|
||||||
|
|
|
@ -17,8 +17,9 @@ import "../styles-uit"
|
||||||
|
|
||||||
Original.Button {
|
Original.Button {
|
||||||
property int color: 0
|
property int color: 0
|
||||||
property int colorScheme: hifi.colorShemes.light
|
property int colorScheme: hifi.colorSchemes.light
|
||||||
property string glyph: ""
|
property string glyph: ""
|
||||||
|
property int size: 32
|
||||||
|
|
||||||
width: 120
|
width: 120
|
||||||
height: 28
|
height: 28
|
||||||
|
@ -65,7 +66,13 @@ Original.Button {
|
||||||
: hifi.buttons.disabledTextColor[control.colorScheme]
|
: hifi.buttons.disabledTextColor[control.colorScheme]
|
||||||
verticalAlignment: Text.AlignVCenter
|
verticalAlignment: Text.AlignVCenter
|
||||||
horizontalAlignment: Text.AlignHCenter
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
anchors {
|
||||||
|
// Tweak horizontal alignment so that it looks right.
|
||||||
|
left: parent.left
|
||||||
|
leftMargin: -0.5
|
||||||
|
}
|
||||||
text: control.glyph
|
text: control.glyph
|
||||||
|
size: control.size
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -56,7 +56,7 @@ SpinBox {
|
||||||
incrementControl: HiFiGlyphs {
|
incrementControl: HiFiGlyphs {
|
||||||
id: incrementButton
|
id: incrementButton
|
||||||
text: hifi.glyphs.caratUp
|
text: hifi.glyphs.caratUp
|
||||||
x: 6
|
x: 10
|
||||||
y: 1
|
y: 1
|
||||||
size: hifi.dimensions.spinnerSize
|
size: hifi.dimensions.spinnerSize
|
||||||
color: styleData.upPressed ? (isLightColorScheme ? hifi.colors.black : hifi.colors.white) : hifi.colors.gray
|
color: styleData.upPressed ? (isLightColorScheme ? hifi.colors.black : hifi.colors.white) : hifi.colors.gray
|
||||||
|
@ -64,8 +64,8 @@ SpinBox {
|
||||||
|
|
||||||
decrementControl: HiFiGlyphs {
|
decrementControl: HiFiGlyphs {
|
||||||
text: hifi.glyphs.caratDn
|
text: hifi.glyphs.caratDn
|
||||||
x: 6
|
x: 10
|
||||||
y: -3
|
y: -1
|
||||||
size: hifi.dimensions.spinnerSize
|
size: hifi.dimensions.spinnerSize
|
||||||
color: styleData.downPressed ? (isLightColorScheme ? hifi.colors.black : hifi.colors.white) : hifi.colors.gray
|
color: styleData.downPressed ? (isLightColorScheme ? hifi.colors.black : hifi.colors.white) : hifi.colors.gray
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,20 +17,68 @@ import "../styles-uit"
|
||||||
TableView {
|
TableView {
|
||||||
id: tableView
|
id: tableView
|
||||||
|
|
||||||
property var tableModel: ListModel { }
|
|
||||||
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
|
||||||
|
property bool expandSelectedRow: false
|
||||||
|
|
||||||
model: tableModel
|
model: ListModel { }
|
||||||
|
|
||||||
TableViewColumn {
|
|
||||||
role: "name"
|
|
||||||
}
|
|
||||||
|
|
||||||
anchors { left: parent.left; right: parent.right }
|
|
||||||
|
|
||||||
headerVisible: false
|
headerVisible: false
|
||||||
headerDelegate: Item { } // Fix OSX QML bug that displays scrollbar starting too low.
|
headerDelegate: Rectangle {
|
||||||
|
height: hifi.dimensions.tableHeaderHeight
|
||||||
|
color: isLightColorScheme ? hifi.colors.tableBackgroundLight : hifi.colors.tableBackgroundDark
|
||||||
|
|
||||||
|
RalewayRegular {
|
||||||
|
id: titleText
|
||||||
|
text: styleData.value
|
||||||
|
size: hifi.fontSizes.tableHeading
|
||||||
|
font.capitalization: Font.AllUppercase
|
||||||
|
color: hifi.colors.baseGrayHighlight
|
||||||
|
anchors {
|
||||||
|
left: parent.left
|
||||||
|
leftMargin: hifi.dimensions.tablePadding
|
||||||
|
verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
HiFiGlyphs {
|
||||||
|
id: titleSort
|
||||||
|
text: sortIndicatorOrder == Qt.AscendingOrder ? hifi.glyphs.caratUp : hifi.glyphs.caratDn
|
||||||
|
color: hifi.colors.baseGrayHighlight
|
||||||
|
size: hifi.fontSizes.tableHeadingIcon
|
||||||
|
anchors {
|
||||||
|
left: titleText.right
|
||||||
|
leftMargin: -hifi.fontSizes.tableHeadingIcon / 3
|
||||||
|
right: parent.right
|
||||||
|
rightMargin: hifi.dimensions.tablePadding
|
||||||
|
verticalCenter: titleText.verticalCenter
|
||||||
|
}
|
||||||
|
visible: sortIndicatorVisible && sortIndicatorColumn === styleData.column
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
width: 1
|
||||||
|
anchors {
|
||||||
|
left: parent.left
|
||||||
|
top: parent.top
|
||||||
|
topMargin: 1
|
||||||
|
bottom: parent.bottom
|
||||||
|
bottomMargin: 2
|
||||||
|
}
|
||||||
|
color: isLightColorScheme ? hifi.colors.lightGrayText : hifi.colors.baseGrayHighlight
|
||||||
|
visible: styleData.column > 0
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
height: 1
|
||||||
|
anchors {
|
||||||
|
left: parent.left
|
||||||
|
right: parent.right
|
||||||
|
bottom: parent.bottom
|
||||||
|
}
|
||||||
|
color: isLightColorScheme ? hifi.colors.lightGrayText : hifi.colors.baseGrayHighlight
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Use rectangle to draw border with rounded corners.
|
// Use rectangle to draw border with rounded corners.
|
||||||
frameVisible: false
|
frameVisible: false
|
||||||
|
@ -50,8 +98,10 @@ TableView {
|
||||||
|
|
||||||
style: TableViewStyle {
|
style: TableViewStyle {
|
||||||
// Needed in order for rows to keep displaying rows after end of table entries.
|
// Needed in order for rows to keep displaying rows after end of table entries.
|
||||||
backgroundColor: parent.isLightColorScheme ? hifi.colors.tableRowLightEven : hifi.colors.tableRowDarkEven
|
backgroundColor: tableView.isLightColorScheme ? hifi.colors.tableBackgroundLight : hifi.colors.tableBackgroundDark
|
||||||
alternateBackgroundColor: parent.isLightColorScheme ? hifi.colors.tableRowLightOdd : hifi.colors.tableRowDarkOdd
|
alternateBackgroundColor: tableView.isLightColorScheme ? hifi.colors.tableRowLightOdd : hifi.colors.tableRowDarkOdd
|
||||||
|
|
||||||
|
padding.top: headerVisible ? hifi.dimensions.tableHeaderHeight: 0
|
||||||
|
|
||||||
handle: Item {
|
handle: Item {
|
||||||
id: scrollbarHandle
|
id: scrollbarHandle
|
||||||
|
@ -59,33 +109,38 @@ TableView {
|
||||||
Rectangle {
|
Rectangle {
|
||||||
anchors {
|
anchors {
|
||||||
fill: parent
|
fill: parent
|
||||||
|
topMargin: 3
|
||||||
|
bottomMargin: 3 // ""
|
||||||
leftMargin: 2 // Move it right
|
leftMargin: 2 // Move it right
|
||||||
rightMargin: -2 // ""
|
rightMargin: -2 // ""
|
||||||
topMargin: 3 // Shrink vertically
|
|
||||||
bottomMargin: 3 // ""
|
|
||||||
}
|
}
|
||||||
radius: 3
|
radius: 3
|
||||||
color: hifi.colors.tableScrollHandle
|
color: isLightColorScheme ? hifi.colors.tableScrollHandleLight : hifi.colors.tableScrollHandleDark
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
scrollBarBackground: Item {
|
scrollBarBackground: Item {
|
||||||
implicitWidth: 10
|
implicitWidth: 9
|
||||||
Rectangle {
|
Rectangle {
|
||||||
anchors {
|
anchors {
|
||||||
fill: parent
|
fill: parent
|
||||||
margins: -1 // Expand
|
margins: -1 // Expand
|
||||||
|
topMargin: headerVisible ? -hifi.dimensions.tableHeaderHeight : -1
|
||||||
}
|
}
|
||||||
color: hifi.colors.baseGrayHighlight
|
color: isLightColorScheme ? hifi.colors.tableBackgroundLight : hifi.colors.tableBackgroundDark
|
||||||
}
|
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
anchors {
|
// Extend header bottom border
|
||||||
fill: parent
|
anchors {
|
||||||
margins: 1 // Shrink
|
top: parent.top
|
||||||
|
topMargin: hifi.dimensions.tableHeaderHeight - 1
|
||||||
|
left: parent.left
|
||||||
|
right: parent.right
|
||||||
|
}
|
||||||
|
height: 1
|
||||||
|
color: isLightColorScheme ? hifi.colors.lightGrayText : hifi.colors.baseGrayHighlight
|
||||||
|
visible: headerVisible
|
||||||
}
|
}
|
||||||
radius: 4
|
|
||||||
color: hifi.colors.tableScrollBackground
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -99,85 +154,11 @@ TableView {
|
||||||
}
|
}
|
||||||
|
|
||||||
rowDelegate: Rectangle {
|
rowDelegate: Rectangle {
|
||||||
height: (styleData.selected ? 1.8 : 1) * hifi.dimensions.tableRowHeight
|
height: (styleData.selected && expandSelectedRow ? 1.8 : 1) * hifi.dimensions.tableRowHeight
|
||||||
color: styleData.selected
|
color: styleData.selected
|
||||||
? hifi.colors.primaryHighlight
|
? hifi.colors.primaryHighlight
|
||||||
: tableView.isLightColorScheme
|
: tableView.isLightColorScheme
|
||||||
? (styleData.alternate ? hifi.colors.tableRowLightEven : hifi.colors.tableRowLightOdd)
|
? (styleData.alternate ? hifi.colors.tableRowLightEven : hifi.colors.tableRowLightOdd)
|
||||||
: (styleData.alternate ? hifi.colors.tableRowDarkEven : hifi.colors.tableRowDarkOdd)
|
: (styleData.alternate ? hifi.colors.tableRowDarkEven : hifi.colors.tableRowDarkOdd)
|
||||||
}
|
}
|
||||||
|
|
||||||
itemDelegate: Item {
|
|
||||||
anchors {
|
|
||||||
left: parent ? parent.left : undefined
|
|
||||||
leftMargin: hifi.dimensions.tablePadding
|
|
||||||
right: parent ? parent.right : undefined
|
|
||||||
rightMargin: hifi.dimensions.tablePadding
|
|
||||||
}
|
|
||||||
|
|
||||||
FiraSansSemiBold {
|
|
||||||
id: textItem
|
|
||||||
text: styleData.value
|
|
||||||
size: hifi.fontSizes.tableText
|
|
||||||
color: colorScheme == hifi.colorSchemes.light
|
|
||||||
? (styleData.selected ? hifi.colors.black : hifi.colors.baseGrayHighlight)
|
|
||||||
: (styleData.selected ? hifi.colors.black : hifi.colors.lightGrayText)
|
|
||||||
anchors {
|
|
||||||
left: parent.left
|
|
||||||
right: parent.right
|
|
||||||
top: parent.top
|
|
||||||
topMargin: 3
|
|
||||||
}
|
|
||||||
|
|
||||||
// FIXME: Put reload item in tableModel passed in from RunningScripts.
|
|
||||||
HiFiGlyphs {
|
|
||||||
id: reloadButton
|
|
||||||
text: hifi.glyphs.reloadSmall
|
|
||||||
color: reloadButtonArea.pressed ? hifi.colors.white : parent.color
|
|
||||||
anchors {
|
|
||||||
top: parent.top
|
|
||||||
right: stopButton.left
|
|
||||||
verticalCenter: parent.verticalCenter
|
|
||||||
}
|
|
||||||
MouseArea {
|
|
||||||
id: reloadButtonArea
|
|
||||||
anchors { fill: parent; margins: -2 }
|
|
||||||
onClicked: reloadScript(model.url)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// FIXME: Put stop item in tableModel passed in from RunningScripts.
|
|
||||||
HiFiGlyphs {
|
|
||||||
id: stopButton
|
|
||||||
text: hifi.glyphs.closeSmall
|
|
||||||
color: stopButtonArea.pressed ? hifi.colors.white : parent.color
|
|
||||||
anchors {
|
|
||||||
top: parent.top
|
|
||||||
right: parent.right
|
|
||||||
verticalCenter: parent.verticalCenter
|
|
||||||
}
|
|
||||||
MouseArea {
|
|
||||||
id: stopButtonArea
|
|
||||||
anchors { fill: parent; margins: -2 }
|
|
||||||
onClicked: stopScript(model.url)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// FIXME: Automatically use aux. information from tableModel
|
|
||||||
FiraSansSemiBold {
|
|
||||||
text: tableModel.get(styleData.row) ? tableModel.get(styleData.row).url : ""
|
|
||||||
elide: Text.ElideMiddle
|
|
||||||
size: hifi.fontSizes.tableText
|
|
||||||
color: colorScheme == hifi.colorSchemes.light
|
|
||||||
? (styleData.selected ? hifi.colors.black : hifi.colors.lightGray)
|
|
||||||
: (styleData.selected ? hifi.colors.black : hifi.colors.lightGrayText)
|
|
||||||
anchors {
|
|
||||||
top: textItem.bottom
|
|
||||||
left: parent.left
|
|
||||||
right: parent.right
|
|
||||||
}
|
|
||||||
visible: styleData.selected
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
//
|
//
|
||||||
// Table.qml
|
// Tree.qml
|
||||||
//
|
//
|
||||||
// Created by David Rowe on 17 Feb 2016
|
// Created by David Rowe on 17 Feb 2016
|
||||||
// Copyright 2016 High Fidelity, Inc.
|
// Copyright 2016 High Fidelity, Inc.
|
||||||
|
@ -85,27 +85,18 @@ TreeView {
|
||||||
bottomMargin: 3 // ""
|
bottomMargin: 3 // ""
|
||||||
}
|
}
|
||||||
radius: 3
|
radius: 3
|
||||||
color: hifi.colors.tableScrollHandle
|
color: hifi.colors.tableScrollHandleDark
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
scrollBarBackground: Item {
|
scrollBarBackground: Item {
|
||||||
implicitWidth: 10
|
implicitWidth: 9
|
||||||
Rectangle {
|
Rectangle {
|
||||||
anchors {
|
anchors {
|
||||||
fill: parent
|
fill: parent
|
||||||
margins: -1 // Expand
|
margins: -1 // Expand
|
||||||
}
|
}
|
||||||
color: hifi.colors.baseGrayHighlight
|
color: hifi.colors.tableBackgroundDark
|
||||||
}
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
anchors {
|
|
||||||
fill: parent
|
|
||||||
margins: 1 // Shrink
|
|
||||||
}
|
|
||||||
radius: 4
|
|
||||||
color: hifi.colors.tableScrollBackground
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,14 @@
|
||||||
import QtQuick 2.0
|
//
|
||||||
|
// FileDialog.qml
|
||||||
|
//
|
||||||
|
// Created by Bradley Austin Davis on 14 Jan 2016
|
||||||
|
// Copyright 2015 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
|
||||||
|
import QtQuick 2.5
|
||||||
import QtQuick.Controls 1.4
|
import QtQuick.Controls 1.4
|
||||||
import Qt.labs.folderlistmodel 2.1
|
import Qt.labs.folderlistmodel 2.1
|
||||||
import Qt.labs.settings 1.0
|
import Qt.labs.settings 1.0
|
||||||
|
@ -6,17 +16,20 @@ import QtQuick.Controls.Styles 1.4
|
||||||
import QtQuick.Dialogs 1.2 as OriginalDialogs
|
import QtQuick.Dialogs 1.2 as OriginalDialogs
|
||||||
|
|
||||||
import ".."
|
import ".."
|
||||||
import "../windows"
|
import "../controls-uit"
|
||||||
import "../styles"
|
import "../styles-uit"
|
||||||
import "../controls" as VrControls
|
import "../windows-uit"
|
||||||
|
|
||||||
import "fileDialog"
|
import "fileDialog"
|
||||||
|
|
||||||
//FIXME implement shortcuts for favorite location
|
//FIXME implement shortcuts for favorite location
|
||||||
ModalWindow {
|
ModalWindow {
|
||||||
id: root
|
id: root
|
||||||
resizable: true
|
//resizable: true
|
||||||
width: 640
|
implicitWidth: 640
|
||||||
height: 480
|
implicitHeight: 480
|
||||||
|
|
||||||
|
HifiConstants { id: hifi }
|
||||||
|
|
||||||
Settings {
|
Settings {
|
||||||
category: "FileDialog"
|
category: "FileDialog"
|
||||||
|
@ -30,12 +43,14 @@ ModalWindow {
|
||||||
// Set from OffscreenUi::getOpenFile()
|
// Set from OffscreenUi::getOpenFile()
|
||||||
property alias caption: root.title;
|
property alias caption: root.title;
|
||||||
// Set from OffscreenUi::getOpenFile()
|
// Set from OffscreenUi::getOpenFile()
|
||||||
property alias dir: model.folder;
|
property alias dir: fileTableModel.folder;
|
||||||
// Set from OffscreenUi::getOpenFile()
|
// Set from OffscreenUi::getOpenFile()
|
||||||
property alias filter: selectionType.filtersString;
|
property alias filter: selectionType.filtersString;
|
||||||
// Set from OffscreenUi::getOpenFile()
|
// Set from OffscreenUi::getOpenFile()
|
||||||
property int options; // <-- FIXME unused
|
property int options; // <-- FIXME unused
|
||||||
|
|
||||||
|
property string iconText: text !== "" ? hifi.glyphs.scriptUpload : ""
|
||||||
|
property int iconSize: 40
|
||||||
|
|
||||||
property bool selectDirectory: false;
|
property bool selectDirectory: false;
|
||||||
property bool showHidden: false;
|
property bool showHidden: false;
|
||||||
|
@ -46,77 +61,120 @@ ModalWindow {
|
||||||
property alias model: fileTableView.model
|
property alias model: fileTableView.model
|
||||||
property var drives: helper.drives()
|
property var drives: helper.drives()
|
||||||
|
|
||||||
|
property int titleWidth: 0
|
||||||
|
|
||||||
signal selectedFile(var file);
|
signal selectedFile(var file);
|
||||||
signal canceled();
|
signal canceled();
|
||||||
|
|
||||||
Component.onCompleted: {
|
Component.onCompleted: {
|
||||||
console.log("Helper " + helper + " drives " + drives)
|
console.log("Helper " + helper + " drives " + drives)
|
||||||
drivesSelector.onCurrentTextChanged.connect(function(){
|
|
||||||
root.dir = helper.pathToUrl(drivesSelector.currentText);
|
// HACK: The following lines force the model to initialize properly such that the go-up button
|
||||||
})
|
// works properly from the initial screen.
|
||||||
|
var initialFolder = folderListModel.folder;
|
||||||
|
fileTableModel.folder = helper.pathToUrl(drives[0]);
|
||||||
|
fileTableModel.folder = initialFolder;
|
||||||
|
|
||||||
|
iconText = root.title !== "" ? hifi.glyphs.scriptUpload : "";
|
||||||
}
|
}
|
||||||
|
|
||||||
Rectangle {
|
Item {
|
||||||
anchors.fill: parent
|
clip: true
|
||||||
color: "white"
|
width: pane.width
|
||||||
|
height: pane.height
|
||||||
|
anchors.margins: 0
|
||||||
|
|
||||||
Row {
|
Row {
|
||||||
id: navControls
|
id: navControls
|
||||||
anchors { left: parent.left; top: parent.top; margins: 8 }
|
anchors {
|
||||||
spacing: 8
|
top: parent.top
|
||||||
// FIXME implement back button
|
topMargin: hifi.dimensions.contentMargin.y
|
||||||
//VrControls.ButtonAwesome {
|
left: parent.left
|
||||||
// id: backButton
|
}
|
||||||
// text: "\uf0a8"
|
spacing: hifi.dimensions.contentSpacing.x
|
||||||
// size: currentDirectory.height
|
|
||||||
// enabled: d.backStack.length != 0
|
GlyphButton {
|
||||||
// MouseArea { anchors.fill: parent; onClicked: d.navigateBack() }
|
|
||||||
//}
|
|
||||||
VrControls.ButtonAwesome {
|
|
||||||
id: upButton
|
id: upButton
|
||||||
enabled: model.parentFolder && model.parentFolder !== ""
|
glyph: hifi.glyphs.levelUp
|
||||||
text: "\uf0aa"
|
width: height
|
||||||
size: 32
|
size: 30
|
||||||
|
enabled: fileTableModel.parentFolder && fileTableModel.parentFolder !== ""
|
||||||
onClicked: d.navigateUp();
|
onClicked: d.navigateUp();
|
||||||
}
|
}
|
||||||
VrControls.ButtonAwesome {
|
|
||||||
|
GlyphButton {
|
||||||
id: homeButton
|
id: homeButton
|
||||||
property var destination: helper.home();
|
property var destination: helper.home();
|
||||||
|
glyph: hifi.glyphs.home
|
||||||
|
size: 28
|
||||||
|
width: height
|
||||||
enabled: d.homeDestination ? true : false
|
enabled: d.homeDestination ? true : false
|
||||||
text: "\uf015"
|
|
||||||
size: 32
|
|
||||||
onClicked: d.navigateHome();
|
onClicked: d.navigateHome();
|
||||||
}
|
}
|
||||||
|
|
||||||
VrControls.ComboBox {
|
|
||||||
id: drivesSelector
|
|
||||||
width: 48
|
|
||||||
height: homeButton.height
|
|
||||||
model: drives
|
|
||||||
visible: drives.length > 1
|
|
||||||
currentIndex: 0
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TextField {
|
ComboBox {
|
||||||
id: currentDirectory
|
id: pathSelector
|
||||||
height: homeButton.height
|
anchors {
|
||||||
style: TextFieldStyle { renderType: Text.QtRendering }
|
top: parent.top
|
||||||
anchors { left: navControls.right; right: parent.right; top: parent.top; margins: 8 }
|
topMargin: hifi.dimensions.contentMargin.y
|
||||||
property var lastValidFolder: helper.urlToPath(model.folder)
|
left: navControls.right
|
||||||
onLastValidFolderChanged: text = lastValidFolder;
|
leftMargin: hifi.dimensions.contentSpacing.x
|
||||||
verticalAlignment: Text.AlignVCenter
|
right: parent.right
|
||||||
font.pointSize: 14
|
}
|
||||||
font.bold: true
|
|
||||||
|
|
||||||
// FIXME add support auto-completion
|
property var lastValidFolder: helper.urlToPath(fileTableModel.folder)
|
||||||
onAccepted: {
|
|
||||||
if (!helper.validFolder(text)) {
|
function calculatePathChoices(folder) {
|
||||||
text = lastValidFolder;
|
var folders = folder.split("/"),
|
||||||
return
|
choices = [],
|
||||||
|
i, length;
|
||||||
|
|
||||||
|
if (folders[folders.length - 1] === "") {
|
||||||
|
folders.pop();
|
||||||
|
}
|
||||||
|
|
||||||
|
choices.push(folders[0]);
|
||||||
|
|
||||||
|
for (i = 1, length = folders.length; i < length; i++) {
|
||||||
|
choices.push(choices[i - 1] + "/" + folders[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (folders[0] === "") {
|
||||||
|
// Special handling for OSX root dir.
|
||||||
|
choices[0] = "/";
|
||||||
|
}
|
||||||
|
|
||||||
|
choices.reverse();
|
||||||
|
|
||||||
|
if (drives && drives.length > 1) {
|
||||||
|
choices.push("This PC");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (choices.length > 0) {
|
||||||
|
pathSelector.model = choices;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onLastValidFolderChanged: {
|
||||||
|
var folder = d.capitalizeDrive(lastValidFolder);
|
||||||
|
calculatePathChoices(folder);
|
||||||
|
}
|
||||||
|
|
||||||
|
onCurrentTextChanged: {
|
||||||
|
var folder = currentText;
|
||||||
|
|
||||||
|
if (/^[a-zA-z]:$/.test(folder)) {
|
||||||
|
folder = "file:///" + folder + "/";
|
||||||
|
} else if (folder === "This PC") {
|
||||||
|
folder = "file:///";
|
||||||
|
} else {
|
||||||
|
folder = helper.pathToUrl(folder);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (helper.urlToPath(folder).toLowerCase() !== helper.urlToPath(fileTableModel.folder).toLowerCase()) {
|
||||||
|
fileTableModel.folder = folder;
|
||||||
}
|
}
|
||||||
model.folder = helper.pathToUrl(text);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -127,67 +185,323 @@ ModalWindow {
|
||||||
property bool currentSelectionIsFolder;
|
property bool currentSelectionIsFolder;
|
||||||
property var backStack: []
|
property var backStack: []
|
||||||
property var tableViewConnection: Connections { target: fileTableView; onCurrentRowChanged: d.update(); }
|
property var tableViewConnection: Connections { target: fileTableView; onCurrentRowChanged: d.update(); }
|
||||||
property var modelConnection: Connections { target: model; onFolderChanged: d.update(); }
|
property var modelConnection: Connections { target: fileTableModel; onFolderChanged: d.update(); }
|
||||||
property var homeDestination: helper.home();
|
property var homeDestination: helper.home();
|
||||||
Component.onCompleted: update();
|
|
||||||
|
function capitalizeDrive(path) {
|
||||||
|
// Consistently capitalize drive letter for Windows.
|
||||||
|
if (/[a-zA-Z]:/.test(path)) {
|
||||||
|
return path.charAt(0).toUpperCase() + path.slice(1);
|
||||||
|
}
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
function update() {
|
function update() {
|
||||||
var row = fileTableView.currentRow;
|
var row = fileTableView.currentRow;
|
||||||
if (row === -1 && root.selectDirectory) {
|
|
||||||
currentSelectionUrl = fileTableView.model.folder;
|
if (row === -1) {
|
||||||
currentSelectionIsFolder = true;
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
currentSelectionUrl = fileTableView.model.get(row, "fileURL");
|
currentSelectionUrl = helper.pathToUrl(fileTableView.model.get(row).filePath);
|
||||||
currentSelectionIsFolder = fileTableView.model.isFolder(row);
|
currentSelectionIsFolder = fileTableView.model.isFolder(row);
|
||||||
if (root.selectDirectory || !currentSelectionIsFolder) {
|
if (root.selectDirectory || !currentSelectionIsFolder) {
|
||||||
currentSelection.text = helper.urlToPath(currentSelectionUrl);
|
currentSelection.text = capitalizeDrive(helper.urlToPath(currentSelectionUrl));
|
||||||
} else {
|
} else {
|
||||||
currentSelection.text = ""
|
currentSelection.text = "";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function navigateUp() {
|
function navigateUp() {
|
||||||
if (model.parentFolder && model.parentFolder !== "") {
|
if (fileTableModel.parentFolder && fileTableModel.parentFolder !== "") {
|
||||||
model.folder = model.parentFolder
|
fileTableModel.folder = fileTableModel.parentFolder;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function navigateHome() {
|
function navigateHome() {
|
||||||
model.folder = homeDestination;
|
fileTableModel.folder = homeDestination;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
FileTableView {
|
FolderListModel {
|
||||||
|
id: folderListModel
|
||||||
|
nameFilters: selectionType.currentFilter
|
||||||
|
showDirsFirst: true
|
||||||
|
showDotAndDotDot: false
|
||||||
|
showFiles: !root.selectDirectory
|
||||||
|
Component.onCompleted: {
|
||||||
|
showFiles = !root.selectDirectory
|
||||||
|
}
|
||||||
|
|
||||||
|
onFolderChanged: {
|
||||||
|
fileTableModel.update(); // Update once the data from the folder change is available.
|
||||||
|
}
|
||||||
|
|
||||||
|
function getItem(index, field) {
|
||||||
|
return get(index, field);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ListModel {
|
||||||
|
// Emulates FolderListModel but contains drive data.
|
||||||
|
id: driveListModel
|
||||||
|
|
||||||
|
property int count: 1
|
||||||
|
|
||||||
|
Component.onCompleted: initialize();
|
||||||
|
|
||||||
|
function initialize() {
|
||||||
|
var drive,
|
||||||
|
i;
|
||||||
|
|
||||||
|
count = drives.length;
|
||||||
|
|
||||||
|
for (i = 0; i < count; i++) {
|
||||||
|
drive = drives[i].slice(0, -1); // Remove trailing "/".
|
||||||
|
append({
|
||||||
|
fileName: drive,
|
||||||
|
fileModified: new Date(0),
|
||||||
|
fileSize: 0,
|
||||||
|
filePath: drive + "/",
|
||||||
|
fileIsDir: true,
|
||||||
|
fileNameSort: drive.toLowerCase()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getItem(index, field) {
|
||||||
|
return get(index)[field];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ListModel {
|
||||||
|
id: fileTableModel
|
||||||
|
|
||||||
|
// FolderListModel has a couple of problems:
|
||||||
|
// 1) Files and directories sort case-sensitively: https://bugreports.qt.io/browse/QTBUG-48757
|
||||||
|
// 2) Cannot browse up to the "computer" level to view Windows drives: https://bugreports.qt.io/browse/QTBUG-42901
|
||||||
|
//
|
||||||
|
// To solve these problems an intermediary ListModel is used that implements proper sorting and can be populated with
|
||||||
|
// drive information when viewing at the computer level.
|
||||||
|
|
||||||
|
property var folder
|
||||||
|
property int sortOrder: Qt.AscendingOrder
|
||||||
|
property int sortColumn: 0
|
||||||
|
property var model: folderListModel
|
||||||
|
property string parentFolder: calculateParentFolder();
|
||||||
|
|
||||||
|
readonly property string rootFolder: "file:///"
|
||||||
|
|
||||||
|
function calculateParentFolder() {
|
||||||
|
if (model === folderListModel) {
|
||||||
|
if (folderListModel.parentFolder.toString() === "" && driveListModel.count > 1) {
|
||||||
|
return rootFolder;
|
||||||
|
} else {
|
||||||
|
return folderListModel.parentFolder;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onFolderChanged: {
|
||||||
|
if (folder === rootFolder) {
|
||||||
|
model = driveListModel;
|
||||||
|
update();
|
||||||
|
} else {
|
||||||
|
var needsUpdate = model === driveListModel && folder === folderListModel.folder;
|
||||||
|
|
||||||
|
model = folderListModel;
|
||||||
|
folderListModel.folder = folder;
|
||||||
|
|
||||||
|
if (needsUpdate) {
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function isFolder(row) {
|
||||||
|
if (row === -1) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return get(row).fileIsDir;
|
||||||
|
}
|
||||||
|
|
||||||
|
function update() {
|
||||||
|
var dataFields = ["fileName", "fileModified", "fileSize"],
|
||||||
|
sortFields = ["fileNameSort", "fileModified", "fileSize"],
|
||||||
|
dataField = dataFields[sortColumn],
|
||||||
|
sortField = sortFields[sortColumn],
|
||||||
|
sortValue,
|
||||||
|
fileName,
|
||||||
|
fileIsDir,
|
||||||
|
comparisonFunction,
|
||||||
|
lower,
|
||||||
|
middle,
|
||||||
|
upper,
|
||||||
|
rows = 0,
|
||||||
|
i;
|
||||||
|
|
||||||
|
clear();
|
||||||
|
|
||||||
|
comparisonFunction = sortOrder === Qt.AscendingOrder
|
||||||
|
? function(a, b) { return a < b; }
|
||||||
|
: function(a, b) { return a > b; }
|
||||||
|
|
||||||
|
for (i = 0; i < model.count; i++) {
|
||||||
|
fileName = model.getItem(i, "fileName");
|
||||||
|
fileIsDir = model.getItem(i, "fileIsDir");
|
||||||
|
|
||||||
|
sortValue = model.getItem(i, dataField);
|
||||||
|
if (dataField === "fileName") {
|
||||||
|
// Directories first by prefixing a "*".
|
||||||
|
// Case-insensitive.
|
||||||
|
sortValue = (fileIsDir ? "*" : "") + sortValue.toLowerCase();
|
||||||
|
}
|
||||||
|
|
||||||
|
lower = 0;
|
||||||
|
upper = rows;
|
||||||
|
while (lower < upper) {
|
||||||
|
middle = Math.floor((lower + upper) / 2);
|
||||||
|
var lessThan;
|
||||||
|
if (comparisonFunction(sortValue, get(middle)[sortField])) {
|
||||||
|
lessThan = true;
|
||||||
|
upper = middle;
|
||||||
|
} else {
|
||||||
|
lessThan = false;
|
||||||
|
lower = middle + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
insert(lower, {
|
||||||
|
fileName: fileName,
|
||||||
|
fileModified: (fileIsDir ? new Date(0) : model.getItem(i, "fileModified")),
|
||||||
|
fileSize: model.getItem(i, "fileSize"),
|
||||||
|
filePath: model.getItem(i, "filePath"),
|
||||||
|
fileIsDir: fileIsDir,
|
||||||
|
fileNameSort: (fileIsDir ? "*" : "") + fileName.toLowerCase()
|
||||||
|
});
|
||||||
|
|
||||||
|
rows++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Table {
|
||||||
id: fileTableView
|
id: fileTableView
|
||||||
anchors { left: parent.left; right: parent.right; top: currentDirectory.bottom; bottom: currentSelection.top; margins: 8 }
|
colorScheme: hifi.colorSchemes.light
|
||||||
|
anchors {
|
||||||
|
top: navControls.bottom
|
||||||
|
topMargin: hifi.dimensions.contentSpacing.y
|
||||||
|
left: parent.left
|
||||||
|
right: parent.right
|
||||||
|
bottom: currentSelection.top
|
||||||
|
bottomMargin: hifi.dimensions.contentSpacing.y + currentSelection.controlHeight - currentSelection.height
|
||||||
|
}
|
||||||
|
headerVisible: !selectDirectory
|
||||||
onDoubleClicked: navigateToRow(row);
|
onDoubleClicked: navigateToRow(row);
|
||||||
focus: true
|
focus: true
|
||||||
Keys.onReturnPressed: navigateToCurrentRow();
|
Keys.onReturnPressed: navigateToCurrentRow();
|
||||||
Keys.onEnterPressed: navigateToCurrentRow();
|
Keys.onEnterPressed: navigateToCurrentRow();
|
||||||
model: FolderListModel {
|
|
||||||
id: model
|
sortIndicatorColumn: 0
|
||||||
nameFilters: selectionType.currentFilter
|
sortIndicatorOrder: Qt.AscendingOrder
|
||||||
showDirsFirst: true
|
sortIndicatorVisible: true
|
||||||
showDotAndDotDot: false
|
|
||||||
showFiles: !root.selectDirectory
|
model: fileTableModel
|
||||||
// For some reason, declaring these bindings directly in the targets doesn't
|
|
||||||
// work for setting the initial state
|
function updateSort() {
|
||||||
Component.onCompleted: {
|
model.sortOrder = sortIndicatorOrder;
|
||||||
currentDirectory.lastValidFolder = Qt.binding(function() { return helper.urlToPath(model.folder); });
|
model.sortColumn = sortIndicatorColumn;
|
||||||
upButton.enabled = Qt.binding(function() { return (model.parentFolder && model.parentFolder != "") ? true : false; });
|
model.update();
|
||||||
showFiles = !root.selectDirectory
|
}
|
||||||
|
|
||||||
|
onSortIndicatorColumnChanged: { updateSort(); }
|
||||||
|
|
||||||
|
onSortIndicatorOrderChanged: { updateSort(); }
|
||||||
|
|
||||||
|
onActiveFocusChanged: {
|
||||||
|
if (activeFocus && currentRow == -1) {
|
||||||
|
fileTableView.selection.select(0)
|
||||||
}
|
}
|
||||||
onFolderChanged: {
|
}
|
||||||
fileTableView.selection.clear();
|
|
||||||
fileTableView.selection.select(0);
|
itemDelegate: Item {
|
||||||
fileTableView.currentRow = 0;
|
clip: true
|
||||||
|
|
||||||
|
FontLoader { id: firaSansSemiBold; source: "../../fonts/FiraSans-SemiBold.ttf"; }
|
||||||
|
FontLoader { id: firaSansRegular; source: "../../fonts/FiraSans-Regular.ttf"; }
|
||||||
|
|
||||||
|
FiraSansSemiBold {
|
||||||
|
text: getText();
|
||||||
|
elide: styleData.elideMode
|
||||||
|
anchors {
|
||||||
|
left: parent.left
|
||||||
|
leftMargin: hifi.dimensions.tablePadding
|
||||||
|
right: parent.right
|
||||||
|
rightMargin: hifi.dimensions.tablePadding
|
||||||
|
verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
|
size: hifi.fontSizes.tableText
|
||||||
|
color: hifi.colors.baseGrayHighlight
|
||||||
|
font.family: (styleData.row !== -1 && fileTableView.model.get(styleData.row).fileIsDir)
|
||||||
|
? firaSansSemiBold.name : firaSansRegular.name
|
||||||
|
|
||||||
|
function getText() {
|
||||||
|
if (styleData.row === -1) {
|
||||||
|
return styleData.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (styleData.column) {
|
||||||
|
case 1: return fileTableView.model.get(styleData.row).fileIsDir ? "" : styleData.value;
|
||||||
|
case 2: return fileTableView.model.get(styleData.row).fileIsDir ? "" : formatSize(styleData.value);
|
||||||
|
default: return styleData.value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function formatSize(size) {
|
||||||
|
var suffixes = [ "bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB" ];
|
||||||
|
var suffixIndex = 0
|
||||||
|
while ((size / 1024.0) > 1.1) {
|
||||||
|
size /= 1024.0;
|
||||||
|
++suffixIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
size = Math.round(size*1000)/1000;
|
||||||
|
size = size.toLocaleString()
|
||||||
|
|
||||||
|
return size + " " + suffixes[suffixIndex];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TableViewColumn {
|
||||||
|
id: fileNameColumn
|
||||||
|
role: "fileName"
|
||||||
|
title: "Name"
|
||||||
|
width: (selectDirectory ? 1.0 : 0.5) * fileTableView.width
|
||||||
|
movable: false
|
||||||
|
resizable: true
|
||||||
|
}
|
||||||
|
TableViewColumn {
|
||||||
|
id: fileMofifiedColumn
|
||||||
|
role: "fileModified"
|
||||||
|
title: "Date"
|
||||||
|
width: 0.3 * fileTableView.width
|
||||||
|
movable: false
|
||||||
|
resizable: true
|
||||||
|
visible: !selectDirectory
|
||||||
|
}
|
||||||
|
TableViewColumn {
|
||||||
|
role: "fileSize"
|
||||||
|
title: "Size"
|
||||||
|
width: fileTableView.width - fileNameColumn.width - fileMofifiedColumn.width
|
||||||
|
movable: false
|
||||||
|
resizable: true
|
||||||
|
visible: !selectDirectory
|
||||||
|
}
|
||||||
|
|
||||||
function navigateToRow(row) {
|
function navigateToRow(row) {
|
||||||
currentRow = row;
|
currentRow = row;
|
||||||
navigateToCurrentRow();
|
navigateToCurrentRow();
|
||||||
|
@ -196,9 +510,9 @@ ModalWindow {
|
||||||
function navigateToCurrentRow() {
|
function navigateToCurrentRow() {
|
||||||
var row = fileTableView.currentRow
|
var row = fileTableView.currentRow
|
||||||
var isFolder = model.isFolder(row);
|
var isFolder = model.isFolder(row);
|
||||||
var file = model.get(row, "fileURL");
|
var file = model.get(row).filePath;
|
||||||
if (isFolder) {
|
if (isFolder) {
|
||||||
fileTableView.model.folder = file
|
fileTableView.model.folder = helper.pathToUrl(file);
|
||||||
} else {
|
} else {
|
||||||
okAction.trigger();
|
okAction.trigger();
|
||||||
}
|
}
|
||||||
|
@ -213,7 +527,7 @@ ModalWindow {
|
||||||
var newPrefix = prefix + event.text.toLowerCase();
|
var newPrefix = prefix + event.text.toLowerCase();
|
||||||
var matchedIndex = -1;
|
var matchedIndex = -1;
|
||||||
for (var i = 0; i < model.count; ++i) {
|
for (var i = 0; i < model.count; ++i) {
|
||||||
var name = model.get(i, "fileName").toLowerCase();
|
var name = model.get(i).fileName.toLowerCase();
|
||||||
if (0 === name.indexOf(newPrefix)) {
|
if (0 === name.indexOf(newPrefix)) {
|
||||||
matchedIndex = i;
|
matchedIndex = i;
|
||||||
break;
|
break;
|
||||||
|
@ -254,14 +568,19 @@ ModalWindow {
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TextField {
|
TextField {
|
||||||
id: currentSelection
|
id: currentSelection
|
||||||
style: TextFieldStyle { renderType: Text.QtRendering }
|
label: "Path:"
|
||||||
anchors { right: root.selectDirectory ? parent.right : selectionType.left; rightMargin: 8; left: parent.left; leftMargin: 8; top: selectionType.top }
|
anchors {
|
||||||
|
left: parent.left
|
||||||
|
right: selectionType.visible ? selectionType.left: parent.right
|
||||||
|
rightMargin: selectionType.visible ? hifi.dimensions.contentSpacing.x : 0
|
||||||
|
bottom: buttonRow.top
|
||||||
|
bottomMargin: hifi.dimensions.contentSpacing.y
|
||||||
|
}
|
||||||
readOnly: !root.saveDialog
|
readOnly: !root.saveDialog
|
||||||
activeFocusOnTab: !readOnly
|
activeFocusOnTab: !readOnly
|
||||||
onActiveFocusChanged: if (activeFocus) { selectAll(); }
|
onActiveFocusChanged: if (activeFocus) { selectAll(); }
|
||||||
|
@ -270,27 +589,34 @@ ModalWindow {
|
||||||
|
|
||||||
FileTypeSelection {
|
FileTypeSelection {
|
||||||
id: selectionType
|
id: selectionType
|
||||||
anchors { bottom: buttonRow.top; bottomMargin: 8; right: parent.right; rightMargin: 8; left: buttonRow.left }
|
anchors {
|
||||||
visible: !selectDirectory
|
top: currentSelection.top
|
||||||
|
left: buttonRow.left
|
||||||
|
right: parent.right
|
||||||
|
}
|
||||||
|
visible: !selectDirectory && filtersCount > 1
|
||||||
KeyNavigation.left: fileTableView
|
KeyNavigation.left: fileTableView
|
||||||
KeyNavigation.right: openButton
|
KeyNavigation.right: openButton
|
||||||
}
|
}
|
||||||
|
|
||||||
Row {
|
Row {
|
||||||
id: buttonRow
|
id: buttonRow
|
||||||
anchors.right: parent.right
|
anchors {
|
||||||
anchors.rightMargin: 8
|
right: parent.right
|
||||||
anchors.bottom: parent.bottom
|
bottom: parent.bottom
|
||||||
anchors.bottomMargin: 8
|
}
|
||||||
spacing: 8
|
spacing: hifi.dimensions.contentSpacing.y
|
||||||
|
|
||||||
Button {
|
Button {
|
||||||
id: openButton
|
id: openButton
|
||||||
|
color: hifi.buttons.blue
|
||||||
action: okAction
|
action: okAction
|
||||||
Keys.onReturnPressed: okAction.trigger()
|
Keys.onReturnPressed: okAction.trigger()
|
||||||
KeyNavigation.up: selectionType
|
KeyNavigation.up: selectionType
|
||||||
KeyNavigation.left: selectionType
|
KeyNavigation.left: selectionType
|
||||||
KeyNavigation.right: cancelButton
|
KeyNavigation.right: cancelButton
|
||||||
}
|
}
|
||||||
|
|
||||||
Button {
|
Button {
|
||||||
id: cancelButton
|
id: cancelButton
|
||||||
action: cancelAction
|
action: cancelAction
|
||||||
|
@ -385,5 +711,3 @@ ModalWindow {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,64 +0,0 @@
|
||||||
import QtQuick 2.0
|
|
||||||
import QtQuick.Controls 1.4
|
|
||||||
|
|
||||||
TableView {
|
|
||||||
id: root
|
|
||||||
onActiveFocusChanged: {
|
|
||||||
if (activeFocus && currentRow == -1) {
|
|
||||||
root.selection.select(0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
itemDelegate: Component {
|
|
||||||
Item {
|
|
||||||
clip: true
|
|
||||||
Text {
|
|
||||||
x: 3
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
color: styleData.textColor
|
|
||||||
elide: styleData.elideMode
|
|
||||||
text: getText();
|
|
||||||
font.italic: root.model.get(styleData.row, "fileIsDir") ? true : false
|
|
||||||
|
|
||||||
function getText() {
|
|
||||||
switch (styleData.column) {
|
|
||||||
//case 1: return Date.fromLocaleString(locale, styleData.value, "yyyy-MM-dd hh:mm:ss");
|
|
||||||
case 2: return root.model.get(styleData.row, "fileIsDir") ? "" : formatSize(styleData.value);
|
|
||||||
default: return styleData.value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
function formatSize(size) {
|
|
||||||
var suffixes = [ "bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB" ];
|
|
||||||
var suffixIndex = 0
|
|
||||||
while ((size / 1024.0) > 1.1) {
|
|
||||||
size /= 1024.0;
|
|
||||||
++suffixIndex;
|
|
||||||
}
|
|
||||||
|
|
||||||
size = Math.round(size*1000)/1000;
|
|
||||||
size = size.toLocaleString()
|
|
||||||
|
|
||||||
return size + " " + suffixes[suffixIndex];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
TableViewColumn {
|
|
||||||
role: "fileName"
|
|
||||||
title: "Name"
|
|
||||||
width: 400
|
|
||||||
}
|
|
||||||
TableViewColumn {
|
|
||||||
role: "fileModified"
|
|
||||||
title: "Date Modified"
|
|
||||||
width: 200
|
|
||||||
}
|
|
||||||
TableViewColumn {
|
|
||||||
role: "fileSize"
|
|
||||||
title: "Size"
|
|
||||||
width: 200
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
|
@ -1,11 +1,22 @@
|
||||||
|
//
|
||||||
|
// FileTypeSelection.qml
|
||||||
|
//
|
||||||
|
// Created by Bradley Austin Davis on 29 Jan 2016
|
||||||
|
// Copyright 2015 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
|
||||||
import QtQuick 2.5
|
import QtQuick 2.5
|
||||||
|
|
||||||
import "../../controls" as VrControls
|
import "../../controls-uit"
|
||||||
|
|
||||||
VrControls.ComboBox {
|
ComboBox {
|
||||||
id: root
|
id: root
|
||||||
property string filtersString: "All Files (*.*)";
|
property string filtersString: "All Files (*.*)";
|
||||||
property var currentFilter: [ "*.*" ];
|
property var currentFilter: [ "*.*" ];
|
||||||
|
property int filtersCount: filtersString.split(';;').length
|
||||||
|
|
||||||
// Per http://doc.qt.io/qt-5/qfiledialog.html#getOpenFileName the string can contain
|
// Per http://doc.qt.io/qt-5/qfiledialog.html#getOpenFileName the string can contain
|
||||||
// multiple filters separated by semicolons
|
// multiple filters separated by semicolons
|
||||||
|
|
|
@ -118,11 +118,89 @@ Window {
|
||||||
}
|
}
|
||||||
|
|
||||||
HifiControls.Table {
|
HifiControls.Table {
|
||||||
tableModel: runningScriptsModel
|
model: runningScriptsModel
|
||||||
|
id: table
|
||||||
height: 185
|
height: 185
|
||||||
colorScheme: hifi.colorSchemes.dark
|
colorScheme: hifi.colorSchemes.dark
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
|
expandSelectedRow: true
|
||||||
|
|
||||||
|
itemDelegate: Item {
|
||||||
|
anchors {
|
||||||
|
left: parent ? parent.left : undefined
|
||||||
|
leftMargin: hifi.dimensions.tablePadding
|
||||||
|
right: parent ? parent.right : undefined
|
||||||
|
rightMargin: hifi.dimensions.tablePadding
|
||||||
|
}
|
||||||
|
|
||||||
|
FiraSansSemiBold {
|
||||||
|
id: textItem
|
||||||
|
text: styleData.value
|
||||||
|
size: hifi.fontSizes.tableText
|
||||||
|
color: table.colorScheme == hifi.colorSchemes.light
|
||||||
|
? (styleData.selected ? hifi.colors.black : hifi.colors.baseGrayHighlight)
|
||||||
|
: (styleData.selected ? hifi.colors.black : hifi.colors.lightGrayText)
|
||||||
|
anchors {
|
||||||
|
left: parent.left
|
||||||
|
right: parent.right
|
||||||
|
top: parent.top
|
||||||
|
topMargin: 3
|
||||||
|
}
|
||||||
|
|
||||||
|
HiFiGlyphs {
|
||||||
|
id: reloadButton
|
||||||
|
text: hifi.glyphs.reloadSmall
|
||||||
|
color: reloadButtonArea.pressed ? hifi.colors.white : parent.color
|
||||||
|
anchors {
|
||||||
|
top: parent.top
|
||||||
|
right: stopButton.left
|
||||||
|
verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
|
MouseArea {
|
||||||
|
id: reloadButtonArea
|
||||||
|
anchors { fill: parent; margins: -2 }
|
||||||
|
onClicked: reloadScript(model.url)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
HiFiGlyphs {
|
||||||
|
id: stopButton
|
||||||
|
text: hifi.glyphs.closeSmall
|
||||||
|
color: stopButtonArea.pressed ? hifi.colors.white : parent.color
|
||||||
|
anchors {
|
||||||
|
top: parent.top
|
||||||
|
right: parent.right
|
||||||
|
verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
|
MouseArea {
|
||||||
|
id: stopButtonArea
|
||||||
|
anchors { fill: parent; margins: -2 }
|
||||||
|
onClicked: stopScript(model.url)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
FiraSansSemiBold {
|
||||||
|
text: runningScriptsModel.get(styleData.row) ? runningScriptsModel.get(styleData.row).url : ""
|
||||||
|
elide: Text.ElideMiddle
|
||||||
|
size: hifi.fontSizes.tableText
|
||||||
|
color: table.colorScheme == hifi.colorSchemes.light
|
||||||
|
? (styleData.selected ? hifi.colors.black : hifi.colors.lightGray)
|
||||||
|
: (styleData.selected ? hifi.colors.black : hifi.colors.lightGrayText)
|
||||||
|
anchors {
|
||||||
|
top: textItem.bottom
|
||||||
|
left: parent.left
|
||||||
|
right: parent.right
|
||||||
|
}
|
||||||
|
visible: styleData.selected
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TableViewColumn {
|
||||||
|
role: "name"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
HifiControls.VerticalSpacer {
|
HifiControls.VerticalSpacer {
|
||||||
|
|
23
interface/resources/qml/styles-uit/FiraSansRegular.qml
Normal file
23
interface/resources/qml/styles-uit/FiraSansRegular.qml
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
//
|
||||||
|
// FiraSansRegular.qml
|
||||||
|
//
|
||||||
|
// Created by David Rowe on 12 May 2016
|
||||||
|
// Copyright 2016 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
|
||||||
|
import QtQuick 2.5
|
||||||
|
import QtQuick.Controls 1.4
|
||||||
|
import QtQuick.Controls.Styles 1.4
|
||||||
|
|
||||||
|
Text {
|
||||||
|
id: root
|
||||||
|
FontLoader { id: firaSansRegular; source: "../../fonts/FiraSans-Regular.ttf"; }
|
||||||
|
property real size: 32
|
||||||
|
font.pixelSize: size
|
||||||
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
horizontalAlignment: Text.AlignLeft
|
||||||
|
font.family: firaSansRegular.name
|
||||||
|
}
|
|
@ -89,12 +89,16 @@ Item {
|
||||||
readonly property color transparent: "#00ffffff"
|
readonly property color transparent: "#00ffffff"
|
||||||
|
|
||||||
// Control specific colors
|
// Control specific colors
|
||||||
readonly property color tableRowLightOdd: white50
|
readonly property color tableRowLightOdd: "#eaeaea" // Equivalent to white50 over #e3e3e3 background
|
||||||
readonly property color tableRowLightEven: "#1a575757"
|
readonly property color tableRowLightEven: "#c6c6c6" // Equivavlent to "#1a575757" over #e3e3e3 background
|
||||||
readonly property color tableRowDarkOdd: "#80393939"
|
readonly property color tableRowDarkOdd: "#2e2e2e" // Equivalent to "#80393939" over #404040 background
|
||||||
readonly property color tableRowDarkEven: "#a6181818"
|
readonly property color tableRowDarkEven: "#1c1c1c" // Equivalent to "#a6181818" over #404040 background
|
||||||
readonly property color tableScrollHandle: "#707070"
|
readonly property color tableBackgroundLight: tableRowLightEven
|
||||||
readonly property color tableScrollBackground: "#323232"
|
readonly property color tableBackgroundDark: tableRowDarkEven
|
||||||
|
readonly property color tableScrollHandleLight: tableRowLightOdd
|
||||||
|
readonly property color tableScrollHandleDark: "#707070"
|
||||||
|
readonly property color tableScrollBackgroundLight: tableRowLightEven
|
||||||
|
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"
|
||||||
readonly property color checkboxDarkStart: "#7d7d7d"
|
readonly property color checkboxDarkStart: "#7d7d7d"
|
||||||
|
@ -137,10 +141,10 @@ Item {
|
||||||
readonly property real textPadding: 8
|
readonly property real textPadding: 8
|
||||||
readonly property real sliderHandleSize: 18
|
readonly property real sliderHandleSize: 18
|
||||||
readonly property real sliderGrooveHeight: 8
|
readonly property real sliderGrooveHeight: 8
|
||||||
readonly property real spinnerSize: 42
|
readonly property real spinnerSize: 50
|
||||||
readonly property real tablePadding: 12
|
readonly property real tablePadding: 12
|
||||||
readonly property real tableRowHeight: largeScreen ? 26 : 23
|
readonly property real tableRowHeight: largeScreen ? 26 : 23
|
||||||
readonly property real tableHeaderHeight: 40
|
readonly property real tableHeaderHeight: 29
|
||||||
readonly property vector2d modalDialogMargin: Qt.vector2d(50, 30)
|
readonly property vector2d modalDialogMargin: Qt.vector2d(50, 30)
|
||||||
readonly property real modalDialogTitleHeight: 40
|
readonly property real modalDialogTitleHeight: 40
|
||||||
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
|
||||||
|
@ -157,6 +161,8 @@ Item {
|
||||||
readonly property real textFieldInput: dimensions.largeScreen ? 15 : 12
|
readonly property real textFieldInput: dimensions.largeScreen ? 15 : 12
|
||||||
readonly property real textFieldInputLabel: dimensions.largeScreen ? 13 : 9
|
readonly property real textFieldInputLabel: dimensions.largeScreen ? 13 : 9
|
||||||
readonly property real textFieldSearchIcon: dimensions.largeScreen ? 30 : 24
|
readonly property real textFieldSearchIcon: dimensions.largeScreen ? 30 : 24
|
||||||
|
readonly property real tableHeading: dimensions.largeScreen ? 12 : 10
|
||||||
|
readonly property real tableHeadingIcon: dimensions.largeScreen ? 40 : 33
|
||||||
readonly property real tableText: dimensions.largeScreen ? 15 : 12
|
readonly property real tableText: dimensions.largeScreen ? 15 : 12
|
||||||
readonly property real buttonLabel: dimensions.largeScreen ? 13 : 9
|
readonly property real buttonLabel: dimensions.largeScreen ? 13 : 9
|
||||||
readonly property real iconButton: dimensions.largeScreen ? 13 : 9
|
readonly property real iconButton: dimensions.largeScreen ? 13 : 9
|
||||||
|
|
|
@ -174,7 +174,6 @@ static const QString FBX_EXTENSION = ".fbx";
|
||||||
static const QString OBJ_EXTENSION = ".obj";
|
static const QString OBJ_EXTENSION = ".obj";
|
||||||
static const QString AVA_JSON_EXTENSION = ".ava.json";
|
static const QString AVA_JSON_EXTENSION = ".ava.json";
|
||||||
|
|
||||||
static const int MSECS_PER_SEC = 1000;
|
|
||||||
static const int MIRROR_VIEW_TOP_PADDING = 5;
|
static const int MIRROR_VIEW_TOP_PADDING = 5;
|
||||||
static const int MIRROR_VIEW_LEFT_PADDING = 10;
|
static const int MIRROR_VIEW_LEFT_PADDING = 10;
|
||||||
static const int MIRROR_VIEW_WIDTH = 265;
|
static const int MIRROR_VIEW_WIDTH = 265;
|
||||||
|
@ -633,7 +632,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) :
|
||||||
connect(&domainHandler, SIGNAL(disconnectedFromDomain()), SLOT(clearDomainOctreeDetails()));
|
connect(&domainHandler, SIGNAL(disconnectedFromDomain()), SLOT(clearDomainOctreeDetails()));
|
||||||
|
|
||||||
// update our location every 5 seconds in the metaverse server, assuming that we are authenticated with one
|
// update our location every 5 seconds in the metaverse server, assuming that we are authenticated with one
|
||||||
const qint64 DATA_SERVER_LOCATION_CHANGE_UPDATE_MSECS = 5 * MSECS_PER_SEC;
|
const qint64 DATA_SERVER_LOCATION_CHANGE_UPDATE_MSECS = 5 * MSECS_PER_SECOND;
|
||||||
|
|
||||||
auto discoverabilityManager = DependencyManager::get<DiscoverabilityManager>();
|
auto discoverabilityManager = DependencyManager::get<DiscoverabilityManager>();
|
||||||
connect(&locationUpdateTimer, &QTimer::timeout, discoverabilityManager.data(), &DiscoverabilityManager::updateLocation);
|
connect(&locationUpdateTimer, &QTimer::timeout, discoverabilityManager.data(), &DiscoverabilityManager::updateLocation);
|
||||||
|
@ -1828,9 +1827,9 @@ bool Application::event(QEvent* event) {
|
||||||
|
|
||||||
// Presentation/painting logic
|
// Presentation/painting logic
|
||||||
// TODO: Decouple presentation and painting loops
|
// TODO: Decouple presentation and painting loops
|
||||||
static bool isPainting = false;
|
static bool isPaintingThrottled = false;
|
||||||
if ((int)event->type() == (int)Present) {
|
if ((int)event->type() == (int)Present) {
|
||||||
if (isPainting) {
|
if (isPaintingThrottled) {
|
||||||
// If painting (triggered by presentation) is hogging the main thread,
|
// If painting (triggered by presentation) is hogging the main thread,
|
||||||
// repost as low priority to avoid hanging the GUI.
|
// repost as low priority to avoid hanging the GUI.
|
||||||
// This has the effect of allowing presentation to exceed the paint budget by X times and
|
// This has the effect of allowing presentation to exceed the paint budget by X times and
|
||||||
|
@ -1838,14 +1837,17 @@ bool Application::event(QEvent* event) {
|
||||||
// (e.g. at a 60FPS target, painting for 17us would fall to 58.82FPS instead of 30FPS).
|
// (e.g. at a 60FPS target, painting for 17us would fall to 58.82FPS instead of 30FPS).
|
||||||
removePostedEvents(this, Present);
|
removePostedEvents(this, Present);
|
||||||
postEvent(this, new QEvent(static_cast<QEvent::Type>(Present)), Qt::LowEventPriority);
|
postEvent(this, new QEvent(static_cast<QEvent::Type>(Present)), Qt::LowEventPriority);
|
||||||
isPainting = false;
|
isPaintingThrottled = false;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
idle();
|
float nsecsElapsed = (float)_lastTimeUpdated.nsecsElapsed();
|
||||||
|
if (shouldPaint(nsecsElapsed)) {
|
||||||
postEvent(this, new QEvent(static_cast<QEvent::Type>(Paint)), Qt::HighEventPriority);
|
_lastTimeUpdated.start();
|
||||||
isPainting = true;
|
idle(nsecsElapsed);
|
||||||
|
postEvent(this, new QEvent(static_cast<QEvent::Type>(Paint)), Qt::HighEventPriority);
|
||||||
|
}
|
||||||
|
isPaintingThrottled = true;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
} else if ((int)event->type() == (int)Paint) {
|
} else if ((int)event->type() == (int)Paint) {
|
||||||
|
@ -1855,7 +1857,7 @@ bool Application::event(QEvent* event) {
|
||||||
|
|
||||||
paintGL();
|
paintGL();
|
||||||
|
|
||||||
isPainting = false;
|
isPaintingThrottled = false;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -2639,10 +2641,9 @@ bool Application::acceptSnapshot(const QString& urlString) {
|
||||||
|
|
||||||
static uint32_t _renderedFrameIndex { INVALID_FRAME };
|
static uint32_t _renderedFrameIndex { INVALID_FRAME };
|
||||||
|
|
||||||
void Application::idle() {
|
bool Application::shouldPaint(float nsecsElapsed) {
|
||||||
// idle is called on a queued connection, so make sure we should be here.
|
if (_aboutToQuit) {
|
||||||
if (_inPaint || _aboutToQuit) {
|
return false;
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
auto displayPlugin = getActiveDisplayPlugin();
|
auto displayPlugin = getActiveDisplayPlugin();
|
||||||
|
@ -2661,16 +2662,21 @@ void Application::idle() {
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
float msecondsSinceLastUpdate = (float)_lastTimeUpdated.nsecsElapsed() / NSECS_PER_USEC / USECS_PER_MSEC;
|
float msecondsSinceLastUpdate = nsecsElapsed / NSECS_PER_USEC / USECS_PER_MSEC;
|
||||||
|
|
||||||
// Throttle if requested
|
// Throttle if requested
|
||||||
if (displayPlugin->isThrottled() && (msecondsSinceLastUpdate < THROTTLED_SIM_FRAME_PERIOD_MS)) {
|
if (displayPlugin->isThrottled() && (msecondsSinceLastUpdate < THROTTLED_SIM_FRAME_PERIOD_MS)) {
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sync up the _renderedFrameIndex
|
// Sync up the _renderedFrameIndex
|
||||||
_renderedFrameIndex = displayPlugin->presentCount();
|
_renderedFrameIndex = displayPlugin->presentCount();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Application::idle(float nsecsElapsed) {
|
||||||
|
|
||||||
// Update the deadlock watchdog
|
// Update the deadlock watchdog
|
||||||
updateHeartbeat();
|
updateHeartbeat();
|
||||||
|
|
||||||
|
@ -2687,7 +2693,7 @@ void Application::idle() {
|
||||||
|
|
||||||
PROFILE_RANGE(__FUNCTION__);
|
PROFILE_RANGE(__FUNCTION__);
|
||||||
|
|
||||||
float secondsSinceLastUpdate = msecondsSinceLastUpdate / MSECS_PER_SECOND;
|
float secondsSinceLastUpdate = nsecsElapsed / NSECS_PER_MSEC / MSECS_PER_SECOND;
|
||||||
|
|
||||||
// If the offscreen Ui has something active that is NOT the root, then assume it has keyboard focus.
|
// If the offscreen Ui has something active that is NOT the root, then assume it has keyboard focus.
|
||||||
if (_keyboardDeviceHasFocus && offscreenUi && offscreenUi->getWindow()->activeFocusItem() != offscreenUi->getRootItem()) {
|
if (_keyboardDeviceHasFocus && offscreenUi && offscreenUi->getWindow()->activeFocusItem() != offscreenUi->getRootItem()) {
|
||||||
|
@ -2697,9 +2703,6 @@ void Application::idle() {
|
||||||
_keyboardDeviceHasFocus = true;
|
_keyboardDeviceHasFocus = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// We're going to execute idle processing, so restart the last idle timer
|
|
||||||
_lastTimeUpdated.start();
|
|
||||||
|
|
||||||
checkChangeCursor();
|
checkChangeCursor();
|
||||||
|
|
||||||
Stats::getInstance()->updateStats();
|
Stats::getInstance()->updateStats();
|
||||||
|
@ -2926,7 +2929,7 @@ void Application::loadSettings() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Application::saveSettings() const {
|
void Application::saveSettings() const {
|
||||||
sessionRunTime.set(_sessionRunTimer.elapsed() / MSECS_PER_SEC);
|
sessionRunTime.set(_sessionRunTimer.elapsed() / MSECS_PER_SECOND);
|
||||||
DependencyManager::get<AudioClient>()->saveSettings();
|
DependencyManager::get<AudioClient>()->saveSettings();
|
||||||
DependencyManager::get<LODManager>()->saveSettings();
|
DependencyManager::get<LODManager>()->saveSettings();
|
||||||
|
|
||||||
|
|
|
@ -329,7 +329,8 @@ private:
|
||||||
|
|
||||||
void cleanupBeforeQuit();
|
void cleanupBeforeQuit();
|
||||||
|
|
||||||
void idle();
|
bool shouldPaint(float nsecsElapsed);
|
||||||
|
void idle(float nsecsElapsed);
|
||||||
void update(float deltaTime);
|
void update(float deltaTime);
|
||||||
|
|
||||||
// Various helper functions called during update()
|
// Various helper functions called during update()
|
||||||
|
|
|
@ -29,6 +29,10 @@ enum CameraMode
|
||||||
};
|
};
|
||||||
|
|
||||||
Q_DECLARE_METATYPE(CameraMode);
|
Q_DECLARE_METATYPE(CameraMode);
|
||||||
|
|
||||||
|
#if defined(__GNUC__) && !defined(__clang__)
|
||||||
|
__attribute__((unused))
|
||||||
|
#endif
|
||||||
static int cameraModeId = qRegisterMetaType<CameraMode>();
|
static int cameraModeId = qRegisterMetaType<CameraMode>();
|
||||||
|
|
||||||
class Camera : public QObject {
|
class Camera : public QObject {
|
||||||
|
|
|
@ -196,7 +196,7 @@ bool AssetMappingModel::isKnownFolder(QString path) const {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int assetMappingModelMetatypeId = qRegisterMetaType<AssetMappingModel*>("AssetMappingModel*");
|
int assetMappingModelMetatypeId = qRegisterMetaType<AssetMappingModel*>("AssetMappingModel*");
|
||||||
|
|
||||||
void AssetMappingModel::refresh() {
|
void AssetMappingModel::refresh() {
|
||||||
qDebug() << "Refreshing asset mapping model";
|
qDebug() << "Refreshing asset mapping model";
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
#include "AnimationCache.h"
|
#include "AnimationCache.h"
|
||||||
#include "AnimationLogging.h"
|
#include "AnimationLogging.h"
|
||||||
|
|
||||||
static int animationPointerMetaTypeId = qRegisterMetaType<AnimationPointer>();
|
int animationPointerMetaTypeId = qRegisterMetaType<AnimationPointer>();
|
||||||
|
|
||||||
AnimationCache::AnimationCache(QObject* parent) :
|
AnimationCache::AnimationCache(QObject* parent) :
|
||||||
ResourceCache(parent)
|
ResourceCache(parent)
|
||||||
|
|
|
@ -14,7 +14,7 @@
|
||||||
#include "AudioLogging.h"
|
#include "AudioLogging.h"
|
||||||
#include "SoundCache.h"
|
#include "SoundCache.h"
|
||||||
|
|
||||||
static int soundPointerMetaTypeId = qRegisterMetaType<SharedSoundPointer>();
|
int soundPointerMetaTypeId = qRegisterMetaType<SharedSoundPointer>();
|
||||||
|
|
||||||
SoundCache::SoundCache(QObject* parent) :
|
SoundCache::SoundCache(QObject* parent) :
|
||||||
ResourceCache(parent)
|
ResourceCache(parent)
|
||||||
|
|
|
@ -336,10 +336,10 @@ QVector<QString> UserInputMapper::getActionNames() const {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int actionMetaTypeId = qRegisterMetaType<Action>();
|
int actionMetaTypeId = qRegisterMetaType<Action>();
|
||||||
static int inputMetaTypeId = qRegisterMetaType<Input>();
|
int inputMetaTypeId = qRegisterMetaType<Input>();
|
||||||
static int inputPairMetaTypeId = qRegisterMetaType<Input::NamedPair>();
|
int inputPairMetaTypeId = qRegisterMetaType<Input::NamedPair>();
|
||||||
static int poseMetaTypeId = qRegisterMetaType<controller::Pose>("Pose");
|
int poseMetaTypeId = qRegisterMetaType<controller::Pose>("Pose");
|
||||||
|
|
||||||
QScriptValue inputToScriptValue(QScriptEngine* engine, const Input& input);
|
QScriptValue inputToScriptValue(QScriptEngine* engine, const Input& input);
|
||||||
void inputFromScriptValue(const QScriptValue& object, Input& input);
|
void inputFromScriptValue(const QScriptValue& object, Input& input);
|
||||||
|
|
|
@ -29,17 +29,16 @@
|
||||||
|
|
||||||
#include "RenderableEntityItem.h"
|
#include "RenderableEntityItem.h"
|
||||||
|
|
||||||
#include "RenderableBoxEntityItem.h"
|
|
||||||
#include "RenderableLightEntityItem.h"
|
#include "RenderableLightEntityItem.h"
|
||||||
#include "RenderableModelEntityItem.h"
|
#include "RenderableModelEntityItem.h"
|
||||||
#include "RenderableParticleEffectEntityItem.h"
|
#include "RenderableParticleEffectEntityItem.h"
|
||||||
#include "RenderableSphereEntityItem.h"
|
|
||||||
#include "RenderableTextEntityItem.h"
|
#include "RenderableTextEntityItem.h"
|
||||||
#include "RenderableWebEntityItem.h"
|
#include "RenderableWebEntityItem.h"
|
||||||
#include "RenderableZoneEntityItem.h"
|
#include "RenderableZoneEntityItem.h"
|
||||||
#include "RenderableLineEntityItem.h"
|
#include "RenderableLineEntityItem.h"
|
||||||
#include "RenderablePolyVoxEntityItem.h"
|
#include "RenderablePolyVoxEntityItem.h"
|
||||||
#include "RenderablePolyLineEntityItem.h"
|
#include "RenderablePolyLineEntityItem.h"
|
||||||
|
#include "RenderableShapeEntityItem.h"
|
||||||
#include "EntitiesRendererLogging.h"
|
#include "EntitiesRendererLogging.h"
|
||||||
#include "AddressManager.h"
|
#include "AddressManager.h"
|
||||||
#include <Rig.h>
|
#include <Rig.h>
|
||||||
|
@ -56,8 +55,6 @@ EntityTreeRenderer::EntityTreeRenderer(bool wantScripts, AbstractViewStateInterf
|
||||||
_dontDoPrecisionPicking(false)
|
_dontDoPrecisionPicking(false)
|
||||||
{
|
{
|
||||||
REGISTER_ENTITY_TYPE_WITH_FACTORY(Model, RenderableModelEntityItem::factory)
|
REGISTER_ENTITY_TYPE_WITH_FACTORY(Model, RenderableModelEntityItem::factory)
|
||||||
REGISTER_ENTITY_TYPE_WITH_FACTORY(Box, RenderableBoxEntityItem::factory)
|
|
||||||
REGISTER_ENTITY_TYPE_WITH_FACTORY(Sphere, RenderableSphereEntityItem::factory)
|
|
||||||
REGISTER_ENTITY_TYPE_WITH_FACTORY(Light, RenderableLightEntityItem::factory)
|
REGISTER_ENTITY_TYPE_WITH_FACTORY(Light, RenderableLightEntityItem::factory)
|
||||||
REGISTER_ENTITY_TYPE_WITH_FACTORY(Text, RenderableTextEntityItem::factory)
|
REGISTER_ENTITY_TYPE_WITH_FACTORY(Text, RenderableTextEntityItem::factory)
|
||||||
REGISTER_ENTITY_TYPE_WITH_FACTORY(Web, RenderableWebEntityItem::factory)
|
REGISTER_ENTITY_TYPE_WITH_FACTORY(Web, RenderableWebEntityItem::factory)
|
||||||
|
@ -66,7 +63,10 @@ EntityTreeRenderer::EntityTreeRenderer(bool wantScripts, AbstractViewStateInterf
|
||||||
REGISTER_ENTITY_TYPE_WITH_FACTORY(Line, RenderableLineEntityItem::factory)
|
REGISTER_ENTITY_TYPE_WITH_FACTORY(Line, RenderableLineEntityItem::factory)
|
||||||
REGISTER_ENTITY_TYPE_WITH_FACTORY(PolyVox, RenderablePolyVoxEntityItem::factory)
|
REGISTER_ENTITY_TYPE_WITH_FACTORY(PolyVox, RenderablePolyVoxEntityItem::factory)
|
||||||
REGISTER_ENTITY_TYPE_WITH_FACTORY(PolyLine, RenderablePolyLineEntityItem::factory)
|
REGISTER_ENTITY_TYPE_WITH_FACTORY(PolyLine, RenderablePolyLineEntityItem::factory)
|
||||||
|
REGISTER_ENTITY_TYPE_WITH_FACTORY(Shape, RenderableShapeEntityItem::factory)
|
||||||
|
REGISTER_ENTITY_TYPE_WITH_FACTORY(Box, RenderableShapeEntityItem::boxFactory)
|
||||||
|
REGISTER_ENTITY_TYPE_WITH_FACTORY(Sphere, RenderableShapeEntityItem::sphereFactory)
|
||||||
|
|
||||||
_currentHoverOverEntityID = UNKNOWN_ENTITY_ID;
|
_currentHoverOverEntityID = UNKNOWN_ENTITY_ID;
|
||||||
_currentClickingOnEntityID = UNKNOWN_ENTITY_ID;
|
_currentClickingOnEntityID = UNKNOWN_ENTITY_ID;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,76 +0,0 @@
|
||||||
//
|
|
||||||
// RenderableBoxEntityItem.cpp
|
|
||||||
// libraries/entities-renderer/src/
|
|
||||||
//
|
|
||||||
// Created by Brad Hefta-Gaub on 8/6/14.
|
|
||||||
// Copyright 2014 High Fidelity, Inc.
|
|
||||||
//
|
|
||||||
// Distributed under the Apache License, Version 2.0.
|
|
||||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
|
||||||
//
|
|
||||||
|
|
||||||
#include "RenderableBoxEntityItem.h"
|
|
||||||
|
|
||||||
#include <glm/gtx/quaternion.hpp>
|
|
||||||
|
|
||||||
#include <gpu/Batch.h>
|
|
||||||
|
|
||||||
#include <GeometryCache.h>
|
|
||||||
#include <ObjectMotionState.h>
|
|
||||||
#include <PerfStat.h>
|
|
||||||
|
|
||||||
#include <render-utils/simple_vert.h>
|
|
||||||
#include <render-utils/simple_frag.h>
|
|
||||||
|
|
||||||
EntityItemPointer RenderableBoxEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) {
|
|
||||||
EntityItemPointer entity{ new RenderableBoxEntityItem(entityID) };
|
|
||||||
entity->setProperties(properties);
|
|
||||||
return entity;
|
|
||||||
}
|
|
||||||
|
|
||||||
void RenderableBoxEntityItem::setUserData(const QString& value) {
|
|
||||||
if (value != getUserData()) {
|
|
||||||
BoxEntityItem::setUserData(value);
|
|
||||||
if (_procedural) {
|
|
||||||
_procedural->parse(value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void RenderableBoxEntityItem::render(RenderArgs* args) {
|
|
||||||
PerformanceTimer perfTimer("RenderableBoxEntityItem::render");
|
|
||||||
Q_ASSERT(getType() == EntityTypes::Box);
|
|
||||||
Q_ASSERT(args->_batch);
|
|
||||||
|
|
||||||
if (!_procedural) {
|
|
||||||
_procedural.reset(new Procedural(this->getUserData()));
|
|
||||||
_procedural->_vertexSource = simple_vert;
|
|
||||||
_procedural->_fragmentSource = simple_frag;
|
|
||||||
_procedural->_state->setCullMode(gpu::State::CULL_NONE);
|
|
||||||
_procedural->_state->setDepthTest(true, true, gpu::LESS_EQUAL);
|
|
||||||
_procedural->_state->setBlendFunction(false,
|
|
||||||
gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA,
|
|
||||||
gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE);
|
|
||||||
}
|
|
||||||
|
|
||||||
gpu::Batch& batch = *args->_batch;
|
|
||||||
glm::vec4 cubeColor(toGlm(getXColor()), getLocalRenderAlpha());
|
|
||||||
|
|
||||||
bool success;
|
|
||||||
auto transToCenter = getTransformToCenter(success);
|
|
||||||
if (!success) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
batch.setModelTransform(transToCenter); // we want to include the scale as well
|
|
||||||
if (_procedural->ready()) {
|
|
||||||
_procedural->prepare(batch, getPosition(), getDimensions());
|
|
||||||
auto color = _procedural->getColor(cubeColor);
|
|
||||||
batch._glColor4f(color.r, color.g, color.b, color.a);
|
|
||||||
DependencyManager::get<GeometryCache>()->renderCube(batch);
|
|
||||||
} else {
|
|
||||||
DependencyManager::get<GeometryCache>()->renderSolidCubeInstance(batch, cubeColor);
|
|
||||||
}
|
|
||||||
static const auto triCount = DependencyManager::get<GeometryCache>()->getCubeTriangleCount();
|
|
||||||
args->_details._trianglesRendered += (int)triCount;
|
|
||||||
}
|
|
|
@ -1,34 +0,0 @@
|
||||||
//
|
|
||||||
// RenderableBoxEntityItem.h
|
|
||||||
// libraries/entities-renderer/src/
|
|
||||||
//
|
|
||||||
// Created by Brad Hefta-Gaub on 8/6/14.
|
|
||||||
// Copyright 2014 High Fidelity, Inc.
|
|
||||||
//
|
|
||||||
// Distributed under the Apache License, Version 2.0.
|
|
||||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
|
||||||
//
|
|
||||||
|
|
||||||
#ifndef hifi_RenderableBoxEntityItem_h
|
|
||||||
#define hifi_RenderableBoxEntityItem_h
|
|
||||||
|
|
||||||
#include <BoxEntityItem.h>
|
|
||||||
#include <procedural/Procedural.h>
|
|
||||||
|
|
||||||
#include "RenderableEntityItem.h"
|
|
||||||
|
|
||||||
class RenderableBoxEntityItem : public BoxEntityItem {
|
|
||||||
public:
|
|
||||||
static EntityItemPointer factory(const EntityItemID& entityID, const EntityItemProperties& properties);
|
|
||||||
RenderableBoxEntityItem(const EntityItemID& entityItemID) : BoxEntityItem(entityItemID) { }
|
|
||||||
|
|
||||||
virtual void render(RenderArgs* args) override;
|
|
||||||
virtual void setUserData(const QString& value) override;
|
|
||||||
|
|
||||||
SIMPLE_RENDERABLE()
|
|
||||||
private:
|
|
||||||
QSharedPointer<Procedural> _procedural;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
#endif // hifi_RenderableBoxEntityItem_h
|
|
113
libraries/entities-renderer/src/RenderableShapeEntityItem.cpp
Normal file
113
libraries/entities-renderer/src/RenderableShapeEntityItem.cpp
Normal file
|
@ -0,0 +1,113 @@
|
||||||
|
//
|
||||||
|
// Created by Bradley Austin Davis on 2016/05/09
|
||||||
|
// Copyright 2013 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "RenderableShapeEntityItem.h"
|
||||||
|
|
||||||
|
#include <glm/gtx/quaternion.hpp>
|
||||||
|
|
||||||
|
#include <gpu/Batch.h>
|
||||||
|
|
||||||
|
#include <DependencyManager.h>
|
||||||
|
#include <GeometryCache.h>
|
||||||
|
#include <PerfStat.h>
|
||||||
|
|
||||||
|
#include <render-utils/simple_vert.h>
|
||||||
|
#include <render-utils/simple_frag.h>
|
||||||
|
|
||||||
|
// Sphere entities should fit inside a cube entity of the same size, so a sphere that has dimensions 1x1x1
|
||||||
|
// is a half unit sphere. However, the geometry cache renders a UNIT sphere, so we need to scale down.
|
||||||
|
static const float SPHERE_ENTITY_SCALE = 0.5f;
|
||||||
|
|
||||||
|
static GeometryCache::Shape MAPPING[entity::NUM_SHAPES] = {
|
||||||
|
GeometryCache::Triangle,
|
||||||
|
GeometryCache::Quad,
|
||||||
|
GeometryCache::Circle,
|
||||||
|
GeometryCache::Cube,
|
||||||
|
GeometryCache::Sphere,
|
||||||
|
GeometryCache::Tetrahedron,
|
||||||
|
GeometryCache::Octahedron,
|
||||||
|
GeometryCache::Dodecahedron,
|
||||||
|
GeometryCache::Icosahedron,
|
||||||
|
GeometryCache::Torus,
|
||||||
|
GeometryCache::Cone,
|
||||||
|
GeometryCache::Cylinder,
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
RenderableShapeEntityItem::Pointer RenderableShapeEntityItem::baseFactory(const EntityItemID& entityID, const EntityItemProperties& properties) {
|
||||||
|
Pointer entity = std::make_shared<RenderableShapeEntityItem>(entityID);
|
||||||
|
entity->setProperties(properties);
|
||||||
|
return entity;
|
||||||
|
}
|
||||||
|
|
||||||
|
EntityItemPointer RenderableShapeEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) {
|
||||||
|
return baseFactory(entityID, properties);
|
||||||
|
}
|
||||||
|
|
||||||
|
EntityItemPointer RenderableShapeEntityItem::boxFactory(const EntityItemID& entityID, const EntityItemProperties& properties) {
|
||||||
|
auto result = baseFactory(entityID, properties);
|
||||||
|
result->setShape(entity::Cube);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
EntityItemPointer RenderableShapeEntityItem::sphereFactory(const EntityItemID& entityID, const EntityItemProperties& properties) {
|
||||||
|
auto result = baseFactory(entityID, properties);
|
||||||
|
result->setShape(entity::Sphere);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void RenderableShapeEntityItem::setUserData(const QString& value) {
|
||||||
|
if (value != getUserData()) {
|
||||||
|
ShapeEntityItem::setUserData(value);
|
||||||
|
if (_procedural) {
|
||||||
|
_procedural->parse(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void RenderableShapeEntityItem::render(RenderArgs* args) {
|
||||||
|
PerformanceTimer perfTimer("RenderableShapeEntityItem::render");
|
||||||
|
//Q_ASSERT(getType() == EntityTypes::Shape);
|
||||||
|
Q_ASSERT(args->_batch);
|
||||||
|
|
||||||
|
if (!_procedural) {
|
||||||
|
_procedural.reset(new Procedural(getUserData()));
|
||||||
|
_procedural->_vertexSource = simple_vert;
|
||||||
|
_procedural->_fragmentSource = simple_frag;
|
||||||
|
_procedural->_state->setCullMode(gpu::State::CULL_NONE);
|
||||||
|
_procedural->_state->setDepthTest(true, true, gpu::LESS_EQUAL);
|
||||||
|
_procedural->_state->setBlendFunction(false,
|
||||||
|
gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA,
|
||||||
|
gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE);
|
||||||
|
}
|
||||||
|
|
||||||
|
gpu::Batch& batch = *args->_batch;
|
||||||
|
glm::vec4 color(toGlm(getXColor()), getLocalRenderAlpha());
|
||||||
|
bool success;
|
||||||
|
Transform modelTransform = getTransformToCenter(success);
|
||||||
|
if (!success) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (_shape == entity::Sphere) {
|
||||||
|
modelTransform.postScale(SPHERE_ENTITY_SCALE);
|
||||||
|
}
|
||||||
|
batch.setModelTransform(modelTransform); // use a transform with scale, rotation, registration point and translation
|
||||||
|
if (_procedural->ready()) {
|
||||||
|
_procedural->prepare(batch, getPosition(), getDimensions());
|
||||||
|
auto outColor = _procedural->getColor(color);
|
||||||
|
batch._glColor4f(outColor.r, outColor.g, outColor.b, outColor.a);
|
||||||
|
DependencyManager::get<GeometryCache>()->renderShape(batch, MAPPING[_shape]);
|
||||||
|
} else {
|
||||||
|
// FIXME, support instanced multi-shape rendering using multidraw indirect
|
||||||
|
DependencyManager::get<GeometryCache>()->renderSolidShapeInstance(batch, MAPPING[_shape], color);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static const auto triCount = DependencyManager::get<GeometryCache>()->getShapeTriangleCount(MAPPING[_shape]);
|
||||||
|
args->_details._trianglesRendered += (int)triCount;
|
||||||
|
}
|
36
libraries/entities-renderer/src/RenderableShapeEntityItem.h
Normal file
36
libraries/entities-renderer/src/RenderableShapeEntityItem.h
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
//
|
||||||
|
// Created by Bradley Austin Davis on 2016/05/09
|
||||||
|
// Copyright 2013 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef hifi_RenderableShapeEntityItem_h
|
||||||
|
#define hifi_RenderableShapeEntityItem_h
|
||||||
|
|
||||||
|
#include <ShapeEntityItem.h>
|
||||||
|
#include <procedural/Procedural.h>
|
||||||
|
|
||||||
|
#include "RenderableEntityItem.h"
|
||||||
|
|
||||||
|
class RenderableShapeEntityItem : public ShapeEntityItem {
|
||||||
|
using Pointer = std::shared_ptr<RenderableShapeEntityItem>;
|
||||||
|
static Pointer baseFactory(const EntityItemID& entityID, const EntityItemProperties& properties);
|
||||||
|
public:
|
||||||
|
static EntityItemPointer factory(const EntityItemID& entityID, const EntityItemProperties& properties);
|
||||||
|
static EntityItemPointer boxFactory(const EntityItemID& entityID, const EntityItemProperties& properties);
|
||||||
|
static EntityItemPointer sphereFactory(const EntityItemID& entityID, const EntityItemProperties& properties);
|
||||||
|
RenderableShapeEntityItem(const EntityItemID& entityItemID) : ShapeEntityItem(entityItemID) {}
|
||||||
|
|
||||||
|
void render(RenderArgs* args) override;
|
||||||
|
void setUserData(const QString& value) override;
|
||||||
|
|
||||||
|
SIMPLE_RENDERABLE();
|
||||||
|
|
||||||
|
private:
|
||||||
|
QSharedPointer<Procedural> _procedural;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif // hifi_RenderableShapeEntityItem_h
|
|
@ -1,80 +0,0 @@
|
||||||
//
|
|
||||||
// RenderableSphereEntityItem.cpp
|
|
||||||
// interface/src
|
|
||||||
//
|
|
||||||
// Created by Brad Hefta-Gaub on 8/6/14.
|
|
||||||
// Copyright 2014 High Fidelity, Inc.
|
|
||||||
//
|
|
||||||
// Distributed under the Apache License, Version 2.0.
|
|
||||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
|
||||||
//
|
|
||||||
|
|
||||||
#include "RenderableSphereEntityItem.h"
|
|
||||||
|
|
||||||
#include <glm/gtx/quaternion.hpp>
|
|
||||||
|
|
||||||
#include <gpu/Batch.h>
|
|
||||||
|
|
||||||
#include <DependencyManager.h>
|
|
||||||
#include <GeometryCache.h>
|
|
||||||
#include <PerfStat.h>
|
|
||||||
|
|
||||||
#include <render-utils/simple_vert.h>
|
|
||||||
#include <render-utils/simple_frag.h>
|
|
||||||
|
|
||||||
// Sphere entities should fit inside a cube entity of the same size, so a sphere that has dimensions 1x1x1
|
|
||||||
// is a half unit sphere. However, the geometry cache renders a UNIT sphere, so we need to scale down.
|
|
||||||
static const float SPHERE_ENTITY_SCALE = 0.5f;
|
|
||||||
|
|
||||||
|
|
||||||
EntityItemPointer RenderableSphereEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) {
|
|
||||||
EntityItemPointer entity{ new RenderableSphereEntityItem(entityID) };
|
|
||||||
entity->setProperties(properties);
|
|
||||||
return entity;
|
|
||||||
}
|
|
||||||
|
|
||||||
void RenderableSphereEntityItem::setUserData(const QString& value) {
|
|
||||||
if (value != getUserData()) {
|
|
||||||
SphereEntityItem::setUserData(value);
|
|
||||||
if (_procedural) {
|
|
||||||
_procedural->parse(value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void RenderableSphereEntityItem::render(RenderArgs* args) {
|
|
||||||
PerformanceTimer perfTimer("RenderableSphereEntityItem::render");
|
|
||||||
Q_ASSERT(getType() == EntityTypes::Sphere);
|
|
||||||
Q_ASSERT(args->_batch);
|
|
||||||
|
|
||||||
if (!_procedural) {
|
|
||||||
_procedural.reset(new Procedural(getUserData()));
|
|
||||||
_procedural->_vertexSource = simple_vert;
|
|
||||||
_procedural->_fragmentSource = simple_frag;
|
|
||||||
_procedural->_state->setCullMode(gpu::State::CULL_NONE);
|
|
||||||
_procedural->_state->setDepthTest(true, true, gpu::LESS_EQUAL);
|
|
||||||
_procedural->_state->setBlendFunction(false,
|
|
||||||
gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA,
|
|
||||||
gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE);
|
|
||||||
}
|
|
||||||
|
|
||||||
gpu::Batch& batch = *args->_batch;
|
|
||||||
glm::vec4 sphereColor(toGlm(getXColor()), getLocalRenderAlpha());
|
|
||||||
bool success;
|
|
||||||
Transform modelTransform = getTransformToCenter(success);
|
|
||||||
if (!success) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
modelTransform.postScale(SPHERE_ENTITY_SCALE);
|
|
||||||
batch.setModelTransform(modelTransform); // use a transform with scale, rotation, registration point and translation
|
|
||||||
if (_procedural->ready()) {
|
|
||||||
_procedural->prepare(batch, getPosition(), getDimensions());
|
|
||||||
auto color = _procedural->getColor(sphereColor);
|
|
||||||
batch._glColor4f(color.r, color.g, color.b, color.a);
|
|
||||||
DependencyManager::get<GeometryCache>()->renderSphere(batch);
|
|
||||||
} else {
|
|
||||||
DependencyManager::get<GeometryCache>()->renderSolidSphereInstance(batch, sphereColor);
|
|
||||||
}
|
|
||||||
static const auto triCount = DependencyManager::get<GeometryCache>()->getSphereTriangleCount();
|
|
||||||
args->_details._trianglesRendered += (int)triCount;
|
|
||||||
}
|
|
|
@ -1,35 +0,0 @@
|
||||||
//
|
|
||||||
// RenderableSphereEntityItem.h
|
|
||||||
// interface/src/entities
|
|
||||||
//
|
|
||||||
// Created by Brad Hefta-Gaub on 8/6/14.
|
|
||||||
// Copyright 2014 High Fidelity, Inc.
|
|
||||||
//
|
|
||||||
// Distributed under the Apache License, Version 2.0.
|
|
||||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
|
||||||
//
|
|
||||||
|
|
||||||
#ifndef hifi_RenderableSphereEntityItem_h
|
|
||||||
#define hifi_RenderableSphereEntityItem_h
|
|
||||||
|
|
||||||
#include <SphereEntityItem.h>
|
|
||||||
#include <procedural/Procedural.h>
|
|
||||||
|
|
||||||
#include "RenderableEntityItem.h"
|
|
||||||
|
|
||||||
class RenderableSphereEntityItem : public SphereEntityItem {
|
|
||||||
public:
|
|
||||||
static EntityItemPointer factory(const EntityItemID& entityID, const EntityItemProperties& properties);
|
|
||||||
RenderableSphereEntityItem(const EntityItemID& entityItemID) : SphereEntityItem(entityItemID) { }
|
|
||||||
|
|
||||||
virtual void render(RenderArgs* args) override;
|
|
||||||
virtual void setUserData(const QString& value) override;
|
|
||||||
|
|
||||||
SIMPLE_RENDERABLE();
|
|
||||||
|
|
||||||
private:
|
|
||||||
QSharedPointer<Procedural> _procedural;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
#endif // hifi_RenderableSphereEntityItem_h
|
|
|
@ -1,103 +0,0 @@
|
||||||
//
|
|
||||||
// BoxEntityItem.cpp
|
|
||||||
// libraries/entities/src
|
|
||||||
//
|
|
||||||
// Created by Brad Hefta-Gaub on 12/4/13.
|
|
||||||
// Copyright 2013 High Fidelity, Inc.
|
|
||||||
//
|
|
||||||
// Distributed under the Apache License, Version 2.0.
|
|
||||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
|
||||||
//
|
|
||||||
|
|
||||||
|
|
||||||
#include <QDebug>
|
|
||||||
|
|
||||||
#include <ByteCountCoding.h>
|
|
||||||
|
|
||||||
#include "BoxEntityItem.h"
|
|
||||||
#include "EntitiesLogging.h"
|
|
||||||
#include "EntityItemProperties.h"
|
|
||||||
#include "EntityTree.h"
|
|
||||||
#include "EntityTreeElement.h"
|
|
||||||
|
|
||||||
EntityItemPointer BoxEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) {
|
|
||||||
EntityItemPointer entity { new BoxEntityItem(entityID) };
|
|
||||||
entity->setProperties(properties);
|
|
||||||
return entity;
|
|
||||||
}
|
|
||||||
|
|
||||||
BoxEntityItem::BoxEntityItem(const EntityItemID& entityItemID) : EntityItem(entityItemID) {
|
|
||||||
_type = EntityTypes::Box;
|
|
||||||
}
|
|
||||||
|
|
||||||
EntityItemProperties BoxEntityItem::getProperties(EntityPropertyFlags desiredProperties) const {
|
|
||||||
EntityItemProperties properties = EntityItem::getProperties(desiredProperties); // get the properties from our base class
|
|
||||||
|
|
||||||
properties._color = getXColor();
|
|
||||||
properties._colorChanged = false;
|
|
||||||
|
|
||||||
return properties;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool BoxEntityItem::setProperties(const EntityItemProperties& properties) {
|
|
||||||
bool somethingChanged = false;
|
|
||||||
somethingChanged = EntityItem::setProperties(properties); // set the properties in our base class
|
|
||||||
|
|
||||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(color, setColor);
|
|
||||||
|
|
||||||
if (somethingChanged) {
|
|
||||||
bool wantDebug = false;
|
|
||||||
if (wantDebug) {
|
|
||||||
uint64_t now = usecTimestampNow();
|
|
||||||
int elapsed = now - getLastEdited();
|
|
||||||
qCDebug(entities) << "BoxEntityItem::setProperties() AFTER update... edited AGO=" << elapsed <<
|
|
||||||
"now=" << now << " getLastEdited()=" << getLastEdited();
|
|
||||||
}
|
|
||||||
setLastEdited(properties._lastEdited);
|
|
||||||
}
|
|
||||||
return somethingChanged;
|
|
||||||
}
|
|
||||||
|
|
||||||
int BoxEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead,
|
|
||||||
ReadBitstreamToTreeParams& args,
|
|
||||||
EntityPropertyFlags& propertyFlags, bool overwriteLocalData,
|
|
||||||
bool& somethingChanged) {
|
|
||||||
|
|
||||||
int bytesRead = 0;
|
|
||||||
const unsigned char* dataAt = data;
|
|
||||||
|
|
||||||
READ_ENTITY_PROPERTY(PROP_COLOR, rgbColor, setColor);
|
|
||||||
|
|
||||||
return bytesRead;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// TODO: eventually only include properties changed since the params.lastViewFrustumSent time
|
|
||||||
EntityPropertyFlags BoxEntityItem::getEntityProperties(EncodeBitstreamParams& params) const {
|
|
||||||
EntityPropertyFlags requestedProperties = EntityItem::getEntityProperties(params);
|
|
||||||
requestedProperties += PROP_COLOR;
|
|
||||||
return requestedProperties;
|
|
||||||
}
|
|
||||||
|
|
||||||
void BoxEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params,
|
|
||||||
EntityTreeElementExtraEncodeData* modelTreeElementExtraEncodeData,
|
|
||||||
EntityPropertyFlags& requestedProperties,
|
|
||||||
EntityPropertyFlags& propertyFlags,
|
|
||||||
EntityPropertyFlags& propertiesDidntFit,
|
|
||||||
int& propertyCount,
|
|
||||||
OctreeElement::AppendState& appendState) const {
|
|
||||||
|
|
||||||
bool successPropertyFits = true;
|
|
||||||
|
|
||||||
APPEND_ENTITY_PROPERTY(PROP_COLOR, getColor());
|
|
||||||
}
|
|
||||||
|
|
||||||
void BoxEntityItem::debugDump() const {
|
|
||||||
quint64 now = usecTimestampNow();
|
|
||||||
qCDebug(entities) << " BOX EntityItem id:" << getEntityItemID() << "---------------------------------------------";
|
|
||||||
qCDebug(entities) << " color:" << _color[0] << "," << _color[1] << "," << _color[2];
|
|
||||||
qCDebug(entities) << " position:" << debugTreeVector(getPosition());
|
|
||||||
qCDebug(entities) << " dimensions:" << debugTreeVector(getDimensions());
|
|
||||||
qCDebug(entities) << " getLastEdited:" << debugTime(getLastEdited(), now);
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,64 +0,0 @@
|
||||||
//
|
|
||||||
// BoxEntityItem.h
|
|
||||||
// libraries/entities/src
|
|
||||||
//
|
|
||||||
// Created by Brad Hefta-Gaub on 12/4/13.
|
|
||||||
// Copyright 2013 High Fidelity, Inc.
|
|
||||||
//
|
|
||||||
// Distributed under the Apache License, Version 2.0.
|
|
||||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
|
||||||
//
|
|
||||||
|
|
||||||
#ifndef hifi_BoxEntityItem_h
|
|
||||||
#define hifi_BoxEntityItem_h
|
|
||||||
|
|
||||||
#include "EntityItem.h"
|
|
||||||
|
|
||||||
class BoxEntityItem : public EntityItem {
|
|
||||||
public:
|
|
||||||
static EntityItemPointer factory(const EntityItemID& entityID, const EntityItemProperties& properties);
|
|
||||||
|
|
||||||
BoxEntityItem(const EntityItemID& entityItemID);
|
|
||||||
|
|
||||||
ALLOW_INSTANTIATION // This class can be instantiated
|
|
||||||
|
|
||||||
// methods for getting/setting all properties of an entity
|
|
||||||
virtual EntityItemProperties getProperties(EntityPropertyFlags desiredProperties = EntityPropertyFlags()) const;
|
|
||||||
virtual bool setProperties(const EntityItemProperties& properties);
|
|
||||||
|
|
||||||
// TODO: eventually only include properties changed since the params.lastViewFrustumSent time
|
|
||||||
virtual EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const;
|
|
||||||
|
|
||||||
virtual void appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params,
|
|
||||||
EntityTreeElementExtraEncodeData* modelTreeElementExtraEncodeData,
|
|
||||||
EntityPropertyFlags& requestedProperties,
|
|
||||||
EntityPropertyFlags& propertyFlags,
|
|
||||||
EntityPropertyFlags& propertiesDidntFit,
|
|
||||||
int& propertyCount,
|
|
||||||
OctreeElement::AppendState& appendState) const;
|
|
||||||
|
|
||||||
virtual int readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead,
|
|
||||||
ReadBitstreamToTreeParams& args,
|
|
||||||
EntityPropertyFlags& propertyFlags, bool overwriteLocalData,
|
|
||||||
bool& somethingChanged);
|
|
||||||
|
|
||||||
const rgbColor& getColor() const { return _color; }
|
|
||||||
xColor getXColor() const { xColor color = { _color[RED_INDEX], _color[GREEN_INDEX], _color[BLUE_INDEX] }; return color; }
|
|
||||||
|
|
||||||
void setColor(const rgbColor& value) { memcpy(_color, value, sizeof(_color)); }
|
|
||||||
void setColor(const xColor& value) {
|
|
||||||
_color[RED_INDEX] = value.red;
|
|
||||||
_color[GREEN_INDEX] = value.green;
|
|
||||||
_color[BLUE_INDEX] = value.blue;
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual ShapeType getShapeType() const { return SHAPE_TYPE_BOX; }
|
|
||||||
virtual bool shouldBePhysical() const { return !isDead(); }
|
|
||||||
|
|
||||||
virtual void debugDump() const;
|
|
||||||
|
|
||||||
protected:
|
|
||||||
rgbColor _color;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif // hifi_BoxEntityItem_h
|
|
|
@ -689,6 +689,7 @@ void EntityItemProperties::copyFromScriptValue(const QScriptValue& object, bool
|
||||||
COPY_PROPERTY_FROM_QSCRIPTVALUE(jointRotations, qVectorQuat, setJointRotations);
|
COPY_PROPERTY_FROM_QSCRIPTVALUE(jointRotations, qVectorQuat, setJointRotations);
|
||||||
COPY_PROPERTY_FROM_QSCRIPTVALUE(jointTranslationsSet, qVectorBool, setJointTranslationsSet);
|
COPY_PROPERTY_FROM_QSCRIPTVALUE(jointTranslationsSet, qVectorBool, setJointTranslationsSet);
|
||||||
COPY_PROPERTY_FROM_QSCRIPTVALUE(jointTranslations, qVectorVec3, setJointTranslations);
|
COPY_PROPERTY_FROM_QSCRIPTVALUE(jointTranslations, qVectorVec3, setJointTranslations);
|
||||||
|
COPY_PROPERTY_FROM_QSCRIPTVALUE(shape, QString, setShape);
|
||||||
|
|
||||||
COPY_PROPERTY_FROM_QSCRIPTVALUE(flyingAllowed, bool, setFlyingAllowed);
|
COPY_PROPERTY_FROM_QSCRIPTVALUE(flyingAllowed, bool, setFlyingAllowed);
|
||||||
COPY_PROPERTY_FROM_QSCRIPTVALUE(ghostingAllowed, bool, setGhostingAllowed);
|
COPY_PROPERTY_FROM_QSCRIPTVALUE(ghostingAllowed, bool, setGhostingAllowed);
|
||||||
|
@ -846,6 +847,8 @@ void EntityItemProperties::entityPropertyFlagsFromScriptValue(const QScriptValue
|
||||||
ADD_PROPERTY_TO_MAP(PROP_JOINT_TRANSLATIONS_SET, JointTranslationsSet, jointTranslationsSet, QVector<bool>);
|
ADD_PROPERTY_TO_MAP(PROP_JOINT_TRANSLATIONS_SET, JointTranslationsSet, jointTranslationsSet, QVector<bool>);
|
||||||
ADD_PROPERTY_TO_MAP(PROP_JOINT_TRANSLATIONS, JointTranslations, jointTranslations, QVector<glm::vec3>);
|
ADD_PROPERTY_TO_MAP(PROP_JOINT_TRANSLATIONS, JointTranslations, jointTranslations, QVector<glm::vec3>);
|
||||||
|
|
||||||
|
ADD_PROPERTY_TO_MAP(PROP_SHAPE, Shape, shape, QString);
|
||||||
|
|
||||||
ADD_GROUP_PROPERTY_TO_MAP(PROP_ANIMATION_URL, Animation, animation, URL, url);
|
ADD_GROUP_PROPERTY_TO_MAP(PROP_ANIMATION_URL, Animation, animation, URL, url);
|
||||||
ADD_GROUP_PROPERTY_TO_MAP(PROP_ANIMATION_FPS, Animation, animation, FPS, fps);
|
ADD_GROUP_PROPERTY_TO_MAP(PROP_ANIMATION_FPS, Animation, animation, FPS, fps);
|
||||||
ADD_GROUP_PROPERTY_TO_MAP(PROP_ANIMATION_FRAME_INDEX, Animation, animation, CurrentFrame, currentFrame);
|
ADD_GROUP_PROPERTY_TO_MAP(PROP_ANIMATION_FRAME_INDEX, Animation, animation, CurrentFrame, currentFrame);
|
||||||
|
@ -1141,7 +1144,9 @@ bool EntityItemProperties::encodeEntityEditPacket(PacketType command, EntityItem
|
||||||
APPEND_ENTITY_PROPERTY(PROP_STROKE_WIDTHS, properties.getStrokeWidths());
|
APPEND_ENTITY_PROPERTY(PROP_STROKE_WIDTHS, properties.getStrokeWidths());
|
||||||
APPEND_ENTITY_PROPERTY(PROP_TEXTURES, properties.getTextures());
|
APPEND_ENTITY_PROPERTY(PROP_TEXTURES, properties.getTextures());
|
||||||
}
|
}
|
||||||
|
if (properties.getType() == EntityTypes::Shape) {
|
||||||
|
APPEND_ENTITY_PROPERTY(PROP_SHAPE, properties.getShape());
|
||||||
|
}
|
||||||
APPEND_ENTITY_PROPERTY(PROP_MARKETPLACE_ID, properties.getMarketplaceID());
|
APPEND_ENTITY_PROPERTY(PROP_MARKETPLACE_ID, properties.getMarketplaceID());
|
||||||
APPEND_ENTITY_PROPERTY(PROP_NAME, properties.getName());
|
APPEND_ENTITY_PROPERTY(PROP_NAME, properties.getName());
|
||||||
APPEND_ENTITY_PROPERTY(PROP_COLLISION_SOUND_URL, properties.getCollisionSoundURL());
|
APPEND_ENTITY_PROPERTY(PROP_COLLISION_SOUND_URL, properties.getCollisionSoundURL());
|
||||||
|
@ -1429,6 +1434,9 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int
|
||||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_STROKE_WIDTHS, QVector<float>, setStrokeWidths);
|
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_STROKE_WIDTHS, QVector<float>, setStrokeWidths);
|
||||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_TEXTURES, QString, setTextures);
|
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_TEXTURES, QString, setTextures);
|
||||||
}
|
}
|
||||||
|
if (properties.getType() == EntityTypes::Shape) {
|
||||||
|
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_SHAPE, QString, setShape);
|
||||||
|
}
|
||||||
|
|
||||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_MARKETPLACE_ID, QString, setMarketplaceID);
|
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_MARKETPLACE_ID, QString, setMarketplaceID);
|
||||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_NAME, QString, setName);
|
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_NAME, QString, setName);
|
||||||
|
@ -1915,6 +1923,7 @@ QList<QString> EntityItemProperties::listChangedProperties() {
|
||||||
if (queryAACubeChanged()) {
|
if (queryAACubeChanged()) {
|
||||||
out += "queryAACube";
|
out += "queryAACube";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (clientOnlyChanged()) {
|
if (clientOnlyChanged()) {
|
||||||
out += "clientOnly";
|
out += "clientOnly";
|
||||||
}
|
}
|
||||||
|
@ -1929,6 +1938,10 @@ QList<QString> EntityItemProperties::listChangedProperties() {
|
||||||
out += "ghostingAllowed";
|
out += "ghostingAllowed";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (shapeChanged()) {
|
||||||
|
out += "shape";
|
||||||
|
}
|
||||||
|
|
||||||
getAnimation().listChangedProperties(out);
|
getAnimation().listChangedProperties(out);
|
||||||
getKeyLight().listChangedProperties(out);
|
getKeyLight().listChangedProperties(out);
|
||||||
getSkybox().listChangedProperties(out);
|
getSkybox().listChangedProperties(out);
|
||||||
|
|
|
@ -50,7 +50,7 @@ const quint64 UNKNOWN_CREATED_TIME = 0;
|
||||||
/// A collection of properties of an entity item used in the scripting API. Translates between the actual properties of an
|
/// A collection of properties of an entity item used in the scripting API. Translates between the actual properties of an
|
||||||
/// entity and a JavaScript style hash/QScriptValue storing a set of properties. Used in scripting to set/get the complete
|
/// entity and a JavaScript style hash/QScriptValue storing a set of properties. Used in scripting to set/get the complete
|
||||||
/// set of entity item properties via JavaScript hashes/QScriptValues
|
/// set of entity item properties via JavaScript hashes/QScriptValues
|
||||||
/// all units for position, dimensions, etc are in meter units
|
/// all units for SI units (meter, second, radian, etc)
|
||||||
class EntityItemProperties {
|
class EntityItemProperties {
|
||||||
friend class EntityItem; // TODO: consider removing this friend relationship and use public methods
|
friend class EntityItem; // TODO: consider removing this friend relationship and use public methods
|
||||||
friend class ModelEntityItem; // TODO: consider removing this friend relationship and use public methods
|
friend class ModelEntityItem; // TODO: consider removing this friend relationship and use public methods
|
||||||
|
@ -64,6 +64,7 @@ class EntityItemProperties {
|
||||||
friend class LineEntityItem; // TODO: consider removing this friend relationship and use public methods
|
friend class LineEntityItem; // TODO: consider removing this friend relationship and use public methods
|
||||||
friend class PolyVoxEntityItem; // TODO: consider removing this friend relationship and use public methods
|
friend class PolyVoxEntityItem; // TODO: consider removing this friend relationship and use public methods
|
||||||
friend class PolyLineEntityItem; // TODO: consider removing this friend relationship and use public methods
|
friend class PolyLineEntityItem; // TODO: consider removing this friend relationship and use public methods
|
||||||
|
friend class ShapeEntityItem; // TODO: consider removing this friend relationship and use public methods
|
||||||
public:
|
public:
|
||||||
EntityItemProperties(EntityPropertyFlags desiredProperties = EntityPropertyFlags());
|
EntityItemProperties(EntityPropertyFlags desiredProperties = EntityPropertyFlags());
|
||||||
virtual ~EntityItemProperties() = default;
|
virtual ~EntityItemProperties() = default;
|
||||||
|
@ -195,6 +196,7 @@ public:
|
||||||
DEFINE_PROPERTY_REF(PROP_PARENT_ID, ParentID, parentID, QUuid, UNKNOWN_ENTITY_ID);
|
DEFINE_PROPERTY_REF(PROP_PARENT_ID, ParentID, parentID, QUuid, UNKNOWN_ENTITY_ID);
|
||||||
DEFINE_PROPERTY_REF(PROP_PARENT_JOINT_INDEX, ParentJointIndex, parentJointIndex, quint16, -1);
|
DEFINE_PROPERTY_REF(PROP_PARENT_JOINT_INDEX, ParentJointIndex, parentJointIndex, quint16, -1);
|
||||||
DEFINE_PROPERTY_REF(PROP_QUERY_AA_CUBE, QueryAACube, queryAACube, AACube, AACube());
|
DEFINE_PROPERTY_REF(PROP_QUERY_AA_CUBE, QueryAACube, queryAACube, AACube, AACube());
|
||||||
|
DEFINE_PROPERTY_REF(PROP_SHAPE, Shape, shape, QString, "Sphere");
|
||||||
|
|
||||||
// these are used when bouncing location data into and out of scripts
|
// these are used when bouncing location data into and out of scripts
|
||||||
DEFINE_PROPERTY_REF(PROP_LOCAL_POSITION, LocalPosition, localPosition, glmVec3, ENTITY_ITEM_ZERO_VEC3);
|
DEFINE_PROPERTY_REF(PROP_LOCAL_POSITION, LocalPosition, localPosition, glmVec3, ENTITY_ITEM_ZERO_VEC3);
|
||||||
|
|
|
@ -175,6 +175,8 @@ enum EntityPropertyList {
|
||||||
PROP_CLIENT_ONLY, // doesn't go over wire
|
PROP_CLIENT_ONLY, // doesn't go over wire
|
||||||
PROP_OWNING_AVATAR_ID, // doesn't go over wire
|
PROP_OWNING_AVATAR_ID, // doesn't go over wire
|
||||||
|
|
||||||
|
PROP_SHAPE,
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// ATTENTION: add new properties to end of list just ABOVE this line
|
// ATTENTION: add new properties to end of list just ABOVE this line
|
||||||
PROP_AFTER_LAST_ITEM,
|
PROP_AFTER_LAST_ITEM,
|
||||||
|
|
|
@ -18,17 +18,16 @@
|
||||||
#include "EntityItemProperties.h"
|
#include "EntityItemProperties.h"
|
||||||
#include "EntityTypes.h"
|
#include "EntityTypes.h"
|
||||||
|
|
||||||
#include "BoxEntityItem.h"
|
|
||||||
#include "LightEntityItem.h"
|
#include "LightEntityItem.h"
|
||||||
#include "ModelEntityItem.h"
|
#include "ModelEntityItem.h"
|
||||||
#include "ParticleEffectEntityItem.h"
|
#include "ParticleEffectEntityItem.h"
|
||||||
#include "SphereEntityItem.h"
|
|
||||||
#include "TextEntityItem.h"
|
#include "TextEntityItem.h"
|
||||||
#include "WebEntityItem.h"
|
#include "WebEntityItem.h"
|
||||||
#include "ZoneEntityItem.h"
|
#include "ZoneEntityItem.h"
|
||||||
#include "LineEntityItem.h"
|
#include "LineEntityItem.h"
|
||||||
#include "PolyVoxEntityItem.h"
|
#include "PolyVoxEntityItem.h"
|
||||||
#include "PolyLineEntityItem.h"
|
#include "PolyLineEntityItem.h"
|
||||||
|
#include "ShapeEntityItem.h"
|
||||||
|
|
||||||
QMap<EntityTypes::EntityType, QString> EntityTypes::_typeToNameMap;
|
QMap<EntityTypes::EntityType, QString> EntityTypes::_typeToNameMap;
|
||||||
QMap<QString, EntityTypes::EntityType> EntityTypes::_nameToTypeMap;
|
QMap<QString, EntityTypes::EntityType> EntityTypes::_nameToTypeMap;
|
||||||
|
@ -39,16 +38,17 @@ const QString ENTITY_TYPE_NAME_UNKNOWN = "Unknown";
|
||||||
|
|
||||||
// Register Entity the default implementations of entity types here...
|
// Register Entity the default implementations of entity types here...
|
||||||
REGISTER_ENTITY_TYPE(Model)
|
REGISTER_ENTITY_TYPE(Model)
|
||||||
REGISTER_ENTITY_TYPE(Box)
|
|
||||||
REGISTER_ENTITY_TYPE(Web)
|
REGISTER_ENTITY_TYPE(Web)
|
||||||
REGISTER_ENTITY_TYPE(Sphere)
|
|
||||||
REGISTER_ENTITY_TYPE(Light)
|
REGISTER_ENTITY_TYPE(Light)
|
||||||
REGISTER_ENTITY_TYPE(Text)
|
REGISTER_ENTITY_TYPE(Text)
|
||||||
REGISTER_ENTITY_TYPE(ParticleEffect)
|
REGISTER_ENTITY_TYPE(ParticleEffect)
|
||||||
REGISTER_ENTITY_TYPE(Zone)
|
REGISTER_ENTITY_TYPE(Zone)
|
||||||
REGISTER_ENTITY_TYPE(Line)
|
REGISTER_ENTITY_TYPE(Line)
|
||||||
REGISTER_ENTITY_TYPE(PolyVox)
|
REGISTER_ENTITY_TYPE(PolyVox)
|
||||||
REGISTER_ENTITY_TYPE(PolyLine);
|
REGISTER_ENTITY_TYPE(PolyLine)
|
||||||
|
REGISTER_ENTITY_TYPE(Shape)
|
||||||
|
REGISTER_ENTITY_TYPE_WITH_FACTORY(Box, ShapeEntityItem::boxFactory)
|
||||||
|
REGISTER_ENTITY_TYPE_WITH_FACTORY(Sphere, ShapeEntityItem::sphereFactory)
|
||||||
|
|
||||||
const QString& EntityTypes::getEntityTypeName(EntityType entityType) {
|
const QString& EntityTypes::getEntityTypeName(EntityType entityType) {
|
||||||
QMap<EntityType, QString>::iterator matchedTypeName = _typeToNameMap.find(entityType);
|
QMap<EntityType, QString>::iterator matchedTypeName = _typeToNameMap.find(entityType);
|
||||||
|
|
|
@ -48,7 +48,8 @@ public:
|
||||||
Line,
|
Line,
|
||||||
PolyVox,
|
PolyVox,
|
||||||
PolyLine,
|
PolyLine,
|
||||||
LAST = PolyLine
|
Shape,
|
||||||
|
LAST = Shape
|
||||||
} EntityType;
|
} EntityType;
|
||||||
|
|
||||||
static const QString& getEntityTypeName(EntityType entityType);
|
static const QString& getEntityTypeName(EntityType entityType);
|
||||||
|
@ -69,9 +70,18 @@ private:
|
||||||
/// named NameEntityItem and must of a static method called factory that takes an EnityItemID, and EntityItemProperties and return a newly
|
/// named NameEntityItem and must of a static method called factory that takes an EnityItemID, and EntityItemProperties and return a newly
|
||||||
/// constructed (heap allocated) instance of your type. e.g. The following prototype:
|
/// constructed (heap allocated) instance of your type. e.g. The following prototype:
|
||||||
// static EntityItemPointer factory(const EntityItemID& entityID, const EntityItemProperties& properties);
|
// static EntityItemPointer factory(const EntityItemID& entityID, const EntityItemProperties& properties);
|
||||||
#define REGISTER_ENTITY_TYPE(x) static bool x##Registration = \
|
#define REGISTER_ENTITY_TYPE(x) bool x##Registration = \
|
||||||
EntityTypes::registerEntityType(EntityTypes::x, #x, x##EntityItem::factory);
|
EntityTypes::registerEntityType(EntityTypes::x, #x, x##EntityItem::factory);
|
||||||
|
|
||||||
|
|
||||||
|
struct EntityRegistrationChecker {
|
||||||
|
EntityRegistrationChecker(bool result, const char* debugMessage) {
|
||||||
|
if (!result) {
|
||||||
|
qDebug() << debugMessage;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
/// Macro for registering entity types with an overloaded factory. Like using the REGISTER_ENTITY_TYPE macro: Make sure to add
|
/// Macro for registering entity types with an overloaded factory. Like using the REGISTER_ENTITY_TYPE macro: Make sure to add
|
||||||
/// an element to the EntityType enum with your name. But unlike REGISTER_ENTITY_TYPE, your class can be named anything
|
/// an element to the EntityType enum with your name. But unlike REGISTER_ENTITY_TYPE, your class can be named anything
|
||||||
/// so long as you provide a static method passed to the macro, that takes an EnityItemID, and EntityItemProperties and
|
/// so long as you provide a static method passed to the macro, that takes an EnityItemID, and EntityItemProperties and
|
||||||
|
@ -79,9 +89,9 @@ private:
|
||||||
// static EntityItemPointer factory(const EntityItemID& entityID, const EntityItemProperties& properties);
|
// static EntityItemPointer factory(const EntityItemID& entityID, const EntityItemProperties& properties);
|
||||||
#define REGISTER_ENTITY_TYPE_WITH_FACTORY(x,y) static bool x##Registration = \
|
#define REGISTER_ENTITY_TYPE_WITH_FACTORY(x,y) static bool x##Registration = \
|
||||||
EntityTypes::registerEntityType(EntityTypes::x, #x, y); \
|
EntityTypes::registerEntityType(EntityTypes::x, #x, y); \
|
||||||
if (!x##Registration) { \
|
EntityRegistrationChecker x##RegistrationChecker( \
|
||||||
qDebug() << "UNEXPECTED: REGISTER_ENTITY_TYPE_WITH_FACTORY(" #x "," #y ") FAILED.!"; \
|
x##Registration, \
|
||||||
}
|
"UNEXPECTED: REGISTER_ENTITY_TYPE_WITH_FACTORY(" #x "," #y ") FAILED.!");
|
||||||
|
|
||||||
|
|
||||||
#endif // hifi_EntityTypes_h
|
#endif // hifi_EntityTypes_h
|
||||||
|
|
230
libraries/entities/src/ShapeEntityItem.cpp
Normal file
230
libraries/entities/src/ShapeEntityItem.cpp
Normal file
|
@ -0,0 +1,230 @@
|
||||||
|
//
|
||||||
|
// Created by Bradley Austin Davis on 2016/05/09
|
||||||
|
// Copyright 2013 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
|
||||||
|
|
||||||
|
#include <glm/gtx/transform.hpp>
|
||||||
|
|
||||||
|
#include <QtCore/QDebug>
|
||||||
|
|
||||||
|
#include <GeometryUtil.h>
|
||||||
|
|
||||||
|
#include "EntitiesLogging.h"
|
||||||
|
#include "EntityItemProperties.h"
|
||||||
|
#include "EntityTree.h"
|
||||||
|
#include "EntityTreeElement.h"
|
||||||
|
#include "ShapeEntityItem.h"
|
||||||
|
|
||||||
|
namespace entity {
|
||||||
|
static const std::vector<QString> shapeStrings { {
|
||||||
|
"Triangle",
|
||||||
|
"Quad",
|
||||||
|
"Circle",
|
||||||
|
"Cube",
|
||||||
|
"Sphere",
|
||||||
|
"Tetrahedron",
|
||||||
|
"Octahedron",
|
||||||
|
"Dodecahedron",
|
||||||
|
"Icosahedron",
|
||||||
|
"Torus",
|
||||||
|
"Cone",
|
||||||
|
"Cylinder"
|
||||||
|
} };
|
||||||
|
|
||||||
|
Shape shapeFromString(const ::QString& shapeString) {
|
||||||
|
for (size_t i = 0; i < shapeStrings.size(); ++i) {
|
||||||
|
if (shapeString.toLower() == shapeStrings[i].toLower()) {
|
||||||
|
return static_cast<Shape>(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Shape::Sphere;
|
||||||
|
}
|
||||||
|
|
||||||
|
::QString stringFromShape(Shape shape) {
|
||||||
|
return shapeStrings[shape];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ShapeEntityItem::Pointer ShapeEntityItem::baseFactory(const EntityItemID& entityID, const EntityItemProperties& properties) {
|
||||||
|
Pointer entity { new ShapeEntityItem(entityID) };
|
||||||
|
entity->setProperties(properties);
|
||||||
|
return entity;
|
||||||
|
}
|
||||||
|
|
||||||
|
EntityItemPointer ShapeEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) {
|
||||||
|
return baseFactory(entityID, properties);
|
||||||
|
}
|
||||||
|
|
||||||
|
EntityItemPointer ShapeEntityItem::boxFactory(const EntityItemID& entityID, const EntityItemProperties& properties) {
|
||||||
|
auto result = baseFactory(entityID, properties);
|
||||||
|
result->setShape(entity::Shape::Cube);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
EntityItemPointer ShapeEntityItem::sphereFactory(const EntityItemID& entityID, const EntityItemProperties& properties) {
|
||||||
|
auto result = baseFactory(entityID, properties);
|
||||||
|
result->setShape(entity::Shape::Sphere);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// our non-pure virtual subclass for now...
|
||||||
|
ShapeEntityItem::ShapeEntityItem(const EntityItemID& entityItemID) : EntityItem(entityItemID) {
|
||||||
|
_type = EntityTypes::Shape;
|
||||||
|
_volumeMultiplier *= PI / 6.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
EntityItemProperties ShapeEntityItem::getProperties(EntityPropertyFlags desiredProperties) const {
|
||||||
|
EntityItemProperties properties = EntityItem::getProperties(desiredProperties); // get the properties from our base class
|
||||||
|
properties.setColor(getXColor());
|
||||||
|
properties.setShape(entity::stringFromShape(getShape()));
|
||||||
|
return properties;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ShapeEntityItem::setShape(const entity::Shape& shape) {
|
||||||
|
_shape = shape;
|
||||||
|
switch (_shape) {
|
||||||
|
case entity::Shape::Cube:
|
||||||
|
_type = EntityTypes::Box;
|
||||||
|
break;
|
||||||
|
case entity::Shape::Sphere:
|
||||||
|
_type = EntityTypes::Sphere;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
_type = EntityTypes::Shape;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ShapeEntityItem::setProperties(const EntityItemProperties& properties) {
|
||||||
|
bool somethingChanged = EntityItem::setProperties(properties); // set the properties in our base class
|
||||||
|
|
||||||
|
SET_ENTITY_PROPERTY_FROM_PROPERTIES(alpha, setAlpha);
|
||||||
|
SET_ENTITY_PROPERTY_FROM_PROPERTIES(color, setColor);
|
||||||
|
SET_ENTITY_PROPERTY_FROM_PROPERTIES(shape, setShape);
|
||||||
|
|
||||||
|
if (somethingChanged) {
|
||||||
|
bool wantDebug = false;
|
||||||
|
if (wantDebug) {
|
||||||
|
uint64_t now = usecTimestampNow();
|
||||||
|
int elapsed = now - getLastEdited();
|
||||||
|
qCDebug(entities) << "ShapeEntityItem::setProperties() AFTER update... edited AGO=" << elapsed <<
|
||||||
|
"now=" << now << " getLastEdited()=" << getLastEdited();
|
||||||
|
}
|
||||||
|
setLastEdited(properties.getLastEdited());
|
||||||
|
}
|
||||||
|
return somethingChanged;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ShapeEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead,
|
||||||
|
ReadBitstreamToTreeParams& args,
|
||||||
|
EntityPropertyFlags& propertyFlags, bool overwriteLocalData,
|
||||||
|
bool& somethingChanged) {
|
||||||
|
|
||||||
|
int bytesRead = 0;
|
||||||
|
const unsigned char* dataAt = data;
|
||||||
|
|
||||||
|
READ_ENTITY_PROPERTY(PROP_SHAPE, QString, setShape);
|
||||||
|
READ_ENTITY_PROPERTY(PROP_COLOR, rgbColor, setColor);
|
||||||
|
READ_ENTITY_PROPERTY(PROP_ALPHA, float, setAlpha);
|
||||||
|
|
||||||
|
return bytesRead;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// TODO: eventually only include properties changed since the params.lastViewFrustumSent time
|
||||||
|
EntityPropertyFlags ShapeEntityItem::getEntityProperties(EncodeBitstreamParams& params) const {
|
||||||
|
EntityPropertyFlags requestedProperties = EntityItem::getEntityProperties(params);
|
||||||
|
requestedProperties += PROP_SHAPE;
|
||||||
|
requestedProperties += PROP_COLOR;
|
||||||
|
requestedProperties += PROP_ALPHA;
|
||||||
|
return requestedProperties;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ShapeEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params,
|
||||||
|
EntityTreeElementExtraEncodeData* modelTreeElementExtraEncodeData,
|
||||||
|
EntityPropertyFlags& requestedProperties,
|
||||||
|
EntityPropertyFlags& propertyFlags,
|
||||||
|
EntityPropertyFlags& propertiesDidntFit,
|
||||||
|
int& propertyCount,
|
||||||
|
OctreeElement::AppendState& appendState) const {
|
||||||
|
|
||||||
|
bool successPropertyFits = true;
|
||||||
|
APPEND_ENTITY_PROPERTY(PROP_SHAPE, entity::stringFromShape(getShape()));
|
||||||
|
APPEND_ENTITY_PROPERTY(PROP_COLOR, getColor());
|
||||||
|
APPEND_ENTITY_PROPERTY(PROP_COLOR, getAlpha());
|
||||||
|
}
|
||||||
|
|
||||||
|
// This value specifes how the shape should be treated by physics calculations.
|
||||||
|
// For now, all polys will act as spheres
|
||||||
|
ShapeType ShapeEntityItem::getShapeType() const {
|
||||||
|
return SHAPE_TYPE_ELLIPSOID;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ShapeEntityItem::setColor(const rgbColor& value) {
|
||||||
|
memcpy(_color, value, sizeof(rgbColor));
|
||||||
|
}
|
||||||
|
|
||||||
|
xColor ShapeEntityItem::getXColor() const {
|
||||||
|
return xColor { _color[0], _color[1], _color[2] };
|
||||||
|
}
|
||||||
|
|
||||||
|
void ShapeEntityItem::setColor(const xColor& value) {
|
||||||
|
setColor(rgbColor { value.red, value.green, value.blue });
|
||||||
|
}
|
||||||
|
|
||||||
|
QColor ShapeEntityItem::getQColor() const {
|
||||||
|
auto& color = getColor();
|
||||||
|
return QColor(color[0], color[1], color[2], (int)(getAlpha() * 255));
|
||||||
|
}
|
||||||
|
|
||||||
|
void ShapeEntityItem::setColor(const QColor& value) {
|
||||||
|
setColor(rgbColor { (uint8_t)value.red(), (uint8_t)value.green(), (uint8_t)value.blue() });
|
||||||
|
setAlpha(value.alpha());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ShapeEntityItem::supportsDetailedRayIntersection() const {
|
||||||
|
return _shape == entity::Sphere;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ShapeEntityItem::findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
|
||||||
|
bool& keepSearching, OctreeElementPointer& element,
|
||||||
|
float& distance, BoxFace& face, glm::vec3& surfaceNormal,
|
||||||
|
void** intersectedObject, bool precisionPicking) const {
|
||||||
|
// determine the ray in the frame of the entity transformed from a unit sphere
|
||||||
|
glm::mat4 entityToWorldMatrix = getEntityToWorldMatrix();
|
||||||
|
glm::mat4 worldToEntityMatrix = glm::inverse(entityToWorldMatrix);
|
||||||
|
glm::vec3 entityFrameOrigin = glm::vec3(worldToEntityMatrix * glm::vec4(origin, 1.0f));
|
||||||
|
glm::vec3 entityFrameDirection = glm::normalize(glm::vec3(worldToEntityMatrix * glm::vec4(direction, 0.0f)));
|
||||||
|
|
||||||
|
float localDistance;
|
||||||
|
// NOTE: unit sphere has center of 0,0,0 and radius of 0.5
|
||||||
|
if (findRaySphereIntersection(entityFrameOrigin, entityFrameDirection, glm::vec3(0.0f), 0.5f, localDistance)) {
|
||||||
|
// determine where on the unit sphere the hit point occured
|
||||||
|
glm::vec3 entityFrameHitAt = entityFrameOrigin + (entityFrameDirection * localDistance);
|
||||||
|
// then translate back to work coordinates
|
||||||
|
glm::vec3 hitAt = glm::vec3(entityToWorldMatrix * glm::vec4(entityFrameHitAt, 1.0f));
|
||||||
|
distance = glm::distance(origin, hitAt);
|
||||||
|
bool success;
|
||||||
|
surfaceNormal = glm::normalize(hitAt - getCenterPosition(success));
|
||||||
|
if (!success) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ShapeEntityItem::debugDump() const {
|
||||||
|
quint64 now = usecTimestampNow();
|
||||||
|
qCDebug(entities) << "SHAPE EntityItem id:" << getEntityItemID() << "---------------------------------------------";
|
||||||
|
qCDebug(entities) << " shape:" << stringFromShape(_shape);
|
||||||
|
qCDebug(entities) << " color:" << _color[0] << "," << _color[1] << "," << _color[2];
|
||||||
|
qCDebug(entities) << " position:" << debugTreeVector(getPosition());
|
||||||
|
qCDebug(entities) << " dimensions:" << debugTreeVector(getDimensions());
|
||||||
|
qCDebug(entities) << " getLastEdited:" << debugTime(getLastEdited(), now);
|
||||||
|
}
|
||||||
|
|
103
libraries/entities/src/ShapeEntityItem.h
Normal file
103
libraries/entities/src/ShapeEntityItem.h
Normal file
|
@ -0,0 +1,103 @@
|
||||||
|
//
|
||||||
|
// Created by Bradley Austin Davis on 2016/05/09
|
||||||
|
// Copyright 2013 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef hifi_ShapeEntityItem_h
|
||||||
|
#define hifi_ShapeEntityItem_h
|
||||||
|
|
||||||
|
#include "EntityItem.h"
|
||||||
|
|
||||||
|
namespace entity {
|
||||||
|
enum Shape {
|
||||||
|
Triangle,
|
||||||
|
Quad,
|
||||||
|
Circle,
|
||||||
|
Cube,
|
||||||
|
Sphere,
|
||||||
|
Tetrahedron,
|
||||||
|
Octahedron,
|
||||||
|
Dodecahedron,
|
||||||
|
Icosahedron,
|
||||||
|
Torus,
|
||||||
|
Cone,
|
||||||
|
Cylinder,
|
||||||
|
NUM_SHAPES,
|
||||||
|
};
|
||||||
|
|
||||||
|
Shape shapeFromString(const ::QString& shapeString);
|
||||||
|
::QString stringFromShape(Shape shape);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class ShapeEntityItem : public EntityItem {
|
||||||
|
using Pointer = std::shared_ptr<ShapeEntityItem>;
|
||||||
|
static Pointer baseFactory(const EntityItemID& entityID, const EntityItemProperties& properties);
|
||||||
|
public:
|
||||||
|
static EntityItemPointer factory(const EntityItemID& entityID, const EntityItemProperties& properties);
|
||||||
|
static EntityItemPointer sphereFactory(const EntityItemID& entityID, const EntityItemProperties& properties);
|
||||||
|
static EntityItemPointer boxFactory(const EntityItemID& entityID, const EntityItemProperties& properties);
|
||||||
|
|
||||||
|
ShapeEntityItem(const EntityItemID& entityItemID);
|
||||||
|
|
||||||
|
void pureVirtualFunctionPlaceHolder() override { };
|
||||||
|
// Triggers warnings on OSX
|
||||||
|
//ALLOW_INSTANTIATION
|
||||||
|
|
||||||
|
// methods for getting/setting all properties of an entity
|
||||||
|
EntityItemProperties getProperties(EntityPropertyFlags desiredProperties = EntityPropertyFlags()) const override;
|
||||||
|
bool setProperties(const EntityItemProperties& properties) override;
|
||||||
|
|
||||||
|
EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const override;
|
||||||
|
|
||||||
|
void appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params,
|
||||||
|
EntityTreeElementExtraEncodeData* modelTreeElementExtraEncodeData,
|
||||||
|
EntityPropertyFlags& requestedProperties,
|
||||||
|
EntityPropertyFlags& propertyFlags,
|
||||||
|
EntityPropertyFlags& propertiesDidntFit,
|
||||||
|
int& propertyCount,
|
||||||
|
OctreeElement::AppendState& appendState) const override;
|
||||||
|
|
||||||
|
int readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead,
|
||||||
|
ReadBitstreamToTreeParams& args,
|
||||||
|
EntityPropertyFlags& propertyFlags, bool overwriteLocalData,
|
||||||
|
bool& somethingChanged) override;
|
||||||
|
|
||||||
|
entity::Shape getShape() const { return _shape; }
|
||||||
|
void setShape(const entity::Shape& shape);
|
||||||
|
void setShape(const QString& shape) { setShape(entity::shapeFromString(shape)); }
|
||||||
|
|
||||||
|
float getAlpha() const { return _alpha; };
|
||||||
|
void setAlpha(float alpha) { _alpha = alpha; }
|
||||||
|
|
||||||
|
const rgbColor& getColor() const { return _color; }
|
||||||
|
void setColor(const rgbColor& value);
|
||||||
|
|
||||||
|
xColor getXColor() const;
|
||||||
|
void setColor(const xColor& value);
|
||||||
|
|
||||||
|
QColor getQColor() const;
|
||||||
|
void setColor(const QColor& value);
|
||||||
|
|
||||||
|
ShapeType getShapeType() const override;
|
||||||
|
bool shouldBePhysical() const override { return !isDead(); }
|
||||||
|
|
||||||
|
bool supportsDetailedRayIntersection() const override;
|
||||||
|
bool findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
|
||||||
|
bool& keepSearching, OctreeElementPointer& element, float& distance,
|
||||||
|
BoxFace& face, glm::vec3& surfaceNormal,
|
||||||
|
void** intersectedObject, bool precisionPicking) const override;
|
||||||
|
|
||||||
|
void debugDump() const override;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
|
||||||
|
float _alpha { 1 };
|
||||||
|
rgbColor _color;
|
||||||
|
entity::Shape _shape { entity::Shape::Sphere };
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // hifi_ShapeEntityItem_h
|
|
@ -1,132 +0,0 @@
|
||||||
//
|
|
||||||
// SphereEntityItem.cpp
|
|
||||||
// libraries/entities/src
|
|
||||||
//
|
|
||||||
// Created by Brad Hefta-Gaub on 12/4/13.
|
|
||||||
// Copyright 2013 High Fidelity, Inc.
|
|
||||||
//
|
|
||||||
// Distributed under the Apache License, Version 2.0.
|
|
||||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
|
||||||
//
|
|
||||||
|
|
||||||
|
|
||||||
#include <glm/gtx/transform.hpp>
|
|
||||||
|
|
||||||
#include <QDebug>
|
|
||||||
|
|
||||||
#include <ByteCountCoding.h>
|
|
||||||
#include <GeometryUtil.h>
|
|
||||||
|
|
||||||
#include "EntitiesLogging.h"
|
|
||||||
#include "EntityItemProperties.h"
|
|
||||||
#include "EntityTree.h"
|
|
||||||
#include "EntityTreeElement.h"
|
|
||||||
#include "SphereEntityItem.h"
|
|
||||||
|
|
||||||
EntityItemPointer SphereEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) {
|
|
||||||
EntityItemPointer entity { new SphereEntityItem(entityID) };
|
|
||||||
entity->setProperties(properties);
|
|
||||||
return entity;
|
|
||||||
}
|
|
||||||
|
|
||||||
// our non-pure virtual subclass for now...
|
|
||||||
SphereEntityItem::SphereEntityItem(const EntityItemID& entityItemID) : EntityItem(entityItemID) {
|
|
||||||
_type = EntityTypes::Sphere;
|
|
||||||
_volumeMultiplier *= PI / 6.0f;
|
|
||||||
}
|
|
||||||
|
|
||||||
EntityItemProperties SphereEntityItem::getProperties(EntityPropertyFlags desiredProperties) const {
|
|
||||||
EntityItemProperties properties = EntityItem::getProperties(desiredProperties); // get the properties from our base class
|
|
||||||
properties.setColor(getXColor());
|
|
||||||
return properties;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool SphereEntityItem::setProperties(const EntityItemProperties& properties) {
|
|
||||||
bool somethingChanged = EntityItem::setProperties(properties); // set the properties in our base class
|
|
||||||
|
|
||||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(color, setColor);
|
|
||||||
|
|
||||||
if (somethingChanged) {
|
|
||||||
bool wantDebug = false;
|
|
||||||
if (wantDebug) {
|
|
||||||
uint64_t now = usecTimestampNow();
|
|
||||||
int elapsed = now - getLastEdited();
|
|
||||||
qCDebug(entities) << "SphereEntityItem::setProperties() AFTER update... edited AGO=" << elapsed <<
|
|
||||||
"now=" << now << " getLastEdited()=" << getLastEdited();
|
|
||||||
}
|
|
||||||
setLastEdited(properties.getLastEdited());
|
|
||||||
}
|
|
||||||
return somethingChanged;
|
|
||||||
}
|
|
||||||
|
|
||||||
int SphereEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead,
|
|
||||||
ReadBitstreamToTreeParams& args,
|
|
||||||
EntityPropertyFlags& propertyFlags, bool overwriteLocalData,
|
|
||||||
bool& somethingChanged) {
|
|
||||||
|
|
||||||
int bytesRead = 0;
|
|
||||||
const unsigned char* dataAt = data;
|
|
||||||
|
|
||||||
READ_ENTITY_PROPERTY(PROP_COLOR, rgbColor, setColor);
|
|
||||||
|
|
||||||
return bytesRead;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// TODO: eventually only include properties changed since the params.lastViewFrustumSent time
|
|
||||||
EntityPropertyFlags SphereEntityItem::getEntityProperties(EncodeBitstreamParams& params) const {
|
|
||||||
EntityPropertyFlags requestedProperties = EntityItem::getEntityProperties(params);
|
|
||||||
requestedProperties += PROP_COLOR;
|
|
||||||
return requestedProperties;
|
|
||||||
}
|
|
||||||
|
|
||||||
void SphereEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params,
|
|
||||||
EntityTreeElementExtraEncodeData* modelTreeElementExtraEncodeData,
|
|
||||||
EntityPropertyFlags& requestedProperties,
|
|
||||||
EntityPropertyFlags& propertyFlags,
|
|
||||||
EntityPropertyFlags& propertiesDidntFit,
|
|
||||||
int& propertyCount,
|
|
||||||
OctreeElement::AppendState& appendState) const {
|
|
||||||
|
|
||||||
bool successPropertyFits = true;
|
|
||||||
APPEND_ENTITY_PROPERTY(PROP_COLOR, getColor());
|
|
||||||
}
|
|
||||||
|
|
||||||
bool SphereEntityItem::findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
|
|
||||||
bool& keepSearching, OctreeElementPointer& element,
|
|
||||||
float& distance, BoxFace& face, glm::vec3& surfaceNormal,
|
|
||||||
void** intersectedObject, bool precisionPicking) const {
|
|
||||||
// determine the ray in the frame of the entity transformed from a unit sphere
|
|
||||||
glm::mat4 entityToWorldMatrix = getEntityToWorldMatrix();
|
|
||||||
glm::mat4 worldToEntityMatrix = glm::inverse(entityToWorldMatrix);
|
|
||||||
glm::vec3 entityFrameOrigin = glm::vec3(worldToEntityMatrix * glm::vec4(origin, 1.0f));
|
|
||||||
glm::vec3 entityFrameDirection = glm::normalize(glm::vec3(worldToEntityMatrix * glm::vec4(direction, 0.0f)));
|
|
||||||
|
|
||||||
float localDistance;
|
|
||||||
// NOTE: unit sphere has center of 0,0,0 and radius of 0.5
|
|
||||||
if (findRaySphereIntersection(entityFrameOrigin, entityFrameDirection, glm::vec3(0.0f), 0.5f, localDistance)) {
|
|
||||||
// determine where on the unit sphere the hit point occured
|
|
||||||
glm::vec3 entityFrameHitAt = entityFrameOrigin + (entityFrameDirection * localDistance);
|
|
||||||
// then translate back to work coordinates
|
|
||||||
glm::vec3 hitAt = glm::vec3(entityToWorldMatrix * glm::vec4(entityFrameHitAt, 1.0f));
|
|
||||||
distance = glm::distance(origin, hitAt);
|
|
||||||
bool success;
|
|
||||||
surfaceNormal = glm::normalize(hitAt - getCenterPosition(success));
|
|
||||||
if (!success) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void SphereEntityItem::debugDump() const {
|
|
||||||
quint64 now = usecTimestampNow();
|
|
||||||
qCDebug(entities) << "SHPERE EntityItem id:" << getEntityItemID() << "---------------------------------------------";
|
|
||||||
qCDebug(entities) << " color:" << _color[0] << "," << _color[1] << "," << _color[2];
|
|
||||||
qCDebug(entities) << " position:" << debugTreeVector(getPosition());
|
|
||||||
qCDebug(entities) << " dimensions:" << debugTreeVector(getDimensions());
|
|
||||||
qCDebug(entities) << " getLastEdited:" << debugTime(getLastEdited(), now);
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,70 +0,0 @@
|
||||||
//
|
|
||||||
// SphereEntityItem.h
|
|
||||||
// libraries/entities/src
|
|
||||||
//
|
|
||||||
// Created by Brad Hefta-Gaub on 12/4/13.
|
|
||||||
// Copyright 2013 High Fidelity, Inc.
|
|
||||||
//
|
|
||||||
// Distributed under the Apache License, Version 2.0.
|
|
||||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
|
||||||
//
|
|
||||||
|
|
||||||
#ifndef hifi_SphereEntityItem_h
|
|
||||||
#define hifi_SphereEntityItem_h
|
|
||||||
|
|
||||||
#include "EntityItem.h"
|
|
||||||
|
|
||||||
class SphereEntityItem : public EntityItem {
|
|
||||||
public:
|
|
||||||
static EntityItemPointer factory(const EntityItemID& entityID, const EntityItemProperties& properties);
|
|
||||||
|
|
||||||
SphereEntityItem(const EntityItemID& entityItemID);
|
|
||||||
|
|
||||||
ALLOW_INSTANTIATION // This class can be instantiated
|
|
||||||
|
|
||||||
// methods for getting/setting all properties of an entity
|
|
||||||
virtual EntityItemProperties getProperties(EntityPropertyFlags desiredProperties = EntityPropertyFlags()) const;
|
|
||||||
virtual bool setProperties(const EntityItemProperties& properties);
|
|
||||||
|
|
||||||
virtual EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const;
|
|
||||||
|
|
||||||
virtual void appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params,
|
|
||||||
EntityTreeElementExtraEncodeData* modelTreeElementExtraEncodeData,
|
|
||||||
EntityPropertyFlags& requestedProperties,
|
|
||||||
EntityPropertyFlags& propertyFlags,
|
|
||||||
EntityPropertyFlags& propertiesDidntFit,
|
|
||||||
int& propertyCount,
|
|
||||||
OctreeElement::AppendState& appendState) const;
|
|
||||||
|
|
||||||
virtual int readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead,
|
|
||||||
ReadBitstreamToTreeParams& args,
|
|
||||||
EntityPropertyFlags& propertyFlags, bool overwriteLocalData,
|
|
||||||
bool& somethingChanged);
|
|
||||||
|
|
||||||
const rgbColor& getColor() const { return _color; }
|
|
||||||
xColor getXColor() const { xColor color = { _color[RED_INDEX], _color[GREEN_INDEX], _color[BLUE_INDEX] }; return color; }
|
|
||||||
|
|
||||||
void setColor(const rgbColor& value) { memcpy(_color, value, sizeof(_color)); }
|
|
||||||
void setColor(const xColor& value) {
|
|
||||||
_color[RED_INDEX] = value.red;
|
|
||||||
_color[GREEN_INDEX] = value.green;
|
|
||||||
_color[BLUE_INDEX] = value.blue;
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual ShapeType getShapeType() const { return SHAPE_TYPE_SPHERE; }
|
|
||||||
virtual bool shouldBePhysical() const { return !isDead(); }
|
|
||||||
|
|
||||||
virtual bool supportsDetailedRayIntersection() const { return true; }
|
|
||||||
virtual bool findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
|
|
||||||
bool& keepSearching, OctreeElementPointer& element, float& distance,
|
|
||||||
BoxFace& face, glm::vec3& surfaceNormal,
|
|
||||||
void** intersectedObject, bool precisionPicking) const;
|
|
||||||
|
|
||||||
virtual void debugDump() const;
|
|
||||||
|
|
||||||
protected:
|
|
||||||
|
|
||||||
rgbColor _color;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif // hifi_SphereEntityItem_h
|
|
|
@ -39,7 +39,7 @@
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
static int FBXGeometryPointerMetaTypeId = qRegisterMetaType<FBXGeometry::Pointer>();
|
int FBXGeometryPointerMetaTypeId = qRegisterMetaType<FBXGeometry::Pointer>();
|
||||||
|
|
||||||
QStringList FBXGeometry::getJointNames() const {
|
QStringList FBXGeometry::getJointNames() const {
|
||||||
QStringList names;
|
QStringList names;
|
||||||
|
@ -130,9 +130,9 @@ QString FBXGeometry::getModelNameOfMesh(int meshIndex) const {
|
||||||
return QString();
|
return QString();
|
||||||
}
|
}
|
||||||
|
|
||||||
static int fbxGeometryMetaTypeId = qRegisterMetaType<FBXGeometry>();
|
int fbxGeometryMetaTypeId = qRegisterMetaType<FBXGeometry>();
|
||||||
static int fbxAnimationFrameMetaTypeId = qRegisterMetaType<FBXAnimationFrame>();
|
int fbxAnimationFrameMetaTypeId = qRegisterMetaType<FBXAnimationFrame>();
|
||||||
static int fbxAnimationFrameVectorMetaTypeId = qRegisterMetaType<QVector<FBXAnimationFrame> >();
|
int fbxAnimationFrameVectorMetaTypeId = qRegisterMetaType<QVector<FBXAnimationFrame> >();
|
||||||
|
|
||||||
|
|
||||||
glm::vec3 parseVec3(const QString& string) {
|
glm::vec3 parseVec3(const QString& string) {
|
||||||
|
|
|
@ -716,9 +716,9 @@ QQmlContext* OffscreenQmlSurface::getRootContext() {
|
||||||
}
|
}
|
||||||
|
|
||||||
Q_DECLARE_METATYPE(std::function<void()>);
|
Q_DECLARE_METATYPE(std::function<void()>);
|
||||||
static auto VoidLambdaType = qRegisterMetaType<std::function<void()>>();
|
auto VoidLambdaType = qRegisterMetaType<std::function<void()>>();
|
||||||
Q_DECLARE_METATYPE(std::function<QVariant()>);
|
Q_DECLARE_METATYPE(std::function<QVariant()>);
|
||||||
static auto VariantLambdaType = qRegisterMetaType<std::function<QVariant()>>();
|
auto VariantLambdaType = qRegisterMetaType<std::function<QVariant()>>();
|
||||||
|
|
||||||
|
|
||||||
void OffscreenQmlSurface::executeOnUiThread(std::function<void()> function, bool blocking ) {
|
void OffscreenQmlSurface::executeOnUiThread(std::function<void()> function, bool blocking ) {
|
||||||
|
|
|
@ -33,7 +33,7 @@ using namespace gpu::gl;
|
||||||
|
|
||||||
|
|
||||||
static const QString DEBUG_FLAG("HIFI_ENABLE_OPENGL_45");
|
static const QString DEBUG_FLAG("HIFI_ENABLE_OPENGL_45");
|
||||||
static bool enableOpenGL45 = QProcessEnvironment::systemEnvironment().contains(DEBUG_FLAG);
|
bool enableOpenGL45 = QProcessEnvironment::systemEnvironment().contains(DEBUG_FLAG);
|
||||||
|
|
||||||
Backend* GLBackend::createBackend() {
|
Backend* GLBackend::createBackend() {
|
||||||
|
|
||||||
|
|
|
@ -22,7 +22,7 @@
|
||||||
|
|
||||||
using namespace gpu;
|
using namespace gpu;
|
||||||
|
|
||||||
static int TexturePointerMetaTypeId = qRegisterMetaType<TexturePointer>();
|
int TexturePointerMetaTypeId = qRegisterMetaType<TexturePointer>();
|
||||||
|
|
||||||
std::atomic<uint32_t> Texture::_textureCPUCount{ 0 };
|
std::atomic<uint32_t> Texture::_textureCPUCount{ 0 };
|
||||||
std::atomic<Texture::Size> Texture::_textureCPUMemoryUsage{ 0 };
|
std::atomic<Texture::Size> Texture::_textureCPUMemoryUsage{ 0 };
|
||||||
|
|
|
@ -144,12 +144,21 @@ bool AddressManager::handleUrl(const QUrl& lookupUrl, LookupTrigger trigger) {
|
||||||
// 4. domain network address (IP or dns resolvable hostname)
|
// 4. domain network address (IP or dns resolvable hostname)
|
||||||
|
|
||||||
// use our regex'ed helpers to figure out what we're supposed to do with this
|
// use our regex'ed helpers to figure out what we're supposed to do with this
|
||||||
if (!handleUsername(lookupUrl.authority())) {
|
if (handleUsername(lookupUrl.authority())) {
|
||||||
|
// handled a username for lookup
|
||||||
|
|
||||||
|
// in case we're failing to connect to where we thought this user was
|
||||||
|
// store their username as previous lookup so we can refresh their location via API
|
||||||
|
_previousLookup = lookupUrl;
|
||||||
|
} else {
|
||||||
// we're assuming this is either a network address or global place name
|
// we're assuming this is either a network address or global place name
|
||||||
// check if it is a network address first
|
// check if it is a network address first
|
||||||
bool hostChanged;
|
bool hostChanged;
|
||||||
if (handleNetworkAddress(lookupUrl.host()
|
if (handleNetworkAddress(lookupUrl.host()
|
||||||
+ (lookupUrl.port() == -1 ? "" : ":" + QString::number(lookupUrl.port())), trigger, hostChanged)) {
|
+ (lookupUrl.port() == -1 ? "" : ":" + QString::number(lookupUrl.port())), trigger, hostChanged)) {
|
||||||
|
|
||||||
|
// a network address lookup clears the previous lookup since we don't expect to re-attempt it
|
||||||
|
_previousLookup.clear();
|
||||||
|
|
||||||
// If the host changed then we have already saved to history
|
// If the host changed then we have already saved to history
|
||||||
if (hostChanged) {
|
if (hostChanged) {
|
||||||
|
@ -165,10 +174,16 @@ bool AddressManager::handleUrl(const QUrl& lookupUrl, LookupTrigger trigger) {
|
||||||
// we may have a path that defines a relative viewpoint - if so we should jump to that now
|
// we may have a path that defines a relative viewpoint - if so we should jump to that now
|
||||||
handlePath(path, trigger);
|
handlePath(path, trigger);
|
||||||
} else if (handleDomainID(lookupUrl.host())){
|
} else if (handleDomainID(lookupUrl.host())){
|
||||||
|
// store this domain ID as the previous lookup in case we're failing to connect and want to refresh API info
|
||||||
|
_previousLookup = lookupUrl;
|
||||||
|
|
||||||
// no place name - this is probably a domain ID
|
// no place name - this is probably a domain ID
|
||||||
// try to look up the domain ID on the metaverse API
|
// try to look up the domain ID on the metaverse API
|
||||||
attemptDomainIDLookup(lookupUrl.host(), lookupUrl.path(), trigger);
|
attemptDomainIDLookup(lookupUrl.host(), lookupUrl.path(), trigger);
|
||||||
} else {
|
} else {
|
||||||
|
// store this place name as the previous lookup in case we fail to connect and want to refresh API info
|
||||||
|
_previousLookup = lookupUrl;
|
||||||
|
|
||||||
// wasn't an address - lookup the place name
|
// wasn't an address - lookup the place name
|
||||||
// we may have a path that defines a relative viewpoint - pass that through the lookup so we can go to it after
|
// we may have a path that defines a relative viewpoint - pass that through the lookup so we can go to it after
|
||||||
attemptPlaceNameLookup(lookupUrl.host(), lookupUrl.path(), trigger);
|
attemptPlaceNameLookup(lookupUrl.host(), lookupUrl.path(), trigger);
|
||||||
|
@ -180,9 +195,13 @@ bool AddressManager::handleUrl(const QUrl& lookupUrl, LookupTrigger trigger) {
|
||||||
} else if (lookupUrl.toString().startsWith('/')) {
|
} else if (lookupUrl.toString().startsWith('/')) {
|
||||||
qCDebug(networking) << "Going to relative path" << lookupUrl.path();
|
qCDebug(networking) << "Going to relative path" << lookupUrl.path();
|
||||||
|
|
||||||
|
// a path lookup clears the previous lookup since we don't expect to re-attempt it
|
||||||
|
_previousLookup.clear();
|
||||||
|
|
||||||
// if this is a relative path then handle it as a relative viewpoint
|
// if this is a relative path then handle it as a relative viewpoint
|
||||||
handlePath(lookupUrl.path(), trigger, true);
|
handlePath(lookupUrl.path(), trigger, true);
|
||||||
emit lookupResultsFinished();
|
emit lookupResultsFinished();
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -276,7 +295,7 @@ void AddressManager::goToAddressFromObject(const QVariantMap& dataObject, const
|
||||||
|
|
||||||
qCDebug(networking) << "Possible domain change required to connect to" << domainHostname
|
qCDebug(networking) << "Possible domain change required to connect to" << domainHostname
|
||||||
<< "on" << domainPort;
|
<< "on" << domainPort;
|
||||||
emit possibleDomainChangeRequired(domainHostname, domainPort);
|
emit possibleDomainChangeRequired(domainHostname, domainPort, domainID);
|
||||||
} else {
|
} else {
|
||||||
QString iceServerAddress = domainObject[DOMAIN_ICE_SERVER_ADDRESS_KEY].toString();
|
QString iceServerAddress = domainObject[DOMAIN_ICE_SERVER_ADDRESS_KEY].toString();
|
||||||
|
|
||||||
|
@ -315,7 +334,10 @@ void AddressManager::goToAddressFromObject(const QVariantMap& dataObject, const
|
||||||
QString overridePath = reply.property(OVERRIDE_PATH_KEY).toString();
|
QString overridePath = reply.property(OVERRIDE_PATH_KEY).toString();
|
||||||
|
|
||||||
if (!overridePath.isEmpty()) {
|
if (!overridePath.isEmpty()) {
|
||||||
handlePath(overridePath, trigger);
|
// make sure we don't re-handle an overriden path if this was a refresh of info from API
|
||||||
|
if (trigger != LookupTrigger::AttemptedRefresh) {
|
||||||
|
handlePath(overridePath, trigger);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// take the path that came back
|
// take the path that came back
|
||||||
const QString PLACE_PATH_KEY = "path";
|
const QString PLACE_PATH_KEY = "path";
|
||||||
|
@ -594,7 +616,7 @@ bool AddressManager::setDomainInfo(const QString& hostname, quint16 port, Lookup
|
||||||
|
|
||||||
DependencyManager::get<NodeList>()->flagTimeForConnectionStep(LimitedNodeList::ConnectionStep::HandleAddress);
|
DependencyManager::get<NodeList>()->flagTimeForConnectionStep(LimitedNodeList::ConnectionStep::HandleAddress);
|
||||||
|
|
||||||
emit possibleDomainChangeRequired(hostname, port);
|
emit possibleDomainChangeRequired(hostname, port, QUuid());
|
||||||
|
|
||||||
return hostChanged;
|
return hostChanged;
|
||||||
}
|
}
|
||||||
|
@ -614,6 +636,13 @@ void AddressManager::goToUser(const QString& username) {
|
||||||
QByteArray(), nullptr, requestParams);
|
QByteArray(), nullptr, requestParams);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AddressManager::refreshPreviousLookup() {
|
||||||
|
// if we have a non-empty previous lookup, fire it again now (but don't re-store it in the history)
|
||||||
|
if (!_previousLookup.isEmpty()) {
|
||||||
|
handleUrl(_previousLookup, LookupTrigger::AttemptedRefresh);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void AddressManager::copyAddress() {
|
void AddressManager::copyAddress() {
|
||||||
QApplication::clipboard()->setText(currentAddress().toString());
|
QApplication::clipboard()->setText(currentAddress().toString());
|
||||||
}
|
}
|
||||||
|
@ -625,7 +654,10 @@ void AddressManager::copyPath() {
|
||||||
void AddressManager::addCurrentAddressToHistory(LookupTrigger trigger) {
|
void AddressManager::addCurrentAddressToHistory(LookupTrigger trigger) {
|
||||||
|
|
||||||
// if we're cold starting and this is called for the first address (from settings) we don't do anything
|
// if we're cold starting and this is called for the first address (from settings) we don't do anything
|
||||||
if (trigger != LookupTrigger::StartupFromSettings && trigger != LookupTrigger::DomainPathResponse) {
|
if (trigger != LookupTrigger::StartupFromSettings
|
||||||
|
&& trigger != LookupTrigger::DomainPathResponse
|
||||||
|
&& trigger != LookupTrigger::AttemptedRefresh) {
|
||||||
|
|
||||||
if (trigger == LookupTrigger::Back) {
|
if (trigger == LookupTrigger::Back) {
|
||||||
// we're about to push to the forward stack
|
// we're about to push to the forward stack
|
||||||
// if it's currently empty emit our signal to say that going forward is now possible
|
// if it's currently empty emit our signal to say that going forward is now possible
|
||||||
|
|
|
@ -48,7 +48,8 @@ public:
|
||||||
Forward,
|
Forward,
|
||||||
StartupFromSettings,
|
StartupFromSettings,
|
||||||
DomainPathResponse,
|
DomainPathResponse,
|
||||||
Internal
|
Internal,
|
||||||
|
AttemptedRefresh
|
||||||
};
|
};
|
||||||
|
|
||||||
bool isConnected();
|
bool isConnected();
|
||||||
|
@ -89,6 +90,8 @@ public slots:
|
||||||
|
|
||||||
void goToUser(const QString& username);
|
void goToUser(const QString& username);
|
||||||
|
|
||||||
|
void refreshPreviousLookup();
|
||||||
|
|
||||||
void storeCurrentAddress();
|
void storeCurrentAddress();
|
||||||
|
|
||||||
void copyAddress();
|
void copyAddress();
|
||||||
|
@ -99,7 +102,7 @@ signals:
|
||||||
void lookupResultIsOffline();
|
void lookupResultIsOffline();
|
||||||
void lookupResultIsNotFound();
|
void lookupResultIsNotFound();
|
||||||
|
|
||||||
void possibleDomainChangeRequired(const QString& newHostname, quint16 newPort);
|
void possibleDomainChangeRequired(const QString& newHostname, quint16 newPort, const QUuid& domainID);
|
||||||
void possibleDomainChangeRequiredViaICEForID(const QString& iceServerHostname, const QUuid& domainID);
|
void possibleDomainChangeRequiredViaICEForID(const QString& iceServerHostname, const QUuid& domainID);
|
||||||
|
|
||||||
void locationChangeRequired(const glm::vec3& newPosition,
|
void locationChangeRequired(const glm::vec3& newPosition,
|
||||||
|
@ -152,6 +155,8 @@ private:
|
||||||
quint64 _lastBackPush = 0;
|
quint64 _lastBackPush = 0;
|
||||||
|
|
||||||
QString _newHostLookupPath;
|
QString _newHostLookupPath;
|
||||||
|
|
||||||
|
QUrl _previousLookup;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // hifi_AddressManager_h
|
#endif // hifi_AddressManager_h
|
||||||
|
|
|
@ -28,16 +28,8 @@
|
||||||
|
|
||||||
DomainHandler::DomainHandler(QObject* parent) :
|
DomainHandler::DomainHandler(QObject* parent) :
|
||||||
QObject(parent),
|
QObject(parent),
|
||||||
_uuid(),
|
|
||||||
_sockAddr(HifiSockAddr(QHostAddress::Null, DEFAULT_DOMAIN_SERVER_PORT)),
|
_sockAddr(HifiSockAddr(QHostAddress::Null, DEFAULT_DOMAIN_SERVER_PORT)),
|
||||||
_assignmentUUID(),
|
|
||||||
_connectionToken(),
|
|
||||||
_iceDomainID(),
|
|
||||||
_iceClientID(),
|
|
||||||
_iceServerSockAddr(),
|
|
||||||
_icePeer(this),
|
_icePeer(this),
|
||||||
_isConnected(false),
|
|
||||||
_settingsObject(),
|
|
||||||
_settingsTimer(this)
|
_settingsTimer(this)
|
||||||
{
|
{
|
||||||
_sockAddr.setObjectName("DomainServer");
|
_sockAddr.setObjectName("DomainServer");
|
||||||
|
@ -105,7 +97,7 @@ void DomainHandler::hardReset() {
|
||||||
softReset();
|
softReset();
|
||||||
|
|
||||||
qCDebug(networking) << "Hard reset in NodeList DomainHandler.";
|
qCDebug(networking) << "Hard reset in NodeList DomainHandler.";
|
||||||
_iceDomainID = QUuid();
|
_pendingDomainID = QUuid();
|
||||||
_iceServerSockAddr = HifiSockAddr();
|
_iceServerSockAddr = HifiSockAddr();
|
||||||
_hostname = QString();
|
_hostname = QString();
|
||||||
_sockAddr.clear();
|
_sockAddr.clear();
|
||||||
|
@ -140,7 +132,7 @@ void DomainHandler::setUUID(const QUuid& uuid) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void DomainHandler::setHostnameAndPort(const QString& hostname, quint16 port) {
|
void DomainHandler::setSocketAndID(const QString& hostname, quint16 port, const QUuid& domainID) {
|
||||||
|
|
||||||
if (hostname != _hostname || _sockAddr.getPort() != port) {
|
if (hostname != _hostname || _sockAddr.getPort() != port) {
|
||||||
// re-set the domain info so that auth information is reloaded
|
// re-set the domain info so that auth information is reloaded
|
||||||
|
@ -169,6 +161,8 @@ void DomainHandler::setHostnameAndPort(const QString& hostname, quint16 port) {
|
||||||
// grab the port by reading the string after the colon
|
// grab the port by reading the string after the colon
|
||||||
_sockAddr.setPort(port);
|
_sockAddr.setPort(port);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_pendingDomainID = domainID;
|
||||||
}
|
}
|
||||||
|
|
||||||
void DomainHandler::setIceServerHostnameAndID(const QString& iceServerHostname, const QUuid& id) {
|
void DomainHandler::setIceServerHostnameAndID(const QString& iceServerHostname, const QUuid& id) {
|
||||||
|
@ -179,7 +173,7 @@ void DomainHandler::setIceServerHostnameAndID(const QString& iceServerHostname,
|
||||||
// refresh our ICE client UUID to something new
|
// refresh our ICE client UUID to something new
|
||||||
_iceClientID = QUuid::createUuid();
|
_iceClientID = QUuid::createUuid();
|
||||||
|
|
||||||
_iceDomainID = id;
|
_pendingDomainID = id;
|
||||||
|
|
||||||
HifiSockAddr* replaceableSockAddr = &_iceServerSockAddr;
|
HifiSockAddr* replaceableSockAddr = &_iceServerSockAddr;
|
||||||
replaceableSockAddr->~HifiSockAddr();
|
replaceableSockAddr->~HifiSockAddr();
|
||||||
|
@ -341,7 +335,7 @@ void DomainHandler::processICEResponsePacket(QSharedPointer<ReceivedMessage> mes
|
||||||
|
|
||||||
DependencyManager::get<NodeList>()->flagTimeForConnectionStep(LimitedNodeList::ConnectionStep::ReceiveDSPeerInformation);
|
DependencyManager::get<NodeList>()->flagTimeForConnectionStep(LimitedNodeList::ConnectionStep::ReceiveDSPeerInformation);
|
||||||
|
|
||||||
if (_icePeer.getUUID() != _iceDomainID) {
|
if (_icePeer.getUUID() != _pendingDomainID) {
|
||||||
qCDebug(networking) << "Received a network peer with ID that does not match current domain. Will not attempt connection.";
|
qCDebug(networking) << "Received a network peer with ID that does not match current domain. Will not attempt connection.";
|
||||||
_icePeer.reset();
|
_icePeer.reset();
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -58,8 +58,8 @@ public:
|
||||||
|
|
||||||
const QUuid& getAssignmentUUID() const { return _assignmentUUID; }
|
const QUuid& getAssignmentUUID() const { return _assignmentUUID; }
|
||||||
void setAssignmentUUID(const QUuid& assignmentUUID) { _assignmentUUID = assignmentUUID; }
|
void setAssignmentUUID(const QUuid& assignmentUUID) { _assignmentUUID = assignmentUUID; }
|
||||||
|
|
||||||
const QUuid& getICEDomainID() const { return _iceDomainID; }
|
const QUuid& getPendingDomainID() const { return _pendingDomainID; }
|
||||||
|
|
||||||
const QUuid& getICEClientID() const { return _iceClientID; }
|
const QUuid& getICEClientID() const { return _iceClientID; }
|
||||||
|
|
||||||
|
@ -85,7 +85,7 @@ public:
|
||||||
|
|
||||||
void softReset();
|
void softReset();
|
||||||
public slots:
|
public slots:
|
||||||
void setHostnameAndPort(const QString& hostname, quint16 port = DEFAULT_DOMAIN_SERVER_PORT);
|
void setSocketAndID(const QString& hostname, quint16 port = DEFAULT_DOMAIN_SERVER_PORT, const QUuid& id = QUuid());
|
||||||
void setIceServerHostnameAndID(const QString& iceServerHostname, const QUuid& id);
|
void setIceServerHostnameAndID(const QString& iceServerHostname, const QUuid& id);
|
||||||
|
|
||||||
void processSettingsPacketList(QSharedPointer<ReceivedMessage> packetList);
|
void processSettingsPacketList(QSharedPointer<ReceivedMessage> packetList);
|
||||||
|
@ -126,11 +126,11 @@ private:
|
||||||
HifiSockAddr _sockAddr;
|
HifiSockAddr _sockAddr;
|
||||||
QUuid _assignmentUUID;
|
QUuid _assignmentUUID;
|
||||||
QUuid _connectionToken;
|
QUuid _connectionToken;
|
||||||
QUuid _iceDomainID;
|
QUuid _pendingDomainID; // ID of domain being connected to, via ICE or direct connection
|
||||||
QUuid _iceClientID;
|
QUuid _iceClientID;
|
||||||
HifiSockAddr _iceServerSockAddr;
|
HifiSockAddr _iceServerSockAddr;
|
||||||
NetworkPeer _icePeer;
|
NetworkPeer _icePeer;
|
||||||
bool _isConnected;
|
bool _isConnected { false };
|
||||||
QJsonObject _settingsObject;
|
QJsonObject _settingsObject;
|
||||||
QString _pendingPath;
|
QString _pendingPath;
|
||||||
QTimer _settingsTimer;
|
QTimer _settingsTimer;
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
#include "HifiSockAddr.h"
|
#include "HifiSockAddr.h"
|
||||||
#include "NetworkLogging.h"
|
#include "NetworkLogging.h"
|
||||||
|
|
||||||
static int hifiSockAddrMetaTypeId = qRegisterMetaType<HifiSockAddr>();
|
int hifiSockAddrMetaTypeId = qRegisterMetaType<HifiSockAddr>();
|
||||||
|
|
||||||
HifiSockAddr::HifiSockAddr() :
|
HifiSockAddr::HifiSockAddr() :
|
||||||
_address(),
|
_address(),
|
||||||
|
|
|
@ -22,9 +22,9 @@
|
||||||
|
|
||||||
const QString UNKNOWN_NodeType_t_NAME = "Unknown";
|
const QString UNKNOWN_NodeType_t_NAME = "Unknown";
|
||||||
|
|
||||||
static int NodePtrMetaTypeId = qRegisterMetaType<Node*>("Node*");
|
int NodePtrMetaTypeId = qRegisterMetaType<Node*>("Node*");
|
||||||
static int sharedPtrNodeMetaTypeId = qRegisterMetaType<QSharedPointer<Node>>("QSharedPointer<Node>");
|
int sharedPtrNodeMetaTypeId = qRegisterMetaType<QSharedPointer<Node>>("QSharedPointer<Node>");
|
||||||
static int sharedNodePtrMetaTypeId = qRegisterMetaType<SharedNodePointer>("SharedNodePointer");
|
int sharedNodePtrMetaTypeId = qRegisterMetaType<SharedNodePointer>("SharedNodePointer");
|
||||||
|
|
||||||
namespace NodeType {
|
namespace NodeType {
|
||||||
QHash<NodeType_t, QString> TypeNameHash;
|
QHash<NodeType_t, QString> TypeNameHash;
|
||||||
|
|
|
@ -50,7 +50,7 @@ NodeList::NodeList(char newOwnerType, unsigned short socketListenPort, unsigned
|
||||||
|
|
||||||
// handle domain change signals from AddressManager
|
// handle domain change signals from AddressManager
|
||||||
connect(addressManager.data(), &AddressManager::possibleDomainChangeRequired,
|
connect(addressManager.data(), &AddressManager::possibleDomainChangeRequired,
|
||||||
&_domainHandler, &DomainHandler::setHostnameAndPort);
|
&_domainHandler, &DomainHandler::setSocketAndID);
|
||||||
|
|
||||||
connect(addressManager.data(), &AddressManager::possibleDomainChangeRequiredViaICEForID,
|
connect(addressManager.data(), &AddressManager::possibleDomainChangeRequiredViaICEForID,
|
||||||
&_domainHandler, &DomainHandler::setIceServerHostnameAndID);
|
&_domainHandler, &DomainHandler::setIceServerHostnameAndID);
|
||||||
|
@ -346,6 +346,14 @@ void NodeList::sendDomainServerCheckIn() {
|
||||||
// increment the count of un-replied check-ins
|
// increment the count of un-replied check-ins
|
||||||
_numNoReplyDomainCheckIns++;
|
_numNoReplyDomainCheckIns++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!_publicSockAddr.isNull() && !_domainHandler.isConnected() && !_domainHandler.getPendingDomainID().isNull()) {
|
||||||
|
// if we aren't connected to the domain-server, and we have an ID
|
||||||
|
// (that we presume belongs to a domain in the HF Metaverse)
|
||||||
|
// we request connection information for the domain every so often to make sure what we have is up to date
|
||||||
|
|
||||||
|
DependencyManager::get<AddressManager>()->refreshPreviousLookup();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void NodeList::handleDSPathQuery(const QString& newPath) {
|
void NodeList::handleDSPathQuery(const QString& newPath) {
|
||||||
|
@ -453,7 +461,7 @@ void NodeList::handleICEConnectionToDomainServer() {
|
||||||
|
|
||||||
LimitedNodeList::sendPeerQueryToIceServer(_domainHandler.getICEServerSockAddr(),
|
LimitedNodeList::sendPeerQueryToIceServer(_domainHandler.getICEServerSockAddr(),
|
||||||
_domainHandler.getICEClientID(),
|
_domainHandler.getICEClientID(),
|
||||||
_domainHandler.getICEDomainID());
|
_domainHandler.getPendingDomainID());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -466,7 +474,7 @@ void NodeList::pingPunchForDomainServer() {
|
||||||
|
|
||||||
if (_domainHandler.getICEPeer().getConnectionAttempts() == 0) {
|
if (_domainHandler.getICEPeer().getConnectionAttempts() == 0) {
|
||||||
qCDebug(networking) << "Sending ping packets to establish connectivity with domain-server with ID"
|
qCDebug(networking) << "Sending ping packets to establish connectivity with domain-server with ID"
|
||||||
<< uuidStringWithoutCurlyBraces(_domainHandler.getICEDomainID());
|
<< uuidStringWithoutCurlyBraces(_domainHandler.getPendingDomainID());
|
||||||
} else {
|
} else {
|
||||||
if (_domainHandler.getICEPeer().getConnectionAttempts() % NUM_DOMAIN_SERVER_PINGS_BEFORE_RESET == 0) {
|
if (_domainHandler.getICEPeer().getConnectionAttempts() % NUM_DOMAIN_SERVER_PINGS_BEFORE_RESET == 0) {
|
||||||
// if we have then nullify the domain handler's network peer and send a fresh ICE heartbeat
|
// if we have then nullify the domain handler's network peer and send a fresh ICE heartbeat
|
||||||
|
|
|
@ -14,8 +14,8 @@
|
||||||
|
|
||||||
#include "QSharedPointer"
|
#include "QSharedPointer"
|
||||||
|
|
||||||
static int receivedMessageMetaTypeId = qRegisterMetaType<ReceivedMessage*>("ReceivedMessage*");
|
int receivedMessageMetaTypeId = qRegisterMetaType<ReceivedMessage*>("ReceivedMessage*");
|
||||||
static int sharedPtrReceivedMessageMetaTypeId = qRegisterMetaType<QSharedPointer<ReceivedMessage>>("QSharedPointer<ReceivedMessage>");
|
int sharedPtrReceivedMessageMetaTypeId = qRegisterMetaType<QSharedPointer<ReceivedMessage>>("QSharedPointer<ReceivedMessage>");
|
||||||
|
|
||||||
static const int HEAD_DATA_SIZE = 512;
|
static const int HEAD_DATA_SIZE = 512;
|
||||||
|
|
||||||
|
|
|
@ -19,7 +19,7 @@
|
||||||
|
|
||||||
using namespace udt;
|
using namespace udt;
|
||||||
|
|
||||||
static int packetMetaTypeId = qRegisterMetaType<Packet*>("Packet*");
|
int packetMetaTypeId = qRegisterMetaType<Packet*>("Packet*");
|
||||||
|
|
||||||
using Key = uint64_t;
|
using Key = uint64_t;
|
||||||
static const std::array<Key, 4> KEYS {{
|
static const std::array<Key, 4> KEYS {{
|
||||||
|
|
|
@ -18,7 +18,7 @@
|
||||||
|
|
||||||
|
|
||||||
Q_DECLARE_METATYPE(PacketType);
|
Q_DECLARE_METATYPE(PacketType);
|
||||||
static int packetTypeMetaTypeId = qRegisterMetaType<PacketType>();
|
int packetTypeMetaTypeId = qRegisterMetaType<PacketType>();
|
||||||
|
|
||||||
const QSet<PacketType> NON_VERIFIED_PACKETS = QSet<PacketType>()
|
const QSet<PacketType> NON_VERIFIED_PACKETS = QSet<PacketType>()
|
||||||
<< PacketType::NodeJsonStats << PacketType::EntityQuery
|
<< PacketType::NodeJsonStats << PacketType::EntityQuery
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
|
|
||||||
using namespace udt;
|
using namespace udt;
|
||||||
|
|
||||||
static int packetListMetaTypeId = qRegisterMetaType<PacketList*>("PacketList*");
|
int packetListMetaTypeId = qRegisterMetaType<PacketList*>("PacketList*");
|
||||||
|
|
||||||
std::unique_ptr<PacketList> PacketList::create(PacketType packetType, QByteArray extendedHeader,
|
std::unique_ptr<PacketList> PacketList::create(PacketType packetType, QByteArray extendedHeader,
|
||||||
bool isReliable, bool isOrdered) {
|
bool isReliable, bool isOrdered) {
|
||||||
|
|
|
@ -51,8 +51,8 @@ static gpu::Stream::FormatPointer INSTANCED_SOLID_STREAM_FORMAT;
|
||||||
|
|
||||||
static const uint SHAPE_VERTEX_STRIDE = sizeof(glm::vec3) * 2; // vertices and normals
|
static const uint SHAPE_VERTEX_STRIDE = sizeof(glm::vec3) * 2; // vertices and normals
|
||||||
static const uint SHAPE_NORMALS_OFFSET = sizeof(glm::vec3);
|
static const uint SHAPE_NORMALS_OFFSET = sizeof(glm::vec3);
|
||||||
static const gpu::Type SHAPE_INDEX_TYPE = gpu::UINT16;
|
static const gpu::Type SHAPE_INDEX_TYPE = gpu::UINT32;
|
||||||
static const uint SHAPE_INDEX_SIZE = sizeof(gpu::uint16);
|
static const uint SHAPE_INDEX_SIZE = sizeof(gpu::uint32);
|
||||||
|
|
||||||
void GeometryCache::ShapeData::setupVertices(gpu::BufferPointer& vertexBuffer, const VertexVector& vertices) {
|
void GeometryCache::ShapeData::setupVertices(gpu::BufferPointer& vertexBuffer, const VertexVector& vertices) {
|
||||||
vertexBuffer->append(vertices);
|
vertexBuffer->append(vertices);
|
||||||
|
@ -112,101 +112,7 @@ void GeometryCache::ShapeData::drawWireInstances(gpu::Batch& batch, size_t count
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const VertexVector& icosahedronVertices() {
|
|
||||||
static const float phi = (1.0f + sqrtf(5.0f)) / 2.0f;
|
|
||||||
static const float a = 0.5f;
|
|
||||||
static const float b = 1.0f / (2.0f * phi);
|
|
||||||
|
|
||||||
static const VertexVector vertices{ //
|
|
||||||
vec3(0, b, -a), vec3(-b, a, 0), vec3(b, a, 0), //
|
|
||||||
vec3(0, b, a), vec3(b, a, 0), vec3(-b, a, 0), //
|
|
||||||
vec3(0, b, a), vec3(-a, 0, b), vec3(0, -b, a), //
|
|
||||||
vec3(0, b, a), vec3(0, -b, a), vec3(a, 0, b), //
|
|
||||||
vec3(0, b, -a), vec3(a, 0, -b), vec3(0, -b, -a),//
|
|
||||||
vec3(0, b, -a), vec3(0, -b, -a), vec3(-a, 0, -b), //
|
|
||||||
vec3(0, -b, a), vec3(-b, -a, 0), vec3(b, -a, 0), //
|
|
||||||
vec3(0, -b, -a), vec3(b, -a, 0), vec3(-b, -a, 0), //
|
|
||||||
vec3(-b, a, 0), vec3(-a, 0, -b), vec3(-a, 0, b), //
|
|
||||||
vec3(-b, -a, 0), vec3(-a, 0, b), vec3(-a, 0, -b), //
|
|
||||||
vec3(b, a, 0), vec3(a, 0, b), vec3(a, 0, -b), //
|
|
||||||
vec3(b, -a, 0), vec3(a, 0, -b), vec3(a, 0, b), //
|
|
||||||
vec3(0, b, a), vec3(-b, a, 0), vec3(-a, 0, b), //
|
|
||||||
vec3(0, b, a), vec3(a, 0, b), vec3(b, a, 0), //
|
|
||||||
vec3(0, b, -a), vec3(-a, 0, -b), vec3(-b, a, 0), //
|
|
||||||
vec3(0, b, -a), vec3(b, a, 0), vec3(a, 0, -b), //
|
|
||||||
vec3(0, -b, -a), vec3(-b, -a, 0), vec3(-a, 0, -b), //
|
|
||||||
vec3(0, -b, -a), vec3(a, 0, -b), vec3(b, -a, 0), //
|
|
||||||
vec3(0, -b, a), vec3(-a, 0, b), vec3(-b, -a, 0), //
|
|
||||||
vec3(0, -b, a), vec3(b, -a, 0), vec3(a, 0, b)
|
|
||||||
}; //
|
|
||||||
return vertices;
|
|
||||||
}
|
|
||||||
|
|
||||||
const VertexVector& tetrahedronVertices() {
|
|
||||||
static const float a = 1.0f / sqrtf(2.0f);
|
|
||||||
static const auto A = vec3(0, 1, a);
|
|
||||||
static const auto B = vec3(0, -1, a);
|
|
||||||
static const auto C = vec3(1, 0, -a);
|
|
||||||
static const auto D = vec3(-1, 0, -a);
|
|
||||||
static const VertexVector vertices{
|
|
||||||
A, B, C,
|
|
||||||
D, B, A,
|
|
||||||
C, D, A,
|
|
||||||
C, B, D,
|
|
||||||
};
|
|
||||||
return vertices;
|
|
||||||
}
|
|
||||||
|
|
||||||
static const size_t TESSELTATION_MULTIPLIER = 4;
|
|
||||||
static const size_t ICOSAHEDRON_TO_SPHERE_TESSELATION_COUNT = 3;
|
static const size_t ICOSAHEDRON_TO_SPHERE_TESSELATION_COUNT = 3;
|
||||||
static const size_t VECTOR_TO_VECTOR_WITH_NORMAL_MULTIPLER = 2;
|
|
||||||
|
|
||||||
|
|
||||||
VertexVector tesselate(const VertexVector& startingTriangles, int count) {
|
|
||||||
VertexVector triangles = startingTriangles;
|
|
||||||
if (0 != (triangles.size() % 3)) {
|
|
||||||
throw std::runtime_error("Bad number of vertices for tesselation");
|
|
||||||
}
|
|
||||||
|
|
||||||
for (size_t i = 0; i < triangles.size(); ++i) {
|
|
||||||
triangles[i] = glm::normalize(triangles[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
VertexVector newTriangles;
|
|
||||||
while (count) {
|
|
||||||
newTriangles.clear();
|
|
||||||
// Tesselation takes one triangle and makes it into 4 triangles
|
|
||||||
// See https://en.wikipedia.org/wiki/Space-filling_tree#/media/File:Space_Filling_Tree_Tri_iter_1_2_3.png
|
|
||||||
newTriangles.reserve(triangles.size() * TESSELTATION_MULTIPLIER);
|
|
||||||
for (size_t i = 0; i < triangles.size(); i += VERTICES_PER_TRIANGLE) {
|
|
||||||
const vec3& a = triangles[i];
|
|
||||||
const vec3& b = triangles[i + 1];
|
|
||||||
const vec3& c = triangles[i + 2];
|
|
||||||
vec3 ab = glm::normalize(a + b);
|
|
||||||
vec3 bc = glm::normalize(b + c);
|
|
||||||
vec3 ca = glm::normalize(c + a);
|
|
||||||
|
|
||||||
newTriangles.push_back(a);
|
|
||||||
newTriangles.push_back(ab);
|
|
||||||
newTriangles.push_back(ca);
|
|
||||||
|
|
||||||
newTriangles.push_back(b);
|
|
||||||
newTriangles.push_back(bc);
|
|
||||||
newTriangles.push_back(ab);
|
|
||||||
|
|
||||||
newTriangles.push_back(c);
|
|
||||||
newTriangles.push_back(ca);
|
|
||||||
newTriangles.push_back(bc);
|
|
||||||
|
|
||||||
newTriangles.push_back(ab);
|
|
||||||
newTriangles.push_back(bc);
|
|
||||||
newTriangles.push_back(ca);
|
|
||||||
}
|
|
||||||
triangles.swap(newTriangles);
|
|
||||||
--count;
|
|
||||||
}
|
|
||||||
return triangles;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t GeometryCache::getShapeTriangleCount(Shape shape) {
|
size_t GeometryCache::getShapeTriangleCount(Shape shape) {
|
||||||
return _shapes[shape]._indexCount / VERTICES_PER_TRIANGLE;
|
return _shapes[shape]._indexCount / VERTICES_PER_TRIANGLE;
|
||||||
|
@ -220,6 +126,324 @@ size_t GeometryCache::getCubeTriangleCount() {
|
||||||
return getShapeTriangleCount(Cube);
|
return getShapeTriangleCount(Cube);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
using Index = uint32_t;
|
||||||
|
using IndexPair = uint64_t;
|
||||||
|
using IndexPairs = std::unordered_set<IndexPair>;
|
||||||
|
|
||||||
|
template <size_t N>
|
||||||
|
using Face = std::array<Index, N>;
|
||||||
|
|
||||||
|
template <size_t N>
|
||||||
|
using FaceVector = std::vector<Face<N>>;
|
||||||
|
|
||||||
|
template <size_t N>
|
||||||
|
struct Solid {
|
||||||
|
VertexVector vertices;
|
||||||
|
FaceVector<N> faces;
|
||||||
|
|
||||||
|
Solid<N>& fitDimension(float newMaxDimension) {
|
||||||
|
float maxDimension = 0;
|
||||||
|
for (const auto& vertex : vertices) {
|
||||||
|
maxDimension = std::max(maxDimension, std::max(std::max(vertex.x, vertex.y), vertex.z));
|
||||||
|
}
|
||||||
|
float multiplier = newMaxDimension / maxDimension;
|
||||||
|
for (auto& vertex : vertices) {
|
||||||
|
vertex *= multiplier;
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
vec3 getFaceNormal(size_t faceIndex) const {
|
||||||
|
vec3 result;
|
||||||
|
const auto& face = faces[faceIndex];
|
||||||
|
for (size_t i = 0; i < N; ++i) {
|
||||||
|
result += vertices[face[i]];
|
||||||
|
}
|
||||||
|
result /= N;
|
||||||
|
return glm::normalize(result);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <size_t N>
|
||||||
|
static size_t triangulatedFaceTriangleCount() {
|
||||||
|
return N - 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <size_t N>
|
||||||
|
static size_t triangulatedFaceIndexCount() {
|
||||||
|
return triangulatedFaceTriangleCount<N>() * VERTICES_PER_TRIANGLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static IndexPair indexToken(Index a, Index b) {
|
||||||
|
if (a > b) {
|
||||||
|
std::swap(a, b);
|
||||||
|
}
|
||||||
|
return (((IndexPair)a) << 32) | ((IndexPair)b);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Solid<3> tesselate(Solid<3> solid, int count) {
|
||||||
|
float length = glm::length(solid.vertices[0]);
|
||||||
|
for (int i = 0; i < count; ++i) {
|
||||||
|
Solid<3> result { solid.vertices, {} };
|
||||||
|
result.vertices.reserve(solid.vertices.size() + solid.faces.size() * 3);
|
||||||
|
for (size_t f = 0; f < solid.faces.size(); ++f) {
|
||||||
|
Index baseVertex = (Index)result.vertices.size();
|
||||||
|
const Face<3>& oldFace = solid.faces[f];
|
||||||
|
const vec3& a = solid.vertices[oldFace[0]];
|
||||||
|
const vec3& b = solid.vertices[oldFace[1]];
|
||||||
|
const vec3& c = solid.vertices[oldFace[2]];
|
||||||
|
vec3 ab = glm::normalize(a + b) * length;
|
||||||
|
vec3 bc = glm::normalize(b + c) * length;
|
||||||
|
vec3 ca = glm::normalize(c + a) * length;
|
||||||
|
result.vertices.push_back(ab);
|
||||||
|
result.vertices.push_back(bc);
|
||||||
|
result.vertices.push_back(ca);
|
||||||
|
result.faces.push_back(Face<3>{ { oldFace[0], baseVertex, baseVertex + 2 } });
|
||||||
|
result.faces.push_back(Face<3>{ { baseVertex, oldFace[1], baseVertex + 1 } });
|
||||||
|
result.faces.push_back(Face<3>{ { baseVertex + 1, oldFace[2], baseVertex + 2 } });
|
||||||
|
result.faces.push_back(Face<3>{ { baseVertex, baseVertex + 1, baseVertex + 2 } });
|
||||||
|
}
|
||||||
|
solid = result;
|
||||||
|
}
|
||||||
|
return solid;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <size_t N>
|
||||||
|
void setupFlatShape(GeometryCache::ShapeData& shapeData, const Solid<N>& shape, gpu::BufferPointer& vertexBuffer, gpu::BufferPointer& indexBuffer) {
|
||||||
|
Index baseVertex = (Index)(vertexBuffer->getSize() / SHAPE_VERTEX_STRIDE);
|
||||||
|
VertexVector vertices;
|
||||||
|
IndexVector solidIndices, wireIndices;
|
||||||
|
IndexPairs wireSeenIndices;
|
||||||
|
|
||||||
|
size_t faceCount = shape.faces.size();
|
||||||
|
size_t faceIndexCount = triangulatedFaceIndexCount<N>();
|
||||||
|
|
||||||
|
vertices.reserve(N * faceCount * 2);
|
||||||
|
solidIndices.reserve(faceIndexCount * faceCount);
|
||||||
|
|
||||||
|
for (size_t f = 0; f < faceCount; ++f) {
|
||||||
|
const Face<N>& face = shape.faces[f];
|
||||||
|
// Compute the face normal
|
||||||
|
vec3 faceNormal = shape.getFaceNormal(f);
|
||||||
|
|
||||||
|
// Create the vertices for the face
|
||||||
|
for (Index i = 0; i < N; ++i) {
|
||||||
|
Index originalIndex = face[i];
|
||||||
|
vertices.push_back(shape.vertices[originalIndex]);
|
||||||
|
vertices.push_back(faceNormal);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the wire indices for unseen edges
|
||||||
|
for (Index i = 0; i < N; ++i) {
|
||||||
|
Index a = i;
|
||||||
|
Index b = (i + 1) % N;
|
||||||
|
auto token = indexToken(face[a], face[b]);
|
||||||
|
if (0 == wireSeenIndices.count(token)) {
|
||||||
|
wireSeenIndices.insert(token);
|
||||||
|
wireIndices.push_back(a + baseVertex);
|
||||||
|
wireIndices.push_back(b + baseVertex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the solid face indices
|
||||||
|
for (Index i = 0; i < N - 2; ++i) {
|
||||||
|
solidIndices.push_back(0 + baseVertex);
|
||||||
|
solidIndices.push_back(i + 1 + baseVertex);
|
||||||
|
solidIndices.push_back(i + 2 + baseVertex);
|
||||||
|
}
|
||||||
|
baseVertex += (Index)N;
|
||||||
|
}
|
||||||
|
|
||||||
|
shapeData.setupVertices(vertexBuffer, vertices);
|
||||||
|
shapeData.setupIndices(indexBuffer, solidIndices, wireIndices);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <size_t N>
|
||||||
|
void setupSmoothShape(GeometryCache::ShapeData& shapeData, const Solid<N>& shape, gpu::BufferPointer& vertexBuffer, gpu::BufferPointer& indexBuffer) {
|
||||||
|
Index baseVertex = (Index)(vertexBuffer->getSize() / SHAPE_VERTEX_STRIDE);
|
||||||
|
|
||||||
|
VertexVector vertices;
|
||||||
|
vertices.reserve(shape.vertices.size() * 2);
|
||||||
|
for (const auto& vertex : shape.vertices) {
|
||||||
|
vertices.push_back(vertex);
|
||||||
|
vertices.push_back(vertex);
|
||||||
|
}
|
||||||
|
|
||||||
|
IndexVector solidIndices, wireIndices;
|
||||||
|
IndexPairs wireSeenIndices;
|
||||||
|
|
||||||
|
size_t faceCount = shape.faces.size();
|
||||||
|
size_t faceIndexCount = triangulatedFaceIndexCount<N>();
|
||||||
|
|
||||||
|
solidIndices.reserve(faceIndexCount * faceCount);
|
||||||
|
|
||||||
|
for (size_t f = 0; f < faceCount; ++f) {
|
||||||
|
const Face<N>& face = shape.faces[f];
|
||||||
|
// Create the wire indices for unseen edges
|
||||||
|
for (Index i = 0; i < N; ++i) {
|
||||||
|
Index a = face[i];
|
||||||
|
Index b = face[(i + 1) % N];
|
||||||
|
auto token = indexToken(a, b);
|
||||||
|
if (0 == wireSeenIndices.count(token)) {
|
||||||
|
wireSeenIndices.insert(token);
|
||||||
|
wireIndices.push_back(a + baseVertex);
|
||||||
|
wireIndices.push_back(b + baseVertex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the solid face indices
|
||||||
|
for (Index i = 0; i < N - 2; ++i) {
|
||||||
|
solidIndices.push_back(face[i] + baseVertex);
|
||||||
|
solidIndices.push_back(face[i + 1] + baseVertex);
|
||||||
|
solidIndices.push_back(face[i + 2] + baseVertex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
shapeData.setupVertices(vertexBuffer, vertices);
|
||||||
|
shapeData.setupIndices(indexBuffer, solidIndices, wireIndices);
|
||||||
|
}
|
||||||
|
|
||||||
|
// The golden ratio
|
||||||
|
static const float PHI = 1.61803398874f;
|
||||||
|
|
||||||
|
static const Solid<3>& tetrahedron() {
|
||||||
|
static const auto A = vec3(1, 1, 1);
|
||||||
|
static const auto B = vec3(1, -1, -1);
|
||||||
|
static const auto C = vec3(-1, 1, -1);
|
||||||
|
static const auto D = vec3(-1, -1, 1);
|
||||||
|
static const Solid<3> TETRAHEDRON = Solid<3>{
|
||||||
|
{ A, B, C, D },
|
||||||
|
FaceVector<3>{
|
||||||
|
Face<3> { { 0, 1, 2 } },
|
||||||
|
Face<3> { { 3, 1, 0 } },
|
||||||
|
Face<3> { { 2, 3, 0 } },
|
||||||
|
Face<3> { { 2, 1, 3 } },
|
||||||
|
}
|
||||||
|
}.fitDimension(0.5f);
|
||||||
|
return TETRAHEDRON;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const Solid<4>& cube() {
|
||||||
|
static const auto A = vec3(1, 1, 1);
|
||||||
|
static const auto B = vec3(-1, 1, 1);
|
||||||
|
static const auto C = vec3(-1, 1, -1);
|
||||||
|
static const auto D = vec3(1, 1, -1);
|
||||||
|
static const Solid<4> CUBE = Solid<4>{
|
||||||
|
{ A, B, C, D, -A, -B, -C, -D },
|
||||||
|
FaceVector<4>{
|
||||||
|
Face<4> { { 3, 2, 1, 0 } },
|
||||||
|
Face<4> { { 0, 1, 7, 6 } },
|
||||||
|
Face<4> { { 1, 2, 4, 7 } },
|
||||||
|
Face<4> { { 2, 3, 5, 4 } },
|
||||||
|
Face<4> { { 3, 0, 6, 5 } },
|
||||||
|
Face<4> { { 4, 5, 6, 7 } },
|
||||||
|
}
|
||||||
|
}.fitDimension(0.5f);
|
||||||
|
return CUBE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const Solid<3>& octahedron() {
|
||||||
|
static const auto A = vec3(0, 1, 0);
|
||||||
|
static const auto B = vec3(0, -1, 0);
|
||||||
|
static const auto C = vec3(0, 0, 1);
|
||||||
|
static const auto D = vec3(0, 0, -1);
|
||||||
|
static const auto E = vec3(1, 0, 0);
|
||||||
|
static const auto F = vec3(-1, 0, 0);
|
||||||
|
static const Solid<3> OCTAHEDRON = Solid<3>{
|
||||||
|
{ A, B, C, D, E, F},
|
||||||
|
FaceVector<3> {
|
||||||
|
Face<3> { { 0, 2, 4, } },
|
||||||
|
Face<3> { { 0, 4, 3, } },
|
||||||
|
Face<3> { { 0, 3, 5, } },
|
||||||
|
Face<3> { { 0, 5, 2, } },
|
||||||
|
Face<3> { { 1, 4, 2, } },
|
||||||
|
Face<3> { { 1, 3, 4, } },
|
||||||
|
Face<3> { { 1, 5, 3, } },
|
||||||
|
Face<3> { { 1, 2, 5, } },
|
||||||
|
}
|
||||||
|
}.fitDimension(0.5f);
|
||||||
|
return OCTAHEDRON;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const Solid<5>& dodecahedron() {
|
||||||
|
static const float P = PHI;
|
||||||
|
static const float IP = 1.0f / PHI;
|
||||||
|
static const vec3 A = vec3(IP, P, 0);
|
||||||
|
static const vec3 B = vec3(-IP, P, 0);
|
||||||
|
static const vec3 C = vec3(-1, 1, 1);
|
||||||
|
static const vec3 D = vec3(0, IP, P);
|
||||||
|
static const vec3 E = vec3(1, 1, 1);
|
||||||
|
static const vec3 F = vec3(1, 1, -1);
|
||||||
|
static const vec3 G = vec3(-1, 1, -1);
|
||||||
|
static const vec3 H = vec3(-P, 0, IP);
|
||||||
|
static const vec3 I = vec3(0, -IP, P);
|
||||||
|
static const vec3 J = vec3(P, 0, IP);
|
||||||
|
|
||||||
|
static const Solid<5> DODECAHEDRON = Solid<5>{
|
||||||
|
{
|
||||||
|
A, B, C, D, E, F, G, H, I, J,
|
||||||
|
-A, -B, -C, -D, -E, -F, -G, -H, -I, -J,
|
||||||
|
},
|
||||||
|
FaceVector<5> {
|
||||||
|
Face<5> { { 0, 1, 2, 3, 4 } },
|
||||||
|
Face<5> { { 0, 5, 18, 6, 1 } },
|
||||||
|
Face<5> { { 1, 6, 19, 7, 2 } },
|
||||||
|
Face<5> { { 2, 7, 15, 8, 3 } },
|
||||||
|
Face<5> { { 3, 8, 16, 9, 4 } },
|
||||||
|
Face<5> { { 4, 9, 17, 5, 0 } },
|
||||||
|
Face<5> { { 14, 13, 12, 11, 10 } },
|
||||||
|
Face<5> { { 11, 16, 8, 15, 10 } },
|
||||||
|
Face<5> { { 12, 17, 9, 16, 11 } },
|
||||||
|
Face<5> { { 13, 18, 5, 17, 12 } },
|
||||||
|
Face<5> { { 14, 19, 6, 18, 13 } },
|
||||||
|
Face<5> { { 10, 15, 7, 19, 14 } },
|
||||||
|
}
|
||||||
|
}.fitDimension(0.5f);
|
||||||
|
return DODECAHEDRON;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const Solid<3>& icosahedron() {
|
||||||
|
static const float N = 1.0f / PHI;
|
||||||
|
static const float P = 1.0f;
|
||||||
|
static const auto A = vec3(N, P, 0);
|
||||||
|
static const auto B = vec3(-N, P, 0);
|
||||||
|
static const auto C = vec3(0, N, P);
|
||||||
|
static const auto D = vec3(P, 0, N);
|
||||||
|
static const auto E = vec3(P, 0, -N);
|
||||||
|
static const auto F = vec3(0, N, -P);
|
||||||
|
|
||||||
|
static const Solid<3> ICOSAHEDRON = Solid<3> {
|
||||||
|
{
|
||||||
|
A, B, C, D, E, F,
|
||||||
|
-A, -B, -C, -D, -E, -F,
|
||||||
|
},
|
||||||
|
FaceVector<3> {
|
||||||
|
Face<3> { { 1, 2, 0 } },
|
||||||
|
Face<3> { { 2, 3, 0 } },
|
||||||
|
Face<3> { { 3, 4, 0 } },
|
||||||
|
Face<3> { { 4, 5, 0 } },
|
||||||
|
Face<3> { { 5, 1, 0 } },
|
||||||
|
|
||||||
|
Face<3> { { 1, 10, 2 } },
|
||||||
|
Face<3> { { 11, 2, 10 } },
|
||||||
|
Face<3> { { 2, 11, 3 } },
|
||||||
|
Face<3> { { 7, 3, 11 } },
|
||||||
|
Face<3> { { 3, 7, 4 } },
|
||||||
|
Face<3> { { 8, 4, 7 } },
|
||||||
|
Face<3> { { 4, 8, 5 } },
|
||||||
|
Face<3> { { 9, 5, 8 } },
|
||||||
|
Face<3> { { 5, 9, 1 } },
|
||||||
|
Face<3> { { 10, 1, 9 } },
|
||||||
|
|
||||||
|
Face<3> { { 8, 7, 6 } },
|
||||||
|
Face<3> { { 9, 8, 6 } },
|
||||||
|
Face<3> { { 10, 9, 6 } },
|
||||||
|
Face<3> { { 11, 10, 6 } },
|
||||||
|
Face<3> { { 7, 11, 6 } },
|
||||||
|
}
|
||||||
|
}.fitDimension(0.5f);
|
||||||
|
return ICOSAHEDRON;
|
||||||
|
}
|
||||||
|
|
||||||
// FIXME solids need per-face vertices, but smooth shaded
|
// FIXME solids need per-face vertices, but smooth shaded
|
||||||
// components do not. Find a way to support using draw elements
|
// components do not. Find a way to support using draw elements
|
||||||
|
@ -229,229 +453,28 @@ size_t GeometryCache::getCubeTriangleCount() {
|
||||||
void GeometryCache::buildShapes() {
|
void GeometryCache::buildShapes() {
|
||||||
auto vertexBuffer = std::make_shared<gpu::Buffer>();
|
auto vertexBuffer = std::make_shared<gpu::Buffer>();
|
||||||
auto indexBuffer = std::make_shared<gpu::Buffer>();
|
auto indexBuffer = std::make_shared<gpu::Buffer>();
|
||||||
size_t startingIndex = 0;
|
|
||||||
|
|
||||||
// Cube
|
// Cube
|
||||||
startingIndex = _shapeVertices->getSize() / SHAPE_VERTEX_STRIDE;
|
setupFlatShape(_shapes[Cube], cube(), _shapeVertices, _shapeIndices);
|
||||||
{
|
|
||||||
ShapeData& shapeData = _shapes[Cube];
|
|
||||||
VertexVector vertices;
|
|
||||||
// front
|
|
||||||
vertices.push_back(vec3(1, 1, 1));
|
|
||||||
vertices.push_back(vec3(0, 0, 1));
|
|
||||||
vertices.push_back(vec3(-1, 1, 1));
|
|
||||||
vertices.push_back(vec3(0, 0, 1));
|
|
||||||
vertices.push_back(vec3(-1, -1, 1));
|
|
||||||
vertices.push_back(vec3(0, 0, 1));
|
|
||||||
vertices.push_back(vec3(1, -1, 1));
|
|
||||||
vertices.push_back(vec3(0, 0, 1));
|
|
||||||
|
|
||||||
// right
|
|
||||||
vertices.push_back(vec3(1, 1, 1));
|
|
||||||
vertices.push_back(vec3(1, 0, 0));
|
|
||||||
vertices.push_back(vec3(1, -1, 1));
|
|
||||||
vertices.push_back(vec3(1, 0, 0));
|
|
||||||
vertices.push_back(vec3(1, -1, -1));
|
|
||||||
vertices.push_back(vec3(1, 0, 0));
|
|
||||||
vertices.push_back(vec3(1, 1, -1));
|
|
||||||
vertices.push_back(vec3(1, 0, 0));
|
|
||||||
|
|
||||||
// top
|
|
||||||
vertices.push_back(vec3(1, 1, 1));
|
|
||||||
vertices.push_back(vec3(0, 1, 0));
|
|
||||||
vertices.push_back(vec3(1, 1, -1));
|
|
||||||
vertices.push_back(vec3(0, 1, 0));
|
|
||||||
vertices.push_back(vec3(-1, 1, -1));
|
|
||||||
vertices.push_back(vec3(0, 1, 0));
|
|
||||||
vertices.push_back(vec3(-1, 1, 1));
|
|
||||||
vertices.push_back(vec3(0, 1, 0));
|
|
||||||
|
|
||||||
// left
|
|
||||||
vertices.push_back(vec3(-1, 1, 1));
|
|
||||||
vertices.push_back(vec3(-1, 0, 0));
|
|
||||||
vertices.push_back(vec3(-1, 1, -1));
|
|
||||||
vertices.push_back(vec3(-1, 0, 0));
|
|
||||||
vertices.push_back(vec3(-1, -1, -1));
|
|
||||||
vertices.push_back(vec3(-1, 0, 0));
|
|
||||||
vertices.push_back(vec3(-1, -1, 1));
|
|
||||||
vertices.push_back(vec3(-1, 0, 0));
|
|
||||||
|
|
||||||
// bottom
|
|
||||||
vertices.push_back(vec3(-1, -1, -1));
|
|
||||||
vertices.push_back(vec3(0, -1, 0));
|
|
||||||
vertices.push_back(vec3(1, -1, -1));
|
|
||||||
vertices.push_back(vec3(0, -1, 0));
|
|
||||||
vertices.push_back(vec3(1, -1, 1));
|
|
||||||
vertices.push_back(vec3(0, -1, 0));
|
|
||||||
vertices.push_back(vec3(-1, -1, 1));
|
|
||||||
vertices.push_back(vec3(0, -1, 0));
|
|
||||||
|
|
||||||
// back
|
|
||||||
vertices.push_back(vec3(1, -1, -1));
|
|
||||||
vertices.push_back(vec3(0, 0, -1));
|
|
||||||
vertices.push_back(vec3(-1, -1, -1));
|
|
||||||
vertices.push_back(vec3(0, 0, -1));
|
|
||||||
vertices.push_back(vec3(-1, 1, -1));
|
|
||||||
vertices.push_back(vec3(0, 0, -1));
|
|
||||||
vertices.push_back(vec3(1, 1, -1));
|
|
||||||
vertices.push_back(vec3(0, 0, -1));
|
|
||||||
|
|
||||||
static const size_t VERTEX_FORMAT_SIZE = 2;
|
|
||||||
static const size_t VERTEX_OFFSET = 0;
|
|
||||||
|
|
||||||
for (size_t i = 0; i < vertices.size(); ++i) {
|
|
||||||
auto vertexIndex = i;
|
|
||||||
// Make a unit cube by having the vertices (at index N)
|
|
||||||
// while leaving the normals (at index N + 1) alone
|
|
||||||
if (VERTEX_OFFSET == vertexIndex % VERTEX_FORMAT_SIZE) {
|
|
||||||
vertices[vertexIndex] *= 0.5f;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
shapeData.setupVertices(_shapeVertices, vertices);
|
|
||||||
|
|
||||||
IndexVector indices{
|
|
||||||
0, 1, 2, 2, 3, 0, // front
|
|
||||||
4, 5, 6, 6, 7, 4, // right
|
|
||||||
8, 9, 10, 10, 11, 8, // top
|
|
||||||
12, 13, 14, 14, 15, 12, // left
|
|
||||||
16, 17, 18, 18, 19, 16, // bottom
|
|
||||||
20, 21, 22, 22, 23, 20 // back
|
|
||||||
};
|
|
||||||
for (auto& index : indices) {
|
|
||||||
index += (uint16_t)startingIndex;
|
|
||||||
}
|
|
||||||
|
|
||||||
IndexVector wireIndices{
|
|
||||||
0, 1, 1, 2, 2, 3, 3, 0, // front
|
|
||||||
20, 21, 21, 22, 22, 23, 23, 20, // back
|
|
||||||
0, 23, 1, 22, 2, 21, 3, 20 // sides
|
|
||||||
};
|
|
||||||
|
|
||||||
for (size_t i = 0; i < wireIndices.size(); ++i) {
|
|
||||||
indices[i] += (uint16_t)startingIndex;
|
|
||||||
}
|
|
||||||
|
|
||||||
shapeData.setupIndices(_shapeIndices, indices, wireIndices);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Tetrahedron
|
// Tetrahedron
|
||||||
startingIndex = _shapeVertices->getSize() / SHAPE_VERTEX_STRIDE;
|
setupFlatShape(_shapes[Tetrahedron], tetrahedron(), _shapeVertices, _shapeIndices);
|
||||||
{
|
// Icosahedron
|
||||||
ShapeData& shapeData = _shapes[Tetrahedron];
|
setupFlatShape(_shapes[Icosahedron], icosahedron(), _shapeVertices, _shapeIndices);
|
||||||
size_t vertexCount = 4;
|
// Octahedron
|
||||||
VertexVector vertices;
|
setupFlatShape(_shapes[Octahedron], octahedron(), _shapeVertices, _shapeIndices);
|
||||||
{
|
// Dodecahedron
|
||||||
VertexVector originalVertices = tetrahedronVertices();
|
setupFlatShape(_shapes[Dodecahedron], dodecahedron(), _shapeVertices, _shapeIndices);
|
||||||
vertexCount = originalVertices.size();
|
|
||||||
vertices.reserve(originalVertices.size() * VECTOR_TO_VECTOR_WITH_NORMAL_MULTIPLER);
|
|
||||||
for (size_t i = 0; i < originalVertices.size(); i += VERTICES_PER_TRIANGLE) {
|
|
||||||
auto triangleStartIndex = i;
|
|
||||||
vec3 faceNormal;
|
|
||||||
for (size_t j = 0; j < VERTICES_PER_TRIANGLE; ++j) {
|
|
||||||
auto triangleVertexIndex = j;
|
|
||||||
auto vertexIndex = triangleStartIndex + triangleVertexIndex;
|
|
||||||
faceNormal += originalVertices[vertexIndex];
|
|
||||||
}
|
|
||||||
faceNormal = glm::normalize(faceNormal);
|
|
||||||
for (size_t j = 0; j < VERTICES_PER_TRIANGLE; ++j) {
|
|
||||||
auto triangleVertexIndex = j;
|
|
||||||
auto vertexIndex = triangleStartIndex + triangleVertexIndex;
|
|
||||||
vertices.push_back(glm::normalize(originalVertices[vertexIndex]));
|
|
||||||
vertices.push_back(faceNormal);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
shapeData.setupVertices(_shapeVertices, vertices);
|
|
||||||
|
|
||||||
IndexVector indices;
|
|
||||||
for (size_t i = 0; i < vertexCount; i += VERTICES_PER_TRIANGLE) {
|
|
||||||
auto triangleStartIndex = i;
|
|
||||||
for (size_t j = 0; j < VERTICES_PER_TRIANGLE; ++j) {
|
|
||||||
auto triangleVertexIndex = j;
|
|
||||||
auto vertexIndex = triangleStartIndex + triangleVertexIndex;
|
|
||||||
indices.push_back((uint16_t)(vertexIndex + startingIndex));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
IndexVector wireIndices{
|
|
||||||
0, 1, 1, 2, 2, 0,
|
|
||||||
0, 3, 1, 3, 2, 3,
|
|
||||||
};
|
|
||||||
|
|
||||||
for (size_t i = 0; i < wireIndices.size(); ++i) {
|
|
||||||
wireIndices[i] += (uint16_t)startingIndex;
|
|
||||||
}
|
|
||||||
|
|
||||||
shapeData.setupIndices(_shapeIndices, indices, wireIndices);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sphere
|
// Sphere
|
||||||
// FIXME this uses way more vertices than required. Should find a way to calculate the indices
|
// FIXME this uses way more vertices than required. Should find a way to calculate the indices
|
||||||
// using shared vertices for better vertex caching
|
// using shared vertices for better vertex caching
|
||||||
startingIndex = _shapeVertices->getSize() / SHAPE_VERTEX_STRIDE;
|
Solid<3> sphere = icosahedron();
|
||||||
{
|
sphere = tesselate(sphere, ICOSAHEDRON_TO_SPHERE_TESSELATION_COUNT);
|
||||||
ShapeData& shapeData = _shapes[Sphere];
|
sphere.fitDimension(1.0f);
|
||||||
VertexVector vertices;
|
setupSmoothShape(_shapes[Sphere], sphere, _shapeVertices, _shapeIndices);
|
||||||
IndexVector indices;
|
|
||||||
{
|
|
||||||
VertexVector originalVertices = tesselate(icosahedronVertices(), ICOSAHEDRON_TO_SPHERE_TESSELATION_COUNT);
|
|
||||||
vertices.reserve(originalVertices.size() * VECTOR_TO_VECTOR_WITH_NORMAL_MULTIPLER);
|
|
||||||
for (size_t i = 0; i < originalVertices.size(); i += VERTICES_PER_TRIANGLE) {
|
|
||||||
auto triangleStartIndex = i;
|
|
||||||
for (int j = 0; j < VERTICES_PER_TRIANGLE; ++j) {
|
|
||||||
auto triangleVertexIndex = j;
|
|
||||||
auto vertexIndex = triangleStartIndex + triangleVertexIndex;
|
|
||||||
const auto& vertex = originalVertices[i + j];
|
|
||||||
// Spheres use the same values for vertices and normals
|
|
||||||
vertices.push_back(vertex);
|
|
||||||
vertices.push_back(vertex);
|
|
||||||
indices.push_back((uint16_t)(vertexIndex + startingIndex));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
shapeData.setupVertices(_shapeVertices, vertices);
|
|
||||||
// FIXME don't use solid indices for wire drawing.
|
|
||||||
shapeData.setupIndices(_shapeIndices, indices, indices);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Icosahedron
|
|
||||||
startingIndex = _shapeVertices->getSize() / SHAPE_VERTEX_STRIDE;
|
|
||||||
{
|
|
||||||
ShapeData& shapeData = _shapes[Icosahedron];
|
|
||||||
|
|
||||||
VertexVector vertices;
|
|
||||||
IndexVector indices;
|
|
||||||
{
|
|
||||||
const VertexVector& originalVertices = icosahedronVertices();
|
|
||||||
vertices.reserve(originalVertices.size() * VECTOR_TO_VECTOR_WITH_NORMAL_MULTIPLER);
|
|
||||||
for (size_t i = 0; i < originalVertices.size(); i += 3) {
|
|
||||||
auto triangleStartIndex = i;
|
|
||||||
vec3 faceNormal;
|
|
||||||
for (int j = 0; j < VERTICES_PER_TRIANGLE; ++j) {
|
|
||||||
auto triangleVertexIndex = j;
|
|
||||||
auto vertexIndex = triangleStartIndex + triangleVertexIndex;
|
|
||||||
faceNormal += originalVertices[vertexIndex];
|
|
||||||
}
|
|
||||||
faceNormal = glm::normalize(faceNormal);
|
|
||||||
for (int j = 0; j < VERTICES_PER_TRIANGLE; ++j) {
|
|
||||||
auto triangleVertexIndex = j;
|
|
||||||
auto vertexIndex = triangleStartIndex + triangleVertexIndex;
|
|
||||||
vertices.push_back(glm::normalize(originalVertices[vertexIndex]));
|
|
||||||
vertices.push_back(faceNormal);
|
|
||||||
indices.push_back((uint16_t)(vertexIndex + startingIndex));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
shapeData.setupVertices(_shapeVertices, vertices);
|
|
||||||
// FIXME don't use solid indices for wire drawing.
|
|
||||||
shapeData.setupIndices(_shapeIndices, indices, indices);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Line
|
// Line
|
||||||
startingIndex = _shapeVertices->getSize() / SHAPE_VERTEX_STRIDE;
|
|
||||||
{
|
{
|
||||||
|
Index baseVertex = (Index)(_shapeVertices->getSize() / SHAPE_VERTEX_STRIDE);
|
||||||
ShapeData& shapeData = _shapes[Line];
|
ShapeData& shapeData = _shapes[Line];
|
||||||
shapeData.setupVertices(_shapeVertices, VertexVector{
|
shapeData.setupVertices(_shapeVertices, VertexVector{
|
||||||
vec3(-0.5, 0, 0), vec3(-0.5f, 0, 0),
|
vec3(-0.5, 0, 0), vec3(-0.5f, 0, 0),
|
||||||
|
@ -459,9 +482,8 @@ void GeometryCache::buildShapes() {
|
||||||
});
|
});
|
||||||
IndexVector wireIndices;
|
IndexVector wireIndices;
|
||||||
// Only two indices
|
// Only two indices
|
||||||
wireIndices.push_back(0 + (uint16_t)startingIndex);
|
wireIndices.push_back(0 + baseVertex);
|
||||||
wireIndices.push_back(1 + (uint16_t)startingIndex);
|
wireIndices.push_back(1 + baseVertex);
|
||||||
|
|
||||||
shapeData.setupIndices(_shapeIndices, IndexVector(), wireIndices);
|
shapeData.setupIndices(_shapeIndices, IndexVector(), wireIndices);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1801,10 +1823,10 @@ uint32_t toCompactColor(const glm::vec4& color) {
|
||||||
|
|
||||||
static const size_t INSTANCE_COLOR_BUFFER = 0;
|
static const size_t INSTANCE_COLOR_BUFFER = 0;
|
||||||
|
|
||||||
void renderInstances(const std::string& name, gpu::Batch& batch, const glm::vec4& color, bool isWire,
|
void renderInstances(gpu::Batch& batch, const glm::vec4& color, bool isWire,
|
||||||
const render::ShapePipelinePointer& pipeline, GeometryCache::Shape shape) {
|
const render::ShapePipelinePointer& pipeline, GeometryCache::Shape shape) {
|
||||||
// Add pipeline to name
|
// Add pipeline to name
|
||||||
std::string instanceName = name + std::to_string(std::hash<render::ShapePipelinePointer>()(pipeline));
|
std::string instanceName = (isWire ? "wire_shapes_" : "solid_shapes_") + std::to_string(shape) + "_" + std::to_string(std::hash<render::ShapePipelinePointer>()(pipeline));
|
||||||
|
|
||||||
// Add color to named buffer
|
// Add color to named buffer
|
||||||
{
|
{
|
||||||
|
@ -1826,14 +1848,16 @@ void renderInstances(const std::string& name, gpu::Batch& batch, const glm::vec4
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GeometryCache::renderSolidShapeInstance(gpu::Batch& batch, GeometryCache::Shape shape, const glm::vec4& color, const render::ShapePipelinePointer& pipeline) {
|
||||||
|
renderInstances(batch, color, false, pipeline, shape);
|
||||||
|
}
|
||||||
|
|
||||||
void GeometryCache::renderSolidSphereInstance(gpu::Batch& batch, const glm::vec4& color, const render::ShapePipelinePointer& pipeline) {
|
void GeometryCache::renderSolidSphereInstance(gpu::Batch& batch, const glm::vec4& color, const render::ShapePipelinePointer& pipeline) {
|
||||||
static const std::string INSTANCE_NAME = __FUNCTION__;
|
renderInstances(batch, color, false, pipeline, GeometryCache::Sphere);
|
||||||
renderInstances(INSTANCE_NAME, batch, color, false, pipeline, GeometryCache::Sphere);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void GeometryCache::renderWireSphereInstance(gpu::Batch& batch, const glm::vec4& color, const render::ShapePipelinePointer& pipeline) {
|
void GeometryCache::renderWireSphereInstance(gpu::Batch& batch, const glm::vec4& color, const render::ShapePipelinePointer& pipeline) {
|
||||||
static const std::string INSTANCE_NAME = __FUNCTION__;
|
renderInstances(batch, color, true, pipeline, GeometryCache::Sphere);
|
||||||
renderInstances(INSTANCE_NAME, batch, color, true, pipeline, GeometryCache::Sphere);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Enable this in a debug build to cause 'box' entities to iterate through all the
|
// Enable this in a debug build to cause 'box' entities to iterate through all the
|
||||||
|
@ -1841,8 +1865,6 @@ void GeometryCache::renderWireSphereInstance(gpu::Batch& batch, const glm::vec4&
|
||||||
//#define DEBUG_SHAPES
|
//#define DEBUG_SHAPES
|
||||||
|
|
||||||
void GeometryCache::renderSolidCubeInstance(gpu::Batch& batch, const glm::vec4& color, const render::ShapePipelinePointer& pipeline) {
|
void GeometryCache::renderSolidCubeInstance(gpu::Batch& batch, const glm::vec4& color, const render::ShapePipelinePointer& pipeline) {
|
||||||
static const std::string INSTANCE_NAME = __FUNCTION__;
|
|
||||||
|
|
||||||
#ifdef DEBUG_SHAPES
|
#ifdef DEBUG_SHAPES
|
||||||
static auto startTime = usecTimestampNow();
|
static auto startTime = usecTimestampNow();
|
||||||
renderInstances(INSTANCE_NAME, batch, color, pipeline, [](gpu::Batch& batch, gpu::Batch::NamedBatchData& data) {
|
renderInstances(INSTANCE_NAME, batch, color, pipeline, [](gpu::Batch& batch, gpu::Batch::NamedBatchData& data) {
|
||||||
|
@ -1876,11 +1898,11 @@ void GeometryCache::renderSolidCubeInstance(gpu::Batch& batch, const glm::vec4&
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
#else
|
#else
|
||||||
renderInstances(INSTANCE_NAME, batch, color, false, pipeline, GeometryCache::Cube);
|
renderInstances(batch, color, false, pipeline, GeometryCache::Cube);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void GeometryCache::renderWireCubeInstance(gpu::Batch& batch, const glm::vec4& color, const render::ShapePipelinePointer& pipeline) {
|
void GeometryCache::renderWireCubeInstance(gpu::Batch& batch, const glm::vec4& color, const render::ShapePipelinePointer& pipeline) {
|
||||||
static const std::string INSTANCE_NAME = __FUNCTION__;
|
static const std::string INSTANCE_NAME = __FUNCTION__;
|
||||||
renderInstances(INSTANCE_NAME, batch, color, true, pipeline, GeometryCache::Cube);
|
renderInstances(batch, color, true, pipeline, GeometryCache::Cube);
|
||||||
}
|
}
|
||||||
|
|
|
@ -121,8 +121,8 @@ inline uint qHash(const Vec4PairVec4Pair& v, uint seed) {
|
||||||
seed);
|
seed);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
using IndexVector = std::vector<uint32_t>;
|
||||||
using VertexVector = std::vector<glm::vec3>;
|
using VertexVector = std::vector<glm::vec3>;
|
||||||
using IndexVector = std::vector<uint16_t>;
|
|
||||||
|
|
||||||
/// Stores cached geometry.
|
/// Stores cached geometry.
|
||||||
class GeometryCache : public Dependency {
|
class GeometryCache : public Dependency {
|
||||||
|
@ -137,7 +137,7 @@ public:
|
||||||
Cube,
|
Cube,
|
||||||
Sphere,
|
Sphere,
|
||||||
Tetrahedron,
|
Tetrahedron,
|
||||||
Octahetron,
|
Octahedron,
|
||||||
Dodecahedron,
|
Dodecahedron,
|
||||||
Icosahedron,
|
Icosahedron,
|
||||||
Torus,
|
Torus,
|
||||||
|
@ -163,6 +163,13 @@ public:
|
||||||
void renderShapeInstances(gpu::Batch& batch, Shape shape, size_t count, gpu::BufferPointer& colorBuffer);
|
void renderShapeInstances(gpu::Batch& batch, Shape shape, size_t count, gpu::BufferPointer& colorBuffer);
|
||||||
void renderWireShapeInstances(gpu::Batch& batch, Shape shape, size_t count, gpu::BufferPointer& colorBuffer);
|
void renderWireShapeInstances(gpu::Batch& batch, Shape shape, size_t count, gpu::BufferPointer& colorBuffer);
|
||||||
|
|
||||||
|
void renderSolidShapeInstance(gpu::Batch& batch, Shape shape, const glm::vec4& color = glm::vec4(1),
|
||||||
|
const render::ShapePipelinePointer& pipeline = _simplePipeline);
|
||||||
|
void renderSolidShapeInstance(gpu::Batch& batch, Shape shape, const glm::vec3& color,
|
||||||
|
const render::ShapePipelinePointer& pipeline = _simplePipeline) {
|
||||||
|
renderSolidShapeInstance(batch, shape, glm::vec4(color, 1.0f), pipeline);
|
||||||
|
}
|
||||||
|
|
||||||
void renderSolidSphereInstance(gpu::Batch& batch, const glm::vec4& color,
|
void renderSolidSphereInstance(gpu::Batch& batch, const glm::vec4& color,
|
||||||
const render::ShapePipelinePointer& pipeline = _simplePipeline);
|
const render::ShapePipelinePointer& pipeline = _simplePipeline);
|
||||||
void renderSolidSphereInstance(gpu::Batch& batch, const glm::vec3& color,
|
void renderSolidSphereInstance(gpu::Batch& batch, const glm::vec3& color,
|
||||||
|
|
|
@ -29,9 +29,9 @@
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
static int nakedModelPointerTypeId = qRegisterMetaType<ModelPointer>();
|
int nakedModelPointerTypeId = qRegisterMetaType<ModelPointer>();
|
||||||
static int weakNetworkGeometryPointerTypeId = qRegisterMetaType<std::weak_ptr<NetworkGeometry> >();
|
int weakNetworkGeometryPointerTypeId = qRegisterMetaType<std::weak_ptr<NetworkGeometry> >();
|
||||||
static int vec3VectorTypeId = qRegisterMetaType<QVector<glm::vec3> >();
|
int vec3VectorTypeId = qRegisterMetaType<QVector<glm::vec3> >();
|
||||||
float Model::FAKE_DIMENSION_PLACEHOLDER = -1.0f;
|
float Model::FAKE_DIMENSION_PLACEHOLDER = -1.0f;
|
||||||
#define HTTP_INVALID_COM "http://invalid.com"
|
#define HTTP_INVALID_COM "http://invalid.com"
|
||||||
|
|
||||||
|
|
|
@ -65,7 +65,7 @@
|
||||||
static const QString SCRIPT_EXCEPTION_FORMAT = "[UncaughtException] %1 in %2:%3";
|
static const QString SCRIPT_EXCEPTION_FORMAT = "[UncaughtException] %1 in %2:%3";
|
||||||
|
|
||||||
Q_DECLARE_METATYPE(QScriptEngine::FunctionSignature)
|
Q_DECLARE_METATYPE(QScriptEngine::FunctionSignature)
|
||||||
static int functionSignatureMetaID = qRegisterMetaType<QScriptEngine::FunctionSignature>();
|
int functionSignatureMetaID = qRegisterMetaType<QScriptEngine::FunctionSignature>();
|
||||||
|
|
||||||
static QScriptValue debugPrint(QScriptContext* context, QScriptEngine* engine){
|
static QScriptValue debugPrint(QScriptContext* context, QScriptEngine* engine){
|
||||||
QString message = "";
|
QString message = "";
|
||||||
|
|
|
@ -21,17 +21,17 @@
|
||||||
|
|
||||||
#include "RegisteredMetaTypes.h"
|
#include "RegisteredMetaTypes.h"
|
||||||
|
|
||||||
static int vec4MetaTypeId = qRegisterMetaType<glm::vec4>();
|
int vec4MetaTypeId = qRegisterMetaType<glm::vec4>();
|
||||||
static int vec3MetaTypeId = qRegisterMetaType<glm::vec3>();
|
int vec3MetaTypeId = qRegisterMetaType<glm::vec3>();
|
||||||
static int qVectorVec3MetaTypeId = qRegisterMetaType<QVector<glm::vec3>>();
|
int qVectorVec3MetaTypeId = qRegisterMetaType<QVector<glm::vec3>>();
|
||||||
static int qVectorQuatMetaTypeId = qRegisterMetaType<QVector<glm::quat>>();
|
int qVectorQuatMetaTypeId = qRegisterMetaType<QVector<glm::quat>>();
|
||||||
static int qVectorBoolMetaTypeId = qRegisterMetaType<QVector<bool>>();
|
int qVectorBoolMetaTypeId = qRegisterMetaType<QVector<bool>>();
|
||||||
static int vec2MetaTypeId = qRegisterMetaType<glm::vec2>();
|
int vec2MetaTypeId = qRegisterMetaType<glm::vec2>();
|
||||||
static int quatMetaTypeId = qRegisterMetaType<glm::quat>();
|
int quatMetaTypeId = qRegisterMetaType<glm::quat>();
|
||||||
static int xColorMetaTypeId = qRegisterMetaType<xColor>();
|
int xColorMetaTypeId = qRegisterMetaType<xColor>();
|
||||||
static int pickRayMetaTypeId = qRegisterMetaType<PickRay>();
|
int pickRayMetaTypeId = qRegisterMetaType<PickRay>();
|
||||||
static int collisionMetaTypeId = qRegisterMetaType<Collision>();
|
int collisionMetaTypeId = qRegisterMetaType<Collision>();
|
||||||
static int qMapURLStringMetaTypeId = qRegisterMetaType<QMap<QUrl,QString>>();
|
int qMapURLStringMetaTypeId = qRegisterMetaType<QMap<QUrl,QString>>();
|
||||||
|
|
||||||
void registerMetaTypes(QScriptEngine* engine) {
|
void registerMetaTypes(QScriptEngine* engine) {
|
||||||
qScriptRegisterMetaType(engine, mat4toScriptValue, mat4FromScriptValue);
|
qScriptRegisterMetaType(engine, mat4toScriptValue, mat4FromScriptValue);
|
||||||
|
|
|
@ -57,7 +57,7 @@ public:
|
||||||
|
|
||||||
void addSample(T sample) {
|
void addSample(T sample) {
|
||||||
if (numSamples > 0) {
|
if (numSamples > 0) {
|
||||||
average = (sample * WEIGHTING) + (average * ONE_MINUS_WEIGHTING);
|
average = ((float)sample * WEIGHTING) + ((float)average * ONE_MINUS_WEIGHTING);
|
||||||
} else {
|
} else {
|
||||||
average = sample;
|
average = sample;
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,161 +15,11 @@
|
||||||
// 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
|
||||||
//
|
//
|
||||||
|
|
||||||
var NUM_ENTITIES = 20000; // number of entities to spawn
|
Script.include("./entitySpawnTool.js");
|
||||||
var ENTITY_SPAWN_LIMIT = 1000;
|
|
||||||
var ENTITY_SPAWN_INTERVAL = 0.1;
|
|
||||||
|
|
||||||
var UPDATE_INTERVAL = 0.05; // Re-randomize the entity's position every x seconds / ms
|
ENTITY_SPAWNER({
|
||||||
var ENTITY_LIFETIME = 30; // Entity timeout (when/if we crash, we need the entities to delete themselves)
|
count: 20000,
|
||||||
var KEEPALIVE_INTERVAL = 5; // Refreshes the timeout every X seconds
|
spawnLimit: 1000,
|
||||||
|
spawnInterval: 0.1,
|
||||||
var RADIUS = 5.0; // Spawn within this radius (square)
|
updateInterval: 0.05
|
||||||
var Y_OFFSET = 1.5; // Spawn at an offset below the avatar
|
});
|
||||||
var TEST_ENTITY_NAME = "EntitySpawnTest";
|
|
||||||
|
|
||||||
(function () {
|
|
||||||
this.makeEntity = function (properties) {
|
|
||||||
var entity = Entities.addEntity(properties);
|
|
||||||
// print("spawning entity: " + JSON.stringify(properties));
|
|
||||||
|
|
||||||
return {
|
|
||||||
update: function (properties) {
|
|
||||||
Entities.editEntity(entity, properties);
|
|
||||||
},
|
|
||||||
destroy: function () {
|
|
||||||
Entities.deleteEntity(entity)
|
|
||||||
},
|
|
||||||
getAge: function () {
|
|
||||||
return Entities.getEntityProperties(entity).age;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
this.randomPositionXZ = function (center, radius) {
|
|
||||||
return {
|
|
||||||
x: center.x + (Math.random() * radius * 2.0) - radius,
|
|
||||||
y: center.y,
|
|
||||||
z: center.z + (Math.random() * radius * 2.0) - radius
|
|
||||||
};
|
|
||||||
}
|
|
||||||
this.randomColor = function () {
|
|
||||||
var shade = Math.floor(Math.random() * 255);
|
|
||||||
var hue = Math.floor(Math.random() * (255 - shade));
|
|
||||||
|
|
||||||
return {
|
|
||||||
red: shade + hue,
|
|
||||||
green: shade,
|
|
||||||
blue: shade
|
|
||||||
};
|
|
||||||
}
|
|
||||||
this.randomDimensions = function () {
|
|
||||||
return {
|
|
||||||
x: 0.1 + Math.random() * 0.5,
|
|
||||||
y: 0.1 + Math.random() * 0.1,
|
|
||||||
z: 0.1 + Math.random() * 0.5
|
|
||||||
};
|
|
||||||
}
|
|
||||||
})();
|
|
||||||
|
|
||||||
(function () {
|
|
||||||
var entities = [];
|
|
||||||
var entitiesToCreate = 0;
|
|
||||||
var entitiesSpawned = 0;
|
|
||||||
|
|
||||||
|
|
||||||
function clear () {
|
|
||||||
var ids = Entities.findEntities(MyAvatar.position, 50);
|
|
||||||
var that = this;
|
|
||||||
ids.forEach(function(id) {
|
|
||||||
var properties = Entities.getEntityProperties(id);
|
|
||||||
if (properties.name == TEST_ENTITY_NAME) {
|
|
||||||
Entities.deleteEntity(id);
|
|
||||||
}
|
|
||||||
}, this);
|
|
||||||
}
|
|
||||||
|
|
||||||
function createEntities () {
|
|
||||||
print("Creating " + NUM_ENTITIES + " entities (UPDATE_INTERVAL = " + UPDATE_INTERVAL + ", KEEPALIVE_INTERVAL = " + KEEPALIVE_INTERVAL + ")");
|
|
||||||
entitiesToCreate = NUM_ENTITIES;
|
|
||||||
Script.update.connect(spawnEntities);
|
|
||||||
}
|
|
||||||
|
|
||||||
var spawnTimer = 0.0;
|
|
||||||
function spawnEntities (dt) {
|
|
||||||
if (entitiesToCreate <= 0) {
|
|
||||||
Script.update.disconnect(spawnEntities);
|
|
||||||
print("Finished spawning entities");
|
|
||||||
}
|
|
||||||
else if ((spawnTimer -= dt) < 0.0){
|
|
||||||
spawnTimer = ENTITY_SPAWN_INTERVAL;
|
|
||||||
|
|
||||||
var n = Math.min(entitiesToCreate, ENTITY_SPAWN_LIMIT);
|
|
||||||
print("Spawning " + n + " entities (" + (entitiesSpawned += n) + ")");
|
|
||||||
|
|
||||||
entitiesToCreate -= n;
|
|
||||||
|
|
||||||
var center = MyAvatar.position;
|
|
||||||
center.y -= Y_OFFSET;
|
|
||||||
|
|
||||||
for (; n > 0; --n) {
|
|
||||||
entities.push(makeEntity({
|
|
||||||
type: "Box",
|
|
||||||
name: TEST_ENTITY_NAME,
|
|
||||||
position: randomPositionXZ(center, RADIUS),
|
|
||||||
color: randomColor(),
|
|
||||||
dimensions: randomDimensions(),
|
|
||||||
lifetime: ENTITY_LIFETIME
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function despawnEntities () {
|
|
||||||
print("despawning entities");
|
|
||||||
entities.forEach(function (entity) {
|
|
||||||
entity.destroy();
|
|
||||||
});
|
|
||||||
entities = [];
|
|
||||||
}
|
|
||||||
|
|
||||||
var keepAliveTimer = 0.0;
|
|
||||||
var updateTimer = 0.0;
|
|
||||||
|
|
||||||
// Runs the following entity updates:
|
|
||||||
// a) refreshes the timeout interval every KEEPALIVE_INTERVAL seconds, and
|
|
||||||
// b) re-randomizes its position every UPDATE_INTERVAL seconds.
|
|
||||||
// This should be sufficient to crash the client until the entity tree bug is fixed (and thereafter if it shows up again).
|
|
||||||
function updateEntities (dt) {
|
|
||||||
var updateLifetime = ((keepAliveTimer -= dt) < 0.0) ? ((keepAliveTimer = KEEPALIVE_INTERVAL), true) : false;
|
|
||||||
var updateProperties = ((updateTimer -= dt) < 0.0) ? ((updateTimer = UPDATE_INTERVAL), true) : false;
|
|
||||||
|
|
||||||
if (updateLifetime || updateProperties) {
|
|
||||||
var center = MyAvatar.position;
|
|
||||||
center.y -= Y_OFFSET;
|
|
||||||
|
|
||||||
entities.forEach((updateLifetime && updateProperties && function (entity) {
|
|
||||||
entity.update({
|
|
||||||
lifetime: entity.getAge() + ENTITY_LIFETIME,
|
|
||||||
position: randomPositionXZ(center, RADIUS)
|
|
||||||
});
|
|
||||||
}) || (updateLifetime && function (entity) {
|
|
||||||
entity.update({
|
|
||||||
lifetime: entity.getAge() + ENTITY_LIFETIME
|
|
||||||
});
|
|
||||||
}) || (updateProperties && function (entity) {
|
|
||||||
entity.update({
|
|
||||||
position: randomPositionXZ(center, RADIUS)
|
|
||||||
});
|
|
||||||
}) || null, this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function init () {
|
|
||||||
Script.update.disconnect(init);
|
|
||||||
clear();
|
|
||||||
createEntities();
|
|
||||||
Script.update.connect(updateEntities);
|
|
||||||
Script.scriptEnding.connect(despawnEntities);
|
|
||||||
}
|
|
||||||
Script.update.connect(init);
|
|
||||||
})();
|
|
||||||
|
|
184
scripts/developer/tests/entitySpawnTool.js
Normal file
184
scripts/developer/tests/entitySpawnTool.js
Normal file
|
@ -0,0 +1,184 @@
|
||||||
|
// entityEditStressTest.js
|
||||||
|
//
|
||||||
|
// Created by Seiji Emery on 8/31/15
|
||||||
|
// Copyright 2015 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// Stress tests the client + server-side entity trees by spawning huge numbers of entities in
|
||||||
|
// close proximity to your avatar and updating them continuously (ie. applying position edits),
|
||||||
|
// with the intent of discovering crashes and other bugs related to the entity, scripting,
|
||||||
|
// rendering, networking, and/or physics subsystems.
|
||||||
|
//
|
||||||
|
// This script was originally created to find + diagnose an a clientside crash caused by improper
|
||||||
|
// locking of the entity tree, but can be reused for other purposes.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
|
||||||
|
ENTITY_SPAWNER = function (properties) {
|
||||||
|
properties = properties || {};
|
||||||
|
var RADIUS = properties.radius || 5.0; // Spawn within this radius (square)
|
||||||
|
var Y_OFFSET = properties.yOffset || 1.5; // Spawn at an offset below the avatar
|
||||||
|
var TEST_ENTITY_NAME = properties.entityName || "EntitySpawnTest";
|
||||||
|
|
||||||
|
var NUM_ENTITIES = properties.count || 1000; // number of entities to spawn
|
||||||
|
var ENTITY_SPAWN_LIMIT = properties.spawnLimit || 100;
|
||||||
|
var ENTITY_SPAWN_INTERVAL = properties.spawnInterval || properties.interval || 1.0;
|
||||||
|
|
||||||
|
var UPDATE_INTERVAL = properties.updateInterval || properties.interval || 0.1; // Re-randomize the entity's position every x seconds / ms
|
||||||
|
var ENTITY_LIFETIME = properties.lifetime || 30; // Entity timeout (when/if we crash, we need the entities to delete themselves)
|
||||||
|
var KEEPALIVE_INTERVAL = properties.keepAlive || 5; // Refreshes the timeout every X seconds
|
||||||
|
var UPDATES = properties.updates || false
|
||||||
|
var SHAPES = properties.shapes || ["Icosahedron", "Tetrahedron", "Cube", "Sphere" ];
|
||||||
|
|
||||||
|
function makeEntity(properties) {
|
||||||
|
var entity = Entities.addEntity(properties);
|
||||||
|
// print("spawning entity: " + JSON.stringify(properties));
|
||||||
|
|
||||||
|
return {
|
||||||
|
update: function (properties) {
|
||||||
|
Entities.editEntity(entity, properties);
|
||||||
|
},
|
||||||
|
destroy: function () {
|
||||||
|
Entities.deleteEntity(entity)
|
||||||
|
},
|
||||||
|
getAge: function () {
|
||||||
|
return Entities.getEntityProperties(entity).age;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function randomPositionXZ(center, radius) {
|
||||||
|
return {
|
||||||
|
x: center.x + (Math.random() * radius * 2.0) - radius,
|
||||||
|
y: center.y,
|
||||||
|
z: center.z + (Math.random() * radius * 2.0) - radius
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function randomPosition(center, radius) {
|
||||||
|
return {
|
||||||
|
x: center.x + (Math.random() * radius * 2.0) - radius,
|
||||||
|
y: center.y + (Math.random() * radius * 2.0) - radius,
|
||||||
|
z: center.z + (Math.random() * radius * 2.0) - radius
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function randomColor() {
|
||||||
|
return {
|
||||||
|
red: Math.floor(Math.random() * 255),
|
||||||
|
green: Math.floor(Math.random() * 255),
|
||||||
|
blue: Math.floor(Math.random() * 255),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function randomDimensions() {
|
||||||
|
return {
|
||||||
|
x: 0.1 + Math.random() * 0.5,
|
||||||
|
y: 0.1 + Math.random() * 0.1,
|
||||||
|
z: 0.1 + Math.random() * 0.5
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
var entities = [];
|
||||||
|
var entitiesToCreate = 0;
|
||||||
|
var entitiesSpawned = 0;
|
||||||
|
var spawnTimer = 0.0;
|
||||||
|
var keepAliveTimer = 0.0;
|
||||||
|
var updateTimer = 0.0;
|
||||||
|
|
||||||
|
function clear () {
|
||||||
|
var ids = Entities.findEntities(MyAvatar.position, 50);
|
||||||
|
var that = this;
|
||||||
|
ids.forEach(function(id) {
|
||||||
|
var properties = Entities.getEntityProperties(id);
|
||||||
|
if (properties.name == TEST_ENTITY_NAME) {
|
||||||
|
Entities.deleteEntity(id);
|
||||||
|
}
|
||||||
|
}, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
function createEntities () {
|
||||||
|
print("Creating " + NUM_ENTITIES + " entities (UPDATE_INTERVAL = " + UPDATE_INTERVAL + ", KEEPALIVE_INTERVAL = " + KEEPALIVE_INTERVAL + ")");
|
||||||
|
entitiesToCreate = NUM_ENTITIES;
|
||||||
|
Script.update.connect(spawnEntities);
|
||||||
|
}
|
||||||
|
|
||||||
|
function spawnEntities (dt) {
|
||||||
|
if (entitiesToCreate <= 0) {
|
||||||
|
Script.update.disconnect(spawnEntities);
|
||||||
|
print("Finished spawning entities");
|
||||||
|
}
|
||||||
|
else if ((spawnTimer -= dt) < 0.0){
|
||||||
|
spawnTimer = ENTITY_SPAWN_INTERVAL;
|
||||||
|
|
||||||
|
var n = Math.min(entitiesToCreate, ENTITY_SPAWN_LIMIT);
|
||||||
|
print("Spawning " + n + " entities (" + (entitiesSpawned += n) + ")");
|
||||||
|
|
||||||
|
entitiesToCreate -= n;
|
||||||
|
|
||||||
|
var center = MyAvatar.position;
|
||||||
|
center.y -= Y_OFFSET;
|
||||||
|
|
||||||
|
for (; n > 0; --n) {
|
||||||
|
entities.push(makeEntity({
|
||||||
|
type: "Shape",
|
||||||
|
shape: SHAPES[n % SHAPES.length],
|
||||||
|
name: TEST_ENTITY_NAME,
|
||||||
|
position: randomPosition(center, RADIUS),
|
||||||
|
color: randomColor(),
|
||||||
|
dimensions: randomDimensions(),
|
||||||
|
lifetime: ENTITY_LIFETIME
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function despawnEntities () {
|
||||||
|
print("despawning entities");
|
||||||
|
entities.forEach(function (entity) {
|
||||||
|
entity.destroy();
|
||||||
|
});
|
||||||
|
entities = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Runs the following entity updates:
|
||||||
|
// a) refreshes the timeout interval every KEEPALIVE_INTERVAL seconds, and
|
||||||
|
// b) re-randomizes its position every UPDATE_INTERVAL seconds.
|
||||||
|
// This should be sufficient to crash the client until the entity tree bug is fixed (and thereafter if it shows up again).
|
||||||
|
function updateEntities (dt) {
|
||||||
|
var updateLifetime = ((keepAliveTimer -= dt) < 0.0) ? ((keepAliveTimer = KEEPALIVE_INTERVAL), true) : false;
|
||||||
|
var updateProperties = ((updateTimer -= dt) < 0.0) ? ((updateTimer = UPDATE_INTERVAL), true) : false;
|
||||||
|
|
||||||
|
if (updateLifetime || updateProperties) {
|
||||||
|
var center = MyAvatar.position;
|
||||||
|
center.y -= Y_OFFSET;
|
||||||
|
|
||||||
|
entities.forEach((updateLifetime && updateProperties && function (entity) {
|
||||||
|
entity.update({
|
||||||
|
lifetime: entity.getAge() + ENTITY_LIFETIME,
|
||||||
|
position: randomPosition(center, RADIUS)
|
||||||
|
});
|
||||||
|
}) || (updateLifetime && function (entity) {
|
||||||
|
entity.update({
|
||||||
|
lifetime: entity.getAge() + ENTITY_LIFETIME
|
||||||
|
});
|
||||||
|
}) || (updateProperties && function (entity) {
|
||||||
|
entity.update({
|
||||||
|
position: randomPosition(center, RADIUS)
|
||||||
|
});
|
||||||
|
}) || null, this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function init () {
|
||||||
|
Script.update.disconnect(init);
|
||||||
|
clear();
|
||||||
|
createEntities();
|
||||||
|
Script.update.connect(updateEntities);
|
||||||
|
Script.scriptEnding.connect(despawnEntities);
|
||||||
|
}
|
||||||
|
|
||||||
|
Script.update.connect(init);
|
||||||
|
};
|
24
scripts/developer/tests/primitivesTest.js
Normal file
24
scripts/developer/tests/primitivesTest.js
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
// entityEditStressTest.js
|
||||||
|
//
|
||||||
|
// Created by Seiji Emery on 8/31/15
|
||||||
|
// Copyright 2015 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// Stress tests the client + server-side entity trees by spawning huge numbers of entities in
|
||||||
|
// close proximity to your avatar and updating them continuously (ie. applying position edits),
|
||||||
|
// with the intent of discovering crashes and other bugs related to the entity, scripting,
|
||||||
|
// rendering, networking, and/or physics subsystems.
|
||||||
|
//
|
||||||
|
// This script was originally created to find + diagnose an a clientside crash caused by improper
|
||||||
|
// locking of the entity tree, but can be reused for other purposes.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
|
||||||
|
Script.include("./entitySpawnTool.js");
|
||||||
|
|
||||||
|
|
||||||
|
ENTITY_SPAWNER({
|
||||||
|
updateInterval: 2.0
|
||||||
|
})
|
||||||
|
|
|
@ -268,7 +268,7 @@ input[type=number]::-webkit-inner-spin-button {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
font-family: hifi-glyphs;
|
font-family: hifi-glyphs;
|
||||||
font-size: 50px;
|
font-size: 46px;
|
||||||
color: #afafaf;
|
color: #afafaf;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
/*background-color: #000000;*/
|
/*background-color: #000000;*/
|
||||||
|
@ -276,17 +276,17 @@ input[type=number]::-webkit-inner-spin-button {
|
||||||
input[type=number]::-webkit-inner-spin-button:before,
|
input[type=number]::-webkit-inner-spin-button:before,
|
||||||
input[type=number]::-webkit-inner-spin-button:after {
|
input[type=number]::-webkit-inner-spin-button:after {
|
||||||
position:absolute;
|
position:absolute;
|
||||||
left: -21px;
|
left: -19px;
|
||||||
line-height: 8px;
|
line-height: 8px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
input[type=number]::-webkit-inner-spin-button:before {
|
input[type=number]::-webkit-inner-spin-button:before {
|
||||||
content: "6";
|
content: "6";
|
||||||
top: 5px;
|
top: 4px;
|
||||||
}
|
}
|
||||||
input[type=number]::-webkit-inner-spin-button:after {
|
input[type=number]::-webkit-inner-spin-button:after {
|
||||||
content: "5";
|
content: "5";
|
||||||
bottom: 6px;
|
bottom: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
input[type=number].hover-up::-webkit-inner-spin-button:before,
|
input[type=number].hover-up::-webkit-inner-spin-button:before,
|
||||||
|
@ -613,7 +613,7 @@ hr {
|
||||||
margin-right: -48px;
|
margin-right: -48px;
|
||||||
position: relative;
|
position: relative;
|
||||||
left: -12px;
|
left: -12px;
|
||||||
top: -11px;
|
top: -9px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dropdown dd {
|
.dropdown dd {
|
||||||
|
|
|
@ -25,6 +25,7 @@
|
||||||
var ICON_FOR_TYPE = {
|
var ICON_FOR_TYPE = {
|
||||||
Box: "V",
|
Box: "V",
|
||||||
Sphere: "n",
|
Sphere: "n",
|
||||||
|
Shape: "n",
|
||||||
ParticleEffect: "",
|
ParticleEffect: "",
|
||||||
Model: "",
|
Model: "",
|
||||||
Web: "q",
|
Web: "q",
|
||||||
|
@ -403,6 +404,10 @@
|
||||||
var elColorGreen = document.getElementById("property-color-green");
|
var elColorGreen = document.getElementById("property-color-green");
|
||||||
var elColorBlue = document.getElementById("property-color-blue");
|
var elColorBlue = document.getElementById("property-color-blue");
|
||||||
|
|
||||||
|
var elShapeSections = document.querySelectorAll(".shape-section");
|
||||||
|
allSections.push(elShapeSections);
|
||||||
|
var elShape = document.getElementById("property-shape");
|
||||||
|
|
||||||
var elLightSections = document.querySelectorAll(".light-section");
|
var elLightSections = document.querySelectorAll(".light-section");
|
||||||
allSections.push(elLightSections);
|
allSections.push(elLightSections);
|
||||||
var elLightSpotLight = document.getElementById("property-light-spot-light");
|
var elLightSpotLight = document.getElementById("property-light-spot-light");
|
||||||
|
@ -666,7 +671,18 @@
|
||||||
elHyperlinkSections[i].style.display = 'table';
|
elHyperlinkSections[i].style.display = 'table';
|
||||||
}
|
}
|
||||||
|
|
||||||
if (properties.type == "Box" || properties.type == "Sphere" || properties.type == "ParticleEffect") {
|
if (properties.type == "Shape" || properties.type == "Box" || properties.type == "Sphere") {
|
||||||
|
for (var i = 0; i < elShapeSections.length; i++) {
|
||||||
|
elShapeSections[i].style.display = 'table';
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (var i = 0; i < elShapeSections.length; i++) {
|
||||||
|
console.log("Hiding shape section " + elShapeSections[i])
|
||||||
|
elShapeSections[i].style.display = 'none';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (properties.type == "Shape" || properties.type == "Box" || properties.type == "Sphere" || properties.type == "ParticleEffect") {
|
||||||
for (var i = 0; i < elColorSections.length; i++) {
|
for (var i = 0; i < elColorSections.length; i++) {
|
||||||
elColorSections[i].style.display = 'table';
|
elColorSections[i].style.display = 'table';
|
||||||
}
|
}
|
||||||
|
@ -958,6 +974,8 @@
|
||||||
elLightExponent.addEventListener('change', createEmitNumberPropertyUpdateFunction('exponent', 2));
|
elLightExponent.addEventListener('change', createEmitNumberPropertyUpdateFunction('exponent', 2));
|
||||||
elLightCutoff.addEventListener('change', createEmitNumberPropertyUpdateFunction('cutoff', 2));
|
elLightCutoff.addEventListener('change', createEmitNumberPropertyUpdateFunction('cutoff', 2));
|
||||||
|
|
||||||
|
elShape.addEventListener('change', createEmitTextPropertyUpdateFunction('shape'));
|
||||||
|
|
||||||
elWebSourceURL.addEventListener('change', createEmitTextPropertyUpdateFunction('sourceUrl'));
|
elWebSourceURL.addEventListener('change', createEmitTextPropertyUpdateFunction('sourceUrl'));
|
||||||
|
|
||||||
elModelURL.addEventListener('change', createEmitTextPropertyUpdateFunction('modelURL'));
|
elModelURL.addEventListener('change', createEmitTextPropertyUpdateFunction('modelURL'));
|
||||||
|
@ -1344,6 +1362,38 @@
|
||||||
<span id="property-id" class="selectable"></span>
|
<span id="property-id" class="selectable"></span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="section-header shape-group shape-section">
|
||||||
|
<label>Shape</label><span>M</span>
|
||||||
|
</div>
|
||||||
|
<div class="shape-group shape-section property dropdown">
|
||||||
|
<label for="property-shape">Shape</label>
|
||||||
|
<select name="SelectShape" id="property-shape">
|
||||||
|
<option value="Cube">Box</option>
|
||||||
|
<option value="Sphere">Sphere</option>
|
||||||
|
<option value="Tetrahedron">Tetrahedron</option>
|
||||||
|
<option value="Octahedron">Octahedron</option>
|
||||||
|
<option value="Icosahedron">Icosahedron</option>
|
||||||
|
<option value="Dodecahedron">Dodecahedron</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="shape-group shape-section property text">
|
||||||
|
<label>Temp</label>
|
||||||
|
<input type="text" id="property-temp-1">
|
||||||
|
</div>
|
||||||
|
<div class="shape-group shape-section property text">
|
||||||
|
<label>Temp</label>
|
||||||
|
<input type="text" id="property-temp-2">
|
||||||
|
</div>
|
||||||
|
<!--
|
||||||
|
<div class="shape-group shape-section property text">
|
||||||
|
<label>Temp</label>
|
||||||
|
<input type="text" id="property-temp-3">
|
||||||
|
</div>
|
||||||
|
<div class="shape-group shape-section property text">
|
||||||
|
<label>Temp</label>
|
||||||
|
<input type="text" id="property-temp-4">
|
||||||
|
</div>
|
||||||
|
-->
|
||||||
|
|
||||||
<div class="section-header hyperlink-group hyperlink-section">
|
<div class="section-header hyperlink-group hyperlink-section">
|
||||||
<label>Hyperlink</label><span>M</span>
|
<label>Hyperlink</label><span>M</span>
|
||||||
|
@ -1678,7 +1728,6 @@
|
||||||
<textarea id="property-model-original-textures" readonly></textarea>
|
<textarea id="property-model-original-textures" readonly></textarea>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
<div class="section-header text-group text-section">
|
<div class="section-header text-group text-section">
|
||||||
<label>Text</label><span>M</span>
|
<label>Text</label><span>M</span>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1717,6 +1766,14 @@
|
||||||
<input type="checkbox" id="property-zone-stage-sun-model-enabled">
|
<input type="checkbox" id="property-zone-stage-sun-model-enabled">
|
||||||
<label for="property-zone-stage-sun-model-enabled">Enable stage sun model</label>
|
<label for="property-zone-stage-sun-model-enabled">Enable stage sun model</label>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="zone-group zone-section property checkbox">
|
||||||
|
<input type="checkbox" id="property-zone-flying-allowed">
|
||||||
|
<label for="property-zone-flying-allowed">Flying allowed</label>
|
||||||
|
</div>
|
||||||
|
<div class="zone-group zone-section property checkbox">
|
||||||
|
<input type="checkbox" id="property-zone-ghosting-allowed">
|
||||||
|
<label for="property-zone-ghosting-allowed">Ghosting allowed</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="sub-section-header zone-group zone-section keylight-section">
|
<div class="sub-section-header zone-group zone-section keylight-section">
|
||||||
<label>Key Light</label>
|
<label>Key Light</label>
|
||||||
|
@ -1801,15 +1858,6 @@
|
||||||
<input type="text" id="property-zone-skybox-url">
|
<input type="text" id="property-zone-skybox-url">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="zone-group zone-section property checkbox">
|
|
||||||
<input type="checkbox" id="property-zone-flying-allowed">
|
|
||||||
<label for="property-zone-flying-allowed"> Flying Allowed</label>
|
|
||||||
</div>
|
|
||||||
<div class="zone-group zone-section property checkbox">
|
|
||||||
<input type="checkbox" id="property-zone-ghosting-allowed">
|
|
||||||
<label for="property-zone-ghosting-allowed"> Ghosting Allowed</label>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
<div class="section-header web-group web-section">
|
<div class="section-header web-group web-section">
|
||||||
<label>Web</label><span>M</span>
|
<label>Web</label><span>M</span>
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
#include <QDir>
|
#include <QDir>
|
||||||
#include <ByteCountCoding.h>
|
#include <ByteCountCoding.h>
|
||||||
|
|
||||||
#include <BoxEntityItem.h>
|
#include <ShapeEntityItem.h>
|
||||||
#include <EntityItemProperties.h>
|
#include <EntityItemProperties.h>
|
||||||
#include <Octree.h>
|
#include <Octree.h>
|
||||||
#include <PathUtils.h>
|
#include <PathUtils.h>
|
||||||
|
@ -155,7 +155,7 @@ int main(int argc, char** argv) {
|
||||||
QFile file(getTestResourceDir() + "packet.bin");
|
QFile file(getTestResourceDir() + "packet.bin");
|
||||||
if (!file.open(QIODevice::ReadOnly)) return -1;
|
if (!file.open(QIODevice::ReadOnly)) return -1;
|
||||||
QByteArray packet = file.readAll();
|
QByteArray packet = file.readAll();
|
||||||
EntityItemPointer item = BoxEntityItem::factory(EntityItemID(), EntityItemProperties());
|
EntityItemPointer item = ShapeEntityItem::boxFactory(EntityItemID(), EntityItemProperties());
|
||||||
ReadBitstreamToTreeParams params;
|
ReadBitstreamToTreeParams params;
|
||||||
params.bitstreamVersion = 33;
|
params.bitstreamVersion = 33;
|
||||||
|
|
||||||
|
|
|
@ -4,4 +4,6 @@ AUTOSCRIBE_SHADER_LIB(gpu model render-utils)
|
||||||
setup_hifi_project(Quick Gui OpenGL Script Widgets)
|
setup_hifi_project(Quick Gui OpenGL Script Widgets)
|
||||||
set_target_properties(${TARGET_NAME} PROPERTIES FOLDER "Tests/manual-tests/")
|
set_target_properties(${TARGET_NAME} PROPERTIES FOLDER "Tests/manual-tests/")
|
||||||
link_hifi_libraries(networking gl gpu gpu-gl procedural shared fbx model model-networking animation script-engine render render-utils )
|
link_hifi_libraries(networking gl gpu gpu-gl procedural shared fbx model model-networking animation script-engine render render-utils )
|
||||||
package_libraries_for_deployment()
|
package_libraries_for_deployment()
|
||||||
|
|
||||||
|
target_nsight()
|
||||||
|
|
20
tests/gpu-test/src/TestHelpers.cpp
Normal file
20
tests/gpu-test/src/TestHelpers.cpp
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
//
|
||||||
|
// Created by Bradley Austin Davis on 2016/05/16
|
||||||
|
// Copyright 2014 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "TestHelpers.h"
|
||||||
|
|
||||||
|
gpu::ShaderPointer makeShader(const std::string & vertexShaderSrc, const std::string & fragmentShaderSrc, const gpu::Shader::BindingSet & bindings) {
|
||||||
|
auto vs = gpu::Shader::createVertex(vertexShaderSrc);
|
||||||
|
auto fs = gpu::Shader::createPixel(fragmentShaderSrc);
|
||||||
|
auto shader = gpu::Shader::createProgram(vs, fs);
|
||||||
|
if (!gpu::Shader::makeProgram(*shader, bindings)) {
|
||||||
|
printf("Could not compile shader\n");
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
return shader;
|
||||||
|
}
|
33
tests/gpu-test/src/TestHelpers.h
Normal file
33
tests/gpu-test/src/TestHelpers.h
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
//
|
||||||
|
// Created by Bradley Austin Davis on 2016/05/16
|
||||||
|
// Copyright 2014 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include <GLMHelpers.h>
|
||||||
|
#include <Transform.h>
|
||||||
|
#include <GeometryCache.h>
|
||||||
|
#include <NumericalConstants.h>
|
||||||
|
|
||||||
|
#include <gpu/Resource.h>
|
||||||
|
#include <gpu/Forward.h>
|
||||||
|
#include <gpu/Shader.h>
|
||||||
|
#include <gpu/Stream.h>
|
||||||
|
|
||||||
|
class GpuTestBase {
|
||||||
|
public:
|
||||||
|
virtual ~GpuTestBase() {}
|
||||||
|
virtual bool isReady() const { return true; }
|
||||||
|
virtual size_t getTestCount() const { return 1; }
|
||||||
|
virtual void renderTest(size_t test, RenderArgs* args) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
uint32_t toCompactColor(const glm::vec4& color);
|
||||||
|
gpu::ShaderPointer makeShader(const std::string & vertexShaderSrc, const std::string & fragmentShaderSrc, const gpu::Shader::BindingSet & bindings);
|
||||||
|
|
84
tests/gpu-test/src/TestInstancedShapes.cpp
Normal file
84
tests/gpu-test/src/TestInstancedShapes.cpp
Normal file
|
@ -0,0 +1,84 @@
|
||||||
|
//
|
||||||
|
// Created by Bradley Austin Davis on 2016/05/16
|
||||||
|
// Copyright 2014 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "TestInstancedShapes.h"
|
||||||
|
|
||||||
|
gpu::Stream::FormatPointer& getInstancedSolidStreamFormat();
|
||||||
|
|
||||||
|
static const size_t TYPE_COUNT = 4;
|
||||||
|
static const size_t ITEM_COUNT = 50;
|
||||||
|
static const float SHAPE_INTERVAL = (PI * 2.0f) / ITEM_COUNT;
|
||||||
|
static const float ITEM_INTERVAL = SHAPE_INTERVAL / TYPE_COUNT;
|
||||||
|
|
||||||
|
static GeometryCache::Shape SHAPE[TYPE_COUNT] = {
|
||||||
|
GeometryCache::Icosahedron,
|
||||||
|
GeometryCache::Cube,
|
||||||
|
GeometryCache::Sphere,
|
||||||
|
GeometryCache::Tetrahedron,
|
||||||
|
};
|
||||||
|
|
||||||
|
const gpu::Element POSITION_ELEMENT { gpu::VEC3, gpu::FLOAT, gpu::XYZ };
|
||||||
|
const gpu::Element NORMAL_ELEMENT { gpu::VEC3, gpu::FLOAT, gpu::XYZ };
|
||||||
|
const gpu::Element COLOR_ELEMENT { gpu::VEC4, gpu::NUINT8, gpu::RGBA };
|
||||||
|
|
||||||
|
|
||||||
|
TestInstancedShapes::TestInstancedShapes() {
|
||||||
|
auto geometryCache = DependencyManager::get<GeometryCache>();
|
||||||
|
colorBuffer = std::make_shared<gpu::Buffer>();
|
||||||
|
|
||||||
|
static const float ITEM_RADIUS = 20;
|
||||||
|
static const vec3 ITEM_TRANSLATION { 0, 0, -ITEM_RADIUS };
|
||||||
|
for (size_t i = 0; i < TYPE_COUNT; ++i) {
|
||||||
|
GeometryCache::Shape shape = SHAPE[i];
|
||||||
|
GeometryCache::ShapeData shapeData = geometryCache->_shapes[shape];
|
||||||
|
//indirectCommand._count
|
||||||
|
float startingInterval = ITEM_INTERVAL * i;
|
||||||
|
std::vector<mat4> typeTransforms;
|
||||||
|
for (size_t j = 0; j < ITEM_COUNT; ++j) {
|
||||||
|
float theta = j * SHAPE_INTERVAL + startingInterval;
|
||||||
|
auto transform = glm::rotate(mat4(), theta, Vectors::UP);
|
||||||
|
transform = glm::rotate(transform, (randFloat() - 0.5f) * PI / 4.0f, Vectors::UNIT_X);
|
||||||
|
transform = glm::translate(transform, ITEM_TRANSLATION);
|
||||||
|
transform = glm::scale(transform, vec3(randFloat() / 2.0f + 0.5f));
|
||||||
|
typeTransforms.push_back(transform);
|
||||||
|
auto color = vec4 { randomColorValue(64), randomColorValue(64), randomColorValue(64), 255 };
|
||||||
|
color /= 255.0f;
|
||||||
|
colors.push_back(color);
|
||||||
|
colorBuffer->append(toCompactColor(color));
|
||||||
|
}
|
||||||
|
transforms.push_back(typeTransforms);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestInstancedShapes::renderTest(size_t testId, RenderArgs* args) {
|
||||||
|
gpu::Batch& batch = *(args->_batch);
|
||||||
|
auto geometryCache = DependencyManager::get<GeometryCache>();
|
||||||
|
geometryCache->bindSimpleProgram(batch);
|
||||||
|
batch.setInputFormat(getInstancedSolidStreamFormat());
|
||||||
|
for (size_t i = 0; i < TYPE_COUNT; ++i) {
|
||||||
|
GeometryCache::Shape shape = SHAPE[i];
|
||||||
|
GeometryCache::ShapeData shapeData = geometryCache->_shapes[shape];
|
||||||
|
|
||||||
|
std::string namedCall = __FUNCTION__ + std::to_string(i);
|
||||||
|
|
||||||
|
//batch.addInstanceModelTransforms(transforms[i]);
|
||||||
|
for (size_t j = 0; j < ITEM_COUNT; ++j) {
|
||||||
|
batch.setModelTransform(transforms[i][j]);
|
||||||
|
batch.setupNamedCalls(namedCall, [=](gpu::Batch& batch, gpu::Batch::NamedBatchData&) {
|
||||||
|
batch.setInputBuffer(gpu::Stream::COLOR, gpu::BufferView(colorBuffer, i * ITEM_COUNT * 4, colorBuffer->getSize(), COLOR_ELEMENT));
|
||||||
|
shapeData.drawInstances(batch, ITEM_COUNT);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
//for (size_t j = 0; j < ITEM_COUNT; ++j) {
|
||||||
|
// batch.setModelTransform(transforms[j + i * ITEM_COUNT]);
|
||||||
|
// shapeData.draw(batch);
|
||||||
|
//}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
23
tests/gpu-test/src/TestInstancedShapes.h
Normal file
23
tests/gpu-test/src/TestInstancedShapes.h
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
//
|
||||||
|
// Created by Bradley Austin Davis on 2016/05/16
|
||||||
|
// Copyright 2014 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "TestHelpers.h"
|
||||||
|
|
||||||
|
class TestInstancedShapes : public GpuTestBase {
|
||||||
|
|
||||||
|
std::vector<std::vector<mat4>> transforms;
|
||||||
|
std::vector<vec4> colors;
|
||||||
|
gpu::BufferPointer colorBuffer;
|
||||||
|
gpu::BufferView instanceXfmView;
|
||||||
|
public:
|
||||||
|
TestInstancedShapes();
|
||||||
|
void renderTest(size_t testId, RenderArgs* args) override;
|
||||||
|
};
|
||||||
|
|
48
tests/gpu-test/src/TestShapes.cpp
Normal file
48
tests/gpu-test/src/TestShapes.cpp
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
//
|
||||||
|
// Created by Bradley Austin Davis on 2016/05/16
|
||||||
|
// Copyright 2014 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "TestShapes.h"
|
||||||
|
|
||||||
|
static const size_t TYPE_COUNT = 6;
|
||||||
|
|
||||||
|
static GeometryCache::Shape SHAPE[TYPE_COUNT] = {
|
||||||
|
GeometryCache::Cube,
|
||||||
|
GeometryCache::Tetrahedron,
|
||||||
|
GeometryCache::Octahedron,
|
||||||
|
GeometryCache::Dodecahedron,
|
||||||
|
GeometryCache::Icosahedron,
|
||||||
|
GeometryCache::Sphere,
|
||||||
|
};
|
||||||
|
|
||||||
|
void TestShapes::renderTest(size_t testId, RenderArgs* args) {
|
||||||
|
gpu::Batch& batch = *(args->_batch);
|
||||||
|
auto geometryCache = DependencyManager::get<GeometryCache>();
|
||||||
|
geometryCache->bindSimpleProgram(batch);
|
||||||
|
|
||||||
|
// Render unlit cube + sphere
|
||||||
|
static auto startSecs = secTimestampNow();
|
||||||
|
float seconds = secTimestampNow() - startSecs;
|
||||||
|
seconds /= 4.0f;
|
||||||
|
batch.setModelTransform(Transform());
|
||||||
|
batch._glColor4f(0.8f, 0.25f, 0.25f, 1.0f);
|
||||||
|
|
||||||
|
bool wire = (seconds - floorf(seconds) > 0.5f);
|
||||||
|
int shapeIndex = ((int)seconds) % TYPE_COUNT;
|
||||||
|
if (wire) {
|
||||||
|
geometryCache->renderWireShape(batch, SHAPE[shapeIndex]);
|
||||||
|
} else {
|
||||||
|
geometryCache->renderShape(batch, SHAPE[shapeIndex]);
|
||||||
|
}
|
||||||
|
|
||||||
|
batch.setModelTransform(Transform().setScale(1.01f));
|
||||||
|
batch._glColor4f(1, 1, 1, 1);
|
||||||
|
geometryCache->renderWireCube(batch);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
22
tests/gpu-test/src/TestShapes.h
Normal file
22
tests/gpu-test/src/TestShapes.h
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
//
|
||||||
|
// Created by Bradley Austin Davis on 2016/05/16
|
||||||
|
// Copyright 2014 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "TestHelpers.h"
|
||||||
|
|
||||||
|
class TestShapes : public GpuTestBase {
|
||||||
|
|
||||||
|
std::vector<std::vector<mat4>> transforms;
|
||||||
|
std::vector<vec4> colors;
|
||||||
|
gpu::BufferPointer colorBuffer;
|
||||||
|
gpu::BufferView instanceXfmView;
|
||||||
|
public:
|
||||||
|
void renderTest(size_t testId, RenderArgs* args) override;
|
||||||
|
};
|
||||||
|
|
180
tests/gpu-test/src/TestWindow.cpp
Normal file
180
tests/gpu-test/src/TestWindow.cpp
Normal file
|
@ -0,0 +1,180 @@
|
||||||
|
//
|
||||||
|
// Created by Bradley Austin Davis on 2016/05/16
|
||||||
|
// Copyright 2014 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "TestWindow.h"
|
||||||
|
|
||||||
|
#include <glm/glm.hpp>
|
||||||
|
#include <glm/gtc/matrix_transform.hpp>
|
||||||
|
|
||||||
|
#include <QtCore/QTimer>
|
||||||
|
#include <QtGui/QResizeEvent>
|
||||||
|
|
||||||
|
#include <gl/GLHelpers.h>
|
||||||
|
#include <gl/QOpenGLDebugLoggerWrapper.h>
|
||||||
|
|
||||||
|
#include <gpu/gl/GLBackend.h>
|
||||||
|
|
||||||
|
#include <GeometryCache.h>
|
||||||
|
#include <DeferredLightingEffect.h>
|
||||||
|
#include <FramebufferCache.h>
|
||||||
|
#include <TextureCache.h>
|
||||||
|
|
||||||
|
#ifdef DEFERRED_LIGHTING
|
||||||
|
extern void initDeferredPipelines(render::ShapePlumber& plumber);
|
||||||
|
extern void initStencilPipeline(gpu::PipelinePointer& pipeline);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
TestWindow::TestWindow() {
|
||||||
|
setSurfaceType(QSurface::OpenGLSurface);
|
||||||
|
|
||||||
|
|
||||||
|
auto timer = new QTimer(this);
|
||||||
|
timer->setInterval(5);
|
||||||
|
connect(timer, &QTimer::timeout, [&] { draw(); });
|
||||||
|
timer->start();
|
||||||
|
|
||||||
|
connect(qApp, &QCoreApplication::aboutToQuit, [this, timer] {
|
||||||
|
timer->stop();
|
||||||
|
_aboutToQuit = true;
|
||||||
|
});
|
||||||
|
|
||||||
|
#ifdef DEFERRED_LIGHTING
|
||||||
|
_light->setType(model::Light::SUN);
|
||||||
|
_light->setAmbientSpherePreset(gpu::SphericalHarmonics::Preset::OLD_TOWN_SQUARE);
|
||||||
|
_light->setIntensity(1.0f);
|
||||||
|
_light->setAmbientIntensity(0.5f);
|
||||||
|
_light->setColor(vec3(1.0f));
|
||||||
|
_light->setPosition(vec3(1, 1, 1));
|
||||||
|
_renderContext->args = _renderArgs;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
QSurfaceFormat format = getDefaultOpenGLSurfaceFormat();
|
||||||
|
format.setOption(QSurfaceFormat::DebugContext);
|
||||||
|
//format.setSwapInterval(0);
|
||||||
|
setFormat(format);
|
||||||
|
_glContext.setFormat(format);
|
||||||
|
_glContext.create();
|
||||||
|
_glContext.makeCurrent(this);
|
||||||
|
show();
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestWindow::initGl() {
|
||||||
|
_glContext.makeCurrent(this);
|
||||||
|
gpu::Context::init<gpu::gl::GLBackend>();
|
||||||
|
_renderArgs->_context = std::make_shared<gpu::Context>();
|
||||||
|
_glContext.makeCurrent(this);
|
||||||
|
DependencyManager::set<GeometryCache>();
|
||||||
|
DependencyManager::set<TextureCache>();
|
||||||
|
DependencyManager::set<FramebufferCache>();
|
||||||
|
DependencyManager::set<DeferredLightingEffect>();
|
||||||
|
resize(QSize(800, 600));
|
||||||
|
|
||||||
|
setupDebugLogger(this);
|
||||||
|
#ifdef DEFERRED_LIGHTING
|
||||||
|
auto deferredLightingEffect = DependencyManager::get<DeferredLightingEffect>();
|
||||||
|
deferredLightingEffect->init();
|
||||||
|
deferredLightingEffect->setGlobalLight(_light);
|
||||||
|
initDeferredPipelines(*_shapePlumber);
|
||||||
|
initStencilPipeline(_opaquePipeline);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestWindow::resizeWindow(const QSize& size) {
|
||||||
|
_size = size;
|
||||||
|
_renderArgs->_viewport = ivec4(0, 0, _size.width(), _size.height());
|
||||||
|
auto fboCache = DependencyManager::get<FramebufferCache>();
|
||||||
|
if (fboCache) {
|
||||||
|
fboCache->setFrameBufferSize(_size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestWindow::beginFrame() {
|
||||||
|
_renderArgs->_context->syncCache();
|
||||||
|
|
||||||
|
#ifdef DEFERRED_LIGHTING
|
||||||
|
auto deferredLightingEffect = DependencyManager::get<DeferredLightingEffect>();
|
||||||
|
deferredLightingEffect->prepare(_renderArgs);
|
||||||
|
#else
|
||||||
|
gpu::doInBatch(_renderArgs->_context, [&](gpu::Batch& batch) {
|
||||||
|
batch.clearColorFramebuffer(gpu::Framebuffer::BUFFER_COLORS, { 0.0f, 0.1f, 0.2f, 1.0f });
|
||||||
|
batch.clearDepthFramebuffer(1e4);
|
||||||
|
batch.setViewportTransform({ 0, 0, _size.width() * devicePixelRatio(), _size.height() * devicePixelRatio() });
|
||||||
|
});
|
||||||
|
#endif
|
||||||
|
|
||||||
|
gpu::doInBatch(_renderArgs->_context, [&](gpu::Batch& batch) {
|
||||||
|
batch.setViewportTransform(_renderArgs->_viewport);
|
||||||
|
batch.setStateScissorRect(_renderArgs->_viewport);
|
||||||
|
batch.setProjectionTransform(_projectionMatrix);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestWindow::endFrame() {
|
||||||
|
#ifdef DEFERRED_LIGHTING
|
||||||
|
RenderArgs* args = _renderContext->args;
|
||||||
|
gpu::doInBatch(args->_context, [&](gpu::Batch& batch) {
|
||||||
|
args->_batch = &batch;
|
||||||
|
auto deferredFboColorDepthStencil = DependencyManager::get<FramebufferCache>()->getDeferredFramebufferDepthColor();
|
||||||
|
batch.setViewportTransform(args->_viewport);
|
||||||
|
batch.setStateScissorRect(args->_viewport);
|
||||||
|
batch.setFramebuffer(deferredFboColorDepthStencil);
|
||||||
|
batch.setPipeline(_opaquePipeline);
|
||||||
|
batch.draw(gpu::TRIANGLE_STRIP, 4);
|
||||||
|
batch.setResourceTexture(0, nullptr);
|
||||||
|
});
|
||||||
|
|
||||||
|
auto deferredLightingEffect = DependencyManager::get<DeferredLightingEffect>();
|
||||||
|
deferredLightingEffect->render(_renderContext);
|
||||||
|
|
||||||
|
gpu::doInBatch(_renderArgs->_context, [&](gpu::Batch& batch) {
|
||||||
|
PROFILE_RANGE_BATCH(batch, "blit");
|
||||||
|
// Blit to screen
|
||||||
|
auto framebufferCache = DependencyManager::get<FramebufferCache>();
|
||||||
|
auto framebuffer = framebufferCache->getLightingFramebuffer();
|
||||||
|
batch.blit(framebuffer, _renderArgs->_viewport, nullptr, _renderArgs->_viewport);
|
||||||
|
});
|
||||||
|
#endif
|
||||||
|
|
||||||
|
gpu::doInBatch(_renderArgs->_context, [&](gpu::Batch& batch) {
|
||||||
|
batch.resetStages();
|
||||||
|
});
|
||||||
|
_glContext.swapBuffers(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestWindow::draw() {
|
||||||
|
if (_aboutToQuit) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Attempting to draw before we're visible and have a valid size will
|
||||||
|
// produce GL errors.
|
||||||
|
if (!isVisible() || _size.width() <= 0 || _size.height() <= 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!_glContext.makeCurrent(this)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::once_flag once;
|
||||||
|
std::call_once(once, [&] { initGl(); });
|
||||||
|
beginFrame();
|
||||||
|
|
||||||
|
renderFrame();
|
||||||
|
|
||||||
|
endFrame();
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestWindow::resizeEvent(QResizeEvent* ev) {
|
||||||
|
resizeWindow(ev->size());
|
||||||
|
float fov_degrees = 60.0f;
|
||||||
|
float aspect_ratio = (float)_size.width() / _size.height();
|
||||||
|
float near_clip = 0.1f;
|
||||||
|
float far_clip = 1000.0f;
|
||||||
|
_projectionMatrix = glm::perspective(glm::radians(fov_degrees), aspect_ratio, near_clip, far_clip);
|
||||||
|
}
|
53
tests/gpu-test/src/TestWindow.h
Normal file
53
tests/gpu-test/src/TestWindow.h
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
//
|
||||||
|
// Created by Bradley Austin Davis on 2016/05/16
|
||||||
|
// Copyright 2014 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QtGui/QWindow>
|
||||||
|
#include <QtCore/QTime>
|
||||||
|
|
||||||
|
#include <GLMHelpers.h>
|
||||||
|
#include <gl/QOpenGLContextWrapper.h>
|
||||||
|
#include <gpu/Forward.h>
|
||||||
|
|
||||||
|
#include <DeferredLightingEffect.h>
|
||||||
|
#include <render/ShapePipeline.h>
|
||||||
|
#include <render/Context.h>
|
||||||
|
|
||||||
|
#define DEFERRED_LIGHTING
|
||||||
|
|
||||||
|
class TestWindow : public QWindow {
|
||||||
|
protected:
|
||||||
|
QOpenGLContextWrapper _glContext;
|
||||||
|
QSize _size;
|
||||||
|
glm::mat4 _projectionMatrix;
|
||||||
|
bool _aboutToQuit { false };
|
||||||
|
|
||||||
|
#ifdef DEFERRED_LIGHTING
|
||||||
|
// Prepare the ShapePipelines
|
||||||
|
render::ShapePlumberPointer _shapePlumber { std::make_shared<render::ShapePlumber>() };
|
||||||
|
render::RenderContextPointer _renderContext { std::make_shared<render::RenderContext>() };
|
||||||
|
gpu::PipelinePointer _opaquePipeline;
|
||||||
|
model::LightPointer _light { std::make_shared<model::Light>() };
|
||||||
|
#endif
|
||||||
|
|
||||||
|
RenderArgs* _renderArgs { new RenderArgs() };
|
||||||
|
|
||||||
|
TestWindow();
|
||||||
|
virtual void initGl();
|
||||||
|
virtual void renderFrame() = 0;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void resizeWindow(const QSize& size);
|
||||||
|
|
||||||
|
void beginFrame();
|
||||||
|
void endFrame();
|
||||||
|
void draw();
|
||||||
|
void resizeEvent(QResizeEvent* ev) override;
|
||||||
|
};
|
||||||
|
|
|
@ -42,238 +42,64 @@
|
||||||
|
|
||||||
#include <GeometryCache.h>
|
#include <GeometryCache.h>
|
||||||
#include <DeferredLightingEffect.h>
|
#include <DeferredLightingEffect.h>
|
||||||
|
#include <FramebufferCache.h>
|
||||||
#include <TextureCache.h>
|
#include <TextureCache.h>
|
||||||
|
#include <PerfStat.h>
|
||||||
|
#include <PathUtils.h>
|
||||||
|
#include <RenderArgs.h>
|
||||||
|
#include <ViewFrustum.h>
|
||||||
|
|
||||||
#include "unlit_frag.h"
|
#include <gpu/Pipeline.h>
|
||||||
#include "unlit_vert.h"
|
#include <gpu/Context.h>
|
||||||
|
|
||||||
class RateCounter {
|
#include <render/Engine.h>
|
||||||
std::vector<float> times;
|
#include <render/Scene.h>
|
||||||
QElapsedTimer timer;
|
#include <render/CullTask.h>
|
||||||
public:
|
#include <render/SortTask.h>
|
||||||
RateCounter() {
|
#include <render/DrawTask.h>
|
||||||
timer.start();
|
#include <render/DrawStatus.h>
|
||||||
}
|
#include <render/DrawSceneOctree.h>
|
||||||
|
#include <render/CullTask.h>
|
||||||
|
|
||||||
void reset() {
|
#include "TestWindow.h"
|
||||||
times.clear();
|
#include "TestInstancedShapes.h"
|
||||||
}
|
#include "TestShapes.h"
|
||||||
|
|
||||||
unsigned int count() const {
|
using namespace render;
|
||||||
return (unsigned int)times.size() - 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
float elapsed() const {
|
|
||||||
if (times.size() < 1) {
|
|
||||||
return 0.0f;
|
|
||||||
}
|
|
||||||
float elapsed = *times.rbegin() - *times.begin();
|
|
||||||
return elapsed;
|
|
||||||
}
|
|
||||||
|
|
||||||
void increment() {
|
|
||||||
times.push_back(timer.elapsed() / 1000.0f);
|
|
||||||
}
|
|
||||||
|
|
||||||
float rate() const {
|
|
||||||
if (elapsed() == 0.0f) {
|
|
||||||
return NAN;
|
|
||||||
}
|
|
||||||
return (float) count() / elapsed();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
uint32_t toCompactColor(const glm::vec4& color);
|
|
||||||
|
|
||||||
|
|
||||||
const char* VERTEX_SHADER = R"SHADER(
|
using TestBuilder = std::function<GpuTestBase*()>;
|
||||||
|
using TestBuilders = std::list<TestBuilder>;
|
||||||
layout(location = 0) in vec4 inPosition;
|
|
||||||
layout(location = 3) in vec2 inTexCoord0;
|
|
||||||
|
|
||||||
struct TransformObject {
|
|
||||||
mat4 _model;
|
|
||||||
mat4 _modelInverse;
|
|
||||||
};
|
|
||||||
|
|
||||||
layout(location=15) in ivec2 _drawCallInfo;
|
|
||||||
|
|
||||||
uniform samplerBuffer transformObjectBuffer;
|
|
||||||
|
|
||||||
TransformObject getTransformObject() {
|
|
||||||
int offset = 8 * _drawCallInfo.x;
|
|
||||||
TransformObject object;
|
|
||||||
object._model[0] = texelFetch(transformObjectBuffer, offset);
|
|
||||||
object._model[1] = texelFetch(transformObjectBuffer, offset + 1);
|
|
||||||
object._model[2] = texelFetch(transformObjectBuffer, offset + 2);
|
|
||||||
object._model[3] = texelFetch(transformObjectBuffer, offset + 3);
|
|
||||||
|
|
||||||
object._modelInverse[0] = texelFetch(transformObjectBuffer, offset + 4);
|
|
||||||
object._modelInverse[1] = texelFetch(transformObjectBuffer, offset + 5);
|
|
||||||
object._modelInverse[2] = texelFetch(transformObjectBuffer, offset + 6);
|
|
||||||
object._modelInverse[3] = texelFetch(transformObjectBuffer, offset + 7);
|
|
||||||
|
|
||||||
return object;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct TransformCamera {
|
|
||||||
mat4 _view;
|
|
||||||
mat4 _viewInverse;
|
|
||||||
mat4 _projectionViewUntranslated;
|
|
||||||
mat4 _projection;
|
|
||||||
mat4 _projectionInverse;
|
|
||||||
vec4 _viewport;
|
|
||||||
};
|
|
||||||
|
|
||||||
layout(std140) uniform transformCameraBuffer {
|
|
||||||
TransformCamera _camera;
|
|
||||||
};
|
|
||||||
|
|
||||||
TransformCamera getTransformCamera() {
|
|
||||||
return _camera;
|
|
||||||
}
|
|
||||||
|
|
||||||
// the interpolated normal
|
|
||||||
out vec2 _texCoord0;
|
|
||||||
|
|
||||||
void main(void) {
|
|
||||||
_texCoord0 = inTexCoord0.st;
|
|
||||||
|
|
||||||
// standard transform
|
|
||||||
TransformCamera cam = getTransformCamera();
|
|
||||||
TransformObject obj = getTransformObject();
|
|
||||||
{ // transformModelToClipPos
|
|
||||||
vec4 eyeWAPos;
|
|
||||||
{ // _transformModelToEyeWorldAlignedPos
|
|
||||||
highp mat4 _mv = obj._model;
|
|
||||||
_mv[3].xyz -= cam._viewInverse[3].xyz;
|
|
||||||
highp vec4 _eyeWApos = (_mv * inPosition);
|
|
||||||
eyeWAPos = _eyeWApos;
|
|
||||||
}
|
|
||||||
gl_Position = cam._projectionViewUntranslated * eyeWAPos;
|
|
||||||
}
|
|
||||||
|
|
||||||
})SHADER";
|
|
||||||
|
|
||||||
const char* FRAGMENT_SHADER = R"SHADER(
|
|
||||||
|
|
||||||
uniform sampler2D originalTexture;
|
|
||||||
|
|
||||||
in vec2 _texCoord0;
|
|
||||||
|
|
||||||
layout(location = 0) out vec4 _fragColor0;
|
|
||||||
|
|
||||||
void main(void) {
|
|
||||||
//_fragColor0 = vec4(_texCoord0, 0.0, 1.0);
|
|
||||||
_fragColor0 = texture(originalTexture, _texCoord0);
|
|
||||||
}
|
|
||||||
)SHADER";
|
|
||||||
|
|
||||||
|
|
||||||
gpu::ShaderPointer makeShader(const std::string & vertexShaderSrc, const std::string & fragmentShaderSrc, const gpu::Shader::BindingSet & bindings) {
|
#define INTERACTIVE
|
||||||
auto vs = gpu::Shader::createVertex(vertexShaderSrc);
|
|
||||||
auto fs = gpu::Shader::createPixel(fragmentShaderSrc);
|
|
||||||
auto shader = gpu::Shader::createProgram(vs, fs);
|
|
||||||
if (!gpu::Shader::makeProgram(*shader, bindings)) {
|
|
||||||
printf("Could not compile shader\n");
|
|
||||||
exit(-1);
|
|
||||||
}
|
|
||||||
return shader;
|
|
||||||
}
|
|
||||||
|
|
||||||
float getSeconds(quint64 start = 0) {
|
class MyTestWindow : public TestWindow {
|
||||||
auto usecs = usecTimestampNow() - start;
|
using Parent = TestWindow;
|
||||||
auto msecs = usecs / USECS_PER_MSEC;
|
TestBuilders _testBuilders;
|
||||||
float seconds = (float)msecs / MSECS_PER_SECOND;
|
GpuTestBase* _currentTest { nullptr };
|
||||||
return seconds;
|
size_t _currentTestId { 0 };
|
||||||
}
|
size_t _currentMaxTests { 0 };
|
||||||
|
|
||||||
static const size_t TYPE_COUNT = 4;
|
|
||||||
static GeometryCache::Shape SHAPE[TYPE_COUNT] = {
|
|
||||||
GeometryCache::Icosahedron,
|
|
||||||
GeometryCache::Cube,
|
|
||||||
GeometryCache::Sphere,
|
|
||||||
GeometryCache::Tetrahedron,
|
|
||||||
//GeometryCache::Line,
|
|
||||||
};
|
|
||||||
|
|
||||||
gpu::Stream::FormatPointer& getInstancedSolidStreamFormat();
|
|
||||||
|
|
||||||
// Creates an OpenGL window that renders a simple unlit scene using the gpu library and GeometryCache
|
|
||||||
// Should eventually get refactored into something that supports multiple gpu backends.
|
|
||||||
class QTestWindow : public QWindow {
|
|
||||||
Q_OBJECT
|
|
||||||
|
|
||||||
QOpenGLContextWrapper _qGlContext;
|
|
||||||
QSize _size;
|
|
||||||
|
|
||||||
gpu::ContextPointer _context;
|
|
||||||
gpu::PipelinePointer _pipeline;
|
|
||||||
glm::mat4 _projectionMatrix;
|
|
||||||
RateCounter fps;
|
|
||||||
QTime _time;
|
|
||||||
glm::mat4 _camera;
|
glm::mat4 _camera;
|
||||||
|
QTime _time;
|
||||||
|
|
||||||
protected:
|
void initGl() override {
|
||||||
void renderText();
|
Parent::initGl();
|
||||||
|
#ifdef INTERACTIVE
|
||||||
private:
|
|
||||||
void resizeWindow(const QSize& size) {
|
|
||||||
_size = size;
|
|
||||||
}
|
|
||||||
|
|
||||||
public:
|
|
||||||
QTestWindow() {
|
|
||||||
setSurfaceType(QSurface::OpenGLSurface);
|
|
||||||
|
|
||||||
QSurfaceFormat format;
|
|
||||||
// Qt Quick may need a depth and stencil buffer. Always make sure these are available.
|
|
||||||
format.setDepthBufferSize(16);
|
|
||||||
format.setStencilBufferSize(8);
|
|
||||||
setGLFormatVersion(format);
|
|
||||||
format.setProfile(QSurfaceFormat::OpenGLContextProfile::CoreProfile);
|
|
||||||
format.setOption(QSurfaceFormat::DebugContext);
|
|
||||||
//format.setSwapInterval(0);
|
|
||||||
|
|
||||||
setFormat(format);
|
|
||||||
|
|
||||||
_qGlContext.setFormat(format);
|
|
||||||
_qGlContext.create();
|
|
||||||
|
|
||||||
show();
|
|
||||||
makeCurrent();
|
|
||||||
setupDebugLogger(this);
|
|
||||||
|
|
||||||
gpu::Context::init<gpu::gl::GLBackend>();
|
|
||||||
_context = std::make_shared<gpu::Context>();
|
|
||||||
makeCurrent();
|
|
||||||
auto shader = makeShader(unlit_vert, unlit_frag, gpu::Shader::BindingSet{});
|
|
||||||
auto state = std::make_shared<gpu::State>();
|
|
||||||
state->setMultisampleEnable(true);
|
|
||||||
state->setDepthTest(gpu::State::DepthTest { true });
|
|
||||||
_pipeline = gpu::Pipeline::create(shader, state);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Clear screen
|
|
||||||
gpu::Batch batch;
|
|
||||||
batch.clearColorFramebuffer(gpu::Framebuffer::BUFFER_COLORS, { 1.0, 0.0, 0.5, 1.0 });
|
|
||||||
_context->render(batch);
|
|
||||||
|
|
||||||
DependencyManager::set<GeometryCache>();
|
|
||||||
DependencyManager::set<TextureCache>();
|
|
||||||
DependencyManager::set<DeferredLightingEffect>();
|
|
||||||
|
|
||||||
resize(QSize(800, 600));
|
|
||||||
|
|
||||||
_time.start();
|
_time.start();
|
||||||
}
|
#endif
|
||||||
|
updateCamera();
|
||||||
virtual ~QTestWindow() {
|
_testBuilders = TestBuilders({
|
||||||
|
//[this] { return new TestFbx(_shapePlumber); },
|
||||||
|
[] { return new TestShapes(); },
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void updateCamera() {
|
void updateCamera() {
|
||||||
float t = _time.elapsed() * 1e-4f;
|
float t = 0;
|
||||||
|
#ifdef INTERACTIVE
|
||||||
|
t = _time.elapsed() * 1e-3f;
|
||||||
|
#endif
|
||||||
glm::vec3 unitscale { 1.0f };
|
glm::vec3 unitscale { 1.0f };
|
||||||
glm::vec3 up { 0.0f, 1.0f, 0.0f };
|
glm::vec3 up { 0.0f, 1.0f, 0.0f };
|
||||||
|
|
||||||
|
@ -283,263 +109,64 @@ public:
|
||||||
static const vec3 camera_focus(0);
|
static const vec3 camera_focus(0);
|
||||||
static const vec3 camera_up(0, 1, 0);
|
static const vec3 camera_up(0, 1, 0);
|
||||||
_camera = glm::inverse(glm::lookAt(camera_position, camera_focus, up));
|
_camera = glm::inverse(glm::lookAt(camera_position, camera_focus, up));
|
||||||
|
|
||||||
|
ViewFrustum frustum;
|
||||||
|
frustum.setPosition(camera_position);
|
||||||
|
frustum.setOrientation(glm::quat_cast(_camera));
|
||||||
|
frustum.setProjection(_projectionMatrix);
|
||||||
|
_renderArgs->setViewFrustum(frustum);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void renderFrame() override {
|
||||||
|
updateCamera();
|
||||||
|
|
||||||
void drawFloorGrid(gpu::Batch& batch) {
|
while ((!_currentTest || (_currentTestId >= _currentMaxTests)) && !_testBuilders.empty()) {
|
||||||
auto geometryCache = DependencyManager::get<GeometryCache>();
|
if (_currentTest) {
|
||||||
// Render grid on xz plane (not the optimal way to do things, but w/e)
|
delete _currentTest;
|
||||||
// Note: GeometryCache::renderGrid will *not* work, as it is apparenly unaffected by batch rotations and renders xy only
|
_currentTest = nullptr;
|
||||||
static const std::string GRID_INSTANCE = "Grid";
|
}
|
||||||
static auto compactColor1 = toCompactColor(vec4 { 0.35f, 0.25f, 0.15f, 1.0f });
|
|
||||||
static auto compactColor2 = toCompactColor(vec4 { 0.15f, 0.25f, 0.35f, 1.0f });
|
|
||||||
static std::vector<glm::mat4> transforms;
|
|
||||||
static gpu::BufferPointer colorBuffer;
|
|
||||||
if (!transforms.empty()) {
|
|
||||||
transforms.reserve(200);
|
|
||||||
colorBuffer = std::make_shared<gpu::Buffer>();
|
|
||||||
for (int i = 0; i < 100; ++i) {
|
|
||||||
{
|
|
||||||
glm::mat4 transform = glm::translate(mat4(), vec3(0, -1, -50 + i));
|
|
||||||
transform = glm::scale(transform, vec3(100, 1, 1));
|
|
||||||
transforms.push_back(transform);
|
|
||||||
colorBuffer->append(compactColor1);
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
_currentTest = _testBuilders.front()();
|
||||||
glm::mat4 transform = glm::mat4_cast(quat(vec3(0, PI / 2.0f, 0)));
|
_testBuilders.pop_front();
|
||||||
transform = glm::translate(transform, vec3(0, -1, -50 + i));
|
|
||||||
transform = glm::scale(transform, vec3(100, 1, 1));
|
if (_currentTest) {
|
||||||
transforms.push_back(transform);
|
_currentMaxTests = _currentTest->getTestCount();
|
||||||
colorBuffer->append(compactColor2);
|
_currentTestId = 0;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
auto pipeline = geometryCache->getSimplePipeline();
|
|
||||||
for (auto& transform : transforms) {
|
|
||||||
batch.setModelTransform(transform);
|
|
||||||
batch.setupNamedCalls(GRID_INSTANCE, [=](gpu::Batch& batch, gpu::Batch::NamedBatchData& data) {
|
|
||||||
batch.setViewTransform(_camera);
|
|
||||||
batch.setPipeline(_pipeline);
|
|
||||||
geometryCache->renderWireShapeInstances(batch, GeometryCache::Line, data.count(), colorBuffer);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void drawSimpleShapes(gpu::Batch& batch) {
|
if (!_currentTest && _testBuilders.empty()) {
|
||||||
auto geometryCache = DependencyManager::get<GeometryCache>();
|
qApp->quit();
|
||||||
static const size_t ITEM_COUNT = 1000;
|
|
||||||
static const float SHAPE_INTERVAL = (PI * 2.0f) / ITEM_COUNT;
|
|
||||||
static const float ITEM_INTERVAL = SHAPE_INTERVAL / TYPE_COUNT;
|
|
||||||
|
|
||||||
static const gpu::Element POSITION_ELEMENT { gpu::VEC3, gpu::FLOAT, gpu::XYZ };
|
|
||||||
static const gpu::Element NORMAL_ELEMENT { gpu::VEC3, gpu::FLOAT, gpu::XYZ };
|
|
||||||
static const gpu::Element COLOR_ELEMENT { gpu::VEC4, gpu::NUINT8, gpu::RGBA };
|
|
||||||
|
|
||||||
static std::vector<Transform> transforms;
|
|
||||||
static std::vector<vec4> colors;
|
|
||||||
static gpu::BufferPointer colorBuffer;
|
|
||||||
static gpu::BufferView colorView;
|
|
||||||
static gpu::BufferView instanceXfmView;
|
|
||||||
if (!colorBuffer) {
|
|
||||||
colorBuffer = std::make_shared<gpu::Buffer>();
|
|
||||||
|
|
||||||
static const float ITEM_RADIUS = 20;
|
|
||||||
static const vec3 ITEM_TRANSLATION { 0, 0, -ITEM_RADIUS };
|
|
||||||
for (size_t i = 0; i < TYPE_COUNT; ++i) {
|
|
||||||
GeometryCache::Shape shape = SHAPE[i];
|
|
||||||
GeometryCache::ShapeData shapeData = geometryCache->_shapes[shape];
|
|
||||||
//indirectCommand._count
|
|
||||||
float startingInterval = ITEM_INTERVAL * i;
|
|
||||||
for (size_t j = 0; j < ITEM_COUNT; ++j) {
|
|
||||||
float theta = j * SHAPE_INTERVAL + startingInterval;
|
|
||||||
auto transform = glm::rotate(mat4(), theta, Vectors::UP);
|
|
||||||
transform = glm::rotate(transform, (randFloat() - 0.5f) * PI / 4.0f, Vectors::UNIT_X);
|
|
||||||
transform = glm::translate(transform, ITEM_TRANSLATION);
|
|
||||||
transform = glm::scale(transform, vec3(randFloat() / 2.0f + 0.5f));
|
|
||||||
transforms.push_back(transform);
|
|
||||||
auto color = vec4 { randomColorValue(64), randomColorValue(64), randomColorValue(64), 255 };
|
|
||||||
color /= 255.0f;
|
|
||||||
colors.push_back(color);
|
|
||||||
colorBuffer->append(toCompactColor(color));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
colorView = gpu::BufferView(colorBuffer, COLOR_ELEMENT);
|
|
||||||
}
|
|
||||||
|
|
||||||
batch.setViewTransform(_camera);
|
|
||||||
batch.setPipeline(_pipeline);
|
|
||||||
batch.setInputFormat(getInstancedSolidStreamFormat());
|
|
||||||
for (size_t i = 0; i < TYPE_COUNT; ++i) {
|
|
||||||
GeometryCache::Shape shape = SHAPE[i];
|
|
||||||
GeometryCache::ShapeData shapeData = geometryCache->_shapes[shape];
|
|
||||||
batch.setInputBuffer(gpu::Stream::COLOR, colorView);
|
|
||||||
for (size_t j = 0; j < ITEM_COUNT; ++j) {
|
|
||||||
batch.setModelTransform(transforms[j]);
|
|
||||||
shapeData.draw(batch);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void drawCenterShape(gpu::Batch& batch) {
|
|
||||||
// Render unlit cube + sphere
|
|
||||||
static auto startUsecs = usecTimestampNow();
|
|
||||||
float seconds = getSeconds(startUsecs);
|
|
||||||
seconds /= 4.0f;
|
|
||||||
batch.setModelTransform(Transform());
|
|
||||||
batch._glColor4f(0.8f, 0.25f, 0.25f, 1.0f);
|
|
||||||
|
|
||||||
bool wire = (seconds - floorf(seconds) > 0.5f);
|
|
||||||
auto geometryCache = DependencyManager::get<GeometryCache>();
|
|
||||||
int shapeIndex = ((int)seconds) % TYPE_COUNT;
|
|
||||||
if (wire) {
|
|
||||||
geometryCache->renderWireShape(batch, SHAPE[shapeIndex]);
|
|
||||||
} else {
|
|
||||||
geometryCache->renderShape(batch, SHAPE[shapeIndex]);
|
|
||||||
}
|
|
||||||
|
|
||||||
batch.setModelTransform(Transform().setScale(2.05f));
|
|
||||||
batch._glColor4f(1, 1, 1, 1);
|
|
||||||
geometryCache->renderWireCube(batch);
|
|
||||||
}
|
|
||||||
|
|
||||||
void drawTerrain(gpu::Batch& batch) {
|
|
||||||
auto geometryCache = DependencyManager::get<GeometryCache>();
|
|
||||||
static std::once_flag once;
|
|
||||||
static gpu::BufferPointer vertexBuffer { std::make_shared<gpu::Buffer>() };
|
|
||||||
static gpu::BufferPointer indexBuffer { std::make_shared<gpu::Buffer>() };
|
|
||||||
|
|
||||||
static gpu::BufferView positionView;
|
|
||||||
static gpu::BufferView textureView;
|
|
||||||
static gpu::Stream::FormatPointer vertexFormat { std::make_shared<gpu::Stream::Format>() };
|
|
||||||
|
|
||||||
static gpu::TexturePointer texture;
|
|
||||||
static gpu::PipelinePointer pipeline;
|
|
||||||
std::call_once(once, [&] {
|
|
||||||
static const uint SHAPE_VERTEX_STRIDE = sizeof(glm::vec4) * 2; // position, normals, textures
|
|
||||||
static const uint SHAPE_TEXTURES_OFFSET = sizeof(glm::vec4);
|
|
||||||
static const gpu::Element POSITION_ELEMENT { gpu::VEC3, gpu::FLOAT, gpu::XYZ };
|
|
||||||
static const gpu::Element TEXTURE_ELEMENT { gpu::VEC2, gpu::FLOAT, gpu::UV };
|
|
||||||
std::vector<vec4> vertices;
|
|
||||||
const int MINX = -1000;
|
|
||||||
const int MAXX = 1000;
|
|
||||||
|
|
||||||
// top
|
|
||||||
vertices.push_back(vec4(MAXX, 0, MAXX, 1));
|
|
||||||
vertices.push_back(vec4(MAXX, MAXX, 0, 0));
|
|
||||||
|
|
||||||
vertices.push_back(vec4(MAXX, 0, MINX, 1));
|
|
||||||
vertices.push_back(vec4(MAXX, 0, 0, 0));
|
|
||||||
|
|
||||||
vertices.push_back(vec4(MINX, 0, MINX, 1));
|
|
||||||
vertices.push_back(vec4(0, 0, 0, 0));
|
|
||||||
|
|
||||||
vertices.push_back(vec4(MINX, 0, MAXX, 1));
|
|
||||||
vertices.push_back(vec4(0, MAXX, 0, 0));
|
|
||||||
|
|
||||||
vertexBuffer->append(vertices);
|
|
||||||
indexBuffer->append(std::vector<uint16_t>({ 0, 1, 2, 2, 3, 0 }));
|
|
||||||
|
|
||||||
positionView = gpu::BufferView(vertexBuffer, 0, vertexBuffer->getSize(), SHAPE_VERTEX_STRIDE, POSITION_ELEMENT);
|
|
||||||
textureView = gpu::BufferView(vertexBuffer, SHAPE_TEXTURES_OFFSET, vertexBuffer->getSize(), SHAPE_VERTEX_STRIDE, TEXTURE_ELEMENT);
|
|
||||||
texture = DependencyManager::get<TextureCache>()->getImageTexture("C:/Users/bdavis/Git/openvr/samples/bin/cube_texture.png");
|
|
||||||
// texture = DependencyManager::get<TextureCache>()->getImageTexture("H:/test.png");
|
|
||||||
//texture = DependencyManager::get<TextureCache>()->getImageTexture("H:/crate_blue.fbm/lambert8SG_Normal_OpenGL.png");
|
|
||||||
|
|
||||||
auto shader = makeShader(VERTEX_SHADER, FRAGMENT_SHADER, gpu::Shader::BindingSet {});
|
|
||||||
auto state = std::make_shared<gpu::State>();
|
|
||||||
state->setMultisampleEnable(false);
|
|
||||||
state->setDepthTest(gpu::State::DepthTest { true });
|
|
||||||
pipeline = gpu::Pipeline::create(shader, state);
|
|
||||||
vertexFormat->setAttribute(gpu::Stream::POSITION);
|
|
||||||
vertexFormat->setAttribute(gpu::Stream::TEXCOORD);
|
|
||||||
});
|
|
||||||
|
|
||||||
static auto start = usecTimestampNow();
|
|
||||||
auto now = usecTimestampNow();
|
|
||||||
if ((now - start) > USECS_PER_SECOND * 1) {
|
|
||||||
start = now;
|
|
||||||
texture->incremementMinMip();
|
|
||||||
}
|
|
||||||
|
|
||||||
batch.setPipeline(pipeline);
|
|
||||||
batch.setInputBuffer(gpu::Stream::POSITION, positionView);
|
|
||||||
batch.setInputBuffer(gpu::Stream::TEXCOORD, textureView);
|
|
||||||
batch.setIndexBuffer(gpu::UINT16, indexBuffer, 0);
|
|
||||||
batch.setInputFormat(vertexFormat);
|
|
||||||
|
|
||||||
batch.setResourceTexture(0, texture);
|
|
||||||
batch.setModelTransform(glm::translate(glm::mat4(), vec3(0, -0.1, 0)));
|
|
||||||
batch.drawIndexed(gpu::TRIANGLES, 6, 0);
|
|
||||||
|
|
||||||
batch.setResourceTexture(0, DependencyManager::get<TextureCache>()->getBlueTexture());
|
|
||||||
batch.setModelTransform(glm::translate(glm::mat4(), vec3(0, -0.2, 0)));
|
|
||||||
batch.drawIndexed(gpu::TRIANGLES, 6, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
void draw() {
|
|
||||||
// Attempting to draw before we're visible and have a valid size will
|
|
||||||
// produce GL errors.
|
|
||||||
if (!isVisible() || _size.width() <= 0 || _size.height() <= 0) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
updateCamera();
|
|
||||||
makeCurrent();
|
|
||||||
|
|
||||||
gpu::Batch batch;
|
|
||||||
batch.resetStages();
|
|
||||||
batch.clearColorFramebuffer(gpu::Framebuffer::BUFFER_COLORS, { 0.0f, 0.1f, 0.2f, 1.0f });
|
|
||||||
batch.clearDepthFramebuffer(1e4);
|
|
||||||
batch.setViewportTransform({ 0, 0, _size.width() * devicePixelRatio(), _size.height() * devicePixelRatio() });
|
|
||||||
batch.setProjectionTransform(_projectionMatrix);
|
|
||||||
|
|
||||||
batch.setViewTransform(_camera);
|
|
||||||
batch.setPipeline(_pipeline);
|
|
||||||
batch.setModelTransform(Transform());
|
|
||||||
|
|
||||||
//drawFloorGrid(batch);
|
// Tests might need to wait for resources to download
|
||||||
//drawSimpleShapes(batch);
|
if (!_currentTest->isReady()) {
|
||||||
//drawCenterShape(batch);
|
return;
|
||||||
drawTerrain(batch);
|
|
||||||
|
|
||||||
_context->render(batch);
|
|
||||||
_qGlContext.swapBuffers(this);
|
|
||||||
|
|
||||||
fps.increment();
|
|
||||||
if (fps.elapsed() >= 0.5f) {
|
|
||||||
qDebug() << "FPS: " << fps.rate();
|
|
||||||
fps.reset();
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
void makeCurrent() {
|
|
||||||
_qGlContext.makeCurrent(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected:
|
gpu::doInBatch(_renderArgs->_context, [&](gpu::Batch& batch) {
|
||||||
void resizeEvent(QResizeEvent* ev) override {
|
batch.setViewTransform(_camera);
|
||||||
resizeWindow(ev->size());
|
_renderArgs->_batch = &batch;
|
||||||
|
_currentTest->renderTest(_currentTestId, _renderArgs);
|
||||||
float fov_degrees = 60.0f;
|
_renderArgs->_batch = nullptr;
|
||||||
float aspect_ratio = (float)_size.width() / _size.height();
|
});
|
||||||
float near_clip = 0.1f;
|
|
||||||
float far_clip = 1000.0f;
|
#ifdef INTERACTIVE
|
||||||
_projectionMatrix = glm::perspective(glm::radians(fov_degrees), aspect_ratio, near_clip, far_clip);
|
|
||||||
}
|
#else
|
||||||
|
// TODO Capture the current rendered framebuffer and save
|
||||||
|
// Increment the test ID
|
||||||
|
++_currentTestId;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
int main(int argc, char** argv) {
|
int main(int argc, char** argv) {
|
||||||
QGuiApplication app(argc, argv);
|
QGuiApplication app(argc, argv);
|
||||||
QTestWindow window;
|
MyTestWindow window;
|
||||||
auto timer = new QTimer(&app);
|
|
||||||
timer->setInterval(0);
|
|
||||||
app.connect(timer, &QTimer::timeout, &app, [&] {
|
|
||||||
window.draw();
|
|
||||||
});
|
|
||||||
timer->start();
|
|
||||||
app.exec();
|
app.exec();
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#include "main.moc"
|
|
||||||
|
|
||||||
|
|
|
@ -157,10 +157,6 @@ protected:
|
||||||
//static const wchar_t* EXAMPLE_TEXT = L"\xC1y Hello 1.0\ny\xC1 line 2\n\xC1y";
|
//static const wchar_t* EXAMPLE_TEXT = L"\xC1y Hello 1.0\ny\xC1 line 2\n\xC1y";
|
||||||
static const glm::uvec2 QUAD_OFFSET(10, 10);
|
static const glm::uvec2 QUAD_OFFSET(10, 10);
|
||||||
|
|
||||||
static const glm::vec3 COLORS[4] = { { 1.0, 1.0, 1.0 }, { 0.5, 1.0, 0.5 }, {
|
|
||||||
1.0, 0.5, 0.5 }, { 0.5, 0.5, 1.0 } };
|
|
||||||
|
|
||||||
|
|
||||||
void testShaderBuild(const char* vs_src, const char * fs_src) {
|
void testShaderBuild(const char* vs_src, const char * fs_src) {
|
||||||
auto vs = gpu::Shader::createVertex(std::string(vs_src));
|
auto vs = gpu::Shader::createVertex(std::string(vs_src));
|
||||||
auto fs = gpu::Shader::createPixel(std::string(fs_src));
|
auto fs = gpu::Shader::createPixel(std::string(fs_src));
|
||||||
|
|
|
@ -211,6 +211,27 @@ ApplicationWindow {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Button {
|
||||||
|
text: "Open File"
|
||||||
|
property var builder: Component {
|
||||||
|
FileDialog {
|
||||||
|
title: "Open File"
|
||||||
|
filter: "All Files (*.*)"
|
||||||
|
//filter: "HTML files (*.html);;Other(*.png)"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onClicked: {
|
||||||
|
var fileDialog = builder.createObject(desktop);
|
||||||
|
fileDialog.canceled.connect(function(){
|
||||||
|
console.log("Cancelled")
|
||||||
|
})
|
||||||
|
fileDialog.selectedFile.connect(function(file){
|
||||||
|
console.log("Selected " + file)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Button {
|
Button {
|
||||||
text: "Add Tab"
|
text: "Add Tab"
|
||||||
onClicked: {
|
onClicked: {
|
||||||
|
@ -246,24 +267,7 @@ ApplicationWindow {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Button {
|
|
||||||
text: "Open File"
|
|
||||||
property var builder: Component {
|
|
||||||
FileDialog { }
|
|
||||||
}
|
|
||||||
|
|
||||||
onClicked: {
|
|
||||||
var fileDialog = builder.createObject(desktop);
|
|
||||||
fileDialog.canceled.connect(function(){
|
|
||||||
console.log("Cancelled")
|
|
||||||
})
|
|
||||||
fileDialog.selectedFile.connect(function(file){
|
|
||||||
console.log("Selected " + file)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Window {
|
Window {
|
||||||
id: blue
|
id: blue
|
||||||
|
|
Loading…
Reference in a new issue