From 6e59abc8f6ec64d8810ae4c80a600d200ba5316e Mon Sep 17 00:00:00 2001 From: David Rowe <david@ctrlaltstudio.com> Date: Tue, 10 May 2016 12:13:46 +1200 Subject: [PATCH 01/35] Switch file selection dialog over to using UI Toolkit files --- .../resources/qml/dialogs/FileDialog.qml | 54 ++++++++++++------- tests/ui/qml/main.qml | 37 +++++++------ 2 files changed, 54 insertions(+), 37 deletions(-) diff --git a/interface/resources/qml/dialogs/FileDialog.qml b/interface/resources/qml/dialogs/FileDialog.qml index 916ef434b6..8bb92b60d6 100644 --- a/interface/resources/qml/dialogs/FileDialog.qml +++ b/interface/resources/qml/dialogs/FileDialog.qml @@ -1,4 +1,14 @@ -import QtQuick 2.0 +// +// Desktop.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 Qt.labs.folderlistmodel 2.1 import Qt.labs.settings 1.0 @@ -6,17 +16,19 @@ import QtQuick.Controls.Styles 1.4 import QtQuick.Dialogs 1.2 as OriginalDialogs import ".." -import "../windows" -import "../styles" -import "../controls" as VrControls +import "../controls-uit" +import "../styles-uit" +import "../windows-uit" + import "fileDialog" //FIXME implement shortcuts for favorite location ModalWindow { id: root - resizable: true - width: 640 - height: 480 + //resizable: true + implicitWidth: 640 + implicitHeight: 480 + HifiConstants { id: hifi } Settings { category: "FileDialog" @@ -46,6 +58,8 @@ ModalWindow { property alias model: fileTableView.model property var drives: helper.drives() + property int titleWidth: 0 + signal selectedFile(var file); signal canceled(); @@ -56,14 +70,17 @@ ModalWindow { }) } - Rectangle { - anchors.fill: parent - color: "white" + Item { + clip: true + width: pane.width + height: pane.height + anchors.margins: 0 Row { id: navControls anchors { left: parent.left; top: parent.top; margins: 8 } spacing: 8 + // FIXME implement back button //VrControls.ButtonAwesome { // id: backButton @@ -72,30 +89,29 @@ ModalWindow { // enabled: d.backStack.length != 0 // MouseArea { anchors.fill: parent; onClicked: d.navigateBack() } //} - VrControls.ButtonAwesome { + + Button { id: upButton enabled: model.parentFolder && model.parentFolder !== "" - text: "\uf0aa" - size: 32 + text: "up" onClicked: d.navigateUp(); } - VrControls.ButtonAwesome { + + Button { id: homeButton property var destination: helper.home(); enabled: d.homeDestination ? true : false - text: "\uf015" - size: 32 + text: "home" onClicked: d.navigateHome(); } - VrControls.ComboBox { + ComboBox { id: drivesSelector width: 48 height: homeButton.height model: drives visible: drives.length > 1 currentIndex: 0 - } } @@ -385,5 +401,3 @@ ModalWindow { } } } - - diff --git a/tests/ui/qml/main.qml b/tests/ui/qml/main.qml index e45749e1de..54ce16fbc2 100644 --- a/tests/ui/qml/main.qml +++ b/tests/ui/qml/main.qml @@ -211,6 +211,26 @@ ApplicationWindow { } } + Button { + text: "Open File" + property var builder: Component { + FileDialog { + title: "Open File" + filter: "*.js" + } + } + + onClicked: { + var fileDialog = builder.createObject(desktop); + fileDialog.canceled.connect(function(){ + console.log("Cancelled") + }) + fileDialog.selectedFile.connect(function(file){ + console.log("Selected " + file) + }) + } + } + Button { text: "Add Tab" onClicked: { @@ -246,24 +266,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 { id: blue From 43ee64c251b83f8ef2ebfb1d1a8f9d77c0cd162e Mon Sep 17 00:00:00 2001 From: David Rowe <david@ctrlaltstudio.com> Date: Tue, 10 May 2016 12:50:32 +1200 Subject: [PATCH 02/35] Upate general layout of file selection dialog --- .../resources/qml/dialogs/FileDialog.qml | 52 +++++++++++++++---- 1 file changed, 41 insertions(+), 11 deletions(-) diff --git a/interface/resources/qml/dialogs/FileDialog.qml b/interface/resources/qml/dialogs/FileDialog.qml index 8bb92b60d6..d74913b301 100644 --- a/interface/resources/qml/dialogs/FileDialog.qml +++ b/interface/resources/qml/dialogs/FileDialog.qml @@ -78,8 +78,12 @@ ModalWindow { Row { id: navControls - anchors { left: parent.left; top: parent.top; margins: 8 } - spacing: 8 + anchors { + top: parent.top + topMargin: hifi.dimensions.contentMargin.y + left: parent.left + } + spacing: hifi.dimensions.contentSpacing.x // FIXME implement back button //VrControls.ButtonAwesome { @@ -119,7 +123,13 @@ ModalWindow { id: currentDirectory height: homeButton.height style: TextFieldStyle { renderType: Text.QtRendering } - anchors { left: navControls.right; right: parent.right; top: parent.top; margins: 8 } + anchors { + top: parent.top + topMargin: hifi.dimensions.contentMargin.y + left: navControls.right + leftMargin: hifi.dimensions.contentSpacing.x + right: parent.right + } property var lastValidFolder: helper.urlToPath(model.folder) onLastValidFolderChanged: text = lastValidFolder; verticalAlignment: Text.AlignVCenter @@ -179,7 +189,14 @@ ModalWindow { FileTableView { id: fileTableView - anchors { left: parent.left; right: parent.right; top: currentDirectory.bottom; bottom: currentSelection.top; margins: 8 } + anchors { + top: navControls.bottom + topMargin: hifi.dimensions.contentSpacing.y + left: parent.left + right: parent.right + bottom: currentSelection.top + bottomMargin: hifi.dimensions.contentSpacing.y + } onDoubleClicked: navigateToRow(row); focus: true Keys.onReturnPressed: navigateToCurrentRow(); @@ -277,7 +294,13 @@ ModalWindow { TextField { id: currentSelection style: TextFieldStyle { renderType: Text.QtRendering } - anchors { right: root.selectDirectory ? parent.right : selectionType.left; rightMargin: 8; left: parent.left; leftMargin: 8; top: selectionType.top } + anchors { + left: parent.left + right: root.selectDirectory ? parent.right : selectionType.left + rightMargin: hifi.dimensions.contentSpacing.x + bottom: buttonRow.top + bottomMargin: hifi.dimensions.contentSpacing.y + } readOnly: !root.saveDialog activeFocusOnTab: !readOnly onActiveFocusChanged: if (activeFocus) { selectAll(); } @@ -286,7 +309,11 @@ ModalWindow { FileTypeSelection { id: selectionType - anchors { bottom: buttonRow.top; bottomMargin: 8; right: parent.right; rightMargin: 8; left: buttonRow.left } + anchors { + top: currentSelection.top + left: buttonRow.left + right: parent.right + } visible: !selectDirectory KeyNavigation.left: fileTableView KeyNavigation.right: openButton @@ -294,19 +321,22 @@ ModalWindow { Row { id: buttonRow - anchors.right: parent.right - anchors.rightMargin: 8 - anchors.bottom: parent.bottom - anchors.bottomMargin: 8 - spacing: 8 + anchors { + right: parent.right + bottom: parent.bottom + } + spacing: hifi.dimensions.contentSpacing.y + Button { id: openButton + color: hifi.buttons.blue action: okAction Keys.onReturnPressed: okAction.trigger() KeyNavigation.up: selectionType KeyNavigation.left: selectionType KeyNavigation.right: cancelButton } + Button { id: cancelButton action: cancelAction From 663a2ddc6446ce3eebb6737f0273ab8042e6fb17 Mon Sep 17 00:00:00 2001 From: David Rowe <david@ctrlaltstudio.com> Date: Tue, 10 May 2016 14:43:54 +1200 Subject: [PATCH 03/35] Style path and filter controls --- interface/resources/qml/dialogs/FileDialog.qml | 8 ++++---- .../qml/dialogs/fileDialog/FileTypeSelection.qml | 14 ++++++++++++-- 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/interface/resources/qml/dialogs/FileDialog.qml b/interface/resources/qml/dialogs/FileDialog.qml index d74913b301..42fe7120ea 100644 --- a/interface/resources/qml/dialogs/FileDialog.qml +++ b/interface/resources/qml/dialogs/FileDialog.qml @@ -1,5 +1,5 @@ // -// Desktop.qml +// FileDialog.qml // // Created by Bradley Austin Davis on 14 Jan 2016 // Copyright 2015 High Fidelity, Inc. @@ -195,7 +195,7 @@ ModalWindow { left: parent.left right: parent.right bottom: currentSelection.top - bottomMargin: hifi.dimensions.contentSpacing.y + bottomMargin: hifi.dimensions.contentSpacing.y + currentSelection.controlHeight - currentSelection.height } onDoubleClicked: navigateToRow(row); focus: true @@ -293,11 +293,11 @@ ModalWindow { TextField { id: currentSelection - style: TextFieldStyle { renderType: Text.QtRendering } + label: "Path:" anchors { left: parent.left right: root.selectDirectory ? parent.right : selectionType.left - rightMargin: hifi.dimensions.contentSpacing.x + rightMargin: root.selectDirectory ? 0 : hifi.dimensions.contentSpacing.x bottom: buttonRow.top bottomMargin: hifi.dimensions.contentSpacing.y } diff --git a/interface/resources/qml/dialogs/fileDialog/FileTypeSelection.qml b/interface/resources/qml/dialogs/fileDialog/FileTypeSelection.qml index 57ad2028ad..3d66b37b67 100644 --- a/interface/resources/qml/dialogs/fileDialog/FileTypeSelection.qml +++ b/interface/resources/qml/dialogs/fileDialog/FileTypeSelection.qml @@ -1,8 +1,18 @@ +// +// 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 "../../controls" as VrControls +import "../../controls-uit" -VrControls.ComboBox { +ComboBox { id: root property string filtersString: "All Files (*.*)"; property var currentFilter: [ "*.*" ]; From b8c0ec86af7d663636c1bde1d2b969b9bb763f55 Mon Sep 17 00:00:00 2001 From: David Rowe <david@ctrlaltstudio.com> Date: Tue, 10 May 2016 15:09:13 +1200 Subject: [PATCH 04/35] Hide file filter if only 1 file type --- interface/resources/qml/dialogs/FileDialog.qml | 6 +++--- .../resources/qml/dialogs/fileDialog/FileTypeSelection.qml | 1 + tests/ui/qml/main.qml | 3 ++- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/interface/resources/qml/dialogs/FileDialog.qml b/interface/resources/qml/dialogs/FileDialog.qml index 42fe7120ea..3be7fbc667 100644 --- a/interface/resources/qml/dialogs/FileDialog.qml +++ b/interface/resources/qml/dialogs/FileDialog.qml @@ -296,8 +296,8 @@ ModalWindow { label: "Path:" anchors { left: parent.left - right: root.selectDirectory ? parent.right : selectionType.left - rightMargin: root.selectDirectory ? 0 : hifi.dimensions.contentSpacing.x + right: selectionType.visible ? selectionType.left: parent.right + rightMargin: selectionType.visible ? hifi.dimensions.contentSpacing.x : 0 bottom: buttonRow.top bottomMargin: hifi.dimensions.contentSpacing.y } @@ -314,7 +314,7 @@ ModalWindow { left: buttonRow.left right: parent.right } - visible: !selectDirectory + visible: !selectDirectory && filtersCount > 1 KeyNavigation.left: fileTableView KeyNavigation.right: openButton } diff --git a/interface/resources/qml/dialogs/fileDialog/FileTypeSelection.qml b/interface/resources/qml/dialogs/fileDialog/FileTypeSelection.qml index 3d66b37b67..50a10974b5 100644 --- a/interface/resources/qml/dialogs/fileDialog/FileTypeSelection.qml +++ b/interface/resources/qml/dialogs/fileDialog/FileTypeSelection.qml @@ -16,6 +16,7 @@ ComboBox { id: root property string filtersString: "All Files (*.*)"; property var currentFilter: [ "*.*" ]; + property int filtersCount: filtersString.split(';;').length // Per http://doc.qt.io/qt-5/qfiledialog.html#getOpenFileName the string can contain // multiple filters separated by semicolons diff --git a/tests/ui/qml/main.qml b/tests/ui/qml/main.qml index 54ce16fbc2..1745658193 100644 --- a/tests/ui/qml/main.qml +++ b/tests/ui/qml/main.qml @@ -216,7 +216,8 @@ ApplicationWindow { property var builder: Component { FileDialog { title: "Open File" - filter: "*.js" + filter: "All Files (*.*)" + //filter: "HTML files (*.html);;Other(*.png)" } } From 88207b727b8cfa6628883a88dfc2256abb1117b7 Mon Sep 17 00:00:00 2001 From: David Rowe <david@ctrlaltstudio.com> Date: Tue, 10 May 2016 15:45:10 +1200 Subject: [PATCH 05/35] Style directory and navigation controls "as is" --- .../qml/controls-uit/GlyphButton.qml | 2 +- .../resources/qml/dialogs/FileDialog.qml | 19 ++++++++----------- 2 files changed, 9 insertions(+), 12 deletions(-) diff --git a/interface/resources/qml/controls-uit/GlyphButton.qml b/interface/resources/qml/controls-uit/GlyphButton.qml index c4ee53c04f..2625dda723 100644 --- a/interface/resources/qml/controls-uit/GlyphButton.qml +++ b/interface/resources/qml/controls-uit/GlyphButton.qml @@ -17,7 +17,7 @@ import "../styles-uit" Original.Button { property int color: 0 - property int colorScheme: hifi.colorShemes.light + property int colorScheme: hifi.colorSchemes.light property string glyph: "" width: 120 diff --git a/interface/resources/qml/dialogs/FileDialog.qml b/interface/resources/qml/dialogs/FileDialog.qml index 3be7fbc667..1f710b4ef5 100644 --- a/interface/resources/qml/dialogs/FileDialog.qml +++ b/interface/resources/qml/dialogs/FileDialog.qml @@ -94,25 +94,26 @@ ModalWindow { // MouseArea { anchors.fill: parent; onClicked: d.navigateBack() } //} - Button { + GlyphButton { id: upButton + glyph: hifi.glyphs.levelUp + width: height enabled: model.parentFolder && model.parentFolder !== "" - text: "up" onClicked: d.navigateUp(); } - Button { + GlyphButton { id: homeButton property var destination: helper.home(); + glyph: hifi.glyphs.home + width: height enabled: d.homeDestination ? true : false - text: "home" onClicked: d.navigateHome(); } ComboBox { id: drivesSelector - width: 48 - height: homeButton.height + width: 62 model: drives visible: drives.length > 1 currentIndex: 0 @@ -121,8 +122,8 @@ ModalWindow { TextField { id: currentDirectory + property var lastValidFolder: helper.urlToPath(model.folder) height: homeButton.height - style: TextFieldStyle { renderType: Text.QtRendering } anchors { top: parent.top topMargin: hifi.dimensions.contentMargin.y @@ -130,11 +131,7 @@ ModalWindow { leftMargin: hifi.dimensions.contentSpacing.x right: parent.right } - property var lastValidFolder: helper.urlToPath(model.folder) onLastValidFolderChanged: text = lastValidFolder; - verticalAlignment: Text.AlignVCenter - font.pointSize: 14 - font.bold: true // FIXME add support auto-completion onAccepted: { From 51d6d99c73cf4ee520784e03b7b113f22b5c4e70 Mon Sep 17 00:00:00 2001 From: David Rowe <david@ctrlaltstudio.com> Date: Wed, 11 May 2016 09:17:11 +1200 Subject: [PATCH 06/35] Fix up files list data and basic layout --- .../qml/dialogs/fileDialog/FileTableView.qml | 29 ++++++++++++++----- 1 file changed, 22 insertions(+), 7 deletions(-) diff --git a/interface/resources/qml/dialogs/fileDialog/FileTableView.qml b/interface/resources/qml/dialogs/fileDialog/FileTableView.qml index 93cdeebab0..d51f9e1ed9 100644 --- a/interface/resources/qml/dialogs/fileDialog/FileTableView.qml +++ b/interface/resources/qml/dialogs/fileDialog/FileTableView.qml @@ -1,4 +1,14 @@ -import QtQuick 2.0 +// +// FileDialog.qml +// +// Created by Bradley Austin Davis on 22 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 TableView { @@ -8,6 +18,7 @@ TableView { root.selection.select(0) } } + //horizontalScrollBarPolicy: Qt.ScrollBarAlwaysOff itemDelegate: Component { Item { @@ -23,6 +34,7 @@ TableView { function getText() { switch (styleData.column) { //case 1: return Date.fromLocaleString(locale, styleData.value, "yyyy-MM-dd hh:mm:ss"); + case 1: return root.model.get(styleData.row, "fileIsDir") ? "" : styleData.value; case 2: return root.model.get(styleData.row, "fileIsDir") ? "" : formatSize(styleData.value); default: return styleData.value; } @@ -45,20 +57,23 @@ TableView { } TableViewColumn { + id: fileNameColumn role: "fileName" title: "Name" - width: 400 + width: Math.floor(0.55 * parent.width) + resizable: true } TableViewColumn { + id: fileMofifiedColumn role: "fileModified" - title: "Date Modified" - width: 200 + title: "Date" + width: Math.floor(0.3 * parent.width) + resizable: true } TableViewColumn { role: "fileSize" title: "Size" - width: 200 + width: Math.floor(0.15 * parent.width) - 16 - 2 // Allow space for vertical scrollbar and borders + resizable: true } } - - From 57fae3fc5121a979bb836cafccd1fd643a18a234 Mon Sep 17 00:00:00 2001 From: David Rowe <david@ctrlaltstudio.com> Date: Thu, 12 May 2016 09:28:25 +1200 Subject: [PATCH 07/35] Add FiraSans-Regular font --- .../resources/fonts/FiraSans-Regular.ttf | Bin 0 -> 403924 bytes .../qml/styles-uit/FiraSansRegular.qml | 23 ++++++++++++++++++ 2 files changed, 23 insertions(+) create mode 100644 interface/resources/fonts/FiraSans-Regular.ttf create mode 100644 interface/resources/qml/styles-uit/FiraSansRegular.qml diff --git a/interface/resources/fonts/FiraSans-Regular.ttf b/interface/resources/fonts/FiraSans-Regular.ttf new file mode 100644 index 0000000000000000000000000000000000000000..d9fdc0e922030c6836a9ad5f808fd7e1c5970873 GIT binary patch literal 403924 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	r-(pXe}H&MjC7NAiF2}5 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%0D7he^vq7s@3w(9EC4-Q2YPk~==bwM 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 War7s!{wz%rN_!OQ*CO@X^#2EvSW`a$ literal 0 HcmV?d00001 diff --git a/interface/resources/qml/styles-uit/FiraSansRegular.qml b/interface/resources/qml/styles-uit/FiraSansRegular.qml new file mode 100644 index 0000000000..4ae0826772 --- /dev/null +++ b/interface/resources/qml/styles-uit/FiraSansRegular.qml @@ -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 +} From c29b82e24de6a0183c3cd838f01e03f9720ba877 Mon Sep 17 00:00:00 2001 From: David Rowe <david@ctrlaltstudio.com> Date: Thu, 12 May 2016 17:08:28 +1200 Subject: [PATCH 08/35] Restyle file selection dialog First pass --- .../qml/controls-uit/AttachmentsTable.qml | 7 +- .../resources/qml/controls-uit/Table.qml | 163 +++++++----------- .../resources/qml/dialogs/FileDialog.qml | 76 +++++++- .../qml/dialogs/fileDialog/FileTableView.qml | 79 --------- .../qml/styles-uit/HifiConstants.qml | 19 +- 5 files changed, 155 insertions(+), 189 deletions(-) delete mode 100644 interface/resources/qml/dialogs/fileDialog/FileTableView.qml diff --git a/interface/resources/qml/controls-uit/AttachmentsTable.qml b/interface/resources/qml/controls-uit/AttachmentsTable.qml index 6022de7f93..ce93b8f4df 100644 --- a/interface/resources/qml/controls-uit/AttachmentsTable.qml +++ b/interface/resources/qml/controls-uit/AttachmentsTable.qml @@ -21,7 +21,6 @@ import "../hifi/models" TableView { id: tableView - // property var tableModel: ListModel { } property int colorScheme: hifi.colorSchemes.light readonly property bool isLightColorScheme: colorScheme == hifi.colorSchemes.light @@ -46,7 +45,7 @@ TableView { RalewayRegular { id: textHeader - size: hifi.fontSizes.tableText + size: hifi.fontSizes.tableHeading color: hifi.colors.lightGrayText text: styleData.value anchors { @@ -87,7 +86,7 @@ TableView { bottomMargin: 3 // "" } radius: 3 - color: hifi.colors.tableScrollHandle + color: hifi.colors.tableScrollHandleDark } } @@ -107,7 +106,7 @@ TableView { margins: 1 // Shrink } radius: 4 - color: hifi.colors.tableScrollBackground + color: hifi.colors.tableScrollBackgroundDark } } diff --git a/interface/resources/qml/controls-uit/Table.qml b/interface/resources/qml/controls-uit/Table.qml index de6950c07e..2a0fe545ef 100644 --- a/interface/resources/qml/controls-uit/Table.qml +++ b/interface/resources/qml/controls-uit/Table.qml @@ -17,20 +17,54 @@ import "../styles-uit" TableView { id: tableView - property var tableModel: ListModel { } property int colorScheme: hifi.colorSchemes.light readonly property bool isLightColorScheme: colorScheme == hifi.colorSchemes.light + property bool expandSelectedRow: false - model: tableModel - - TableViewColumn { - role: "name" - } - - anchors { left: parent.left; right: parent.right } + model: ListModel { } 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 { + text: styleData.value + size: hifi.fontSizes.tableHeading + font.capitalization: Font.AllUppercase + color: hifi.colors.baseGrayHighlight + anchors { + left: parent.left + leftMargin: hifi.dimensions.tablePadding + right: parent.right + rightMargin: hifi.dimensions.tablePadding + verticalCenter: parent.verticalCenter + } + } + + 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. frameVisible: false @@ -50,8 +84,10 @@ TableView { style: TableViewStyle { // Needed in order for rows to keep displaying rows after end of table entries. - backgroundColor: parent.isLightColorScheme ? hifi.colors.tableRowLightEven : hifi.colors.tableRowDarkEven - alternateBackgroundColor: parent.isLightColorScheme ? hifi.colors.tableRowLightOdd : hifi.colors.tableRowDarkOdd + backgroundColor: tableView.isLightColorScheme ? hifi.colors.tableBackgroundLight : hifi.colors.tableBackgroundDark + alternateBackgroundColor: tableView.isLightColorScheme ? hifi.colors.tableRowLightOdd : hifi.colors.tableRowDarkOdd + + padding.top: headerVisible ? hifi.dimensions.tableHeaderHeight: 0 handle: Item { id: scrollbarHandle @@ -59,33 +95,38 @@ TableView { Rectangle { anchors { fill: parent + topMargin: 3 + bottomMargin: 3 // "" leftMargin: 2 // Move it right rightMargin: -2 // "" - topMargin: 3 // Shrink vertically - bottomMargin: 3 // "" } radius: 3 - color: hifi.colors.tableScrollHandle + color: isLightColorScheme ? hifi.colors.tableScrollHandleLight : hifi.colors.tableScrollHandleDark } } scrollBarBackground: Item { - implicitWidth: 10 + implicitWidth: 9 Rectangle { anchors { fill: parent margins: -1 // Expand + topMargin: headerVisible ? -hifi.dimensions.tableHeaderHeight : -1 } - color: hifi.colors.baseGrayHighlight - } + color: isLightColorScheme ? hifi.colors.tableBackgroundLight : hifi.colors.tableBackgroundDark - Rectangle { - anchors { - fill: parent - margins: 1 // Shrink + Rectangle { + // Extend header bottom border + anchors { + 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 +140,11 @@ TableView { } rowDelegate: Rectangle { - height: (styleData.selected ? 1.8 : 1) * hifi.dimensions.tableRowHeight + height: (styleData.selected && expandSelectedRow ? 1.8 : 1) * hifi.dimensions.tableRowHeight color: styleData.selected ? hifi.colors.primaryHighlight : tableView.isLightColorScheme ? (styleData.alternate ? hifi.colors.tableRowLightEven : hifi.colors.tableRowLightOdd) : (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 - } - } } diff --git a/interface/resources/qml/dialogs/FileDialog.qml b/interface/resources/qml/dialogs/FileDialog.qml index 1f710b4ef5..ac0858c134 100644 --- a/interface/resources/qml/dialogs/FileDialog.qml +++ b/interface/resources/qml/dialogs/FileDialog.qml @@ -28,6 +28,7 @@ ModalWindow { //resizable: true implicitWidth: 640 implicitHeight: 480 + HifiConstants { id: hifi } Settings { @@ -184,8 +185,9 @@ ModalWindow { } } - FileTableView { + Table { id: fileTableView + colorScheme: hifi.colorSchemes.light anchors { top: navControls.bottom topMargin: hifi.dimensions.contentSpacing.y @@ -194,10 +196,12 @@ ModalWindow { bottom: currentSelection.top bottomMargin: hifi.dimensions.contentSpacing.y + currentSelection.controlHeight - currentSelection.height } + headerVisible: true onDoubleClicked: navigateToRow(row); focus: true Keys.onReturnPressed: navigateToCurrentRow(); Keys.onEnterPressed: navigateToCurrentRow(); + model: FolderListModel { id: model nameFilters: selectionType.currentFilter @@ -218,6 +222,76 @@ ModalWindow { } } + onActiveFocusChanged: { + if (activeFocus && currentRow == -1) { + fileTableView.selection.select(0) + } + } + + itemDelegate: Item { + 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: fileTableView.model.get(styleData.row, "fileIsDir") ? firaSansSemiBold.name : firaSansRegular.name + + function getText() { + 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: 0.5 * fileTableView.width + resizable: true + } + TableViewColumn { + id: fileMofifiedColumn + role: "fileModified" + title: "Date" + width: 0.3 * fileTableView.width + resizable: true + } + TableViewColumn { + role: "fileSize" + title: "Size" + width: fileTableView.width - fileNameColumn.width - fileMofifiedColumn.width + resizable: true + } + function navigateToRow(row) { currentRow = row; navigateToCurrentRow(); diff --git a/interface/resources/qml/dialogs/fileDialog/FileTableView.qml b/interface/resources/qml/dialogs/fileDialog/FileTableView.qml deleted file mode 100644 index d51f9e1ed9..0000000000 --- a/interface/resources/qml/dialogs/fileDialog/FileTableView.qml +++ /dev/null @@ -1,79 +0,0 @@ -// -// FileDialog.qml -// -// Created by Bradley Austin Davis on 22 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 - -TableView { - id: root - onActiveFocusChanged: { - if (activeFocus && currentRow == -1) { - root.selection.select(0) - } - } - //horizontalScrollBarPolicy: Qt.ScrollBarAlwaysOff - - 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 1: return root.model.get(styleData.row, "fileIsDir") ? "" : styleData.value; - 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 { - id: fileNameColumn - role: "fileName" - title: "Name" - width: Math.floor(0.55 * parent.width) - resizable: true - } - TableViewColumn { - id: fileMofifiedColumn - role: "fileModified" - title: "Date" - width: Math.floor(0.3 * parent.width) - resizable: true - } - TableViewColumn { - role: "fileSize" - title: "Size" - width: Math.floor(0.15 * parent.width) - 16 - 2 // Allow space for vertical scrollbar and borders - resizable: true - } -} diff --git a/interface/resources/qml/styles-uit/HifiConstants.qml b/interface/resources/qml/styles-uit/HifiConstants.qml index cb4d2157ca..4d5052f086 100644 --- a/interface/resources/qml/styles-uit/HifiConstants.qml +++ b/interface/resources/qml/styles-uit/HifiConstants.qml @@ -89,12 +89,16 @@ Item { readonly property color transparent: "#00ffffff" // Control specific colors - readonly property color tableRowLightOdd: white50 - readonly property color tableRowLightEven: "#1a575757" - readonly property color tableRowDarkOdd: "#80393939" - readonly property color tableRowDarkEven: "#a6181818" - readonly property color tableScrollHandle: "#707070" - readonly property color tableScrollBackground: "#323232" + readonly property color tableRowLightOdd: "#eaeaea" // Equivalent to white50 over #e3e3e3 background + readonly property color tableRowLightEven: "#c6c6c6" // Equivavlent to "#1a575757" over #e3e3e3 background + readonly property color tableRowDarkOdd: "#2e2e2e" // Equivalent to "#80393939" over #404040 background + readonly property color tableRowDarkEven: "#1c1c1c" // Equivalent to "#a6181818" over #404040 background + readonly property color tableBackgroundLight: tableRowLightEven + 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 checkboxLightFinish: "#afafaf" readonly property color checkboxDarkStart: "#7d7d7d" @@ -140,7 +144,7 @@ Item { readonly property real spinnerSize: 42 readonly property real tablePadding: 12 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 real modalDialogTitleHeight: 40 readonly property real controlLineHeight: 28 // Height of spinbox control on 1920 x 1080 monitor @@ -157,6 +161,7 @@ Item { readonly property real textFieldInput: dimensions.largeScreen ? 15 : 12 readonly property real textFieldInputLabel: dimensions.largeScreen ? 13 : 9 readonly property real textFieldSearchIcon: dimensions.largeScreen ? 30 : 24 + readonly property real tableHeading: dimensions.largeScreen ? 12 : 10 readonly property real tableText: dimensions.largeScreen ? 15 : 12 readonly property real buttonLabel: dimensions.largeScreen ? 13 : 9 readonly property real iconButton: dimensions.largeScreen ? 13 : 9 From b6fcb77d6f76306ad2a4022c2430b7656e9583fc Mon Sep 17 00:00:00 2001 From: David Rowe <david@ctrlaltstudio.com> Date: Thu, 12 May 2016 17:16:56 +1200 Subject: [PATCH 09/35] Update Running Scripts list to use updated QML table control --- .../qml/hifi/dialogs/RunningScripts.qml | 80 ++++++++++++++++++- 1 file changed, 79 insertions(+), 1 deletion(-) diff --git a/interface/resources/qml/hifi/dialogs/RunningScripts.qml b/interface/resources/qml/hifi/dialogs/RunningScripts.qml index 071789fe16..31bb553809 100644 --- a/interface/resources/qml/hifi/dialogs/RunningScripts.qml +++ b/interface/resources/qml/hifi/dialogs/RunningScripts.qml @@ -118,11 +118,89 @@ Window { } HifiControls.Table { - tableModel: runningScriptsModel + model: runningScriptsModel + id: table height: 185 colorScheme: hifi.colorSchemes.dark anchors.left: parent.left 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 { From d798e562fc7b101c836e0ef74f711b09c507f5a9 Mon Sep 17 00:00:00 2001 From: David Rowe <david@ctrlaltstudio.com> Date: Thu, 12 May 2016 17:18:30 +1200 Subject: [PATCH 10/35] Update scrollbar in tree view to match table scrollbar Used in Running Scripts and Asset Browser dialogs. --- interface/resources/qml/controls-uit/Tree.qml | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/interface/resources/qml/controls-uit/Tree.qml b/interface/resources/qml/controls-uit/Tree.qml index 6d4d202e2c..aa1d10f030 100644 --- a/interface/resources/qml/controls-uit/Tree.qml +++ b/interface/resources/qml/controls-uit/Tree.qml @@ -1,5 +1,5 @@ // -// Table.qml +// Tree.qml // // Created by David Rowe on 17 Feb 2016 // Copyright 2016 High Fidelity, Inc. @@ -85,27 +85,18 @@ TreeView { bottomMargin: 3 // "" } radius: 3 - color: hifi.colors.tableScrollHandle + color: hifi.colors.tableScrollHandleDark } } scrollBarBackground: Item { - implicitWidth: 10 + implicitWidth: 9 Rectangle { anchors { fill: parent margins: -1 // Expand } - color: hifi.colors.baseGrayHighlight - } - - Rectangle { - anchors { - fill: parent - margins: 1 // Shrink - } - radius: 4 - color: hifi.colors.tableScrollBackground + color: hifi.colors.tableBackgroundDark } } From 7be33a85848fe9434201ff2ece0602604ae788a0 Mon Sep 17 00:00:00 2001 From: David Rowe <david@ctrlaltstudio.com> Date: Thu, 12 May 2016 17:25:44 +1200 Subject: [PATCH 11/35] Don't show header or dates and sizes when choosing a directory --- interface/resources/qml/dialogs/FileDialog.qml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/interface/resources/qml/dialogs/FileDialog.qml b/interface/resources/qml/dialogs/FileDialog.qml index ac0858c134..93c89e7393 100644 --- a/interface/resources/qml/dialogs/FileDialog.qml +++ b/interface/resources/qml/dialogs/FileDialog.qml @@ -196,7 +196,7 @@ ModalWindow { bottom: currentSelection.top bottomMargin: hifi.dimensions.contentSpacing.y + currentSelection.controlHeight - currentSelection.height } - headerVisible: true + headerVisible: !selectDirectory onDoubleClicked: navigateToRow(row); focus: true Keys.onReturnPressed: navigateToCurrentRow(); @@ -275,7 +275,7 @@ ModalWindow { id: fileNameColumn role: "fileName" title: "Name" - width: 0.5 * fileTableView.width + width: (selectDirectory ? 1.0 : 0.5) * fileTableView.width resizable: true } TableViewColumn { @@ -284,12 +284,14 @@ ModalWindow { title: "Date" width: 0.3 * fileTableView.width resizable: true + visible: !selectDirectory } TableViewColumn { role: "fileSize" title: "Size" width: fileTableView.width - fileNameColumn.width - fileMofifiedColumn.width resizable: true + visible: !selectDirectory } function navigateToRow(row) { From 3b34b908d7f066635b055baa6c32dc9f78eb5c78 Mon Sep 17 00:00:00 2001 From: David Rowe <david@ctrlaltstudio.com> Date: Fri, 13 May 2016 17:39:15 +1200 Subject: [PATCH 12/35] Tweak level-up and home buttons --- interface/resources/qml/controls-uit/GlyphButton.qml | 7 +++++++ interface/resources/qml/dialogs/FileDialog.qml | 2 ++ 2 files changed, 9 insertions(+) diff --git a/interface/resources/qml/controls-uit/GlyphButton.qml b/interface/resources/qml/controls-uit/GlyphButton.qml index 2625dda723..ac353b5a52 100644 --- a/interface/resources/qml/controls-uit/GlyphButton.qml +++ b/interface/resources/qml/controls-uit/GlyphButton.qml @@ -19,6 +19,7 @@ Original.Button { property int color: 0 property int colorScheme: hifi.colorSchemes.light property string glyph: "" + property int size: 32 width: 120 height: 28 @@ -65,7 +66,13 @@ Original.Button { : hifi.buttons.disabledTextColor[control.colorScheme] verticalAlignment: Text.AlignVCenter horizontalAlignment: Text.AlignHCenter + anchors { + // Tweak horizontal alignment so that it looks right. + left: parent.left + leftMargin: -0.5 + } text: control.glyph + size: control.size } } } diff --git a/interface/resources/qml/dialogs/FileDialog.qml b/interface/resources/qml/dialogs/FileDialog.qml index 93c89e7393..1175c8a14f 100644 --- a/interface/resources/qml/dialogs/FileDialog.qml +++ b/interface/resources/qml/dialogs/FileDialog.qml @@ -99,6 +99,7 @@ ModalWindow { id: upButton glyph: hifi.glyphs.levelUp width: height + size: 30 enabled: model.parentFolder && model.parentFolder !== "" onClicked: d.navigateUp(); } @@ -107,6 +108,7 @@ ModalWindow { id: homeButton property var destination: helper.home(); glyph: hifi.glyphs.home + size: 28 width: height enabled: d.homeDestination ? true : false onClicked: d.navigateHome(); From c21087ad8726f754e215c8fabd7c0c899b9faeee Mon Sep 17 00:00:00 2001 From: David Rowe <david@ctrlaltstudio.com> Date: Sat, 14 May 2016 13:04:18 +1200 Subject: [PATCH 13/35] Add icon to title --- interface/resources/qml/dialogs/FileDialog.qml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/interface/resources/qml/dialogs/FileDialog.qml b/interface/resources/qml/dialogs/FileDialog.qml index 1175c8a14f..a3c5c02f9b 100644 --- a/interface/resources/qml/dialogs/FileDialog.qml +++ b/interface/resources/qml/dialogs/FileDialog.qml @@ -49,6 +49,8 @@ ModalWindow { // Set from OffscreenUi::getOpenFile() property int options; // <-- FIXME unused + property string iconText: text !== "" ? hifi.glyphs.scriptUpload : "" + property int iconSize: 40 property bool selectDirectory: false; property bool showHidden: false; @@ -69,6 +71,8 @@ ModalWindow { drivesSelector.onCurrentTextChanged.connect(function(){ root.dir = helper.pathToUrl(drivesSelector.currentText); }) + + iconText = root.title !== "" ? hifi.glyphs.scriptUpload : ""; } Item { From 14c733cd6e382fc4db961bc6c72eec5d937aa87d Mon Sep 17 00:00:00 2001 From: David Rowe <david@ctrlaltstudio.com> Date: Sat, 14 May 2016 17:19:18 +1200 Subject: [PATCH 14/35] Make columns sortable --- .../resources/qml/controls-uit/Table.qml | 18 +++++++++++++++-- .../resources/qml/dialogs/FileDialog.qml | 20 ++++++++++++++++++- .../qml/styles-uit/HifiConstants.qml | 1 + 3 files changed, 36 insertions(+), 3 deletions(-) diff --git a/interface/resources/qml/controls-uit/Table.qml b/interface/resources/qml/controls-uit/Table.qml index 2a0fe545ef..35029ad8bf 100644 --- a/interface/resources/qml/controls-uit/Table.qml +++ b/interface/resources/qml/controls-uit/Table.qml @@ -29,6 +29,7 @@ TableView { color: isLightColorScheme ? hifi.colors.tableBackgroundLight : hifi.colors.tableBackgroundDark RalewayRegular { + id: titleText text: styleData.value size: hifi.fontSizes.tableHeading font.capitalization: Font.AllUppercase @@ -36,12 +37,25 @@ TableView { anchors { left: parent.left leftMargin: hifi.dimensions.tablePadding - right: parent.right - rightMargin: 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 { diff --git a/interface/resources/qml/dialogs/FileDialog.qml b/interface/resources/qml/dialogs/FileDialog.qml index a3c5c02f9b..f185508457 100644 --- a/interface/resources/qml/dialogs/FileDialog.qml +++ b/interface/resources/qml/dialogs/FileDialog.qml @@ -208,6 +208,10 @@ ModalWindow { Keys.onReturnPressed: navigateToCurrentRow(); Keys.onEnterPressed: navigateToCurrentRow(); + sortIndicatorColumn: 0 + sortIndicatorOrder: Qt.AscendingOrder + sortIndicatorVisible: true + model: FolderListModel { id: model nameFilters: selectionType.currentFilter @@ -228,7 +232,18 @@ ModalWindow { } } - onActiveFocusChanged: { + function updateSort() { + model.sortReversed = sortIndicatorColumn == 0 + ? (sortIndicatorOrder == Qt.DescendingOrder) + : (sortIndicatorOrder == Qt.AscendingOrder); // Date and size fields have opposite sense + model.sortField = sortIndicatorColumn + 1; + } + + onSortIndicatorColumnChanged: { updateSort(); } + + onSortIndicatorOrderChanged: { updateSort(); } + + onActiveFocusChanged: { if (activeFocus && currentRow == -1) { fileTableView.selection.select(0) } @@ -282,6 +297,7 @@ ModalWindow { role: "fileName" title: "Name" width: (selectDirectory ? 1.0 : 0.5) * fileTableView.width + movable: false resizable: true } TableViewColumn { @@ -289,6 +305,7 @@ ModalWindow { role: "fileModified" title: "Date" width: 0.3 * fileTableView.width + movable: false resizable: true visible: !selectDirectory } @@ -296,6 +313,7 @@ ModalWindow { role: "fileSize" title: "Size" width: fileTableView.width - fileNameColumn.width - fileMofifiedColumn.width + movable: false resizable: true visible: !selectDirectory } diff --git a/interface/resources/qml/styles-uit/HifiConstants.qml b/interface/resources/qml/styles-uit/HifiConstants.qml index 4d5052f086..640fe8625b 100644 --- a/interface/resources/qml/styles-uit/HifiConstants.qml +++ b/interface/resources/qml/styles-uit/HifiConstants.qml @@ -162,6 +162,7 @@ Item { readonly property real textFieldInputLabel: dimensions.largeScreen ? 13 : 9 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 buttonLabel: dimensions.largeScreen ? 13 : 9 readonly property real iconButton: dimensions.largeScreen ? 13 : 9 From 1fc3d9229c9735e445533f678a1b7777df65de81 Mon Sep 17 00:00:00 2001 From: David Rowe <david@ctrlaltstudio.com> Date: Tue, 17 May 2016 10:51:42 +1200 Subject: [PATCH 15/35] Fix initial drive change and go-up not working properly on first screen --- interface/resources/qml/dialogs/FileDialog.qml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/interface/resources/qml/dialogs/FileDialog.qml b/interface/resources/qml/dialogs/FileDialog.qml index f185508457..9c68767e2b 100644 --- a/interface/resources/qml/dialogs/FileDialog.qml +++ b/interface/resources/qml/dialogs/FileDialog.qml @@ -72,6 +72,12 @@ ModalWindow { root.dir = helper.pathToUrl(drivesSelector.currentText); }) + // HACK: The following two lines force the model to initialize properly such that: + // - Selecting a different drive at the initial screen updates the path displayed. + // - The go-up button works properly from the initial screen. + root.dir = helper.pathToUrl(drivesSelector.currentText); + root.dir = helper.pathToUrl(currentDirectory.lastValidFolder); + iconText = root.title !== "" ? hifi.glyphs.scriptUpload : ""; } From e0483585b85822a4d6220fefc8e08683a89d236f Mon Sep 17 00:00:00 2001 From: David Rowe <david@ctrlaltstudio.com> Date: Tue, 17 May 2016 14:43:49 +1200 Subject: [PATCH 16/35] Consistently capitalize drive letter for Windows --- interface/resources/qml/dialogs/FileDialog.qml | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/interface/resources/qml/dialogs/FileDialog.qml b/interface/resources/qml/dialogs/FileDialog.qml index 9c68767e2b..e8b6b9b2b7 100644 --- a/interface/resources/qml/dialogs/FileDialog.qml +++ b/interface/resources/qml/dialogs/FileDialog.qml @@ -144,7 +144,16 @@ ModalWindow { leftMargin: hifi.dimensions.contentSpacing.x right: parent.right } - onLastValidFolderChanged: text = lastValidFolder; + + 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; + } + + onLastValidFolderChanged: text = capitalizeDrive(lastValidFolder); // FIXME add support auto-completion onAccepted: { From 17d41dd7b977c833e97961c64df3752ed574a73b Mon Sep 17 00:00:00 2001 From: David Rowe <david@ctrlaltstudio.com> Date: Wed, 18 May 2016 13:25:11 +1200 Subject: [PATCH 17/35] HiFi glyphs font file update with fixed up/down carats Update QML and HTML spinboxes accordingly. --- interface/resources/fonts/hifi-glyphs.ttf | Bin 23944 -> 24204 bytes .../resources/qml/controls-uit/SpinBox.qml | 6 +++--- .../qml/styles-uit/HifiConstants.qml | 2 +- scripts/system/html/edit-style.css | 8 ++++---- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/interface/resources/fonts/hifi-glyphs.ttf b/interface/resources/fonts/hifi-glyphs.ttf index 1b0d4f3fe61a60a3cf499f3557dd29bb924bc924..89d47670122b68a27ce80d77da0227390d817ac1 100644 GIT binary patch delta 960 zcmYL|TTGi}7{`Ba`yriL4&&T@UxC6X9V?WwavHFXL17!GaVYJ8!pE_&GDtz0GsrG> zu`Ih-{H88*F_Q~ixG-j&-iTg!VUR4*L?b3M1e3)qlPfc$$zm|=miQ!Z-v9g0`#gCM zzvt?g;`&!&1pyejjGiv9x3_(Px3?fT56CS|&0cQt3Iw}=yw097SLa4s2M9ml8=F<< z`~F?;0NoAHS*O&6`Tc$L5NrhuQ{lHJo$}rqBwUB?k<f%Xp7>kN-@vaxWhgYUqDoG& z3nCpT49zZH%zm2o9f;3>o$%b4S~l2R1kwZGms$1VJewj2qB?e<3+n8|AC;Rv=-&eo zou6A+Z2mCmJqX+dX)S&^0jW=3T^dfV`-|6f#NtOay!>L;g_qwI_2RtNAcn*N?T#4I zo{JG}SXWx}1O05X9Z%3he8};N2pN4`$w;yH!+?PorQg{-pl#@smOAQb;5a8}<RqtQ z#!DZkI71ulxah_~2c1kaLn}>qsG*i>`sv~<eq<DKnbItJvr<VZWjHCPiVBY6riUSV zX~D-a&98Tx0|XghkaG+(g32gkj5EO`A#F`>^VHQh96!-`@^rJe@6?&Lc2~EfqjP$u zwaHUcTixGv)-Nl$Q+fH;g2F>ZwqpC?k|UL+WzO=dilgqHq23nXG0iBMq(E?BP%D?N zXdcO+eJ<6lIA8zPc*NLb3>#OCJ4vRb_2iD^pHfCsK22>+T~Av~dy?);|04ZqhLjP> zxSx4Ab24)^>tI$m>+9^U?DunAIk!zIrbg3M)6Zs$dD6UL{#~}nL3vC5N0F2irB)eL zZYVnzmu1v))ABg?UY;-SL4Gj*8|!XCcfnra{q!Ph(L-CC?UHS?_;T@Gd$zsM?u=eZ z_#i7fpj@*?Gd#PQT8`q;?B#ztws|BR;$ZBT4%sS%uEh7K3Iy9c77k&K`68<z<_mLG z^qDV7Bvj+TttI+P^wkcx=Jwwd=d~^WJp)$JBYH)jb}3M(6Cv%R!1L(G=bj1elfgUF ziIa1YB{ec`P~TJ+)riAoP$QAKCCB)Mm*S0BYk}C`M;B)(3CNCnv9+EV1QNXDDUR2} O`ql4aJO1Z3y!;>i_TZ5K delta 954 zcmY+CTTGjE7{-79wm`=y$I{ZnR|<SkHr5r&aSX~hGX~?hQVxZE<J1ujO~({(hB0I= zOQXrYWixYEdV>iUW=)K7P7OD@g{aX8H`CySUJ!PN#%MB3nK!=4oA=3+H&31??;Cw0 zZao&O2*ALbXz6Wh8|;pJxV;X+dBEP@(%Qy-5+T?HIC};>-k#69x<Ld0|Kx%aUTN-o z1ez#loU_Wc@Zlj^2(|$F+4(nSj$Loef`m2b3x}qasid{#pMd8;RVXyQs_06^E(jwa zhZdGsthUT15Ff{v=9eZF+r30T=r(}u1!X17*J2z*P2A9;vM{|@_1F*o0}!$B(zWFS z<>xOTunxNSkHQJ~?aPn0CerHu;JAi({a9|<e>F9h{X3#w%&9&R6ngcc2#W93zl5UQ zVMh&UJRah4j@o{thOnYNLUwe#jK@UJE7=>ndi5)fv|5jk2F}q$Gc8=8jdlWb(nU8H zDd!TW>7kcC`Z-T4jns0MGmJ36Afwok$i+z>`4k{iND(fIIl)OvIE9;PJd{#~ml`Ul zqzXTm8KQ$>>eMl9rDY6-aV7|Ig-NEEW`<cpTva!;uJL+b!?~vBmJ4m|fzGb(i{+P2 z_w@GlpKoofJ$q(kU~tqf<vR273*^EgSMiCHC8ylgp3*XJO+{su|MF1BaGgV|OGr%8 zA4@)-Vn|I(&&bR&W}D187OQPc8J|$gbhp(fy1Lcml-`uz4PAyQL)5Toc$r$8`YdfR z?I8WF^lvjl8PBrr8uiB4jN94C*>d)s>>o^>rkkcsv)3Fo|B@5V`NYz1`OI2sy<%Or z{%P~sZrYyP6YW0x4f~#yBgs;!R3~*v5$S>Sjl<-ab3AZt<=S(1o$omJ^X}#SU65le zxGl@_UeSn4=UQ_;DyH~Th(#0bo7H#yzF3|V&G!g)cq-h2_zeqP<w8^9e_9Q3{)KQ0 zInE1nL7W$Ib!?;IS5xf8kU=DPaZ{ll21>MEcZKR6zANU`&Ea*u7!ZTv68UOmL{{G$ z`A45LvlMw<iA=@R(F2jGpHw2svU_UrKX5&)z8!q1wvRhpR}ttqazfkzuvP!TcmMxb G%l^NRd)>YO diff --git a/interface/resources/qml/controls-uit/SpinBox.qml b/interface/resources/qml/controls-uit/SpinBox.qml index 599d94c28d..5d78cfc5a8 100755 --- a/interface/resources/qml/controls-uit/SpinBox.qml +++ b/interface/resources/qml/controls-uit/SpinBox.qml @@ -56,7 +56,7 @@ SpinBox { incrementControl: HiFiGlyphs { id: incrementButton text: hifi.glyphs.caratUp - x: 6 + x: 10 y: 1 size: hifi.dimensions.spinnerSize color: styleData.upPressed ? (isLightColorScheme ? hifi.colors.black : hifi.colors.white) : hifi.colors.gray @@ -64,8 +64,8 @@ SpinBox { decrementControl: HiFiGlyphs { text: hifi.glyphs.caratDn - x: 6 - y: -3 + x: 10 + y: -1 size: hifi.dimensions.spinnerSize color: styleData.downPressed ? (isLightColorScheme ? hifi.colors.black : hifi.colors.white) : hifi.colors.gray } diff --git a/interface/resources/qml/styles-uit/HifiConstants.qml b/interface/resources/qml/styles-uit/HifiConstants.qml index 640fe8625b..16f150b2d9 100644 --- a/interface/resources/qml/styles-uit/HifiConstants.qml +++ b/interface/resources/qml/styles-uit/HifiConstants.qml @@ -141,7 +141,7 @@ Item { readonly property real textPadding: 8 readonly property real sliderHandleSize: 18 readonly property real sliderGrooveHeight: 8 - readonly property real spinnerSize: 42 + readonly property real spinnerSize: 50 readonly property real tablePadding: 12 readonly property real tableRowHeight: largeScreen ? 26 : 23 readonly property real tableHeaderHeight: 29 diff --git a/scripts/system/html/edit-style.css b/scripts/system/html/edit-style.css index 5eaa3c6497..4083e580b5 100644 --- a/scripts/system/html/edit-style.css +++ b/scripts/system/html/edit-style.css @@ -268,7 +268,7 @@ input[type=number]::-webkit-inner-spin-button { height: 100%; overflow: hidden; font-family: hifi-glyphs; - font-size: 50px; + font-size: 46px; color: #afafaf; cursor: pointer; /*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:after { position:absolute; - left: -21px; + left: -19px; line-height: 8px; text-align: center; } input[type=number]::-webkit-inner-spin-button:before { content: "6"; - top: 5px; + top: 4px; } input[type=number]::-webkit-inner-spin-button:after { content: "5"; - bottom: 6px; + bottom: 4px; } input[type=number].hover-up::-webkit-inner-spin-button:before, From e1d885004f55b98681f94ebc4e92170cad584472 Mon Sep 17 00:00:00 2001 From: David Rowe <david@ctrlaltstudio.com> Date: Wed, 18 May 2016 13:59:13 +1200 Subject: [PATCH 18/35] Fix carat in drop-down boxes --- interface/resources/qml/controls-uit/ComboBox.qml | 2 +- scripts/system/html/edit-style.css | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/interface/resources/qml/controls-uit/ComboBox.qml b/interface/resources/qml/controls-uit/ComboBox.qml index f99e18b12b..cd6dc8ede0 100755 --- a/interface/resources/qml/controls-uit/ComboBox.qml +++ b/interface/resources/qml/controls-uit/ComboBox.qml @@ -91,7 +91,7 @@ FocusScope { HiFiGlyphs { anchors { top: parent.top - topMargin: -8 + topMargin: -11 horizontalCenter: parent.horizontalCenter } size: hifi.dimensions.spinnerSize diff --git a/scripts/system/html/edit-style.css b/scripts/system/html/edit-style.css index 4083e580b5..19d1cd95a9 100644 --- a/scripts/system/html/edit-style.css +++ b/scripts/system/html/edit-style.css @@ -613,7 +613,7 @@ hr { margin-right: -48px; position: relative; left: -12px; - top: -11px; + top: -9px; } .dropdown dd { From 551c6698835683a8dab27040904c9c1f711327fc Mon Sep 17 00:00:00 2001 From: David Rowe <david@ctrlaltstudio.com> Date: Thu, 19 May 2016 09:24:15 +1200 Subject: [PATCH 19/35] Move FolderListModel to be stand-alone --- .../resources/qml/dialogs/FileDialog.qml | 58 ++++++++++--------- 1 file changed, 30 insertions(+), 28 deletions(-) diff --git a/interface/resources/qml/dialogs/FileDialog.qml b/interface/resources/qml/dialogs/FileDialog.qml index e8b6b9b2b7..4f6e7ef050 100644 --- a/interface/resources/qml/dialogs/FileDialog.qml +++ b/interface/resources/qml/dialogs/FileDialog.qml @@ -43,7 +43,7 @@ ModalWindow { // Set from OffscreenUi::getOpenFile() property alias caption: root.title; // Set from OffscreenUi::getOpenFile() - property alias dir: model.folder; + property alias dir: folderListModel.folder; // Set from OffscreenUi::getOpenFile() property alias filter: selectionType.filtersString; // Set from OffscreenUi::getOpenFile() @@ -110,7 +110,7 @@ ModalWindow { glyph: hifi.glyphs.levelUp width: height size: 30 - enabled: model.parentFolder && model.parentFolder !== "" + enabled: folderListModel.parentFolder && folderListModel.parentFolder !== "" onClicked: d.navigateUp(); } @@ -135,7 +135,7 @@ ModalWindow { TextField { id: currentDirectory - property var lastValidFolder: helper.urlToPath(model.folder) + property var lastValidFolder: helper.urlToPath(folderListModel.folder) height: homeButton.height anchors { top: parent.top @@ -161,7 +161,7 @@ ModalWindow { text = lastValidFolder; return } - model.folder = helper.pathToUrl(text); + folderListModel.folder = helper.pathToUrl(text); } } @@ -172,7 +172,7 @@ ModalWindow { property bool currentSelectionIsFolder; property var backStack: [] property var tableViewConnection: Connections { target: fileTableView; onCurrentRowChanged: d.update(); } - property var modelConnection: Connections { target: model; onFolderChanged: d.update(); } + property var modelConnection: Connections { target: model; onFolderChanged: d.update(); } // DJRTODO property var homeDestination: helper.home(); Component.onCompleted: update(); @@ -194,18 +194,38 @@ ModalWindow { } function navigateUp() { - if (model.parentFolder && model.parentFolder !== "") { - model.folder = model.parentFolder + if (folderListModel.parentFolder && folderListModel.parentFolder !== "") { + folderListModel.folder = folderListModel.parentFolder return true; } } function navigateHome() { - model.folder = homeDestination; + folderListModel.folder = homeDestination; return true; } } + FolderListModel { + id: folderListModel + nameFilters: selectionType.currentFilter + showDirsFirst: true + showDotAndDotDot: false + showFiles: !root.selectDirectory + // For some reason, declaring these bindings directly in the targets doesn't + // work for setting the initial state + Component.onCompleted: { + currentDirectory.lastValidFolder = Qt.binding(function() { return helper.urlToPath(folder); }); + upButton.enabled = Qt.binding(function() { return (parentFolder && parentFolder != "") ? true : false; }); + showFiles = !root.selectDirectory + } + onFolderChanged: { + fileTableView.selection.clear(); + fileTableView.selection.select(0); + fileTableView.currentRow = 0; + } + } + Table { id: fileTableView colorScheme: hifi.colorSchemes.light @@ -227,25 +247,7 @@ ModalWindow { sortIndicatorOrder: Qt.AscendingOrder sortIndicatorVisible: true - model: FolderListModel { - id: model - nameFilters: selectionType.currentFilter - showDirsFirst: true - showDotAndDotDot: false - showFiles: !root.selectDirectory - // For some reason, declaring these bindings directly in the targets doesn't - // work for setting the initial state - Component.onCompleted: { - currentDirectory.lastValidFolder = Qt.binding(function() { return helper.urlToPath(model.folder); }); - upButton.enabled = Qt.binding(function() { return (model.parentFolder && model.parentFolder != "") ? true : false; }); - showFiles = !root.selectDirectory - } - onFolderChanged: { - fileTableView.selection.clear(); - fileTableView.selection.select(0); - fileTableView.currentRow = 0; - } - } + model: folderListModel function updateSort() { model.sortReversed = sortIndicatorColumn == 0 @@ -343,7 +345,7 @@ ModalWindow { var isFolder = model.isFolder(row); var file = model.get(row, "fileURL"); if (isFolder) { - fileTableView.model.folder = file + fileTableView.model.folder = file; } else { okAction.trigger(); } From 862a88cf292cdb2cd1e6712cf156db0d6793f427 Mon Sep 17 00:00:00 2001 From: David Rowe <david@ctrlaltstudio.com> Date: Thu, 19 May 2016 11:29:21 +1200 Subject: [PATCH 20/35] Insert an intermediary ListModel suitable for sorting and drives --- .../resources/qml/dialogs/FileDialog.qml | 59 ++++++++++++++++--- 1 file changed, 50 insertions(+), 9 deletions(-) diff --git a/interface/resources/qml/dialogs/FileDialog.qml b/interface/resources/qml/dialogs/FileDialog.qml index 4f6e7ef050..fbe8df79c0 100644 --- a/interface/resources/qml/dialogs/FileDialog.qml +++ b/interface/resources/qml/dialogs/FileDialog.qml @@ -184,7 +184,7 @@ ModalWindow { return; } - currentSelectionUrl = fileTableView.model.get(row, "fileURL"); + currentSelectionUrl = helper.pathToUrl(fileTableView.model.get(row).filePath); currentSelectionIsFolder = fileTableView.model.isFolder(row); if (root.selectDirectory || !currentSelectionIsFolder) { currentSelection.text = helper.urlToPath(currentSelectionUrl); @@ -220,12 +220,49 @@ ModalWindow { showFiles = !root.selectDirectory } onFolderChanged: { + fileTableModel.update(); fileTableView.selection.clear(); fileTableView.selection.select(0); fileTableView.currentRow = 0; } } + 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 + + onFolderChanged: folderListModel.folder = folder; + + function isFolder(row) { + if (row === -1) { + return false; + } + return get(row).fileIsDir; + } + + function update() { + var i; + clear(); + for (i = 0; i < folderListModel.count; i++) { + append({ + fileName: folderListModel.get(i, "fileName"), + fileModified: folderListModel.get(i, "fileModified"), + fileSize: folderListModel.get(i, "fileSize"), + filePath: folderListModel.get(i, "filePath"), + fileIsDir: folderListModel.get(i, "fileIsDir") + }); + } + } + } + Table { id: fileTableView colorScheme: hifi.colorSchemes.light @@ -247,7 +284,7 @@ ModalWindow { sortIndicatorOrder: Qt.AscendingOrder sortIndicatorVisible: true - model: folderListModel + model: fileTableModel function updateSort() { model.sortReversed = sortIndicatorColumn == 0 @@ -284,12 +321,17 @@ ModalWindow { } size: hifi.fontSizes.tableText color: hifi.colors.baseGrayHighlight - font.family: fileTableView.model.get(styleData.row, "fileIsDir") ? firaSansSemiBold.name : firaSansRegular.name + 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); + 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; } } @@ -343,9 +385,9 @@ ModalWindow { function navigateToCurrentRow() { var row = fileTableView.currentRow var isFolder = model.isFolder(row); - var file = model.get(row, "fileURL"); + var file = model.get(row).filePath; if (isFolder) { - fileTableView.model.folder = file; + fileTableView.model.folder = helper.pathToUrl(file); } else { okAction.trigger(); } @@ -360,7 +402,7 @@ ModalWindow { var newPrefix = prefix + event.text.toLowerCase(); var matchedIndex = -1; 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)) { matchedIndex = i; break; @@ -401,7 +443,6 @@ ModalWindow { } break; } - } } From 585467094c58e177c59e9da30b18e5b77bcf8d7b Mon Sep 17 00:00:00 2001 From: David Rowe <david@ctrlaltstudio.com> Date: Thu, 19 May 2016 14:16:00 +1200 Subject: [PATCH 21/35] Sort files case-insensitively, directories first --- .../resources/qml/dialogs/FileDialog.qml | 64 ++++++++++++++++--- 1 file changed, 55 insertions(+), 9 deletions(-) diff --git a/interface/resources/qml/dialogs/FileDialog.qml b/interface/resources/qml/dialogs/FileDialog.qml index fbe8df79c0..e7d96cedf9 100644 --- a/interface/resources/qml/dialogs/FileDialog.qml +++ b/interface/resources/qml/dialogs/FileDialog.qml @@ -238,6 +238,8 @@ ModalWindow { // drive information when viewing at the computer level. property var folder + property int sortOrder: Qt.AscendingOrder + property int sortColumn: 0 onFolderChanged: folderListModel.folder = folder; @@ -249,16 +251,61 @@ ModalWindow { } function update() { - var i; + 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 < folderListModel.count; i++) { - append({ - fileName: folderListModel.get(i, "fileName"), - fileModified: folderListModel.get(i, "fileModified"), + fileName = folderListModel.get(i, "fileName"); + fileIsDir = folderListModel.get(i, "fileIsDir"); + + sortValue = folderListModel.get(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) : folderListModel.get(i, "fileModified")), fileSize: folderListModel.get(i, "fileSize"), filePath: folderListModel.get(i, "filePath"), - fileIsDir: folderListModel.get(i, "fileIsDir") + fileIsDir: fileIsDir, + fileNameSort: (fileIsDir ? "*" : "") + fileName.toLowerCase() }); + + rows++; } } } @@ -287,10 +334,9 @@ ModalWindow { model: fileTableModel function updateSort() { - model.sortReversed = sortIndicatorColumn == 0 - ? (sortIndicatorOrder == Qt.DescendingOrder) - : (sortIndicatorOrder == Qt.AscendingOrder); // Date and size fields have opposite sense - model.sortField = sortIndicatorColumn + 1; + model.sortOrder = sortIndicatorOrder; + model.sortColumn = sortIndicatorColumn; + model.update(); } onSortIndicatorColumnChanged: { updateSort(); } From 41311bc3df3859be96291d7a06fb44605f98146b Mon Sep 17 00:00:00 2001 From: David Rowe <david@ctrlaltstudio.com> Date: Thu, 19 May 2016 17:13:07 +1200 Subject: [PATCH 22/35] Finish decoupling FolderListModel --- .../resources/qml/dialogs/FileDialog.qml | 43 ++++++++++--------- 1 file changed, 22 insertions(+), 21 deletions(-) diff --git a/interface/resources/qml/dialogs/FileDialog.qml b/interface/resources/qml/dialogs/FileDialog.qml index e7d96cedf9..5674123b4d 100644 --- a/interface/resources/qml/dialogs/FileDialog.qml +++ b/interface/resources/qml/dialogs/FileDialog.qml @@ -43,7 +43,7 @@ ModalWindow { // Set from OffscreenUi::getOpenFile() property alias caption: root.title; // Set from OffscreenUi::getOpenFile() - property alias dir: folderListModel.folder; + property alias dir: fileTableModel.folder; // Set from OffscreenUi::getOpenFile() property alias filter: selectionType.filtersString; // Set from OffscreenUi::getOpenFile() @@ -75,8 +75,9 @@ ModalWindow { // HACK: The following two lines force the model to initialize properly such that: // - Selecting a different drive at the initial screen updates the path displayed. // - The go-up button works properly from the initial screen. + var initialFolder = currentDirectory.lastValidFolder; root.dir = helper.pathToUrl(drivesSelector.currentText); - root.dir = helper.pathToUrl(currentDirectory.lastValidFolder); + root.dir = helper.pathToUrl(initialFolder); iconText = root.title !== "" ? hifi.glyphs.scriptUpload : ""; } @@ -110,7 +111,7 @@ ModalWindow { glyph: hifi.glyphs.levelUp width: height size: 30 - enabled: folderListModel.parentFolder && folderListModel.parentFolder !== "" + enabled: fileTableModel.parentFolder && fileTableModel.parentFolder !== "" onClicked: d.navigateUp(); } @@ -135,7 +136,7 @@ ModalWindow { TextField { id: currentDirectory - property var lastValidFolder: helper.urlToPath(folderListModel.folder) + property var lastValidFolder: helper.urlToPath(fileTableModel.folder) height: homeButton.height anchors { top: parent.top @@ -161,7 +162,7 @@ ModalWindow { text = lastValidFolder; return } - folderListModel.folder = helper.pathToUrl(text); + fileTableModel.folder = helper.pathToUrl(text); } } @@ -172,15 +173,13 @@ ModalWindow { property bool currentSelectionIsFolder; property var backStack: [] property var tableViewConnection: Connections { target: fileTableView; onCurrentRowChanged: d.update(); } - property var modelConnection: Connections { target: model; onFolderChanged: d.update(); } // DJRTODO + property var modelConnection: Connections { target: fileTableModel; onFolderChanged: d.update(); } property var homeDestination: helper.home(); - Component.onCompleted: update(); function update() { var row = fileTableView.currentRow; - if (row === -1 && root.selectDirectory) { - currentSelectionUrl = fileTableView.model.folder; - currentSelectionIsFolder = true; + + if (row === -1) { return; } @@ -189,19 +188,21 @@ ModalWindow { if (root.selectDirectory || !currentSelectionIsFolder) { currentSelection.text = helper.urlToPath(currentSelectionUrl); } else { - currentSelection.text = "" + currentSelection.text = ""; } } function navigateUp() { - if (folderListModel.parentFolder && folderListModel.parentFolder !== "") { - folderListModel.folder = folderListModel.parentFolder + if (fileTableModel.parentFolder && fileTableModel.parentFolder !== "") { + fileTableModel.folder = fileTableModel.parentFolder return true; + } else if (true) { + } } function navigateHome() { - folderListModel.folder = homeDestination; + fileTableModel.folder = homeDestination; return true; } } @@ -215,15 +216,11 @@ ModalWindow { // For some reason, declaring these bindings directly in the targets doesn't // work for setting the initial state Component.onCompleted: { - currentDirectory.lastValidFolder = Qt.binding(function() { return helper.urlToPath(folder); }); - upButton.enabled = Qt.binding(function() { return (parentFolder && parentFolder != "") ? true : false; }); + currentDirectory.lastValidFolder = helper.urlToPath(folder); showFiles = !root.selectDirectory } onFolderChanged: { - fileTableModel.update(); - fileTableView.selection.clear(); - fileTableView.selection.select(0); - fileTableView.currentRow = 0; + fileTableModel.update(); // Update once the data from the folder change is available. } } @@ -240,8 +237,12 @@ ModalWindow { property var folder property int sortOrder: Qt.AscendingOrder property int sortColumn: 0 + property string parentFolder: folderListModel.parentFolder - onFolderChanged: folderListModel.folder = folder; + onFolderChanged: { + folderListModel.folder = folder; + currentDirectory.lastValidFolder = helper.urlToPath(folder); + } function isFolder(row) { if (row === -1) { From dac043a52d5c7f47255373e10cad89446d3297be Mon Sep 17 00:00:00 2001 From: David Rowe <david@ctrlaltstudio.com> Date: Fri, 20 May 2016 10:49:38 +1200 Subject: [PATCH 23/35] Provide navigation up to the PC level to display drives in table --- .../resources/qml/dialogs/FileDialog.qml | 87 ++++++++++++++++--- 1 file changed, 74 insertions(+), 13 deletions(-) diff --git a/interface/resources/qml/dialogs/FileDialog.qml b/interface/resources/qml/dialogs/FileDialog.qml index 5674123b4d..0e936ff0a3 100644 --- a/interface/resources/qml/dialogs/FileDialog.qml +++ b/interface/resources/qml/dialogs/FileDialog.qml @@ -194,10 +194,8 @@ ModalWindow { function navigateUp() { if (fileTableModel.parentFolder && fileTableModel.parentFolder !== "") { - fileTableModel.folder = fileTableModel.parentFolder + fileTableModel.folder = fileTableModel.parentFolder; return true; - } else if (true) { - } } @@ -222,6 +220,42 @@ ModalWindow { 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 { @@ -237,10 +271,37 @@ ModalWindow { property var folder property int sortOrder: Qt.AscendingOrder property int sortColumn: 0 - property string parentFolder: folderListModel.parentFolder + 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: { - folderListModel.folder = folder; + if (folder === rootFolder) { + model = driveListModel; + update(); + } else { + var needsUpdate = model === driveListModel && folder === folderListModel.folder; + + model = folderListModel; + folderListModel.folder = folder; + + if (needsUpdate) { + update(); + } + } currentDirectory.lastValidFolder = helper.urlToPath(folder); } @@ -265,18 +326,18 @@ ModalWindow { 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 < folderListModel.count; i++) { - fileName = folderListModel.get(i, "fileName"); - fileIsDir = folderListModel.get(i, "fileIsDir"); - - sortValue = folderListModel.get(i, dataField); + 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. @@ -299,9 +360,9 @@ ModalWindow { insert(lower, { fileName: fileName, - fileModified: (fileIsDir ? new Date(0) : folderListModel.get(i, "fileModified")), - fileSize: folderListModel.get(i, "fileSize"), - filePath: folderListModel.get(i, "filePath"), + 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() }); From 11aeaba114fc4970767d8640d1f7530116d6d19b Mon Sep 17 00:00:00 2001 From: David Rowe <david@ctrlaltstudio.com> Date: Fri, 20 May 2016 11:32:14 +1200 Subject: [PATCH 24/35] Capitalize drive letter of chosen file --- interface/resources/qml/dialogs/FileDialog.qml | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/interface/resources/qml/dialogs/FileDialog.qml b/interface/resources/qml/dialogs/FileDialog.qml index 0e936ff0a3..56b7cb0505 100644 --- a/interface/resources/qml/dialogs/FileDialog.qml +++ b/interface/resources/qml/dialogs/FileDialog.qml @@ -176,6 +176,14 @@ ModalWindow { property var modelConnection: Connections { target: fileTableModel; onFolderChanged: d.update(); } property var homeDestination: helper.home(); + 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() { var row = fileTableView.currentRow; @@ -186,7 +194,7 @@ ModalWindow { currentSelectionUrl = helper.pathToUrl(fileTableView.model.get(row).filePath); currentSelectionIsFolder = fileTableView.model.isFolder(row); if (root.selectDirectory || !currentSelectionIsFolder) { - currentSelection.text = helper.urlToPath(currentSelectionUrl); + currentSelection.text = capitalizeDrive(helper.urlToPath(currentSelectionUrl)); } else { currentSelection.text = ""; } From e8d7f9614aed57bad25f940c82282c25b91ed92e Mon Sep 17 00:00:00 2001 From: Stephen Birarda <commit@birarda.com> Date: Thu, 19 May 2016 15:51:17 -0700 Subject: [PATCH 25/35] refresh API info during re-connect - case 570 --- libraries/networking/src/AddressManager.cpp | 44 ++++++++++++++++++--- libraries/networking/src/AddressManager.h | 9 ++++- libraries/networking/src/DomainHandler.cpp | 18 +++------ libraries/networking/src/DomainHandler.h | 10 ++--- libraries/networking/src/NodeList.cpp | 14 +++++-- 5 files changed, 67 insertions(+), 28 deletions(-) diff --git a/libraries/networking/src/AddressManager.cpp b/libraries/networking/src/AddressManager.cpp index a97f4df35d..35a9d9293d 100644 --- a/libraries/networking/src/AddressManager.cpp +++ b/libraries/networking/src/AddressManager.cpp @@ -144,12 +144,21 @@ bool AddressManager::handleUrl(const QUrl& lookupUrl, LookupTrigger trigger) { // 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 - 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 // check if it is a network address first bool hostChanged; 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 (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 handlePath(path, trigger); } 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 // try to look up the domain ID on the metaverse API attemptDomainIDLookup(lookupUrl.host(), lookupUrl.path(), trigger); } 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 // 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); @@ -180,9 +195,13 @@ bool AddressManager::handleUrl(const QUrl& lookupUrl, LookupTrigger trigger) { } else if (lookupUrl.toString().startsWith('/')) { 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 handlePath(lookupUrl.path(), trigger, true); emit lookupResultsFinished(); + return true; } @@ -276,7 +295,7 @@ void AddressManager::goToAddressFromObject(const QVariantMap& dataObject, const qCDebug(networking) << "Possible domain change required to connect to" << domainHostname << "on" << domainPort; - emit possibleDomainChangeRequired(domainHostname, domainPort); + emit possibleDomainChangeRequired(domainHostname, domainPort, domainID); } else { QString iceServerAddress = domainObject[DOMAIN_ICE_SERVER_ADDRESS_KEY].toString(); @@ -309,7 +328,10 @@ void AddressManager::goToAddressFromObject(const QVariantMap& dataObject, const QString overridePath = reply.property(OVERRIDE_PATH_KEY).toString(); 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 { // take the path that came back const QString PLACE_PATH_KEY = "path"; @@ -586,7 +608,7 @@ bool AddressManager::setDomainInfo(const QString& hostname, quint16 port, Lookup DependencyManager::get<NodeList>()->flagTimeForConnectionStep(LimitedNodeList::ConnectionStep::HandleAddress); - emit possibleDomainChangeRequired(hostname, port); + emit possibleDomainChangeRequired(hostname, port, QUuid()); return hostChanged; } @@ -606,6 +628,13 @@ void AddressManager::goToUser(const QString& username) { 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() { QApplication::clipboard()->setText(currentAddress().toString()); } @@ -617,7 +646,10 @@ void AddressManager::copyPath() { 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 (trigger != LookupTrigger::StartupFromSettings && trigger != LookupTrigger::DomainPathResponse) { + if (trigger != LookupTrigger::StartupFromSettings + && trigger != LookupTrigger::DomainPathResponse + && trigger != LookupTrigger::AttemptedRefresh) { + if (trigger == LookupTrigger::Back) { // 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 diff --git a/libraries/networking/src/AddressManager.h b/libraries/networking/src/AddressManager.h index dd0dbd9f38..0780cfb2c2 100644 --- a/libraries/networking/src/AddressManager.h +++ b/libraries/networking/src/AddressManager.h @@ -48,7 +48,8 @@ public: Forward, StartupFromSettings, DomainPathResponse, - Internal + Internal, + AttemptedRefresh }; bool isConnected(); @@ -88,6 +89,8 @@ public slots: void goToUser(const QString& username); + void refreshPreviousLookup(); + void storeCurrentAddress(); void copyAddress(); @@ -98,7 +101,7 @@ signals: void lookupResultIsOffline(); 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 locationChangeRequired(const glm::vec3& newPosition, @@ -150,6 +153,8 @@ private: quint64 _lastBackPush = 0; QString _newHostLookupPath; + + QUrl _previousLookup; }; #endif // hifi_AddressManager_h diff --git a/libraries/networking/src/DomainHandler.cpp b/libraries/networking/src/DomainHandler.cpp index 44ce63e6c6..b3c3a28829 100644 --- a/libraries/networking/src/DomainHandler.cpp +++ b/libraries/networking/src/DomainHandler.cpp @@ -28,16 +28,8 @@ DomainHandler::DomainHandler(QObject* parent) : QObject(parent), - _uuid(), _sockAddr(HifiSockAddr(QHostAddress::Null, DEFAULT_DOMAIN_SERVER_PORT)), - _assignmentUUID(), - _connectionToken(), - _iceDomainID(), - _iceClientID(), - _iceServerSockAddr(), _icePeer(this), - _isConnected(false), - _settingsObject(), _settingsTimer(this) { _sockAddr.setObjectName("DomainServer"); @@ -105,7 +97,7 @@ void DomainHandler::hardReset() { softReset(); qCDebug(networking) << "Hard reset in NodeList DomainHandler."; - _iceDomainID = QUuid(); + _pendingDomainID = QUuid(); _iceServerSockAddr = HifiSockAddr(); _hostname = QString(); _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) { // 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 _sockAddr.setPort(port); } + + _pendingDomainID = domainID; } 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 _iceClientID = QUuid::createUuid(); - _iceDomainID = id; + _pendingDomainID = id; HifiSockAddr* replaceableSockAddr = &_iceServerSockAddr; replaceableSockAddr->~HifiSockAddr(); @@ -341,7 +335,7 @@ void DomainHandler::processICEResponsePacket(QSharedPointer<ReceivedMessage> mes 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."; _icePeer.reset(); } else { diff --git a/libraries/networking/src/DomainHandler.h b/libraries/networking/src/DomainHandler.h index 03141d8fef..c6269191d2 100644 --- a/libraries/networking/src/DomainHandler.h +++ b/libraries/networking/src/DomainHandler.h @@ -58,8 +58,8 @@ public: const QUuid& getAssignmentUUID() const { return _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; } @@ -85,7 +85,7 @@ public: void softReset(); 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 processSettingsPacketList(QSharedPointer<ReceivedMessage> packetList); @@ -126,11 +126,11 @@ private: HifiSockAddr _sockAddr; QUuid _assignmentUUID; QUuid _connectionToken; - QUuid _iceDomainID; + QUuid _pendingDomainID; // ID of domain being connected to, via ICE or direct connection QUuid _iceClientID; HifiSockAddr _iceServerSockAddr; NetworkPeer _icePeer; - bool _isConnected; + bool _isConnected { false }; QJsonObject _settingsObject; QString _pendingPath; QTimer _settingsTimer; diff --git a/libraries/networking/src/NodeList.cpp b/libraries/networking/src/NodeList.cpp index 482d0366fd..831f0a4995 100644 --- a/libraries/networking/src/NodeList.cpp +++ b/libraries/networking/src/NodeList.cpp @@ -50,7 +50,7 @@ NodeList::NodeList(char newOwnerType, unsigned short socketListenPort, unsigned // handle domain change signals from AddressManager connect(addressManager.data(), &AddressManager::possibleDomainChangeRequired, - &_domainHandler, &DomainHandler::setHostnameAndPort); + &_domainHandler, &DomainHandler::setSocketAndID); connect(addressManager.data(), &AddressManager::possibleDomainChangeRequiredViaICEForID, &_domainHandler, &DomainHandler::setIceServerHostnameAndID); @@ -344,6 +344,14 @@ void NodeList::sendDomainServerCheckIn() { // increment the count of un-replied check-ins _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) { @@ -451,7 +459,7 @@ void NodeList::handleICEConnectionToDomainServer() { LimitedNodeList::sendPeerQueryToIceServer(_domainHandler.getICEServerSockAddr(), _domainHandler.getICEClientID(), - _domainHandler.getICEDomainID()); + _domainHandler.getPendingDomainID()); } } @@ -464,7 +472,7 @@ void NodeList::pingPunchForDomainServer() { if (_domainHandler.getICEPeer().getConnectionAttempts() == 0) { qCDebug(networking) << "Sending ping packets to establish connectivity with domain-server with ID" - << uuidStringWithoutCurlyBraces(_domainHandler.getICEDomainID()); + << uuidStringWithoutCurlyBraces(_domainHandler.getPendingDomainID()); } else { 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 From d3304ee65c52dd589cc04ed194768e17f30650df Mon Sep 17 00:00:00 2001 From: Stephen Birarda <commit@birarda.com> Date: Thu, 19 May 2016 18:01:13 -0700 Subject: [PATCH 26/35] make some overriding methods as override --- assignment-client/src/entities/EntityServer.h | 4 ++-- tests/controllers/src/main.cpp | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/assignment-client/src/entities/EntityServer.h b/assignment-client/src/entities/EntityServer.h index 1685f08e01..0486a97ede 100644 --- a/assignment-client/src/entities/EntityServer.h +++ b/assignment-client/src/entities/EntityServer.h @@ -60,8 +60,8 @@ public: virtual void trackViewerGone(const QUuid& sessionID) override; public slots: - virtual void nodeAdded(SharedNodePointer node); - virtual void nodeKilled(SharedNodePointer node); + virtual void nodeAdded(SharedNodePointer node) override; + virtual void nodeKilled(SharedNodePointer node) override; void pruneDeletedEntities(); protected: diff --git a/tests/controllers/src/main.cpp b/tests/controllers/src/main.cpp index e978dd9a38..3a5b4a4a4d 100644 --- a/tests/controllers/src/main.cpp +++ b/tests/controllers/src/main.cpp @@ -89,7 +89,7 @@ public: virtual GLWidget* getPrimaryWidget() override { return nullptr; } virtual MainWindow* getPrimaryWindow() override { return nullptr; } virtual QOpenGLContext* getPrimaryContext() override { return nullptr; } - virtual ui::Menu* getPrimaryMenu() { return nullptr; } + virtual ui::Menu* getPrimaryMenu() override { return nullptr; } virtual bool isForeground() override { return true; } virtual const DisplayPluginPointer getActiveDisplayPlugin() const override { return DisplayPluginPointer(); } }; From b2bbf72be2b10e0801cb281d9d96f52f2bf449bd Mon Sep 17 00:00:00 2001 From: David Rowe <david@ctrlaltstudio.com> Date: Fri, 20 May 2016 18:48:51 +1200 Subject: [PATCH 27/35] Replace drive drop-down plus text field with a paths drop-down --- .../resources/qml/dialogs/FileDialog.qml | 97 ++++++++++--------- 1 file changed, 51 insertions(+), 46 deletions(-) diff --git a/interface/resources/qml/dialogs/FileDialog.qml b/interface/resources/qml/dialogs/FileDialog.qml index 56b7cb0505..78d5943479 100644 --- a/interface/resources/qml/dialogs/FileDialog.qml +++ b/interface/resources/qml/dialogs/FileDialog.qml @@ -68,16 +68,12 @@ ModalWindow { Component.onCompleted: { console.log("Helper " + helper + " drives " + drives) - drivesSelector.onCurrentTextChanged.connect(function(){ - root.dir = helper.pathToUrl(drivesSelector.currentText); - }) - // HACK: The following two lines force the model to initialize properly such that: - // - Selecting a different drive at the initial screen updates the path displayed. - // - The go-up button works properly from the initial screen. - var initialFolder = currentDirectory.lastValidFolder; - root.dir = helper.pathToUrl(drivesSelector.currentText); - root.dir = helper.pathToUrl(initialFolder); + // 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 : ""; } @@ -97,15 +93,6 @@ ModalWindow { } spacing: hifi.dimensions.contentSpacing.x - // FIXME implement back button - //VrControls.ButtonAwesome { - // id: backButton - // text: "\uf0a8" - // size: currentDirectory.height - // enabled: d.backStack.length != 0 - // MouseArea { anchors.fill: parent; onClicked: d.navigateBack() } - //} - GlyphButton { id: upButton glyph: hifi.glyphs.levelUp @@ -124,20 +111,10 @@ ModalWindow { enabled: d.homeDestination ? true : false onClicked: d.navigateHome(); } - - ComboBox { - id: drivesSelector - width: 62 - model: drives - visible: drives.length > 1 - currentIndex: 0 - } } - TextField { - id: currentDirectory - property var lastValidFolder: helper.urlToPath(fileTableModel.folder) - height: homeButton.height + ComboBox { + id: pathSelector anchors { top: parent.top topMargin: hifi.dimensions.contentMargin.y @@ -146,23 +123,54 @@ ModalWindow { right: parent.right } - function capitalizeDrive(path) { - // Consistently capitalize drive letter for Windows. - if (/[a-zA-Z]:/.test(path)) { - return path.charAt(0).toUpperCase() + path.slice(1); + property var lastValidFolder: helper.urlToPath(fileTableModel.folder) + + function calculatePathChoices(folder) { + var folders = folder.split("/"), + choices = [], + i, length; + + if (folders[folders.length - 1] === "") { + folders.pop(); + } + + if (folders[0] !== "") { + choices.push(folders[0]); + } + + for (i = 1, length = folders.length; i < length; i++) { + choices.push(choices[i - 1] + "/" + folders[i]); + } + choices.reverse(); + + if (drives && drives.length > 1) { + choices.push("This PC"); + } + + if (choices.length > 0) { + pathSelector.model = choices; } - return path; } - onLastValidFolderChanged: text = capitalizeDrive(lastValidFolder); + onLastValidFolderChanged: { + var folder = d.capitalizeDrive(lastValidFolder); + calculatePathChoices(folder); + } - // FIXME add support auto-completion - onAccepted: { - if (!helper.validFolder(text)) { - text = lastValidFolder; - return + 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; } - fileTableModel.folder = helper.pathToUrl(text); } } @@ -219,12 +227,10 @@ ModalWindow { showDirsFirst: true showDotAndDotDot: false showFiles: !root.selectDirectory - // For some reason, declaring these bindings directly in the targets doesn't - // work for setting the initial state Component.onCompleted: { - currentDirectory.lastValidFolder = helper.urlToPath(folder); showFiles = !root.selectDirectory } + onFolderChanged: { fileTableModel.update(); // Update once the data from the folder change is available. } @@ -310,7 +316,6 @@ ModalWindow { update(); } } - currentDirectory.lastValidFolder = helper.urlToPath(folder); } function isFolder(row) { From 5a27d656c52d6524940d38a84d55799259c56e4b Mon Sep 17 00:00:00 2001 From: David Rowe <david@ctrlaltstudio.com> Date: Sat, 21 May 2016 13:03:58 +1200 Subject: [PATCH 28/35] Potential OSX fix plus extra logging --- interface/resources/qml/dialogs/FileDialog.qml | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/interface/resources/qml/dialogs/FileDialog.qml b/interface/resources/qml/dialogs/FileDialog.qml index 78d5943479..b00ee76b5e 100644 --- a/interface/resources/qml/dialogs/FileDialog.qml +++ b/interface/resources/qml/dialogs/FileDialog.qml @@ -130,17 +130,23 @@ ModalWindow { choices = [], i, length; + console.log("####### folder parts: " + JSON.stringify(folders)); + if (folders[folders.length - 1] === "") { folders.pop(); } - if (folders[0] !== "") { - choices.push(folders[0]); - } + 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) { @@ -154,6 +160,9 @@ ModalWindow { onLastValidFolderChanged: { var folder = d.capitalizeDrive(lastValidFolder); + + console.log("####### lastValidFolder: " + folder); + calculatePathChoices(folder); } From 40e862cf9e624b50f91f267c87148ceeb7fef505 Mon Sep 17 00:00:00 2001 From: Seth Alves <seth.alves@gmail.com> Date: Sat, 21 May 2016 16:53:01 -0700 Subject: [PATCH 29/35] quiet gcc 5 warnings --- .../octree/OctreeInboundPacketProcessor.cpp | 6 ++--- interface/src/Camera.h | 4 ++++ .../AssetMappingsScriptingInterface.cpp | 2 +- libraries/animation/src/AnimationCache.cpp | 2 +- libraries/audio/src/SoundCache.cpp | 2 +- .../src/controllers/UserInputMapper.cpp | 8 +++---- libraries/entities/src/EntityTypes.h | 2 +- libraries/fbx/src/FBXReader.cpp | 8 +++---- libraries/gl/src/gl/OffscreenQmlSurface.cpp | 4 ++-- libraries/gpu-gl/src/gpu/gl/GLBackend.cpp | 2 +- libraries/gpu/src/gpu/Texture.cpp | 2 +- libraries/networking/src/HifiSockAddr.cpp | 2 +- libraries/networking/src/Node.cpp | 6 ++--- libraries/networking/src/ReceivedMessage.cpp | 4 ++-- libraries/networking/src/udt/Packet.cpp | 2 +- .../networking/src/udt/PacketHeaders.cpp | 2 +- libraries/networking/src/udt/PacketList.cpp | 2 +- libraries/render-utils/src/Model.cpp | 6 ++--- libraries/script-engine/src/ScriptEngine.cpp | 2 +- libraries/shared/src/RegisteredMetaTypes.cpp | 22 +++++++++---------- libraries/shared/src/SimpleMovingAverage.h | 2 +- tests/render-utils/src/main.cpp | 4 ---- 22 files changed, 48 insertions(+), 48 deletions(-) diff --git a/assignment-client/src/octree/OctreeInboundPacketProcessor.cpp b/assignment-client/src/octree/OctreeInboundPacketProcessor.cpp index c5d010871c..32f3de2ff6 100644 --- a/assignment-client/src/octree/OctreeInboundPacketProcessor.cpp +++ b/assignment-client/src/octree/OctreeInboundPacketProcessor.cpp @@ -153,7 +153,7 @@ void OctreeInboundPacketProcessor::processPacket(QSharedPointer<ReceivedMessage> qDebug() << " maxSize=" << maxSize; qDebug("OctreeInboundPacketProcessor::processPacket() %hhu " "payload=%p payloadLength=%lld editData=%p payloadPosition=%lld maxSize=%d", - packetType, message->getRawMessage(), message->getSize(), editData, + (int)packetType, message->getRawMessage(), message->getSize(), editData, message->getPosition(), maxSize); } @@ -191,7 +191,7 @@ void OctreeInboundPacketProcessor::processPacket(QSharedPointer<ReceivedMessage> if (debugProcessPacket) { qDebug("OctreeInboundPacketProcessor::processPacket() DONE LOOPING FOR %hhu " "payload=%p payloadLength=%lld editData=%p payloadPosition=%lld", - packetType, message->getRawMessage(), message->getSize(), editData, message->getPosition()); + (int)packetType, message->getRawMessage(), message->getSize(), editData, message->getPosition()); } // 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); } else { - qDebug("unknown packet ignored... packetType=%hhu", packetType); + qDebug("unknown packet ignored... packetType=%hhu", (int)packetType); } } diff --git a/interface/src/Camera.h b/interface/src/Camera.h index 017bd742a4..486b98c100 100644 --- a/interface/src/Camera.h +++ b/interface/src/Camera.h @@ -29,6 +29,10 @@ enum CameraMode }; Q_DECLARE_METATYPE(CameraMode); + +#if defined(__GNUC__) && !defined(__clang__) +__attribute__((unused)) +#endif static int cameraModeId = qRegisterMetaType<CameraMode>(); class Camera : public QObject { diff --git a/interface/src/scripting/AssetMappingsScriptingInterface.cpp b/interface/src/scripting/AssetMappingsScriptingInterface.cpp index 965b3a9e0c..f1198c9d5b 100644 --- a/interface/src/scripting/AssetMappingsScriptingInterface.cpp +++ b/interface/src/scripting/AssetMappingsScriptingInterface.cpp @@ -196,7 +196,7 @@ bool AssetMappingModel::isKnownFolder(QString path) const { return false; } -static int assetMappingModelMetatypeId = qRegisterMetaType<AssetMappingModel*>("AssetMappingModel*"); +int assetMappingModelMetatypeId = qRegisterMetaType<AssetMappingModel*>("AssetMappingModel*"); void AssetMappingModel::refresh() { qDebug() << "Refreshing asset mapping model"; diff --git a/libraries/animation/src/AnimationCache.cpp b/libraries/animation/src/AnimationCache.cpp index 482c4211cb..7601fbc782 100644 --- a/libraries/animation/src/AnimationCache.cpp +++ b/libraries/animation/src/AnimationCache.cpp @@ -15,7 +15,7 @@ #include "AnimationCache.h" #include "AnimationLogging.h" -static int animationPointerMetaTypeId = qRegisterMetaType<AnimationPointer>(); +int animationPointerMetaTypeId = qRegisterMetaType<AnimationPointer>(); AnimationCache::AnimationCache(QObject* parent) : ResourceCache(parent) diff --git a/libraries/audio/src/SoundCache.cpp b/libraries/audio/src/SoundCache.cpp index 96a2cee204..6b34c68959 100644 --- a/libraries/audio/src/SoundCache.cpp +++ b/libraries/audio/src/SoundCache.cpp @@ -14,7 +14,7 @@ #include "AudioLogging.h" #include "SoundCache.h" -static int soundPointerMetaTypeId = qRegisterMetaType<SharedSoundPointer>(); +int soundPointerMetaTypeId = qRegisterMetaType<SharedSoundPointer>(); SoundCache::SoundCache(QObject* parent) : ResourceCache(parent) diff --git a/libraries/controllers/src/controllers/UserInputMapper.cpp b/libraries/controllers/src/controllers/UserInputMapper.cpp index c0d3ff40c0..c1ee3ce36c 100755 --- a/libraries/controllers/src/controllers/UserInputMapper.cpp +++ b/libraries/controllers/src/controllers/UserInputMapper.cpp @@ -336,10 +336,10 @@ QVector<QString> UserInputMapper::getActionNames() const { return result; } -static int actionMetaTypeId = qRegisterMetaType<Action>(); -static int inputMetaTypeId = qRegisterMetaType<Input>(); -static int inputPairMetaTypeId = qRegisterMetaType<Input::NamedPair>(); -static int poseMetaTypeId = qRegisterMetaType<controller::Pose>("Pose"); +int actionMetaTypeId = qRegisterMetaType<Action>(); +int inputMetaTypeId = qRegisterMetaType<Input>(); +int inputPairMetaTypeId = qRegisterMetaType<Input::NamedPair>(); +int poseMetaTypeId = qRegisterMetaType<controller::Pose>("Pose"); QScriptValue inputToScriptValue(QScriptEngine* engine, const Input& input); void inputFromScriptValue(const QScriptValue& object, Input& input); diff --git a/libraries/entities/src/EntityTypes.h b/libraries/entities/src/EntityTypes.h index 3536327d18..fe37c9b5f7 100644 --- a/libraries/entities/src/EntityTypes.h +++ b/libraries/entities/src/EntityTypes.h @@ -69,7 +69,7 @@ private: /// 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: // 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); /// Macro for registering entity types with an overloaded factory. Like using the REGISTER_ENTITY_TYPE macro: Make sure to add diff --git a/libraries/fbx/src/FBXReader.cpp b/libraries/fbx/src/FBXReader.cpp index 65cae5343a..12cd0b0a91 100644 --- a/libraries/fbx/src/FBXReader.cpp +++ b/libraries/fbx/src/FBXReader.cpp @@ -39,7 +39,7 @@ using namespace std; -static int FBXGeometryPointerMetaTypeId = qRegisterMetaType<FBXGeometry::Pointer>(); +int FBXGeometryPointerMetaTypeId = qRegisterMetaType<FBXGeometry::Pointer>(); QStringList FBXGeometry::getJointNames() const { QStringList names; @@ -130,9 +130,9 @@ QString FBXGeometry::getModelNameOfMesh(int meshIndex) const { return QString(); } -static int fbxGeometryMetaTypeId = qRegisterMetaType<FBXGeometry>(); -static int fbxAnimationFrameMetaTypeId = qRegisterMetaType<FBXAnimationFrame>(); -static int fbxAnimationFrameVectorMetaTypeId = qRegisterMetaType<QVector<FBXAnimationFrame> >(); +int fbxGeometryMetaTypeId = qRegisterMetaType<FBXGeometry>(); +int fbxAnimationFrameMetaTypeId = qRegisterMetaType<FBXAnimationFrame>(); +int fbxAnimationFrameVectorMetaTypeId = qRegisterMetaType<QVector<FBXAnimationFrame> >(); glm::vec3 parseVec3(const QString& string) { diff --git a/libraries/gl/src/gl/OffscreenQmlSurface.cpp b/libraries/gl/src/gl/OffscreenQmlSurface.cpp index e90ef9e782..388ca26482 100644 --- a/libraries/gl/src/gl/OffscreenQmlSurface.cpp +++ b/libraries/gl/src/gl/OffscreenQmlSurface.cpp @@ -716,9 +716,9 @@ QQmlContext* OffscreenQmlSurface::getRootContext() { } 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()>); -static auto VariantLambdaType = qRegisterMetaType<std::function<QVariant()>>(); +auto VariantLambdaType = qRegisterMetaType<std::function<QVariant()>>(); void OffscreenQmlSurface::executeOnUiThread(std::function<void()> function, bool blocking ) { diff --git a/libraries/gpu-gl/src/gpu/gl/GLBackend.cpp b/libraries/gpu-gl/src/gpu/gl/GLBackend.cpp index 4d264995ae..ece1ee730c 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLBackend.cpp +++ b/libraries/gpu-gl/src/gpu/gl/GLBackend.cpp @@ -33,7 +33,7 @@ using namespace gpu::gl; 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() { diff --git a/libraries/gpu/src/gpu/Texture.cpp b/libraries/gpu/src/gpu/Texture.cpp index 3c8d39a838..f9a8f3b26b 100755 --- a/libraries/gpu/src/gpu/Texture.cpp +++ b/libraries/gpu/src/gpu/Texture.cpp @@ -22,7 +22,7 @@ using namespace gpu; -static int TexturePointerMetaTypeId = qRegisterMetaType<TexturePointer>(); +int TexturePointerMetaTypeId = qRegisterMetaType<TexturePointer>(); std::atomic<uint32_t> Texture::_textureCPUCount{ 0 }; std::atomic<Texture::Size> Texture::_textureCPUMemoryUsage{ 0 }; diff --git a/libraries/networking/src/HifiSockAddr.cpp b/libraries/networking/src/HifiSockAddr.cpp index bc4c1a1599..bde0aca7b7 100644 --- a/libraries/networking/src/HifiSockAddr.cpp +++ b/libraries/networking/src/HifiSockAddr.cpp @@ -16,7 +16,7 @@ #include "HifiSockAddr.h" #include "NetworkLogging.h" -static int hifiSockAddrMetaTypeId = qRegisterMetaType<HifiSockAddr>(); +int hifiSockAddrMetaTypeId = qRegisterMetaType<HifiSockAddr>(); HifiSockAddr::HifiSockAddr() : _address(), diff --git a/libraries/networking/src/Node.cpp b/libraries/networking/src/Node.cpp index e4fe292223..1e1cec2413 100644 --- a/libraries/networking/src/Node.cpp +++ b/libraries/networking/src/Node.cpp @@ -22,9 +22,9 @@ const QString UNKNOWN_NodeType_t_NAME = "Unknown"; -static int NodePtrMetaTypeId = qRegisterMetaType<Node*>("Node*"); -static int sharedPtrNodeMetaTypeId = qRegisterMetaType<QSharedPointer<Node>>("QSharedPointer<Node>"); -static int sharedNodePtrMetaTypeId = qRegisterMetaType<SharedNodePointer>("SharedNodePointer"); +int NodePtrMetaTypeId = qRegisterMetaType<Node*>("Node*"); +int sharedPtrNodeMetaTypeId = qRegisterMetaType<QSharedPointer<Node>>("QSharedPointer<Node>"); +int sharedNodePtrMetaTypeId = qRegisterMetaType<SharedNodePointer>("SharedNodePointer"); namespace NodeType { QHash<NodeType_t, QString> TypeNameHash; diff --git a/libraries/networking/src/ReceivedMessage.cpp b/libraries/networking/src/ReceivedMessage.cpp index 6ef2b7c7d4..cd3eb03473 100644 --- a/libraries/networking/src/ReceivedMessage.cpp +++ b/libraries/networking/src/ReceivedMessage.cpp @@ -14,8 +14,8 @@ #include "QSharedPointer" -static int receivedMessageMetaTypeId = qRegisterMetaType<ReceivedMessage*>("ReceivedMessage*"); -static int sharedPtrReceivedMessageMetaTypeId = qRegisterMetaType<QSharedPointer<ReceivedMessage>>("QSharedPointer<ReceivedMessage>"); +int receivedMessageMetaTypeId = qRegisterMetaType<ReceivedMessage*>("ReceivedMessage*"); +int sharedPtrReceivedMessageMetaTypeId = qRegisterMetaType<QSharedPointer<ReceivedMessage>>("QSharedPointer<ReceivedMessage>"); static const int HEAD_DATA_SIZE = 512; diff --git a/libraries/networking/src/udt/Packet.cpp b/libraries/networking/src/udt/Packet.cpp index d46cae2404..e852332317 100644 --- a/libraries/networking/src/udt/Packet.cpp +++ b/libraries/networking/src/udt/Packet.cpp @@ -19,7 +19,7 @@ using namespace udt; -static int packetMetaTypeId = qRegisterMetaType<Packet*>("Packet*"); +int packetMetaTypeId = qRegisterMetaType<Packet*>("Packet*"); using Key = uint64_t; static const std::array<Key, 4> KEYS {{ diff --git a/libraries/networking/src/udt/PacketHeaders.cpp b/libraries/networking/src/udt/PacketHeaders.cpp index b04d582d6d..915c2f44ba 100644 --- a/libraries/networking/src/udt/PacketHeaders.cpp +++ b/libraries/networking/src/udt/PacketHeaders.cpp @@ -18,7 +18,7 @@ Q_DECLARE_METATYPE(PacketType); -static int packetTypeMetaTypeId = qRegisterMetaType<PacketType>(); +int packetTypeMetaTypeId = qRegisterMetaType<PacketType>(); const QSet<PacketType> NON_VERIFIED_PACKETS = QSet<PacketType>() << PacketType::NodeJsonStats << PacketType::EntityQuery diff --git a/libraries/networking/src/udt/PacketList.cpp b/libraries/networking/src/udt/PacketList.cpp index b12b7e6938..ea84548210 100644 --- a/libraries/networking/src/udt/PacketList.cpp +++ b/libraries/networking/src/udt/PacketList.cpp @@ -15,7 +15,7 @@ using namespace udt; -static int packetListMetaTypeId = qRegisterMetaType<PacketList*>("PacketList*"); +int packetListMetaTypeId = qRegisterMetaType<PacketList*>("PacketList*"); std::unique_ptr<PacketList> PacketList::create(PacketType packetType, QByteArray extendedHeader, bool isReliable, bool isOrdered) { diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index 2fe4427333..9ef16b2daa 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -29,9 +29,9 @@ using namespace std; -static int nakedModelPointerTypeId = qRegisterMetaType<ModelPointer>(); -static int weakNetworkGeometryPointerTypeId = qRegisterMetaType<std::weak_ptr<NetworkGeometry> >(); -static int vec3VectorTypeId = qRegisterMetaType<QVector<glm::vec3> >(); +int nakedModelPointerTypeId = qRegisterMetaType<ModelPointer>(); +int weakNetworkGeometryPointerTypeId = qRegisterMetaType<std::weak_ptr<NetworkGeometry> >(); +int vec3VectorTypeId = qRegisterMetaType<QVector<glm::vec3> >(); float Model::FAKE_DIMENSION_PLACEHOLDER = -1.0f; #define HTTP_INVALID_COM "http://invalid.com" diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index 7c3aad877e..c9d5ca35b0 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -65,7 +65,7 @@ static const QString SCRIPT_EXCEPTION_FORMAT = "[UncaughtException] %1 in %2:%3"; Q_DECLARE_METATYPE(QScriptEngine::FunctionSignature) -static int functionSignatureMetaID = qRegisterMetaType<QScriptEngine::FunctionSignature>(); +int functionSignatureMetaID = qRegisterMetaType<QScriptEngine::FunctionSignature>(); static QScriptValue debugPrint(QScriptContext* context, QScriptEngine* engine){ QString message = ""; diff --git a/libraries/shared/src/RegisteredMetaTypes.cpp b/libraries/shared/src/RegisteredMetaTypes.cpp index 53fa8b30cf..8e6c1ef6ed 100644 --- a/libraries/shared/src/RegisteredMetaTypes.cpp +++ b/libraries/shared/src/RegisteredMetaTypes.cpp @@ -21,17 +21,17 @@ #include "RegisteredMetaTypes.h" -static int vec4MetaTypeId = qRegisterMetaType<glm::vec4>(); -static int vec3MetaTypeId = qRegisterMetaType<glm::vec3>(); -static int qVectorVec3MetaTypeId = qRegisterMetaType<QVector<glm::vec3>>(); -static int qVectorQuatMetaTypeId = qRegisterMetaType<QVector<glm::quat>>(); -static int qVectorBoolMetaTypeId = qRegisterMetaType<QVector<bool>>(); -static int vec2MetaTypeId = qRegisterMetaType<glm::vec2>(); -static int quatMetaTypeId = qRegisterMetaType<glm::quat>(); -static int xColorMetaTypeId = qRegisterMetaType<xColor>(); -static int pickRayMetaTypeId = qRegisterMetaType<PickRay>(); -static int collisionMetaTypeId = qRegisterMetaType<Collision>(); -static int qMapURLStringMetaTypeId = qRegisterMetaType<QMap<QUrl,QString>>(); +int vec4MetaTypeId = qRegisterMetaType<glm::vec4>(); +int vec3MetaTypeId = qRegisterMetaType<glm::vec3>(); +int qVectorVec3MetaTypeId = qRegisterMetaType<QVector<glm::vec3>>(); +int qVectorQuatMetaTypeId = qRegisterMetaType<QVector<glm::quat>>(); +int qVectorBoolMetaTypeId = qRegisterMetaType<QVector<bool>>(); +int vec2MetaTypeId = qRegisterMetaType<glm::vec2>(); +int quatMetaTypeId = qRegisterMetaType<glm::quat>(); +int xColorMetaTypeId = qRegisterMetaType<xColor>(); +int pickRayMetaTypeId = qRegisterMetaType<PickRay>(); +int collisionMetaTypeId = qRegisterMetaType<Collision>(); +int qMapURLStringMetaTypeId = qRegisterMetaType<QMap<QUrl,QString>>(); void registerMetaTypes(QScriptEngine* engine) { qScriptRegisterMetaType(engine, mat4toScriptValue, mat4FromScriptValue); diff --git a/libraries/shared/src/SimpleMovingAverage.h b/libraries/shared/src/SimpleMovingAverage.h index 296911ae3e..dd25705f7e 100644 --- a/libraries/shared/src/SimpleMovingAverage.h +++ b/libraries/shared/src/SimpleMovingAverage.h @@ -57,7 +57,7 @@ public: void addSample(T sample) { if (numSamples > 0) { - average = (sample * WEIGHTING) + (average * ONE_MINUS_WEIGHTING); + average = ((float)sample * WEIGHTING) + ((float)average * ONE_MINUS_WEIGHTING); } else { average = sample; } diff --git a/tests/render-utils/src/main.cpp b/tests/render-utils/src/main.cpp index db6598c43d..ccb91590c3 100644 --- a/tests/render-utils/src/main.cpp +++ b/tests/render-utils/src/main.cpp @@ -157,10 +157,6 @@ protected: //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::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) { auto vs = gpu::Shader::createVertex(std::string(vs_src)); auto fs = gpu::Shader::createPixel(std::string(fs_src)); From d7142aee806e570ea07cef9c360ff89cc1c7ca89 Mon Sep 17 00:00:00 2001 From: Seth Alves <seth.alves@gmail.com> Date: Sat, 21 May 2016 18:06:21 -0700 Subject: [PATCH 30/35] try to make osx clang happy, also --- .../src/octree/OctreeInboundPacketProcessor.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/assignment-client/src/octree/OctreeInboundPacketProcessor.cpp b/assignment-client/src/octree/OctreeInboundPacketProcessor.cpp index 32f3de2ff6..d2fef4dfbd 100644 --- a/assignment-client/src/octree/OctreeInboundPacketProcessor.cpp +++ b/assignment-client/src/octree/OctreeInboundPacketProcessor.cpp @@ -153,7 +153,7 @@ void OctreeInboundPacketProcessor::processPacket(QSharedPointer<ReceivedMessage> qDebug() << " maxSize=" << maxSize; qDebug("OctreeInboundPacketProcessor::processPacket() %hhu " "payload=%p payloadLength=%lld editData=%p payloadPosition=%lld maxSize=%d", - (int)packetType, message->getRawMessage(), message->getSize(), editData, + (unsigned char)packetType, message->getRawMessage(), message->getSize(), editData, message->getPosition(), maxSize); } @@ -191,7 +191,7 @@ void OctreeInboundPacketProcessor::processPacket(QSharedPointer<ReceivedMessage> if (debugProcessPacket) { qDebug("OctreeInboundPacketProcessor::processPacket() DONE LOOPING FOR %hhu " "payload=%p payloadLength=%lld editData=%p payloadPosition=%lld", - (int)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. @@ -208,7 +208,7 @@ void OctreeInboundPacketProcessor::processPacket(QSharedPointer<ReceivedMessage> } trackInboundPacket(nodeUUID, sequence, transitTime, editsInPacket, processTime, lockWaitTime); } else { - qDebug("unknown packet ignored... packetType=%hhu", (int)packetType); + qDebug("unknown packet ignored... packetType=%hhu", (unsigned char)packetType); } } From cd1e910844ca3d7e9f6d0e722a6df07473fd9672 Mon Sep 17 00:00:00 2001 From: Brad Davis <bdavis@saintandreas.org> Date: Tue, 10 May 2016 09:27:01 -0700 Subject: [PATCH 31/35] Add a generic shape primitive --- .../src/EntityTreeRenderer.cpp | 10 +- .../src/RenderableBoxEntityItem.cpp | 76 ------ .../src/RenderableBoxEntityItem.h | 34 --- .../src/RenderableShapeEntityItem.cpp | 113 +++++++++ .../src/RenderableShapeEntityItem.h | 36 +++ .../src/RenderableSphereEntityItem.cpp | 80 ------ .../src/RenderableSphereEntityItem.h | 35 --- libraries/entities/src/BoxEntityItem.cpp | 103 -------- libraries/entities/src/BoxEntityItem.h | 64 ----- .../entities/src/EntityItemProperties.cpp | 15 +- libraries/entities/src/EntityItemProperties.h | 4 +- libraries/entities/src/EntityPropertyFlags.h | 2 + libraries/entities/src/EntityTypes.cpp | 10 +- libraries/entities/src/EntityTypes.h | 18 +- libraries/entities/src/ShapeEntityItem.cpp | 230 ++++++++++++++++++ libraries/entities/src/ShapeEntityItem.h | 103 ++++++++ libraries/entities/src/SphereEntityItem.cpp | 132 ---------- libraries/entities/src/SphereEntityItem.h | 70 ------ libraries/render-utils/src/GeometryCache.cpp | 41 ++-- libraries/render-utils/src/GeometryCache.h | 7 + .../developer/tests/entityEditStressTest.js | 164 +------------ scripts/developer/tests/entitySpawnTool.js | 184 ++++++++++++++ scripts/developer/tests/primitivesTest.js | 24 ++ scripts/system/html/entityProperties.html | 51 +++- tests/entities/src/main.cpp | 4 +- 25 files changed, 819 insertions(+), 791 deletions(-) delete mode 100644 libraries/entities-renderer/src/RenderableBoxEntityItem.cpp delete mode 100644 libraries/entities-renderer/src/RenderableBoxEntityItem.h create mode 100644 libraries/entities-renderer/src/RenderableShapeEntityItem.cpp create mode 100644 libraries/entities-renderer/src/RenderableShapeEntityItem.h delete mode 100644 libraries/entities-renderer/src/RenderableSphereEntityItem.cpp delete mode 100644 libraries/entities-renderer/src/RenderableSphereEntityItem.h delete mode 100644 libraries/entities/src/BoxEntityItem.cpp delete mode 100644 libraries/entities/src/BoxEntityItem.h create mode 100644 libraries/entities/src/ShapeEntityItem.cpp create mode 100644 libraries/entities/src/ShapeEntityItem.h delete mode 100644 libraries/entities/src/SphereEntityItem.cpp delete mode 100644 libraries/entities/src/SphereEntityItem.h create mode 100644 scripts/developer/tests/entitySpawnTool.js create mode 100644 scripts/developer/tests/primitivesTest.js diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.cpp b/libraries/entities-renderer/src/EntityTreeRenderer.cpp index bec2fa9b8d..56f6438e70 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.cpp +++ b/libraries/entities-renderer/src/EntityTreeRenderer.cpp @@ -29,17 +29,16 @@ #include "RenderableEntityItem.h" -#include "RenderableBoxEntityItem.h" #include "RenderableLightEntityItem.h" #include "RenderableModelEntityItem.h" #include "RenderableParticleEffectEntityItem.h" -#include "RenderableSphereEntityItem.h" #include "RenderableTextEntityItem.h" #include "RenderableWebEntityItem.h" #include "RenderableZoneEntityItem.h" #include "RenderableLineEntityItem.h" #include "RenderablePolyVoxEntityItem.h" #include "RenderablePolyLineEntityItem.h" +#include "RenderableShapeEntityItem.h" #include "EntitiesRendererLogging.h" #include "AddressManager.h" #include <Rig.h> @@ -56,8 +55,6 @@ EntityTreeRenderer::EntityTreeRenderer(bool wantScripts, AbstractViewStateInterf _dontDoPrecisionPicking(false) { 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(Text, RenderableTextEntityItem::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(PolyVox, RenderablePolyVoxEntityItem::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; _currentClickingOnEntityID = UNKNOWN_ENTITY_ID; } diff --git a/libraries/entities-renderer/src/RenderableBoxEntityItem.cpp b/libraries/entities-renderer/src/RenderableBoxEntityItem.cpp deleted file mode 100644 index e392450c08..0000000000 --- a/libraries/entities-renderer/src/RenderableBoxEntityItem.cpp +++ /dev/null @@ -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; -} diff --git a/libraries/entities-renderer/src/RenderableBoxEntityItem.h b/libraries/entities-renderer/src/RenderableBoxEntityItem.h deleted file mode 100644 index 67f881dbd8..0000000000 --- a/libraries/entities-renderer/src/RenderableBoxEntityItem.h +++ /dev/null @@ -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 diff --git a/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp b/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp new file mode 100644 index 0000000000..7d30b7a47c --- /dev/null +++ b/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp @@ -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::Octahetron, + 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::Cube) { + 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; +} diff --git a/libraries/entities-renderer/src/RenderableShapeEntityItem.h b/libraries/entities-renderer/src/RenderableShapeEntityItem.h new file mode 100644 index 0000000000..b18370b13c --- /dev/null +++ b/libraries/entities-renderer/src/RenderableShapeEntityItem.h @@ -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 diff --git a/libraries/entities-renderer/src/RenderableSphereEntityItem.cpp b/libraries/entities-renderer/src/RenderableSphereEntityItem.cpp deleted file mode 100644 index c3437b0e4a..0000000000 --- a/libraries/entities-renderer/src/RenderableSphereEntityItem.cpp +++ /dev/null @@ -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; -} diff --git a/libraries/entities-renderer/src/RenderableSphereEntityItem.h b/libraries/entities-renderer/src/RenderableSphereEntityItem.h deleted file mode 100644 index 5efe49854a..0000000000 --- a/libraries/entities-renderer/src/RenderableSphereEntityItem.h +++ /dev/null @@ -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 diff --git a/libraries/entities/src/BoxEntityItem.cpp b/libraries/entities/src/BoxEntityItem.cpp deleted file mode 100644 index bf02d383ab..0000000000 --- a/libraries/entities/src/BoxEntityItem.cpp +++ /dev/null @@ -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); -} - diff --git a/libraries/entities/src/BoxEntityItem.h b/libraries/entities/src/BoxEntityItem.h deleted file mode 100644 index 6196346b9a..0000000000 --- a/libraries/entities/src/BoxEntityItem.h +++ /dev/null @@ -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 diff --git a/libraries/entities/src/EntityItemProperties.cpp b/libraries/entities/src/EntityItemProperties.cpp index 99285b4986..f273507d0d 100644 --- a/libraries/entities/src/EntityItemProperties.cpp +++ b/libraries/entities/src/EntityItemProperties.cpp @@ -689,6 +689,7 @@ void EntityItemProperties::copyFromScriptValue(const QScriptValue& object, bool COPY_PROPERTY_FROM_QSCRIPTVALUE(jointRotations, qVectorQuat, setJointRotations); COPY_PROPERTY_FROM_QSCRIPTVALUE(jointTranslationsSet, qVectorBool, setJointTranslationsSet); 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(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, 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_FPS, Animation, animation, FPS, fps); 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_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_NAME, properties.getName()); 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_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_NAME, QString, setName); @@ -1915,6 +1923,7 @@ QList<QString> EntityItemProperties::listChangedProperties() { if (queryAACubeChanged()) { out += "queryAACube"; } + if (clientOnlyChanged()) { out += "clientOnly"; } @@ -1929,6 +1938,10 @@ QList<QString> EntityItemProperties::listChangedProperties() { out += "ghostingAllowed"; } + if (shapeChanged()) { + out += "shape"; + } + getAnimation().listChangedProperties(out); getKeyLight().listChangedProperties(out); getSkybox().listChangedProperties(out); diff --git a/libraries/entities/src/EntityItemProperties.h b/libraries/entities/src/EntityItemProperties.h index 59749cfef5..6018ba793f 100644 --- a/libraries/entities/src/EntityItemProperties.h +++ b/libraries/entities/src/EntityItemProperties.h @@ -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 /// 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 -/// all units for position, dimensions, etc are in meter units +/// all units for SI units (meter, second, radian, etc) class EntityItemProperties { 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 @@ -64,6 +64,7 @@ class EntityItemProperties { 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 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: EntityItemProperties(EntityPropertyFlags desiredProperties = EntityPropertyFlags()); 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_JOINT_INDEX, ParentJointIndex, parentJointIndex, quint16, -1); 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 DEFINE_PROPERTY_REF(PROP_LOCAL_POSITION, LocalPosition, localPosition, glmVec3, ENTITY_ITEM_ZERO_VEC3); diff --git a/libraries/entities/src/EntityPropertyFlags.h b/libraries/entities/src/EntityPropertyFlags.h index 20c451eaae..36bb37c8f3 100644 --- a/libraries/entities/src/EntityPropertyFlags.h +++ b/libraries/entities/src/EntityPropertyFlags.h @@ -175,6 +175,8 @@ enum EntityPropertyList { PROP_CLIENT_ONLY, // 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 PROP_AFTER_LAST_ITEM, diff --git a/libraries/entities/src/EntityTypes.cpp b/libraries/entities/src/EntityTypes.cpp index 52c2242629..7b1133c2aa 100644 --- a/libraries/entities/src/EntityTypes.cpp +++ b/libraries/entities/src/EntityTypes.cpp @@ -18,17 +18,16 @@ #include "EntityItemProperties.h" #include "EntityTypes.h" -#include "BoxEntityItem.h" #include "LightEntityItem.h" #include "ModelEntityItem.h" #include "ParticleEffectEntityItem.h" -#include "SphereEntityItem.h" #include "TextEntityItem.h" #include "WebEntityItem.h" #include "ZoneEntityItem.h" #include "LineEntityItem.h" #include "PolyVoxEntityItem.h" #include "PolyLineEntityItem.h" +#include "ShapeEntityItem.h" QMap<EntityTypes::EntityType, QString> EntityTypes::_typeToNameMap; 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_TYPE(Model) -REGISTER_ENTITY_TYPE(Box) REGISTER_ENTITY_TYPE(Web) -REGISTER_ENTITY_TYPE(Sphere) REGISTER_ENTITY_TYPE(Light) REGISTER_ENTITY_TYPE(Text) REGISTER_ENTITY_TYPE(ParticleEffect) REGISTER_ENTITY_TYPE(Zone) REGISTER_ENTITY_TYPE(Line) 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) { QMap<EntityType, QString>::iterator matchedTypeName = _typeToNameMap.find(entityType); diff --git a/libraries/entities/src/EntityTypes.h b/libraries/entities/src/EntityTypes.h index 3536327d18..be3be03713 100644 --- a/libraries/entities/src/EntityTypes.h +++ b/libraries/entities/src/EntityTypes.h @@ -48,7 +48,8 @@ public: Line, PolyVox, PolyLine, - LAST = PolyLine + Shape, + LAST = Shape } EntityType; static const QString& getEntityTypeName(EntityType entityType); @@ -72,6 +73,15 @@ private: #define REGISTER_ENTITY_TYPE(x) static bool x##Registration = \ 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 /// 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 @@ -79,9 +89,9 @@ private: // static EntityItemPointer factory(const EntityItemID& entityID, const EntityItemProperties& properties); #define REGISTER_ENTITY_TYPE_WITH_FACTORY(x,y) static bool x##Registration = \ EntityTypes::registerEntityType(EntityTypes::x, #x, y); \ - if (!x##Registration) { \ - qDebug() << "UNEXPECTED: REGISTER_ENTITY_TYPE_WITH_FACTORY(" #x "," #y ") FAILED.!"; \ - } + EntityRegistrationChecker x##RegistrationChecker( \ + x##Registration, \ + "UNEXPECTED: REGISTER_ENTITY_TYPE_WITH_FACTORY(" #x "," #y ") FAILED.!"); #endif // hifi_EntityTypes_h diff --git a/libraries/entities/src/ShapeEntityItem.cpp b/libraries/entities/src/ShapeEntityItem.cpp new file mode 100644 index 0000000000..a24c7e1df5 --- /dev/null +++ b/libraries/entities/src/ShapeEntityItem.cpp @@ -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", + "Octahetron", + "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); +} + diff --git a/libraries/entities/src/ShapeEntityItem.h b/libraries/entities/src/ShapeEntityItem.h new file mode 100644 index 0000000000..f3cb2abd66 --- /dev/null +++ b/libraries/entities/src/ShapeEntityItem.h @@ -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, + Octahetron, + 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 diff --git a/libraries/entities/src/SphereEntityItem.cpp b/libraries/entities/src/SphereEntityItem.cpp deleted file mode 100644 index 7ad7b39f20..0000000000 --- a/libraries/entities/src/SphereEntityItem.cpp +++ /dev/null @@ -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); -} - diff --git a/libraries/entities/src/SphereEntityItem.h b/libraries/entities/src/SphereEntityItem.h deleted file mode 100644 index fda5eab009..0000000000 --- a/libraries/entities/src/SphereEntityItem.h +++ /dev/null @@ -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 diff --git a/libraries/render-utils/src/GeometryCache.cpp b/libraries/render-utils/src/GeometryCache.cpp index 15bf44744c..6852d17882 100644 --- a/libraries/render-utils/src/GeometryCache.cpp +++ b/libraries/render-utils/src/GeometryCache.cpp @@ -112,10 +112,12 @@ void GeometryCache::ShapeData::drawWireInstances(gpu::Batch& batch, size_t count } } +// The golden ratio +static const float PHI = 1.61803398874f; + 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 float a = 1; + static const float b = PHI / 2.0f; static const VertexVector vertices{ // vec3(0, b, -a), vec3(-b, a, 0), vec3(b, a, 0), // @@ -143,11 +145,10 @@ const VertexVector& icosahedronVertices() { } 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 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 VertexVector vertices{ A, B, C, D, B, A, @@ -356,7 +357,7 @@ void GeometryCache::buildShapes() { 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(originalVertices[vertexIndex]); vertices.push_back(faceNormal); } } @@ -437,7 +438,7 @@ void GeometryCache::buildShapes() { 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(originalVertices[vertexIndex]); vertices.push_back(faceNormal); indices.push_back((uint16_t)(vertexIndex + startingIndex)); } @@ -1801,10 +1802,10 @@ uint32_t toCompactColor(const glm::vec4& color) { 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) { // 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 { @@ -1826,14 +1827,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) { - static const std::string INSTANCE_NAME = __FUNCTION__; - renderInstances(INSTANCE_NAME, batch, color, false, pipeline, GeometryCache::Sphere); + renderInstances(batch, color, false, pipeline, GeometryCache::Sphere); } void GeometryCache::renderWireSphereInstance(gpu::Batch& batch, const glm::vec4& color, const render::ShapePipelinePointer& pipeline) { - static const std::string INSTANCE_NAME = __FUNCTION__; - renderInstances(INSTANCE_NAME, batch, color, true, pipeline, GeometryCache::Sphere); + renderInstances(batch, color, true, pipeline, GeometryCache::Sphere); } // Enable this in a debug build to cause 'box' entities to iterate through all the @@ -1841,8 +1844,6 @@ void GeometryCache::renderWireSphereInstance(gpu::Batch& batch, const glm::vec4& //#define DEBUG_SHAPES void GeometryCache::renderSolidCubeInstance(gpu::Batch& batch, const glm::vec4& color, const render::ShapePipelinePointer& pipeline) { - static const std::string INSTANCE_NAME = __FUNCTION__; - #ifdef DEBUG_SHAPES static auto startTime = usecTimestampNow(); renderInstances(INSTANCE_NAME, batch, color, pipeline, [](gpu::Batch& batch, gpu::Batch::NamedBatchData& data) { @@ -1876,11 +1877,11 @@ void GeometryCache::renderSolidCubeInstance(gpu::Batch& batch, const glm::vec4& } }); #else - renderInstances(INSTANCE_NAME, batch, color, false, pipeline, GeometryCache::Cube); + renderInstances(batch, color, false, pipeline, GeometryCache::Cube); #endif } void GeometryCache::renderWireCubeInstance(gpu::Batch& batch, const glm::vec4& color, const render::ShapePipelinePointer& pipeline) { static const std::string INSTANCE_NAME = __FUNCTION__; - renderInstances(INSTANCE_NAME, batch, color, true, pipeline, GeometryCache::Cube); + renderInstances(batch, color, true, pipeline, GeometryCache::Cube); } diff --git a/libraries/render-utils/src/GeometryCache.h b/libraries/render-utils/src/GeometryCache.h index c4531aa102..7fa543abe2 100644 --- a/libraries/render-utils/src/GeometryCache.h +++ b/libraries/render-utils/src/GeometryCache.h @@ -163,6 +163,13 @@ public: 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 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, const render::ShapePipelinePointer& pipeline = _simplePipeline); void renderSolidSphereInstance(gpu::Batch& batch, const glm::vec3& color, diff --git a/scripts/developer/tests/entityEditStressTest.js b/scripts/developer/tests/entityEditStressTest.js index 2d3c8ad0e1..8fa06a968d 100644 --- a/scripts/developer/tests/entityEditStressTest.js +++ b/scripts/developer/tests/entityEditStressTest.js @@ -15,161 +15,11 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -var NUM_ENTITIES = 20000; // number of entities to spawn -var ENTITY_SPAWN_LIMIT = 1000; -var ENTITY_SPAWN_INTERVAL = 0.1; +Script.include("./entitySpawnTool.js"); -var UPDATE_INTERVAL = 0.05; // Re-randomize the entity's position every x seconds / ms -var ENTITY_LIFETIME = 30; // Entity timeout (when/if we crash, we need the entities to delete themselves) -var KEEPALIVE_INTERVAL = 5; // Refreshes the timeout every X seconds - -var RADIUS = 5.0; // Spawn within this radius (square) -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); -})(); \ No newline at end of file +ENTITY_SPAWNER({ + count: 20000, + spawnLimit: 1000, + spawnInterval: 0.1, + updateInterval: 0.05 +}); diff --git a/scripts/developer/tests/entitySpawnTool.js b/scripts/developer/tests/entitySpawnTool.js new file mode 100644 index 0000000000..d88933b867 --- /dev/null +++ b/scripts/developer/tests/entitySpawnTool.js @@ -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); +}; \ No newline at end of file diff --git a/scripts/developer/tests/primitivesTest.js b/scripts/developer/tests/primitivesTest.js new file mode 100644 index 0000000000..e401963a83 --- /dev/null +++ b/scripts/developer/tests/primitivesTest.js @@ -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 +}) + diff --git a/scripts/system/html/entityProperties.html b/scripts/system/html/entityProperties.html index efe7e6cc65..4a3b5a14a4 100644 --- a/scripts/system/html/entityProperties.html +++ b/scripts/system/html/entityProperties.html @@ -25,6 +25,7 @@ var ICON_FOR_TYPE = { Box: "V", Sphere: "n", + Shape: "n", ParticleEffect: "", Model: "", Web: "q", @@ -403,6 +404,10 @@ var elColorGreen = document.getElementById("property-color-green"); 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"); allSections.push(elLightSections); var elLightSpotLight = document.getElementById("property-light-spot-light"); @@ -666,7 +671,18 @@ 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++) { elColorSections[i].style.display = 'table'; } @@ -958,6 +974,8 @@ elLightExponent.addEventListener('change', createEmitNumberPropertyUpdateFunction('exponent', 2)); elLightCutoff.addEventListener('change', createEmitNumberPropertyUpdateFunction('cutoff', 2)); + elShape.addEventListener('change', createEmitTextPropertyUpdateFunction('shape')); + elWebSourceURL.addEventListener('change', createEmitTextPropertyUpdateFunction('sourceUrl')); elModelURL.addEventListener('change', createEmitTextPropertyUpdateFunction('modelURL')); @@ -1344,6 +1362,36 @@ <span id="property-id" class="selectable"></span> </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">Cube</option> + <option value="Sphere">Sphere</option> + <option value="Icosahedron">Icosahedron</option> + <option value="Tetrahedron">Tetrahedron</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"> <label>Hyperlink</label><span>M</span> @@ -1678,7 +1726,6 @@ <textarea id="property-model-original-textures" readonly></textarea> </div> - <div class="section-header text-group text-section"> <label>Text</label><span>M</span> </div> diff --git a/tests/entities/src/main.cpp b/tests/entities/src/main.cpp index c0e21276d8..792ef7d9c6 100644 --- a/tests/entities/src/main.cpp +++ b/tests/entities/src/main.cpp @@ -16,7 +16,7 @@ #include <QDir> #include <ByteCountCoding.h> -#include <BoxEntityItem.h> +#include <ShapeEntityItem.h> #include <EntityItemProperties.h> #include <Octree.h> #include <PathUtils.h> @@ -155,7 +155,7 @@ int main(int argc, char** argv) { QFile file(getTestResourceDir() + "packet.bin"); if (!file.open(QIODevice::ReadOnly)) return -1; QByteArray packet = file.readAll(); - EntityItemPointer item = BoxEntityItem::factory(EntityItemID(), EntityItemProperties()); + EntityItemPointer item = ShapeEntityItem::boxFactory(EntityItemID(), EntityItemProperties()); ReadBitstreamToTreeParams params; params.bitstreamVersion = 33; From 2c703e963cdb9780c241381948806416deffdd6a Mon Sep 17 00:00:00 2001 From: Brad Davis <bdavis@saintandreas.org> Date: Sun, 22 May 2016 17:41:50 -0700 Subject: [PATCH 32/35] More shapes --- .../src/RenderableShapeEntityItem.cpp | 4 +- libraries/entities/src/ShapeEntityItem.cpp | 2 +- libraries/entities/src/ShapeEntityItem.h | 2 +- libraries/render-utils/src/GeometryCache.cpp | 651 +++++++++--------- libraries/render-utils/src/GeometryCache.h | 4 +- scripts/system/html/entityProperties.html | 6 +- tests/gpu-test/CMakeLists.txt | 4 +- tests/gpu-test/src/TestHelpers.cpp | 20 + tests/gpu-test/src/TestHelpers.h | 33 + tests/gpu-test/src/TestInstancedShapes.cpp | 84 +++ tests/gpu-test/src/TestInstancedShapes.h | 23 + tests/gpu-test/src/TestShapes.cpp | 48 ++ tests/gpu-test/src/TestShapes.h | 22 + tests/gpu-test/src/TestWindow.cpp | 180 +++++ tests/gpu-test/src/TestWindow.h | 53 ++ tests/gpu-test/src/main.cpp | 539 +++------------ 16 files changed, 895 insertions(+), 780 deletions(-) create mode 100644 tests/gpu-test/src/TestHelpers.cpp create mode 100644 tests/gpu-test/src/TestHelpers.h create mode 100644 tests/gpu-test/src/TestInstancedShapes.cpp create mode 100644 tests/gpu-test/src/TestInstancedShapes.h create mode 100644 tests/gpu-test/src/TestShapes.cpp create mode 100644 tests/gpu-test/src/TestShapes.h create mode 100644 tests/gpu-test/src/TestWindow.cpp create mode 100644 tests/gpu-test/src/TestWindow.h diff --git a/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp b/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp index 7d30b7a47c..c93ae252e3 100644 --- a/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp @@ -30,7 +30,7 @@ static GeometryCache::Shape MAPPING[entity::NUM_SHAPES] = { GeometryCache::Cube, GeometryCache::Sphere, GeometryCache::Tetrahedron, - GeometryCache::Octahetron, + GeometryCache::Octahedron, GeometryCache::Dodecahedron, GeometryCache::Icosahedron, GeometryCache::Torus, @@ -93,7 +93,7 @@ void RenderableShapeEntityItem::render(RenderArgs* args) { if (!success) { return; } - if (_shape != entity::Cube) { + if (_shape == entity::Sphere) { modelTransform.postScale(SPHERE_ENTITY_SCALE); } batch.setModelTransform(modelTransform); // use a transform with scale, rotation, registration point and translation diff --git a/libraries/entities/src/ShapeEntityItem.cpp b/libraries/entities/src/ShapeEntityItem.cpp index a24c7e1df5..2527dedab2 100644 --- a/libraries/entities/src/ShapeEntityItem.cpp +++ b/libraries/entities/src/ShapeEntityItem.cpp @@ -27,7 +27,7 @@ namespace entity { "Cube", "Sphere", "Tetrahedron", - "Octahetron", + "Octahedron", "Dodecahedron", "Icosahedron", "Torus", diff --git a/libraries/entities/src/ShapeEntityItem.h b/libraries/entities/src/ShapeEntityItem.h index f3cb2abd66..2ae4ae2ca1 100644 --- a/libraries/entities/src/ShapeEntityItem.h +++ b/libraries/entities/src/ShapeEntityItem.h @@ -19,7 +19,7 @@ namespace entity { Cube, Sphere, Tetrahedron, - Octahetron, + Octahedron, Dodecahedron, Icosahedron, Torus, diff --git a/libraries/render-utils/src/GeometryCache.cpp b/libraries/render-utils/src/GeometryCache.cpp index 6852d17882..02aca4216e 100644 --- a/libraries/render-utils/src/GeometryCache.cpp +++ b/libraries/render-utils/src/GeometryCache.cpp @@ -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_NORMALS_OFFSET = sizeof(glm::vec3); -static const gpu::Type SHAPE_INDEX_TYPE = gpu::UINT16; -static const uint SHAPE_INDEX_SIZE = sizeof(gpu::uint16); +static const gpu::Type SHAPE_INDEX_TYPE = gpu::UINT32; +static const uint SHAPE_INDEX_SIZE = sizeof(gpu::uint32); void GeometryCache::ShapeData::setupVertices(gpu::BufferPointer& vertexBuffer, const VertexVector& vertices) { vertexBuffer->append(vertices); @@ -112,102 +112,7 @@ void GeometryCache::ShapeData::drawWireInstances(gpu::Batch& batch, size_t count } } -// The golden ratio -static const float PHI = 1.61803398874f; - -const VertexVector& icosahedronVertices() { - static const float a = 1; - static const float b = PHI / 2.0f; - - 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 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 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 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) { return _shapes[shape]._indexCount / VERTICES_PER_TRIANGLE; @@ -221,6 +126,324 @@ size_t GeometryCache::getCubeTriangleCount() { 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 // components do not. Find a way to support using draw elements @@ -230,229 +453,28 @@ size_t GeometryCache::getCubeTriangleCount() { void GeometryCache::buildShapes() { auto vertexBuffer = std::make_shared<gpu::Buffer>(); auto indexBuffer = std::make_shared<gpu::Buffer>(); - size_t startingIndex = 0; - // Cube - startingIndex = _shapeVertices->getSize() / SHAPE_VERTEX_STRIDE; - { - 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); - } - + setupFlatShape(_shapes[Cube], cube(), _shapeVertices, _shapeIndices); // Tetrahedron - startingIndex = _shapeVertices->getSize() / SHAPE_VERTEX_STRIDE; - { - ShapeData& shapeData = _shapes[Tetrahedron]; - size_t vertexCount = 4; - VertexVector vertices; - { - VertexVector originalVertices = tetrahedronVertices(); - 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(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); - } - + setupFlatShape(_shapes[Tetrahedron], tetrahedron(), _shapeVertices, _shapeIndices); + // Icosahedron + setupFlatShape(_shapes[Icosahedron], icosahedron(), _shapeVertices, _shapeIndices); + // Octahedron + setupFlatShape(_shapes[Octahedron], octahedron(), _shapeVertices, _shapeIndices); + // Dodecahedron + setupFlatShape(_shapes[Dodecahedron], dodecahedron(), _shapeVertices, _shapeIndices); + // Sphere // FIXME this uses way more vertices than required. Should find a way to calculate the indices // using shared vertices for better vertex caching - startingIndex = _shapeVertices->getSize() / SHAPE_VERTEX_STRIDE; - { - ShapeData& shapeData = _shapes[Sphere]; - VertexVector vertices; - 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(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); - } + Solid<3> sphere = icosahedron(); + sphere = tesselate(sphere, ICOSAHEDRON_TO_SPHERE_TESSELATION_COUNT); + sphere.fitDimension(1.0f); + setupSmoothShape(_shapes[Sphere], sphere, _shapeVertices, _shapeIndices); // Line - startingIndex = _shapeVertices->getSize() / SHAPE_VERTEX_STRIDE; { + Index baseVertex = (Index)(_shapeVertices->getSize() / SHAPE_VERTEX_STRIDE); ShapeData& shapeData = _shapes[Line]; shapeData.setupVertices(_shapeVertices, VertexVector{ vec3(-0.5, 0, 0), vec3(-0.5f, 0, 0), @@ -460,9 +482,8 @@ void GeometryCache::buildShapes() { }); IndexVector wireIndices; // Only two indices - wireIndices.push_back(0 + (uint16_t)startingIndex); - wireIndices.push_back(1 + (uint16_t)startingIndex); - + wireIndices.push_back(0 + baseVertex); + wireIndices.push_back(1 + baseVertex); shapeData.setupIndices(_shapeIndices, IndexVector(), wireIndices); } diff --git a/libraries/render-utils/src/GeometryCache.h b/libraries/render-utils/src/GeometryCache.h index 7fa543abe2..a2f79de029 100644 --- a/libraries/render-utils/src/GeometryCache.h +++ b/libraries/render-utils/src/GeometryCache.h @@ -121,8 +121,8 @@ inline uint qHash(const Vec4PairVec4Pair& v, uint seed) { seed); } +using IndexVector = std::vector<uint32_t>; using VertexVector = std::vector<glm::vec3>; -using IndexVector = std::vector<uint16_t>; /// Stores cached geometry. class GeometryCache : public Dependency { @@ -137,7 +137,7 @@ public: Cube, Sphere, Tetrahedron, - Octahetron, + Octahedron, Dodecahedron, Icosahedron, Torus, diff --git a/scripts/system/html/entityProperties.html b/scripts/system/html/entityProperties.html index 4a3b5a14a4..c611fc5b38 100644 --- a/scripts/system/html/entityProperties.html +++ b/scripts/system/html/entityProperties.html @@ -1368,10 +1368,12 @@ <div class="shape-group shape-section property dropdown"> <label for="property-shape">Shape</label> <select name="SelectShape" id="property-shape"> - <option value="Cube">Cube</option> + <option value="Cube">Box</option> <option value="Sphere">Sphere</option> - <option value="Icosahedron">Icosahedron</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"> diff --git a/tests/gpu-test/CMakeLists.txt b/tests/gpu-test/CMakeLists.txt index 4fc6143ff5..21ae9c5a99 100644 --- a/tests/gpu-test/CMakeLists.txt +++ b/tests/gpu-test/CMakeLists.txt @@ -4,4 +4,6 @@ AUTOSCRIBE_SHADER_LIB(gpu model render-utils) setup_hifi_project(Quick Gui OpenGL Script Widgets) 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 ) -package_libraries_for_deployment() \ No newline at end of file +package_libraries_for_deployment() + +target_nsight() diff --git a/tests/gpu-test/src/TestHelpers.cpp b/tests/gpu-test/src/TestHelpers.cpp new file mode 100644 index 0000000000..75586da904 --- /dev/null +++ b/tests/gpu-test/src/TestHelpers.cpp @@ -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; +} diff --git a/tests/gpu-test/src/TestHelpers.h b/tests/gpu-test/src/TestHelpers.h new file mode 100644 index 0000000000..fd8989f628 --- /dev/null +++ b/tests/gpu-test/src/TestHelpers.h @@ -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); + diff --git a/tests/gpu-test/src/TestInstancedShapes.cpp b/tests/gpu-test/src/TestInstancedShapes.cpp new file mode 100644 index 0000000000..6a98ee58b9 --- /dev/null +++ b/tests/gpu-test/src/TestInstancedShapes.cpp @@ -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); + //} + } +} + diff --git a/tests/gpu-test/src/TestInstancedShapes.h b/tests/gpu-test/src/TestInstancedShapes.h new file mode 100644 index 0000000000..b509a13e60 --- /dev/null +++ b/tests/gpu-test/src/TestInstancedShapes.h @@ -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; +}; + diff --git a/tests/gpu-test/src/TestShapes.cpp b/tests/gpu-test/src/TestShapes.cpp new file mode 100644 index 0000000000..253d89cf61 --- /dev/null +++ b/tests/gpu-test/src/TestShapes.cpp @@ -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); +} + + + diff --git a/tests/gpu-test/src/TestShapes.h b/tests/gpu-test/src/TestShapes.h new file mode 100644 index 0000000000..606d3a45f7 --- /dev/null +++ b/tests/gpu-test/src/TestShapes.h @@ -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; +}; + diff --git a/tests/gpu-test/src/TestWindow.cpp b/tests/gpu-test/src/TestWindow.cpp new file mode 100644 index 0000000000..4fe25e989d --- /dev/null +++ b/tests/gpu-test/src/TestWindow.cpp @@ -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); +} diff --git a/tests/gpu-test/src/TestWindow.h b/tests/gpu-test/src/TestWindow.h new file mode 100644 index 0000000000..b7f8df48f5 --- /dev/null +++ b/tests/gpu-test/src/TestWindow.h @@ -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; +}; + diff --git a/tests/gpu-test/src/main.cpp b/tests/gpu-test/src/main.cpp index b80539b33a..e672fe3c86 100644 --- a/tests/gpu-test/src/main.cpp +++ b/tests/gpu-test/src/main.cpp @@ -42,238 +42,64 @@ #include <GeometryCache.h> #include <DeferredLightingEffect.h> +#include <FramebufferCache.h> #include <TextureCache.h> +#include <PerfStat.h> +#include <PathUtils.h> +#include <RenderArgs.h> +#include <ViewFrustum.h> -#include "unlit_frag.h" -#include "unlit_vert.h" +#include <gpu/Pipeline.h> +#include <gpu/Context.h> -class RateCounter { - std::vector<float> times; - QElapsedTimer timer; -public: - RateCounter() { - timer.start(); - } +#include <render/Engine.h> +#include <render/Scene.h> +#include <render/CullTask.h> +#include <render/SortTask.h> +#include <render/DrawTask.h> +#include <render/DrawStatus.h> +#include <render/DrawSceneOctree.h> +#include <render/CullTask.h> - void reset() { - times.clear(); - } +#include "TestWindow.h" +#include "TestInstancedShapes.h" +#include "TestShapes.h" - unsigned int count() const { - 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); +using namespace render; -const char* VERTEX_SHADER = R"SHADER( - -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"; +using TestBuilder = std::function<GpuTestBase*()>; +using TestBuilders = std::list<TestBuilder>; -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; -} +#define INTERACTIVE -float getSeconds(quint64 start = 0) { - auto usecs = usecTimestampNow() - start; - auto msecs = usecs / USECS_PER_MSEC; - float seconds = (float)msecs / MSECS_PER_SECOND; - return seconds; -} - -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; +class MyTestWindow : public TestWindow { + using Parent = TestWindow; + TestBuilders _testBuilders; + GpuTestBase* _currentTest { nullptr }; + size_t _currentTestId { 0 }; + size_t _currentMaxTests { 0 }; glm::mat4 _camera; + QTime _time; -protected: - void renderText(); - -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)); - + void initGl() override { + Parent::initGl(); +#ifdef INTERACTIVE _time.start(); - } - - virtual ~QTestWindow() { +#endif + updateCamera(); + _testBuilders = TestBuilders({ + //[this] { return new TestFbx(_shapePlumber); }, + [] { return new TestShapes(); }, + }); } 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 up { 0.0f, 1.0f, 0.0f }; @@ -283,263 +109,64 @@ public: static const vec3 camera_focus(0); static const vec3 camera_up(0, 1, 0); _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) { - auto geometryCache = DependencyManager::get<GeometryCache>(); - // Render grid on xz plane (not the optimal way to do things, but w/e) - // Note: GeometryCache::renderGrid will *not* work, as it is apparenly unaffected by batch rotations and renders xy only - 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); - } + while ((!_currentTest || (_currentTestId >= _currentMaxTests)) && !_testBuilders.empty()) { + if (_currentTest) { + delete _currentTest; + _currentTest = nullptr; + } - { - glm::mat4 transform = glm::mat4_cast(quat(vec3(0, PI / 2.0f, 0))); - transform = glm::translate(transform, vec3(0, -1, -50 + i)); - transform = glm::scale(transform, vec3(100, 1, 1)); - transforms.push_back(transform); - colorBuffer->append(compactColor2); - } + _currentTest = _testBuilders.front()(); + _testBuilders.pop_front(); + + if (_currentTest) { + _currentMaxTests = _currentTest->getTestCount(); + _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) { - auto geometryCache = DependencyManager::get<GeometryCache>(); - 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) { + if (!_currentTest && _testBuilders.empty()) { + qApp->quit(); 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); - //drawSimpleShapes(batch); - //drawCenterShape(batch); - drawTerrain(batch); - - _context->render(batch); - _qGlContext.swapBuffers(this); - - fps.increment(); - if (fps.elapsed() >= 0.5f) { - qDebug() << "FPS: " << fps.rate(); - fps.reset(); + // Tests might need to wait for resources to download + if (!_currentTest->isReady()) { + return; } - } - - void makeCurrent() { - _qGlContext.makeCurrent(this); - } -protected: - void resizeEvent(QResizeEvent* ev) override { - 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); - } + gpu::doInBatch(_renderArgs->_context, [&](gpu::Batch& batch) { + batch.setViewTransform(_camera); + _renderArgs->_batch = &batch; + _currentTest->renderTest(_currentTestId, _renderArgs); + _renderArgs->_batch = nullptr; + }); + +#ifdef INTERACTIVE + +#else + // TODO Capture the current rendered framebuffer and save + // Increment the test ID + ++_currentTestId; +#endif + } }; + int main(int argc, char** argv) { QGuiApplication app(argc, argv); - QTestWindow window; - auto timer = new QTimer(&app); - timer->setInterval(0); - app.connect(timer, &QTimer::timeout, &app, [&] { - window.draw(); - }); - timer->start(); + MyTestWindow window; app.exec(); return 0; } -#include "main.moc" - From 099a675a186e4f91c88829d806ea38963bba5604 Mon Sep 17 00:00:00 2001 From: David Rowe <david@ctrlaltstudio.com> Date: Tue, 24 May 2016 08:03:29 +1200 Subject: [PATCH 33/35] Remove extra logging --- interface/resources/qml/dialogs/FileDialog.qml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/interface/resources/qml/dialogs/FileDialog.qml b/interface/resources/qml/dialogs/FileDialog.qml index b00ee76b5e..5cd972a38f 100644 --- a/interface/resources/qml/dialogs/FileDialog.qml +++ b/interface/resources/qml/dialogs/FileDialog.qml @@ -130,8 +130,6 @@ ModalWindow { choices = [], i, length; - console.log("####### folder parts: " + JSON.stringify(folders)); - if (folders[folders.length - 1] === "") { folders.pop(); } @@ -160,9 +158,6 @@ ModalWindow { onLastValidFolderChanged: { var folder = d.capitalizeDrive(lastValidFolder); - - console.log("####### lastValidFolder: " + folder); - calculatePathChoices(folder); } From 7dabce9cff02f74db3a9b737ca98b06708d57580 Mon Sep 17 00:00:00 2001 From: Zach Pomerantz <zach@highfidelity.io> Date: Mon, 23 May 2016 19:04:35 -0700 Subject: [PATCH 34/35] Check throttle before idling/painting --- interface/src/Application.cpp | 45 +++++++++++++++++++---------------- interface/src/Application.h | 3 ++- 2 files changed, 26 insertions(+), 22 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index b0e4880011..7d1610c78e 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -174,7 +174,6 @@ static const QString FBX_EXTENSION = ".fbx"; static const QString OBJ_EXTENSION = ".obj"; 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_LEFT_PADDING = 10; 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())); // 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>(); connect(&locationUpdateTimer, &QTimer::timeout, discoverabilityManager.data(), &DiscoverabilityManager::updateLocation); @@ -1828,9 +1827,9 @@ bool Application::event(QEvent* event) { // Presentation/painting logic // TODO: Decouple presentation and painting loops - static bool isPainting = false; + static bool isPaintingThrottled = false; if ((int)event->type() == (int)Present) { - if (isPainting) { + if (isPaintingThrottled) { // If painting (triggered by presentation) is hogging the main thread, // 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 @@ -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). removePostedEvents(this, Present); postEvent(this, new QEvent(static_cast<QEvent::Type>(Present)), Qt::LowEventPriority); - isPainting = false; + isPaintingThrottled = false; return true; } - idle(); - - postEvent(this, new QEvent(static_cast<QEvent::Type>(Paint)), Qt::HighEventPriority); - isPainting = true; + float nsecsElapsed = (float)_lastTimeUpdated.nsecsElapsed(); + if (shouldPaint(nsecsElapsed)) { + _lastTimeUpdated.start(); + idle(nsecsElapsed); + postEvent(this, new QEvent(static_cast<QEvent::Type>(Paint)), Qt::HighEventPriority); + } + isPaintingThrottled = true; return true; } else if ((int)event->type() == (int)Paint) { @@ -1855,7 +1857,7 @@ bool Application::event(QEvent* event) { paintGL(); - isPainting = false; + isPaintingThrottled = false; return true; } @@ -2639,10 +2641,9 @@ bool Application::acceptSnapshot(const QString& urlString) { static uint32_t _renderedFrameIndex { INVALID_FRAME }; -void Application::idle() { - // idle is called on a queued connection, so make sure we should be here. - if (_inPaint || _aboutToQuit) { - return; +bool Application::shouldPaint(float nsecsElapsed) { + if (_aboutToQuit) { + return false; } auto displayPlugin = getActiveDisplayPlugin(); @@ -2661,16 +2662,21 @@ void Application::idle() { } #endif - float msecondsSinceLastUpdate = (float)_lastTimeUpdated.nsecsElapsed() / NSECS_PER_USEC / USECS_PER_MSEC; + float msecondsSinceLastUpdate = nsecsElapsed / NSECS_PER_USEC / USECS_PER_MSEC; // Throttle if requested if (displayPlugin->isThrottled() && (msecondsSinceLastUpdate < THROTTLED_SIM_FRAME_PERIOD_MS)) { - return; + return false; } // Sync up the _renderedFrameIndex _renderedFrameIndex = displayPlugin->presentCount(); + return true; +} + +void Application::idle(float nsecsElapsed) { + // Update the deadlock watchdog updateHeartbeat(); @@ -2687,7 +2693,7 @@ void Application::idle() { 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 (_keyboardDeviceHasFocus && offscreenUi && offscreenUi->getWindow()->activeFocusItem() != offscreenUi->getRootItem()) { @@ -2697,9 +2703,6 @@ void Application::idle() { _keyboardDeviceHasFocus = true; } - // We're going to execute idle processing, so restart the last idle timer - _lastTimeUpdated.start(); - checkChangeCursor(); Stats::getInstance()->updateStats(); @@ -2926,7 +2929,7 @@ void Application::loadSettings() { } void Application::saveSettings() const { - sessionRunTime.set(_sessionRunTimer.elapsed() / MSECS_PER_SEC); + sessionRunTime.set(_sessionRunTimer.elapsed() / MSECS_PER_SECOND); DependencyManager::get<AudioClient>()->saveSettings(); DependencyManager::get<LODManager>()->saveSettings(); diff --git a/interface/src/Application.h b/interface/src/Application.h index 28dbcead47..11a591776e 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -329,7 +329,8 @@ private: void cleanupBeforeQuit(); - void idle(); + bool shouldPaint(float nsecsElapsed); + void idle(float nsecsElapsed); void update(float deltaTime); // Various helper functions called during update() From 3391430f0a2f061c95a9bdf7f74bbfdbc0ab62ec Mon Sep 17 00:00:00 2001 From: David Rowe <david@ctrlaltstudio.com> Date: Tue, 24 May 2016 20:28:49 +1200 Subject: [PATCH 35/35] Tidy zone flying and ghosting entities editor options Move options to top of section from under "skybox" subsection Fix capitalization of labels --- scripts/system/html/entityProperties.html | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/scripts/system/html/entityProperties.html b/scripts/system/html/entityProperties.html index efe7e6cc65..67d168481b 100644 --- a/scripts/system/html/entityProperties.html +++ b/scripts/system/html/entityProperties.html @@ -1717,6 +1717,14 @@ <input type="checkbox" id="property-zone-stage-sun-model-enabled"> <label for="property-zone-stage-sun-model-enabled">Enable stage sun model</label> </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"> <label>Key Light</label> @@ -1801,15 +1809,6 @@ <input type="text" id="property-zone-skybox-url"> </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"> <label>Web</label><span>M</span>