diff --git a/.eslintrc.js b/.eslintrc.js
index 54ff0a1268..5667a04984 100644
--- a/.eslintrc.js
+++ b/.eslintrc.js
@@ -68,7 +68,7 @@ module.exports = {
"eqeqeq": ["error", "always"],
"indent": ["error", 4, { "SwitchCase": 1 }],
"keyword-spacing": ["error", { "before": true, "after": true }],
- "max-len": ["error", 192, 4],
+ "max-len": ["error", 128, 4],
"new-cap": ["error"],
"no-floating-decimal": ["error"],
//"no-magic-numbers": ["error", { "ignore": [0, 1], "ignoreArrayIndexes": true }],
diff --git a/.gitignore b/.gitignore
index 072e6001da..c1eef3817f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -64,6 +64,10 @@ gvr-interface/libs/*
# ignore files for various dev environments
TAGS
*.sw[po]
+*.qmlc
+
+# ignore QML compilation output
+*.qmlc
# ignore node files for the console
node_modules
diff --git a/assignment-client/src/AssignmentClientMonitor.cpp b/assignment-client/src/AssignmentClientMonitor.cpp
index 5539d6a0bb..1868ccfafe 100644
--- a/assignment-client/src/AssignmentClientMonitor.cpp
+++ b/assignment-client/src/AssignmentClientMonitor.cpp
@@ -28,6 +28,10 @@
const QString ASSIGNMENT_CLIENT_MONITOR_TARGET_NAME = "assignment-client-monitor";
const int WAIT_FOR_CHILD_MSECS = 1000;
+#ifdef Q_OS_WIN
+HANDLE PROCESS_GROUP = createProcessGroup();
+#endif
+
AssignmentClientMonitor::AssignmentClientMonitor(const unsigned int numAssignmentClientForks,
const unsigned int minAssignmentClientForks,
const unsigned int maxAssignmentClientForks,
@@ -202,6 +206,10 @@ void AssignmentClientMonitor::spawnChildClient() {
assignmentClient->setProcessChannelMode(QProcess::ForwardedChannels);
assignmentClient->start(QCoreApplication::applicationFilePath(), _childArguments);
+#ifdef Q_OS_WIN
+ addProcessToGroup(PROCESS_GROUP, assignmentClient->processId());
+#endif
+
QString stdoutPath, stderrPath;
if (_wantsChildFileLogging) {
diff --git a/assignment-client/src/octree/OctreeSendThread.cpp b/assignment-client/src/octree/OctreeSendThread.cpp
index 89e3d403fc..3ae653307f 100644
--- a/assignment-client/src/octree/OctreeSendThread.cpp
+++ b/assignment-client/src/octree/OctreeSendThread.cpp
@@ -82,8 +82,12 @@ bool OctreeSendThread::process() {
if (auto node = _node.lock()) {
OctreeQueryNode* nodeData = static_cast(node->getLinkedData());
- // Sometimes the node data has not yet been linked, in which case we can't really do anything
- if (nodeData && !nodeData->isShuttingDown()) {
+ // If we don't have the OctreeQueryNode at all
+ // or it's uninitialized because we haven't received a query yet from the client
+ // or we don't know where we should send packets for this node
+ // or we're shutting down
+ // then we can't send an entity data packet
+ if (nodeData && nodeData->hasReceivedFirstQuery() && node->getActiveSocket() && !nodeData->isShuttingDown()) {
bool viewFrustumChanged = nodeData->updateCurrentViewFrustum();
packetDistributor(node, nodeData, viewFrustumChanged);
}
diff --git a/cmake/externals/hifiAudioCodec/CMakeLists.txt b/cmake/externals/hifiAudioCodec/CMakeLists.txt
index a30396c6fd..e3ba36a440 100644
--- a/cmake/externals/hifiAudioCodec/CMakeLists.txt
+++ b/cmake/externals/hifiAudioCodec/CMakeLists.txt
@@ -5,43 +5,41 @@ set(EXTERNAL_NAME hifiAudioCodec)
string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER)
-if (NOT ANDROID)
-
- if (WIN32 OR APPLE)
- ExternalProject_Add(
- ${EXTERNAL_NAME}
- URL http://s3.amazonaws.com/hifi-public/dependencies/codecSDK-1.zip
- URL_MD5 23ec3fe51eaa155ea159a4971856fc13
- CONFIGURE_COMMAND ""
- BUILD_COMMAND ""
- INSTALL_COMMAND ""
- LOG_DOWNLOAD 1
- )
- else ()
- ExternalProject_Add(
- ${EXTERNAL_NAME}
- URL http://s3.amazonaws.com/hifi-public/dependencies/codecSDK-linux.zip
- URL_MD5 7d37914a18aa4de971d2f45dd3043bde
- CONFIGURE_COMMAND ""
- BUILD_COMMAND ""
- INSTALL_COMMAND ""
- LOG_DOWNLOAD 1
- )
- endif()
-
- # Hide this external target (for ide users)
- set_target_properties(${EXTERNAL_NAME} PROPERTIES FOLDER "hidden/externals")
-
- ExternalProject_Get_Property(${EXTERNAL_NAME} SOURCE_DIR)
-
- set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIRS ${SOURCE_DIR}/include CACHE TYPE INTERNAL)
-
- if (WIN32)
- set(${EXTERNAL_NAME_UPPER}_LIBRARIES ${SOURCE_DIR}/Release/audio.lib CACHE TYPE INTERNAL)
- elseif(APPLE)
- set(${EXTERNAL_NAME_UPPER}_LIBRARIES ${SOURCE_DIR}/Release/libaudio.a CACHE TYPE INTERNAL)
- elseif(NOT ANDROID)
- set(${EXTERNAL_NAME_UPPER}_LIBRARIES ${SOURCE_DIR}/Release/libaudio.a CACHE TYPE INTERNAL)
- endif()
-
+if (WIN32)
+ set(DOWNLOAD_URL http://s3.amazonaws.com/hifi-public/dependencies/codecSDK-win-2.0.zip)
+ set(DOWNLOAD_MD5 9199d4dbd6b16bed736b235efe980e67)
+elseif (APPLE)
+ set(DOWNLOAD_URL http://s3.amazonaws.com/hifi-public/dependencies/codecSDK-mac-2.0.zip)
+ set(DOWNLOAD_MD5 21649881e7d5dc94f922179be96f76ba)
+elseif (ANDROID)
+ set(DOWNLOAD_URL http://s3.amazonaws.com/hifi-public/dependencies/codecSDK-android-2.0.zip)
+ set(DOWNLOAD_MD5 aef2a852600d498d58aa586668191683)
+elseif (UNIX)
+ set(DOWNLOAD_URL http://s3.amazonaws.com/hifi-public/dependencies/codecSDK-linux-2.0.zip)
+ set(DOWNLOAD_MD5 67fb7755f9bcafb98a9fceea53bc7481)
+else()
+ return()
+endif()
+
+ExternalProject_Add(
+ ${EXTERNAL_NAME}
+ URL ${DOWNLOAD_URL}
+ URL_MD5 ${DOWNLOAD_MD5}
+ CONFIGURE_COMMAND ""
+ BUILD_COMMAND ""
+ INSTALL_COMMAND ""
+ LOG_DOWNLOAD 1
+)
+
+# Hide this external target (for ide users)
+set_target_properties(${EXTERNAL_NAME} PROPERTIES FOLDER "hidden/externals")
+
+ExternalProject_Get_Property(${EXTERNAL_NAME} SOURCE_DIR)
+
+set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIRS ${SOURCE_DIR}/include CACHE TYPE INTERNAL)
+
+if (WIN32)
+ set(${EXTERNAL_NAME_UPPER}_LIBRARIES ${SOURCE_DIR}/Release/audio.lib CACHE TYPE INTERNAL)
+else()
+ set(${EXTERNAL_NAME_UPPER}_LIBRARIES ${SOURCE_DIR}/Release/libaudio.a CACHE TYPE INTERNAL)
endif()
diff --git a/domain-server/resources/web/content/index.shtml b/domain-server/resources/web/content/index.shtml
index e1ba5499b6..0e48c1eff8 100644
--- a/domain-server/resources/web/content/index.shtml
+++ b/domain-server/resources/web/content/index.shtml
@@ -19,12 +19,13 @@
Upload an entities file (e.g.: models.json.gz) to replace the content of this domain.
Note: Your domain's content will be replaced by the content you upload, but the backup files of your domain's content will not immediately be changed.
-
- If your domain has any content that you would like to re-use at a later date, save a manual backup of your models.json.gz file, which is usually stored at the following paths:
-
If your domain has any content that you would like to re-use at a later date, save a manual backup of your models.json.gz file, which is usually stored at the following paths:
- Add your High Fidelity username and any other usernames to grant administrator privileges.
-
+ Add your High Fidelity username and any other usernames to grant administrator privileges
@@ -78,7 +77,7 @@
- Who can connect to your domain?
+ Who can connect to your domain?
@@ -87,25 +86,21 @@
@@ -113,7 +108,7 @@
- Who can rez items in your domain?
+ Who can rez items in your domain?
@@ -122,33 +117,32 @@
-
+
+
+
+
-
+
@@ -188,35 +182,37 @@
-
+
+
+
+
-
+
-
+
-
-
+
+
Congratulations! You have successfully setup and configured your cloud hosted domain.
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
diff --git a/domain-server/resources/web/wizard/js/wizard.js b/domain-server/resources/web/wizard/js/wizard.js
index 1af3f305b7..57e85973f4 100644
--- a/domain-server/resources/web/wizard/js/wizard.js
+++ b/domain-server/resources/web/wizard/js/wizard.js
@@ -2,6 +2,8 @@ var Metaverse = {
accessToken: null
}
+var currentStepNumber;
+
$(document).ready(function(){
Strings.ADD_PLACE_NOT_CONNECTED_MESSAGE = "You must have an access token to query your High Fidelity places.
" +
"Please go back and connect your account.";
@@ -9,6 +11,22 @@ $(document).ready(function(){
$('#connect-account-btn').attr('href', URLs.METAVERSE_URL + "/user/tokens/new?for_domain_server=true");
$('[data-toggle="tooltip"]').tooltip();
+
+ $('.perms-link').on('click', function() {
+ var modal_body = '
';
+ modal_body += 'None - No one will have permissions. Only you and the users your have given administrator privileges to will have permissions.';
+ modal_body += 'Friends - Users who are your Friends in High Fidelity.';
+ modal_body += 'Users logged into High Fidelity - Users who are currently logged into High Fidelity.';
+ modal_body += 'Everyone - Anyone who uses High Fidelity.';
+ modal_body += '
';
+
+ dialog = bootbox.dialog({
+ title: "User definition",
+ message: modal_body,
+ closeButton: true
+ });
+ return false;
+ });
$('body').on('click', '.next-button', function() {
goToNextStep();
@@ -56,6 +74,36 @@ $(document).ready(function(){
exploreSettings();
});
+ $('input[type=radio][name=connect-radio]').change(function() {
+ var inputs = $('input[type=radio][name=rez-radio]');
+ var disabled = [];
+
+ switch (this.value) {
+ case 'none':
+ disabled = inputs.splice(1);
+ break;
+ case 'friends':
+ disabled = inputs.splice(2);
+ break;
+ case 'logged-in':
+ disabled = inputs.splice(3);
+ break;
+ case 'everyone':
+ disabled = inputs.splice(4);
+ break;
+ }
+
+ $.each(inputs, function() {
+ $(this).prop('disabled', false);
+ });
+ $.each(disabled, function() {
+ if ($(this).prop('checked')) {
+ $(inputs.last()).prop('checked', true);
+ }
+ $(this).prop('disabled', true);
+ });
+ });
+
reloadSettings(function(success) {
if (success) {
getDomainFromAPI();
@@ -73,12 +121,12 @@ $(document).ready(function(){
});
function setupWizardSteps() {
- var stepsCompleted = Settings.data.values.wizard.steps_completed;
+ currentStepNumber = Settings.data.values.wizard.steps_completed;
var steps = null;
if (Settings.data.values.wizard.cloud_domain) {
$('.desktop-only').remove();
- $('.wizard-step').find('.back-button').hide();
+ $('.wizard-step:first').find('.back-button').hide();
steps = $('.wizard-step');
$(steps).each(function(i) {
@@ -86,7 +134,7 @@ function setupWizardSteps() {
});
$('#permissions-description').html('You have been assigned administrator privileges to this domain.');
- $('#admin-description').html('Add more High Fidelity usernames to grant administrator privileges.');
+ $('#admin-description').html('Add more High Fidelity usernames');
} else {
$('.cloud-only').remove();
$('#save-permissions').text("Finish");
@@ -96,12 +144,12 @@ function setupWizardSteps() {
$(this).children(".step-title").text("Step " + (i + 1) + " of " + steps.length);
});
- if (stepsCompleted == 0) {
+ if (currentStepNumber == 0) {
$('#skip-wizard-button').show();
}
}
- var currentStep = steps[stepsCompleted];
+ var currentStep = steps[currentStepNumber];
$(currentStep).show();
}
@@ -204,7 +252,7 @@ function goToNextStep() {
currentStep.hide();
nextStep.show();
- var currentStepNumber = parseInt(Settings.data.values.wizard.steps_completed) + 1;
+ currentStepNumber += 1;
postSettings({
"wizard": {
@@ -233,7 +281,7 @@ function goToPreviousStep() {
currentStep.hide();
previousStep.show();
- var currentStepNumber = parseInt(Settings.data.values.wizard.steps_completed) - 1;
+ currentStepNumber -= 1;
postSettings({
"wizard": {
@@ -439,7 +487,7 @@ function saveUsernamePassword() {
return;
}
- var currentStepNumber = parseInt(Settings.data.values.wizard.steps_completed) + 1;
+ currentStepNumber += 1;
var formJSON = {
"security": {
diff --git a/interface/resources/fonts/hifi-glyphs.ttf b/interface/resources/fonts/hifi-glyphs.ttf
index 3db48602b1..4cc5a0fe4f 100644
Binary files a/interface/resources/fonts/hifi-glyphs.ttf and b/interface/resources/fonts/hifi-glyphs.ttf differ
diff --git a/interface/resources/images/lowerKeyboard.png b/interface/resources/images/lowerKeyboard.png
new file mode 100644
index 0000000000..d379b028ab
Binary files /dev/null and b/interface/resources/images/lowerKeyboard.png differ
diff --git a/interface/resources/qml/AddressBarDialog.qml b/interface/resources/qml/AddressBarDialog.qml
deleted file mode 100644
index 60d2bacc62..0000000000
--- a/interface/resources/qml/AddressBarDialog.qml
+++ /dev/null
@@ -1,532 +0,0 @@
-//
-// AddressBarDialog.qml
-//
-// Created by Austin Davis on 2015/04/14
-// 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 Hifi 1.0
-import QtQuick 2.4
-import "controls"
-import "styles"
-import "windows"
-import "hifi"
-import "hifi/toolbars"
-import "styles-uit" as HifiStyles
-import "controls-uit" as HifiControls
-
-Window {
- id: root
- HifiConstants { id: hifi }
- HifiStyles.HifiConstants { id: hifiStyleConstants }
-
- objectName: "AddressBarDialog"
- title: "Go To:"
-
- shown: false
- destroyOnHidden: false
- resizable: false
- pinnable: false;
-
- width: addressBarDialog.implicitWidth
- height: addressBarDialog.implicitHeight
- property int gap: 14
-
- onShownChanged: {
- addressBarDialog.keyboardEnabled = HMD.active;
- addressBarDialog.observeShownChanged(shown);
- }
- Component.onCompleted: {
- root.parentChanged.connect(center);
- center();
- }
- Component.onDestruction: {
- root.parentChanged.disconnect(center);
- }
-
- function center() {
- // Explicitly center in order to avoid warnings at shutdown
- anchors.centerIn = parent;
- }
-
- function resetAfterTeleport() {
- storyCardFrame.shown = root.shown = false;
- }
- function goCard(targetString) {
- if (0 !== targetString.indexOf('hifi://')) {
- storyCardHTML.url = addressBarDialog.metaverseServerUrl + targetString;
- storyCardFrame.shown = true;
- return;
- }
- addressLine.text = targetString;
- toggleOrGo(true);
- clearAddressLineTimer.start();
- }
- property var allStories: [];
- property int cardWidth: 212;
- property int cardHeight: 152;
- property string metaverseBase: addressBarDialog.metaverseServerUrl + "/api/v1/";
- property bool isCursorVisible: false // Override default cursor visibility.
-
- AddressBarDialog {
- id: addressBarDialog
-
- property bool keyboardEnabled: false
- property bool keyboardRaised: false
- property bool punctuationMode: false
-
- implicitWidth: backgroundImage.width
- implicitHeight: scroll.height + gap + backgroundImage.height + (keyboardEnabled ? keyboard.height : 0);
-
- // The buttons have their button state changed on hover, so we have to manually fix them up here
- onBackEnabledChanged: backArrow.buttonState = addressBarDialog.backEnabled ? 1 : 0;
- onForwardEnabledChanged: forwardArrow.buttonState = addressBarDialog.forwardEnabled ? 1 : 0;
- onReceivedHifiSchemeURL: resetAfterTeleport();
-
- // Update location after using back and forward buttons.
- onHostChanged: updateLocationTextTimer.start();
-
- ListModel { id: suggestions }
-
- ListView {
- id: scroll
- height: cardHeight + scroll.stackedCardShadowHeight
- property int stackedCardShadowHeight: 10;
- spacing: gap;
- clip: true;
- anchors {
- left: backgroundImage.left
- right: swipe.left
- bottom: backgroundImage.top
- }
- model: suggestions;
- orientation: ListView.Horizontal;
- delegate: Card {
- width: cardWidth;
- height: cardHeight;
- goFunction: goCard;
- userName: model.username;
- placeName: model.place_name;
- hifiUrl: model.place_name + model.path;
- thumbnail: model.thumbnail_url;
- imageUrl: model.image_url;
- action: model.action;
- timestamp: model.created_at;
- onlineUsers: model.online_users;
- storyId: model.metaverseId;
- drillDownToPlace: model.drillDownToPlace;
- shadowHeight: scroll.stackedCardShadowHeight;
- hoverThunk: function () { ListView.view.currentIndex = index; }
- unhoverThunk: function () { ListView.view.currentIndex = -1; }
- }
- highlightMoveDuration: -1;
- highlightMoveVelocity: -1;
- highlight: Rectangle { color: "transparent"; border.width: 4; border.color: hifiStyleConstants.colors.blueHighlight; z: 1; }
- }
- Image { // Just a visual indicator that the user can swipe the cards over to see more.
- id: swipe;
- source: "../images/swipe-chevron.svg";
- width: 72;
- visible: suggestions.count > 3;
- anchors {
- right: backgroundImage.right;
- top: scroll.top;
- }
- MouseArea {
- anchors.fill: parent
- onClicked: scroll.currentIndex = (scroll.currentIndex < 0) ? 3 : (scroll.currentIndex + 3)
- }
- }
-
- Row {
- spacing: 2 * hifi.layout.spacing;
- anchors {
- top: parent.top;
- left: parent.left;
- leftMargin: 150;
- topMargin: -30;
- }
- property var selected: allTab;
- TextButton {
- id: allTab;
- text: "ALL";
- property string includeActions: 'snapshot,concurrency';
- selected: allTab === selectedTab;
- action: tabSelect;
- }
- TextButton {
- id: placeTab;
- text: "PLACES";
- property string includeActions: 'concurrency';
- selected: placeTab === selectedTab;
- action: tabSelect;
- }
- TextButton {
- id: snapsTab;
- text: "SNAPS";
- property string includeActions: 'snapshot';
- selected: snapsTab === selectedTab;
- action: tabSelect;
- }
- }
-
- Image {
- id: backgroundImage
- source: "../images/address-bar-856.svg"
- width: 856
- height: 100
- anchors {
- bottom: parent.keyboardEnabled ? keyboard.top : parent.bottom;
- }
- property int inputAreaHeight: 70
- property int inputAreaStep: (height - inputAreaHeight) / 2
-
- ToolbarButton {
- id: homeButton
- imageURL: "../images/home.svg"
- onClicked: {
- addressBarDialog.loadHome();
- root.shown = false;
- }
- anchors {
- left: parent.left
- leftMargin: homeButton.width / 2
- verticalCenter: parent.verticalCenter
- }
- }
-
- ToolbarButton {
- id: backArrow;
- imageURL: "../images/backward.svg";
- onClicked: addressBarDialog.loadBack();
- anchors {
- left: homeButton.right
- verticalCenter: parent.verticalCenter
- }
- }
- ToolbarButton {
- id: forwardArrow;
- imageURL: "../images/forward.svg";
- onClicked: addressBarDialog.loadForward();
- anchors {
- left: backArrow.right
- verticalCenter: parent.verticalCenter
- }
- }
-
- HifiStyles.RalewayLight {
- id: notice;
- font.pixelSize: hifi.fonts.pixelSize * 0.50;
- anchors {
- top: parent.top
- topMargin: parent.inputAreaStep + 12
- left: addressLine.left
- right: addressLine.right
- }
- }
- HifiStyles.FiraSansRegular {
- id: location;
- font.pixelSize: addressLine.font.pixelSize;
- color: "gray";
- clip: true;
- anchors.fill: addressLine;
- visible: addressLine.text.length === 0
- }
- TextInput {
- id: addressLine
- focus: true
- anchors {
- top: parent.top
- bottom: parent.bottom
- left: forwardArrow.right
- right: parent.right
- leftMargin: forwardArrow.width
- rightMargin: forwardArrow.width / 2
- topMargin: parent.inputAreaStep + (2 * hifi.layout.spacing)
- bottomMargin: parent.inputAreaStep
- }
- font.pixelSize: hifi.fonts.pixelSize * 0.75
- cursorVisible: false
- onTextChanged: {
- filterChoicesByText();
- updateLocationText(text.length > 0);
- if (!isCursorVisible && text.length > 0) {
- isCursorVisible = true;
- cursorVisible = true;
- }
- }
- onActiveFocusChanged: {
- cursorVisible = isCursorVisible && focus;
- }
- MouseArea {
- // If user clicks in address bar show cursor to indicate ability to enter address.
- anchors.fill: parent
- onClicked: {
- isCursorVisible = true;
- parent.cursorVisible = true;
- parent.forceActiveFocus();
- }
- }
- }
- }
-
- Timer {
- // Delay updating location text a bit to avoid flicker of content and so that connection status is valid.
- id: updateLocationTextTimer
- running: false
- interval: 500 // ms
- repeat: false
- onTriggered: updateLocationText(false);
- }
-
- Timer {
- // Delay clearing address line so as to avoid flicker of "not connected" being displayed after entering an address.
- id: clearAddressLineTimer
- running: false
- interval: 100 // ms
- repeat: false
- onTriggered: {
- addressLine.text = "";
- isCursorVisible = false;
- }
- }
-
- Window {
- width: 938
- height: 625
- HifiControls.WebView {
- anchors.fill: parent;
- id: storyCardHTML;
- }
- id: storyCardFrame;
-
- shown: false;
- destroyOnCloseButton: false;
- pinnable: false;
-
- anchors {
- verticalCenter: backgroundImage.verticalCenter;
- horizontalCenter: scroll.horizontalCenter;
- }
- z: 100
- }
-
- HifiControls.Keyboard {
- id: keyboard
- raised: parent.keyboardEnabled // Ignore keyboardRaised; keep keyboard raised if enabled (i.e., in HMD).
- numeric: parent.punctuationMode
- anchors {
- bottom: parent.bottom
- left: parent.left
- right: parent.right
- }
- }
- }
-
- function getRequest(url, cb) { // cb(error, responseOfCorrectContentType) of url. General for 'get' text/html/json, but without redirects.
- // TODO: make available to other .qml.
- var request = new XMLHttpRequest();
- // QT bug: apparently doesn't handle onload. Workaround using readyState.
- request.onreadystatechange = function () {
- var READY_STATE_DONE = 4;
- var HTTP_OK = 200;
- if (request.readyState >= READY_STATE_DONE) {
- var error = (request.status !== HTTP_OK) && request.status.toString() + ':' + request.statusText,
- response = !error && request.responseText,
- contentType = !error && request.getResponseHeader('content-type');
- if (!error && contentType.indexOf('application/json') === 0) {
- try {
- response = JSON.parse(response);
- } catch (e) {
- error = e;
- }
- }
- cb(error, response);
- }
- };
- request.open("GET", url, true);
- request.send();
- }
-
- function identity(x) {
- return x;
- }
-
- function handleError(url, error, data, cb) { // cb(error) and answer truthy if needed, else falsey
- if (!error && (data.status === 'success')) {
- return;
- }
- if (!error) { // Create a message from the data
- error = data.status + ': ' + data.error;
- }
- if (typeof(error) === 'string') { // Make a proper Error object
- error = new Error(error);
- }
- error.message += ' in ' + url; // Include the url.
- cb(error);
- return true;
- }
- function resolveUrl(url) {
- return (url.indexOf('/') === 0) ? (addressBarDialog.metaverseServerUrl + url) : url;
- }
-
- function makeModelData(data) { // create a new obj from data
- // ListModel elements will only ever have those properties that are defined by the first obj that is added.
- // So here we make sure that we have all the properties we need, regardless of whether it is a place data or user story.
- var name = data.place_name,
- tags = data.tags || [data.action, data.username],
- description = data.description || "",
- thumbnail_url = data.thumbnail_url || "";
- return {
- place_name: name,
- username: data.username || "",
- path: data.path || "",
- created_at: data.created_at || "",
- action: data.action || "",
- thumbnail_url: resolveUrl(thumbnail_url),
- image_url: resolveUrl(data.details.image_url),
-
- metaverseId: (data.id || "").toString(), // Some are strings from server while others are numbers. Model objects require uniformity.
-
- tags: tags,
- description: description,
- online_users: data.details.concurrency || 0,
- drillDownToPlace: false,
-
- searchText: [name].concat(tags, description || []).join(' ').toUpperCase()
- }
- }
- function suggestable(place) {
- if (place.action === 'snapshot') {
- return true;
- }
- return (place.place_name !== AddressManager.placename); // Not our entry, but do show other entry points to current domain.
- }
- property var selectedTab: allTab;
- function tabSelect(textButton) {
- selectedTab = textButton;
- fillDestinations();
- }
- property var placeMap: ({});
- function addToSuggestions(place) {
- var collapse = allTab.selected && (place.action !== 'concurrency');
- if (collapse) {
- var existing = placeMap[place.place_name];
- if (existing) {
- existing.drillDownToPlace = true;
- return;
- }
- }
- suggestions.append(place);
- if (collapse) {
- placeMap[place.place_name] = suggestions.get(suggestions.count - 1);
- } else if (place.action === 'concurrency') {
- suggestions.get(suggestions.count - 1).drillDownToPlace = true; // Don't change raw place object (in allStories).
- }
- }
- property int requestId: 0;
- function getUserStoryPage(pageNumber, cb) { // cb(error) after all pages of domain data have been added to model
- var options = [
- 'now=' + new Date().toISOString(),
- 'include_actions=' + selectedTab.includeActions,
- 'restriction=' + (Account.isLoggedIn() ? 'open,hifi' : 'open'),
- 'require_online=true',
- 'protocol=' + encodeURIComponent(AddressManager.protocolVersion()),
- 'page=' + pageNumber
- ];
- var url = metaverseBase + 'user_stories?' + options.join('&');
- var thisRequestId = ++requestId;
- getRequest(url, function (error, data) {
- if ((thisRequestId !== requestId) || handleError(url, error, data, cb)) {
- return;
- }
- var stories = data.user_stories.map(function (story) { // explicit single-argument function
- return makeModelData(story, url);
- });
- allStories = allStories.concat(stories);
- stories.forEach(makeFilteredPlaceProcessor());
- if ((data.current_page < data.total_pages) && (data.current_page <= 10)) { // just 10 pages = 100 stories for now
- return getUserStoryPage(pageNumber + 1, cb);
- }
- cb();
- });
- }
- function makeFilteredPlaceProcessor() { // answer a function(placeData) that adds it to suggestions if it matches
- var words = addressLine.text.toUpperCase().split(/\s+/).filter(identity),
- data = allStories;
- function matches(place) {
- if (!words.length) {
- return suggestable(place);
- }
- return words.every(function (word) {
- return place.searchText.indexOf(word) >= 0;
- });
- }
- return function (place) {
- if (matches(place)) {
- addToSuggestions(place);
- }
- };
- }
- function filterChoicesByText() {
- suggestions.clear();
- placeMap = {};
- allStories.forEach(makeFilteredPlaceProcessor());
- }
-
- function fillDestinations() {
- allStories = [];
- suggestions.clear();
- placeMap = {};
- getUserStoryPage(1, function (error) {
- console.log('user stories query', error || 'ok', allStories.length);
- });
- }
-
- function updateLocationText(enteringAddress) {
- if (enteringAddress) {
- notice.text = "Go to a place, @user, path or network address";
- notice.color = hifiStyleConstants.colors.baseGrayHighlight;
- } else {
- notice.text = AddressManager.isConnected ? "Your location:" : "Not Connected";
- notice.color = AddressManager.isConnected ? hifiStyleConstants.colors.baseGrayHighlight : hifiStyleConstants.colors.redHighlight;
- // Display hostname, which includes ip address, localhost, and other non-placenames.
- location.text = (AddressManager.placename || AddressManager.hostname || '') + (AddressManager.pathname ? AddressManager.pathname.match(/\/[^\/]+/)[0] : '');
- }
- }
-
- onVisibleChanged: {
- updateLocationText(false);
- if (visible) {
- addressLine.forceActiveFocus();
- fillDestinations();
- }
- }
-
- function toggleOrGo(fromSuggestions) {
- if (addressLine.text !== "") {
- addressBarDialog.loadAddress(addressLine.text, fromSuggestions)
- }
- root.shown = false;
- }
-
- Keys.onPressed: {
- switch (event.key) {
- case Qt.Key_Escape:
- case Qt.Key_Back:
- root.shown = false
- clearAddressLineTimer.start();
- event.accepted = true
- break
- case Qt.Key_Enter:
- case Qt.Key_Return:
- toggleOrGo()
- clearAddressLineTimer.start();
- event.accepted = true
- break
- }
- }
-}
diff --git a/interface/resources/qml/controls-uit/Key.qml b/interface/resources/qml/controls-uit/Key.qml
index e54250c872..b0e965e79f 100644
--- a/interface/resources/qml/controls-uit/Key.qml
+++ b/interface/resources/qml/controls-uit/Key.qml
@@ -5,10 +5,16 @@ Item {
id: keyItem
width: 45
height: 50
+
+ property int contentPadding: 4
property string glyph: "a"
property bool toggle: false // does this button have the toggle behaivor?
property bool toggled: false // is this button currently toggled?
property alias mouseArea: mouseArea1
+ property alias fontFamily: letter.font.family;
+ property alias fontPixelSize: letter.font.pixelSize
+ property alias verticalAlignment: letter.verticalAlignment
+ property alias letterAnchors: letter.anchors
function resetToggledMode(mode) {
toggled = mode;
@@ -105,14 +111,8 @@ Item {
color: "#121212"
radius: 2
border.color: "#00000000"
- anchors.right: parent.right
- anchors.rightMargin: 4
- anchors.left: parent.left
- anchors.leftMargin: 4
- anchors.bottom: parent.bottom
- anchors.bottomMargin: 4
- anchors.top: parent.top
- anchors.topMargin: 4
+ anchors.fill: parent
+ anchors.margins: contentPadding
}
Text {
diff --git a/interface/resources/qml/controls-uit/Keyboard.qml b/interface/resources/qml/controls-uit/Keyboard.qml
index 66a61742c9..76b66178d4 100644
--- a/interface/resources/qml/controls-uit/Keyboard.qml
+++ b/interface/resources/qml/controls-uit/Keyboard.qml
@@ -8,7 +8,8 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
-import QtQuick 2.0
+import QtQuick 2.7
+import QtGraphicalEffects 1.0
import "."
Rectangle {
@@ -55,6 +56,8 @@ Rectangle {
return ">";
} else if (str === "/") {
return "?";
+ } else if (str === "-") {
+ return "_";
} else {
return str.toUpperCase(str);
}
@@ -67,6 +70,8 @@ Rectangle {
return ".";
} else if (str === "?") {
return "/";
+ } else if (str === "_") {
+ return "-";
} else {
return str.toLowerCase(str);
}
@@ -85,7 +90,7 @@ Rectangle {
onShiftModeChanged: {
forEachKey(function (key) {
- if (/[a-z]/i.test(key.glyph)) {
+ if (/[a-z-_]/i.test(key.glyph)) {
if (shiftMode) {
key.glyph = keyboardBase.toUpper(key.glyph);
} else {
@@ -112,8 +117,6 @@ Rectangle {
}
Rectangle {
- y: 0
- x: 0
height: showMirrorText ? mirrorTextHeight : 0
width: keyboardWidth
color: "#252525"
@@ -122,13 +125,18 @@ Rectangle {
TextInput {
id: mirrorText
visible: showMirrorText
- FontLoader { id: ralewaySemiBold; source: "../../fonts/Raleway-SemiBold.ttf"; }
- font.family: ralewaySemiBold.name
- font.pointSize: 13.5
+ FontLoader { id: font; source: "../../fonts/FiraSans-Regular.ttf"; }
+ font.family: font.name
+ font.pixelSize: 20
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
- color: "#FFFFFF";
- anchors.fill: parent
+ color: "#00B4EF";
+ anchors.left: parent.left
+ anchors.leftMargin: 10
+ anchors.right: parent.right
+ anchors.top: parent.top
+ anchors.bottom: parent.bottom
+
wrapMode: Text.WordWrap
readOnly: false // we need this to allow control to accept QKeyEvent
selectByMouse: false
@@ -140,16 +148,15 @@ Rectangle {
event.accepted = true;
}
}
- }
- MouseArea { // ... and we need this mouse area to prevent mirrorText from getting mouse events to ensure it will never get focus
- anchors.fill: parent
+ MouseArea { // ... and we need this mouse area to prevent mirrorText from getting mouse events to ensure it will never get focus
+ anchors.fill: parent
+ }
}
}
Rectangle {
id: keyboardRect
- x: 0
y: showMirrorText ? mirrorTextHeight : 0
width: keyboardWidth
height: raisedHeight
@@ -158,6 +165,8 @@ Rectangle {
anchors.bottom: parent.bottom
anchors.bottomMargin: 0
+ FontLoader { id: hiFiGlyphs; source: pathToFonts + "fonts/hifi-glyphs.ttf"; }
+
Column {
id: columnAlpha
width: keyboardWidth
@@ -221,7 +230,7 @@ Rectangle {
Key { width: 43; glyph: "b"; }
Key { width: 43; glyph: "n"; }
Key { width: 43; glyph: "m"; }
- Key { width: 43; glyph: "_"; }
+ Key { width: 43; glyph: "-"; }
Key { width: 43; glyph: "/"; }
Key { width: 43; glyph: "?"; }
}
@@ -240,8 +249,13 @@ Rectangle {
Key { width: 231; glyph: " "; }
Key { width: 43; glyph: ","; }
Key { width: 43; glyph: "."; }
- Key { width: 43; glyph: "\u276C"; }
- Key { width: 43; glyph: "\u276D"; }
+ Key {
+ fontFamily: hiFiGlyphs.name;
+ fontPixelSize: 48;
+ letterAnchors.topMargin: -4;
+ verticalAlignment: Text.AlignVCenter;
+ width: 86; glyph: "\ue02b";
+ }
}
}
@@ -328,8 +342,13 @@ Rectangle {
Key { width: 231; glyph: " "; }
Key { width: 43; glyph: ","; }
Key { width: 43; glyph: "."; }
- Key { width: 43; glyph: "\u276C"; }
- Key { width: 43; glyph: "\u276D"; }
+ Key {
+ fontFamily: hiFiGlyphs.name;
+ fontPixelSize: 48;
+ letterAnchors.topMargin: -4;
+ verticalAlignment: Text.AlignVCenter;
+ width: 86; glyph: "\ue02b";
+ }
}
}
}
diff --git a/interface/resources/qml/controls/FlickableWebViewCore.qml b/interface/resources/qml/controls/FlickableWebViewCore.qml
index cbc4d19334..29944781c1 100644
--- a/interface/resources/qml/controls/FlickableWebViewCore.qml
+++ b/interface/resources/qml/controls/FlickableWebViewCore.qml
@@ -27,6 +27,12 @@ Item {
id: hifi
}
+ function unfocus() {
+ webViewCore.runJavaScript("if (document.activeElement) document.activeElement.blur();", function(result) {
+ console.log('unfocus completed: ', result);
+ });
+ }
+
function onLoadingChanged(loadRequest) {
if (WebEngineView.LoadStartedStatus === loadRequest.status) {
diff --git a/interface/resources/qml/controls/TabletWebScreen.qml b/interface/resources/qml/controls/TabletWebScreen.qml
index e06ff51569..501e321f0d 100644
--- a/interface/resources/qml/controls/TabletWebScreen.qml
+++ b/interface/resources/qml/controls/TabletWebScreen.qml
@@ -10,6 +10,11 @@ Item {
property alias urlTag: webroot.urlTag
property bool keyboardEnabled: true // FIXME - Keyboard HMD only: Default to false
property bool keyboardRaised: false
+ onKeyboardRaisedChanged: {
+ if(!keyboardRaised) {
+ webroot.unfocus();
+ }
+ }
property bool punctuationMode: false
// FIXME - Keyboard HMD only: Make Interface either set keyboardRaised property directly in OffscreenQmlSurface
diff --git a/interface/resources/qml/controls/TabletWebView.qml b/interface/resources/qml/controls/TabletWebView.qml
index 8cd61bc90b..477422cfa1 100644
--- a/interface/resources/qml/controls/TabletWebView.qml
+++ b/interface/resources/qml/controls/TabletWebView.qml
@@ -15,6 +15,11 @@ Item {
property string scriptURL
property bool keyboardEnabled: false
property bool keyboardRaised: false
+ onKeyboardRaisedChanged: {
+ if(!keyboardRaised) {
+ webroot.unfocus();
+ }
+ }
property bool punctuationMode: false
property bool passwordField: false
property bool isDesktop: false
diff --git a/interface/resources/qml/controls/WebView.qml b/interface/resources/qml/controls/WebView.qml
index 923c8f3fa1..931c64e1ef 100644
--- a/interface/resources/qml/controls/WebView.qml
+++ b/interface/resources/qml/controls/WebView.qml
@@ -12,6 +12,11 @@ Item {
property alias urlTag: webroot.urlTag
property bool keyboardEnabled: true // FIXME - Keyboard HMD only: Default to false
property bool keyboardRaised: false
+ onKeyboardRaisedChanged: {
+ if(!keyboardRaised) {
+ webroot.unfocus();
+ }
+ }
property bool punctuationMode: false
property bool passwordField: false
property alias flickable: webroot.interactive
diff --git a/interface/resources/qml/hifi/commerce/checkout/Checkout.qml b/interface/resources/qml/hifi/commerce/checkout/Checkout.qml
index dfe0c319e5..6c4e020694 100644
--- a/interface/resources/qml/hifi/commerce/checkout/Checkout.qml
+++ b/interface/resources/qml/hifi/commerce/checkout/Checkout.qml
@@ -596,9 +596,7 @@ Rectangle {
anchors.right: parent.right;
text: root.isWearable ? "Wear It" : "Rez It"
onClicked: {
- if (urlHandler.canHandleUrl(root.itemHref)) {
- urlHandler.handleUrl(root.itemHref);
- }
+ sendToScript({method: 'checkout_rezClicked', itemHref: root.itemHref, isWearable: root.isWearable});
rezzedNotifContainer.visible = true;
rezzedNotifContainerTimer.start();
}
diff --git a/interface/resources/qml/hifi/commerce/inspectionCertificate/InspectionCertificate.qml b/interface/resources/qml/hifi/commerce/inspectionCertificate/InspectionCertificate.qml
index b6c29a1fad..14ed9ece67 100644
--- a/interface/resources/qml/hifi/commerce/inspectionCertificate/InspectionCertificate.qml
+++ b/interface/resources/qml/hifi/commerce/inspectionCertificate/InspectionCertificate.qml
@@ -113,21 +113,6 @@ Rectangle {
}
}
- onVisibleChanged: {
- if (!visible) {
- titleBarText.text = "Certificate";
- popText.text = "PROOF OF PURCHASE";
- root.certificateId = "";
- root.itemName = "--";
- root.itemOwner = "--";
- root.itemEdition = "--";
- root.dateOfPurchase = "--";
- root.marketplaceUrl = "";
- root.isMyCert = false;
- errorText.text = "";
- }
- }
-
// This object is always used in a popup.
// This MouseArea is used to prevent a user from being
// able to click on a button/mouseArea underneath the popup.
@@ -420,6 +405,18 @@ Rectangle {
case 'inspectionCertificate_setCertificateId':
root.certificateId = message.certificateId;
break;
+ case 'inspectionCertificate_resetCert':
+ titleBarText.text = "Certificate";
+ popText.text = "PROOF OF PURCHASE";
+ root.certificateId = "";
+ root.itemName = "--";
+ root.itemOwner = "--";
+ root.itemEdition = "--";
+ root.dateOfPurchase = "--";
+ root.marketplaceUrl = "";
+ root.isMyCert = false;
+ errorText.text = "";
+ break;
default:
console.log('Unrecognized message from marketplaces.js:', JSON.stringify(message));
}
diff --git a/interface/resources/qml/hifi/commerce/purchases/PurchasedItem.qml b/interface/resources/qml/hifi/commerce/purchases/PurchasedItem.qml
index fb42865ba4..15ebada0c4 100644
--- a/interface/resources/qml/hifi/commerce/purchases/PurchasedItem.qml
+++ b/interface/resources/qml/hifi/commerce/purchases/PurchasedItem.qml
@@ -346,9 +346,7 @@ Item {
enabled: (root.canRezCertifiedItems || root.isWearable) && root.purchaseStatus !== "invalidated";
onClicked: {
- if (urlHandler.canHandleUrl(root.itemHref)) {
- urlHandler.handleUrl(root.itemHref);
- }
+ sendToPurchases({method: 'purchases_rezClicked', itemHref: root.itemHref, isWearable: root.isWearable});
rezzedNotifContainer.visible = true;
rezzedNotifContainerTimer.start();
}
diff --git a/interface/resources/qml/hifi/commerce/purchases/Purchases.qml b/interface/resources/qml/hifi/commerce/purchases/Purchases.qml
index f292f9603e..1ea488ac98 100644
--- a/interface/resources/qml/hifi/commerce/purchases/Purchases.qml
+++ b/interface/resources/qml/hifi/commerce/purchases/Purchases.qml
@@ -442,6 +442,8 @@ Rectangle {
onSendToPurchases: {
if (msg.method === 'purchases_itemInfoClicked') {
sendToScript({method: 'purchases_itemInfoClicked', itemId: itemId});
+ } else if (msg.method === "purchases_rezClicked") {
+ sendToScript({method: 'purchases_rezClicked', itemHref: itemHref, isWearable: isWearable});
} else if (msg.method === 'purchases_itemCertificateClicked') {
inspectionCertificate.visible = true;
inspectionCertificate.isLightbox = true;
diff --git a/interface/resources/qml/hifi/commerce/wallet/WalletHome.qml b/interface/resources/qml/hifi/commerce/wallet/WalletHome.qml
index 1fe0dcc58b..fd7ce0fdfd 100644
--- a/interface/resources/qml/hifi/commerce/wallet/WalletHome.qml
+++ b/interface/resources/qml/hifi/commerce/wallet/WalletHome.qml
@@ -196,7 +196,38 @@ Item {
anchors.bottom: parent.bottom;
anchors.left: parent.left;
anchors.right: parent.right;
- anchors.rightMargin: 24;
+
+ Item {
+ visible: transactionHistoryModel.count === 0 && root.historyReceived;
+ anchors.centerIn: parent;
+ width: parent.width - 12;
+ height: parent.height;
+
+ HifiControlsUit.Separator {
+ colorScheme: 1;
+ anchors.left: parent.left;
+ anchors.right: parent.right;
+ anchors.top: parent.top;
+ }
+
+ RalewayRegular {
+ id: noActivityText;
+ text: "The Wallet app is in closed Beta.
To request entry and receive free HFC, please contact " +
+ "info@highfidelity.com with your High Fidelity account username and the email address registered to that account.";
+ // Text size
+ size: 24;
+ // Style
+ color: hifi.colors.blueAccent;
+ anchors.left: parent.left;
+ anchors.leftMargin: 12;
+ anchors.right: parent.right;
+ anchors.rightMargin: 12;
+ anchors.verticalCenter: parent.verticalCenter;
+ height: paintedHeight;
+ wrapMode: Text.WordWrap;
+ horizontalAlignment: Text.AlignHCenter;
+ }
+ }
ListView {
id: transactionHistory;
@@ -294,17 +325,6 @@ Item {
}
}
}
-
- // This should never be visible (since you immediately get 100 HFC)
- FiraSansRegular {
- id: emptyTransationHistory;
- size: 24;
- visible: !transactionHistory.visible && root.historyReceived;
- text: "Recent Activity Unavailable";
- anchors.fill: parent;
- horizontalAlignment: Text.AlignHCenter;
- verticalAlignment: Text.AlignVCenter;
- }
}
}
@@ -350,7 +370,9 @@ Item {
}
root.pendingCount = pendingCount;
- transactionHistoryModel.insert(0, {"transaction_type": "pendingCount"});
+ if (pendingCount > 0) {
+ transactionHistoryModel.insert(0, {"transaction_type": "pendingCount"});
+ }
}
//
diff --git a/interface/resources/qml/hifi/commerce/wallet/WalletSetup.qml b/interface/resources/qml/hifi/commerce/wallet/WalletSetup.qml
index 99fe933bd6..1a62fe6f0d 100644
--- a/interface/resources/qml/hifi/commerce/wallet/WalletSetup.qml
+++ b/interface/resources/qml/hifi/commerce/wallet/WalletSetup.qml
@@ -53,8 +53,6 @@ Item {
onWalletAuthenticatedStatusResult: {
if (isAuthenticated) {
root.activeView = "step_4";
- } else {
- root.activeView = "step_3";
}
}
diff --git a/interface/resources/qml/hifi/dialogs/TabletRunningScripts.qml b/interface/resources/qml/hifi/dialogs/TabletRunningScripts.qml
index 80c1b58444..83f91c78c5 100644
--- a/interface/resources/qml/hifi/dialogs/TabletRunningScripts.qml
+++ b/interface/resources/qml/hifi/dialogs/TabletRunningScripts.qml
@@ -32,6 +32,8 @@ Rectangle {
color: hifi.colors.baseGray
+ property bool keyboardEnabled: HMD.active
+ property bool keyboardRaised: false
LetterboxMessage {
id: letterBoxMessage
@@ -380,7 +382,7 @@ Rectangle {
Component.onCompleted: scriptsModel.filterRegExp = new RegExp("^.*$", "i")
onActiveFocusChanged: {
// raise the keyboard
- keyboard.raised = activeFocus;
+ root.keyboardRaised = activeFocus;
// scroll to the bottom of the content area.
if (activeFocus) {
@@ -481,7 +483,7 @@ Rectangle {
HifiControls.Keyboard {
id: keyboard
- raised: false
+ raised: parent.keyboardEnabled && parent.keyboardRaised
numeric: false
anchors {
bottom: parent.bottom
diff --git a/interface/resources/qml/hifi/tablet/NewModelDialog.qml b/interface/resources/qml/hifi/tablet/NewModelDialog.qml
index 47d28486a9..3debc8b9e7 100644
--- a/interface/resources/qml/hifi/tablet/NewModelDialog.qml
+++ b/interface/resources/qml/hifi/tablet/NewModelDialog.qml
@@ -11,8 +11,11 @@
import QtQuick 2.5
import QtQuick.Controls 1.4
+import QtQuick.Dialogs 1.2 as OriginalDialogs
+
import "../../styles-uit"
import "../../controls-uit"
+import "../dialogs"
Rectangle {
id: newModelDialog
@@ -25,6 +28,15 @@ Rectangle {
property bool punctuationMode: false
property bool keyboardRasied: false
+ function errorMessageBox(message) {
+ return desktop.messageBox({
+ icon: hifi.icons.warning,
+ defaultButton: OriginalDialogs.StandardButton.Ok,
+ title: "Error",
+ text: message
+ });
+ }
+
Item {
id: column1
anchors.rightMargin: 10
@@ -98,7 +110,6 @@ Rectangle {
CheckBox {
id: dynamic
text: qsTr("Dynamic")
-
}
Row {
@@ -117,6 +128,7 @@ Rectangle {
Text {
id: text2
width: 160
+ x: dynamic.width / 2
color: "#ffffff"
text: qsTr("Models with automatic collisions set to 'Exact' cannot be dynamic, and should not be used as floors")
wrapMode: Text.WordWrap
@@ -139,15 +151,40 @@ Rectangle {
ComboBox {
id: collisionType
+
+ property int priorIndex: 0
+ property string staticMeshCollisionText: "Exact - All polygons"
+ property var collisionArray: ["No Collision",
+ "Basic - Whole model",
+ "Good - Sub-meshes",
+ staticMeshCollisionText,
+ "Box",
+ "Sphere"]
+
width: 200
z: 100
transformOrigin: Item.Center
- model: ["No Collision",
- "Basic - Whole model",
- "Good - Sub-meshes",
- "Exact - All polygons",
- "Box",
- "Sphere"]
+ model: collisionArray
+
+ onCurrentIndexChanged: {
+ if (collisionArray[currentIndex] === staticMeshCollisionText) {
+
+ if (dynamic.checked) {
+ currentIndex = priorIndex;
+
+ errorMessageBox("Models with Automatic Collisions set to \""
+ + staticMeshCollisionText + "\" cannot be dynamic.");
+ //--EARLY EXIT--( Can't have a static mesh model that's dynamic )
+ return;
+ }
+
+ dynamic.enabled = false;
+ } else {
+ dynamic.enabled = true;
+ }
+
+ priorIndex = currentIndex;
+ }
}
Row {
@@ -155,10 +192,10 @@ Rectangle {
width: 200
height: 400
spacing: 5
-
- anchors {
- rightMargin: 15
- }
+
+ anchors.horizontalCenter: column3.horizontalCenter
+ anchors.horizontalCenterOffset: -20
+
Button {
id: button1
text: qsTr("Add")
diff --git a/interface/resources/qml/hifi/tablet/Tablet.qml b/interface/resources/qml/hifi/tablet/Tablet.qml
index 66e3dfdbbb..2a086bae94 100644
--- a/interface/resources/qml/hifi/tablet/Tablet.qml
+++ b/interface/resources/qml/hifi/tablet/Tablet.qml
@@ -8,8 +8,8 @@ import "../audio" as HifiAudio
Item {
id: tablet
objectName: "tablet"
- property int rowIndex: 0
- property int columnIndex: 0
+ property int rowIndex: 6 // by default
+ property int columnIndex: 1 // point to 'go to location'
property int count: (flowMain.children.length - 1)
// used to look up a button by its uuid
diff --git a/interface/resources/qml/hifi/tablet/TabletAddressDialog.qml b/interface/resources/qml/hifi/tablet/TabletAddressDialog.qml
index 4d9a83817a..6aa3b8e7fe 100644
--- a/interface/resources/qml/hifi/tablet/TabletAddressDialog.qml
+++ b/interface/resources/qml/hifi/tablet/TabletAddressDialog.qml
@@ -11,6 +11,7 @@
import Hifi 1.0
import QtQuick 2.5
import QtQuick.Controls 1.4
+import QtQuick.Controls.Styles 1.4
import QtGraphicalEffects 1.0
import "../../controls"
import "../../styles"
@@ -83,7 +84,6 @@ StackView {
anchors.centerIn = parent;
}
-
function resetAfterTeleport() {
//storyCardFrame.shown = root.shown = false;
}
@@ -134,7 +134,8 @@ StackView {
bottom: parent.bottom
}
- onHostChanged: updateLocationTextTimer.start();
+ onHostChanged: updateLocationTextTimer.restart();
+
Rectangle {
id: navBar
width: parent.width
@@ -205,16 +206,16 @@ StackView {
anchors {
top: parent.top;
left: addressLineContainer.left;
- right: addressLineContainer.right;
}
}
HifiStyles.FiraSansRegular {
id: location;
anchors {
- left: addressLineContainer.left;
- leftMargin: 8;
- verticalCenter: addressLineContainer.verticalCenter;
+ left: notice.right
+ leftMargin: 8
+ right: addressLineContainer.right
+ verticalCenter: notice.verticalCenter
}
font.pixelSize: addressLine.font.pixelSize;
color: "gray";
@@ -222,7 +223,7 @@ StackView {
visible: addressLine.text.length === 0
}
- TextInput {
+ TextField {
id: addressLine
width: addressLineContainer.width - addressLineContainer.anchors.leftMargin - addressLineContainer.anchors.rightMargin;
anchors {
@@ -230,7 +231,6 @@ StackView {
leftMargin: 8;
verticalCenter: addressLineContainer.verticalCenter;
}
- font.pixelSize: hifi.fonts.pixelSize * 0.75
onTextChanged: {
updateLocationText(text.length > 0);
}
@@ -238,6 +238,17 @@ StackView {
addressBarDialog.keyboardEnabled = false;
toggleOrGo();
}
+ placeholderText: "Type domain address here"
+ verticalAlignment: TextInput.AlignBottom
+ style: TextFieldStyle {
+ textColor: hifi.colors.text
+ placeholderTextColor: "gray"
+ font {
+ family: hifi.fonts.fontFamily
+ pixelSize: hifi.fonts.pixelSize * 0.75
+ }
+ background: Item {}
+ }
}
Rectangle {
@@ -347,7 +358,7 @@ StackView {
// Delay updating location text a bit to avoid flicker of content and so that connection status is valid.
id: updateLocationTextTimer
running: false
- interval: 500 // ms
+ interval: 1000 // ms
repeat: false
onTriggered: updateLocationText(false);
}
@@ -366,7 +377,7 @@ StackView {
HifiControls.Keyboard {
id: keyboard
- raised: parent.keyboardEnabled
+ raised: parent.keyboardEnabled && parent.keyboardRaised
numeric: parent.punctuationMode
anchors {
bottom: parent.bottom
diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp
index 4f051697ad..c53b2c993c 100644
--- a/interface/src/Application.cpp
+++ b/interface/src/Application.cpp
@@ -1700,8 +1700,6 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
lastLeftHandPose = leftHandPose;
lastRightHandPose = rightHandPose;
- properties["local_socket_changes"] = DependencyManager::get()->getStat(LOCAL_SOCKET_CHANGE_STAT).toInt();
-
UserActivityLogger::getInstance().logAction("stats", properties);
});
sendStatsTimer->start();
@@ -1825,6 +1823,9 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
// Preload Tablet sounds
DependencyManager::get()->preloadSounds();
+ _pendingIdleEvent = false;
+ _pendingRenderEvent = false;
+
qCDebug(interfaceapp) << "Metaverse session ID is" << uuidStringWithoutCurlyBraces(accountManager->getSessionID());
}
@@ -2693,7 +2694,7 @@ bool Application::importFromZIP(const QString& filePath) {
qDebug() << "A zip file has been dropped in: " << filePath;
QUrl empty;
// handle Blocks download from Marketplace
- if (filePath.contains("vr.google.com/downloads")) {
+ if (filePath.contains("poly.google.com/downloads")) {
addAssetToWorldFromURL(filePath);
} else {
qApp->getFileDownloadInterface()->runUnzip(filePath, empty, true, true, false);
@@ -4441,7 +4442,7 @@ void Application::cameraModeChanged() {
void Application::cameraMenuChanged() {
auto menu = Menu::getInstance();
if (menu->isOptionChecked(MenuOption::FullscreenMirror)) {
- if (_myCamera.getMode() != CAMERA_MODE_MIRROR) {
+ if (!isHMDMode() && _myCamera.getMode() != CAMERA_MODE_MIRROR) {
_myCamera.setMode(CAMERA_MODE_MIRROR);
getMyAvatar()->reset(false, false, false); // to reset any active MyAvatar::FollowHelpers
}
@@ -6235,7 +6236,7 @@ void Application::addAssetToWorldFromURL(QString url) {
if (url.contains("filename")) {
filename = url.section("filename=", 1, 1); // Filename is in "?filename=" parameter at end of URL.
}
- if (url.contains("vr.google.com/downloads")) {
+ if (url.contains("poly.google.com/downloads")) {
filename = url.section('/', -1);
if (url.contains("noDownload")) {
filename.remove(".zip?noDownload=false");
@@ -6270,7 +6271,7 @@ void Application::addAssetToWorldFromURLRequestFinished() {
if (url.contains("filename")) {
filename = url.section("filename=", 1, 1); // Filename is in "?filename=" parameter at end of URL.
}
- if (url.contains("vr.google.com/downloads")) {
+ if (url.contains("poly.google.com/downloads")) {
filename = url.section('/', -1);
if (url.contains("noDownload")) {
filename.remove(".zip?noDownload=false");
@@ -7271,6 +7272,10 @@ void Application::updateDisplayMode() {
menu->setIsOptionChecked(MenuOption::FirstPerson, true);
cameraMenuChanged();
}
+
+ // Remove the mirror camera option from menu if in HMD mode
+ auto mirrorAction = menu->getActionForOption(MenuOption::FullscreenMirror);
+ mirrorAction->setVisible(!isHmd);
Q_ASSERT_X(_displayPlugin, "Application::updateDisplayMode", "could not find an activated display plugin");
}
diff --git a/interface/src/Application.h b/interface/src/Application.h
index fbfb3979be..19a6dfdac1 100644
--- a/interface/src/Application.h
+++ b/interface/src/Application.h
@@ -708,7 +708,7 @@ private:
friend class RenderEventHandler;
- std::atomic _pendingIdleEvent { false };
- std::atomic _pendingRenderEvent { false };
+ std::atomic _pendingIdleEvent { true };
+ std::atomic _pendingRenderEvent { true };
};
#endif // hifi_Application_h
diff --git a/interface/src/LODManager.cpp b/interface/src/LODManager.cpp
index d3c8746e16..01ccbd0d9a 100644
--- a/interface/src/LODManager.cpp
+++ b/interface/src/LODManager.cpp
@@ -54,7 +54,9 @@ void LODManager::autoAdjustLOD(float batchTime, float engineRunTime, float delta
float renderTime = batchTime + OVERLAY_AND_SWAP_TIME_BUDGET;
float maxTime = glm::max(renderTime, engineRunTime);
const float BLEND_TIMESCALE = 0.3f; // sec
- float blend = BLEND_TIMESCALE / deltaTimeSec;
+ const float MIN_DELTA_TIME = 0.001f;
+ const float safeDeltaTime = glm::max(deltaTimeSec, MIN_DELTA_TIME);
+ float blend = BLEND_TIMESCALE / safeDeltaTime;
if (blend > 1.0f) {
blend = 1.0f;
}
diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp
index 76205bdde9..0dfcf93a65 100755
--- a/interface/src/avatar/MyAvatar.cpp
+++ b/interface/src/avatar/MyAvatar.cpp
@@ -135,7 +135,7 @@ MyAvatar::MyAvatar(QThread* thread) :
connect(&domainHandler, &DomainHandler::settingsReceived, this, &MyAvatar::restrictScaleFromDomainSettings);
// when we leave a domain we lift whatever restrictions that domain may have placed on our scale
- connect(&domainHandler, &DomainHandler::disconnectedFromDomain, this, &MyAvatar::clearScaleRestriction);
+ connect(&domainHandler, &DomainHandler::disconnectedFromDomain, this, &MyAvatar::leaveDomain);
_bodySensorMatrix = deriveBodyFromHMDSensor();
@@ -2279,6 +2279,18 @@ void MyAvatar::restrictScaleFromDomainSettings(const QJsonObject& domainSettings
settings.endGroup();
}
+void MyAvatar::leaveDomain() {
+ clearScaleRestriction();
+ saveAvatarScale();
+}
+
+void MyAvatar::saveAvatarScale() {
+ Settings settings;
+ settings.beginGroup("Avatar");
+ settings.setValue("scale", _targetScale);
+ settings.endGroup();
+}
+
void MyAvatar::clearScaleRestriction() {
_domainMinimumScale = MIN_AVATAR_SCALE;
_domainMaximumScale = MAX_AVATAR_SCALE;
diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h
index 288e6dd638..e4e8f8d02c 100644
--- a/interface/src/avatar/MyAvatar.h
+++ b/interface/src/avatar/MyAvatar.h
@@ -622,6 +622,9 @@ signals:
void attachmentsChanged();
void scaleChanged();
+private slots:
+ void leaveDomain();
+
private:
bool requiresSafeLanding(const glm::vec3& positionIn, glm::vec3& positionOut);
@@ -638,6 +641,8 @@ private:
virtual int parseDataFromBuffer(const QByteArray& buffer) override;
virtual glm::vec3 getSkeletonPosition() const override;
+ void saveAvatarScale();
+
glm::vec3 getScriptedMotorVelocity() const { return _scriptedMotorVelocity; }
float getScriptedMotorTimescale() const { return _scriptedMotorTimescale; }
QString getScriptedMotorFrame() const;
diff --git a/interface/src/commerce/Ledger.cpp b/interface/src/commerce/Ledger.cpp
index f607c923ee..904847cb5f 100644
--- a/interface/src/commerce/Ledger.cpp
+++ b/interface/src/commerce/Ledger.cpp
@@ -90,7 +90,7 @@ void Ledger::buy(const QString& hfc_key, int cost, const QString& asset_id, cons
signedSend("transaction", transactionString, hfc_key, "buy", "buySuccess", "buyFailure", controlled_failure);
}
-bool Ledger::receiveAt(const QString& hfc_key, const QString& old_key, const QString& machine_fingerprint) {
+bool Ledger::receiveAt(const QString& hfc_key, const QString& old_key) {
auto accountManager = DependencyManager::get();
if (!accountManager->isLoggedIn()) {
qCWarning(commerce) << "Cannot set receiveAt when not logged in.";
@@ -99,13 +99,7 @@ bool Ledger::receiveAt(const QString& hfc_key, const QString& old_key, const QSt
return false; // We know right away that we will fail, so tell the caller.
}
- QJsonObject transaction;
- transaction["hfc_key"] = hfc_key;
- transaction["machine_fingerprint"] = machine_fingerprint;
- QJsonDocument transactionDoc{ transaction };
- auto transactionString = transactionDoc.toJson(QJsonDocument::Compact);
-
- signedSend("transaction", transactionString, old_key, "receive_at", "receiveAtSuccess", "receiveAtFailure");
+ signedSend("public_key", hfc_key.toUtf8(), old_key, "receive_at", "receiveAtSuccess", "receiveAtFailure");
return true; // Note that there may still be an asynchronous signal of failure that callers might be interested in.
}
diff --git a/interface/src/commerce/Ledger.h b/interface/src/commerce/Ledger.h
index 54f3f780f3..42eb0ffc49 100644
--- a/interface/src/commerce/Ledger.h
+++ b/interface/src/commerce/Ledger.h
@@ -26,7 +26,7 @@ class Ledger : public QObject, public Dependency {
public:
void buy(const QString& hfc_key, int cost, const QString& asset_id, const QString& inventory_key, const bool controlled_failure = false);
- bool receiveAt(const QString& hfc_key, const QString& old_key, const QString& machine_fingerprint);
+ bool receiveAt(const QString& hfc_key, const QString& old_key);
void balance(const QStringList& keys);
void inventory(const QStringList& keys);
void history(const QStringList& keys);
diff --git a/interface/src/commerce/Wallet.cpp b/interface/src/commerce/Wallet.cpp
index 9bc8dc9e43..85632ff8f1 100644
--- a/interface/src/commerce/Wallet.cpp
+++ b/interface/src/commerce/Wallet.cpp
@@ -16,7 +16,6 @@
#include "ui/ImageProvider.h"
#include "scripting/HMDScriptingInterface.h"
-#include
#include
#include
#include
@@ -542,8 +541,7 @@ bool Wallet::generateKeyPair() {
// 2. It is maximally private, and we can step back from that later if desired.
// 3. It maximally exercises all the machinery, so we are most likely to surface issues now.
auto ledger = DependencyManager::get();
- QString machineFingerprint = uuidStringWithoutCurlyBraces(FingerprintUtils::getMachineFingerprint());
- return ledger->receiveAt(key, oldKey, machineFingerprint);
+ return ledger->receiveAt(key, oldKey);
}
QStringList Wallet::listPublicKeys() {
diff --git a/interface/src/raypick/LaserPointer.cpp b/interface/src/raypick/LaserPointer.cpp
index 32dd74279b..71a547533b 100644
--- a/interface/src/raypick/LaserPointer.cpp
+++ b/interface/src/raypick/LaserPointer.cpp
@@ -15,7 +15,7 @@
#include "RayPickScriptingInterface.h"
LaserPointer::LaserPointer(const QVariant& rayProps, const RenderStateMap& renderStates, const DefaultRenderStateMap& defaultRenderStates,
- const bool faceAvatar, const bool centerEndY, const bool lockEnd, const bool distanceScaleEnd, const bool enabled) :
+ const bool faceAvatar, const bool centerEndY, const bool lockEnd, const bool distanceScaleEnd, const bool scaleWithAvatar, const bool enabled) :
_renderingEnabled(enabled),
_renderStates(renderStates),
_defaultRenderStates(defaultRenderStates),
@@ -23,6 +23,7 @@ LaserPointer::LaserPointer(const QVariant& rayProps, const RenderStateMap& rende
_centerEndY(centerEndY),
_lockEnd(lockEnd),
_distanceScaleEnd(distanceScaleEnd),
+ _scaleWithAvatar(scaleWithAvatar),
_rayPickUID(DependencyManager::get()->createRayPick(rayProps))
{
@@ -94,6 +95,10 @@ void LaserPointer::editRenderState(const std::string& state, const QVariant& sta
if (endDim.isValid()) {
_renderStates[state].setEndDim(vec3FromVariant(endDim));
}
+ QVariant lineWidth = pathProps.toMap()["lineWidth"];
+ if (lineWidth.isValid()) {
+ _renderStates[state].setLineWidth(lineWidth.toFloat());
+ }
});
}
@@ -152,6 +157,7 @@ void LaserPointer::updateRenderState(const RenderState& renderState, const Inter
}
}
}
+
QVariant end = vec3toVariant(endVec);
if (!renderState.getPathID().isNull()) {
QVariantMap pathProps;
@@ -159,6 +165,9 @@ void LaserPointer::updateRenderState(const RenderState& renderState, const Inter
pathProps.insert("end", end);
pathProps.insert("visible", true);
pathProps.insert("ignoreRayIntersection", renderState.doesPathIgnoreRays());
+ if (_scaleWithAvatar) {
+ pathProps.insert("lineWidth", renderState.getLineWidth() * DependencyManager::get()->getMyAvatar()->getSensorToWorldScale());
+ }
qApp->getOverlays().editOverlay(renderState.getPathID(), pathProps);
}
if (!renderState.getEndID().isNull()) {
@@ -166,7 +175,7 @@ void LaserPointer::updateRenderState(const RenderState& renderState, const Inter
glm::quat faceAvatarRotation = DependencyManager::get()->getMyAvatar()->getOrientation() * glm::quat(glm::radians(glm::vec3(0.0f, 180.0f, 0.0f)));
glm::vec3 dim = vec3FromVariant(qApp->getOverlays().getProperty(renderState.getEndID(), "dimensions").value);
if (_distanceScaleEnd) {
- dim = renderState.getEndDim() * glm::distance(pickRay.origin, endVec) * DependencyManager::get()->getMyAvatar()->getSensorToWorldScale();
+ dim = renderState.getEndDim() * glm::distance(pickRay.origin, endVec);
endProps.insert("dimensions", vec3toVariant(dim));
}
if (_centerEndY) {
@@ -258,6 +267,7 @@ RenderState::RenderState(const OverlayID& startID, const OverlayID& pathID, cons
}
if (!_pathID.isNull()) {
_pathIgnoreRays = qApp->getOverlays().getProperty(_pathID, "ignoreRayIntersection").value.toBool();
+ _lineWidth = qApp->getOverlays().getProperty(_pathID, "lineWidth").value.toFloat();
}
if (!_endID.isNull()) {
_endDim = vec3FromVariant(qApp->getOverlays().getProperty(_endID, "dimensions").value);
diff --git a/interface/src/raypick/LaserPointer.h b/interface/src/raypick/LaserPointer.h
index 896752a96e..d38070c05c 100644
--- a/interface/src/raypick/LaserPointer.h
+++ b/interface/src/raypick/LaserPointer.h
@@ -43,6 +43,9 @@ public:
void setEndDim(const glm::vec3& endDim) { _endDim = endDim; }
const glm::vec3& getEndDim() const { return _endDim; }
+ void setLineWidth(const float& lineWidth) { _lineWidth = lineWidth; }
+ const float& getLineWidth() const { return _lineWidth; }
+
void deleteOverlays();
private:
@@ -54,6 +57,7 @@ private:
bool _endIgnoreRays;
glm::vec3 _endDim;
+ float _lineWidth;
};
@@ -66,7 +70,7 @@ public:
typedef std::unordered_map> DefaultRenderStateMap;
LaserPointer(const QVariant& rayProps, const RenderStateMap& renderStates, const DefaultRenderStateMap& defaultRenderStates,
- const bool faceAvatar, const bool centerEndY, const bool lockEnd, const bool distanceScaleEnd, const bool enabled);
+ const bool faceAvatar, const bool centerEndY, const bool lockEnd, const bool distanceScaleEnd, const bool scaleWithAvatar, const bool enabled);
~LaserPointer();
QUuid getRayUID() { return _rayPickUID; }
@@ -97,6 +101,7 @@ private:
bool _centerEndY;
bool _lockEnd;
bool _distanceScaleEnd;
+ bool _scaleWithAvatar;
LockEndObject _lockEndObject;
const QUuid _rayPickUID;
diff --git a/interface/src/raypick/LaserPointerManager.cpp b/interface/src/raypick/LaserPointerManager.cpp
index 45420d1488..143451d0d3 100644
--- a/interface/src/raypick/LaserPointerManager.cpp
+++ b/interface/src/raypick/LaserPointerManager.cpp
@@ -11,9 +11,9 @@
#include "LaserPointerManager.h"
QUuid LaserPointerManager::createLaserPointer(const QVariant& rayProps, const LaserPointer::RenderStateMap& renderStates, const LaserPointer::DefaultRenderStateMap& defaultRenderStates,
- const bool faceAvatar, const bool centerEndY, const bool lockEnd, const bool distanceScaleEnd, const bool enabled) {
+ const bool faceAvatar, const bool centerEndY, const bool lockEnd, const bool distanceScaleEnd, const bool scaleWithAvatar, const bool enabled) {
QUuid result;
- std::shared_ptr laserPointer = std::make_shared(rayProps, renderStates, defaultRenderStates, faceAvatar, centerEndY, lockEnd, distanceScaleEnd, enabled);
+ std::shared_ptr laserPointer = std::make_shared(rayProps, renderStates, defaultRenderStates, faceAvatar, centerEndY, lockEnd, distanceScaleEnd, scaleWithAvatar, enabled);
if (!laserPointer->getRayUID().isNull()) {
result = QUuid::createUuid();
withWriteLock([&] { _laserPointers[result] = laserPointer; });
diff --git a/interface/src/raypick/LaserPointerManager.h b/interface/src/raypick/LaserPointerManager.h
index 25089a291a..3f8f962679 100644
--- a/interface/src/raypick/LaserPointerManager.h
+++ b/interface/src/raypick/LaserPointerManager.h
@@ -25,7 +25,7 @@ class LaserPointerManager : protected ReadWriteLockable {
public:
QUuid createLaserPointer(const QVariant& rayProps, const LaserPointer::RenderStateMap& renderStates, const LaserPointer::DefaultRenderStateMap& defaultRenderStates,
- const bool faceAvatar, const bool centerEndY, const bool lockEnd, const bool distanceScaleEnd, const bool enabled);
+ const bool faceAvatar, const bool centerEndY, const bool lockEnd, const bool distanceScaleEnd, const bool scaleWithAvatar, const bool enabled);
void removeLaserPointer(const QUuid& uid);
void enableLaserPointer(const QUuid& uid) const;
diff --git a/interface/src/raypick/LaserPointerScriptingInterface.cpp b/interface/src/raypick/LaserPointerScriptingInterface.cpp
index e8d28bfab2..eb69d610ad 100644
--- a/interface/src/raypick/LaserPointerScriptingInterface.cpp
+++ b/interface/src/raypick/LaserPointerScriptingInterface.cpp
@@ -51,6 +51,11 @@ QUuid LaserPointerScriptingInterface::createLaserPointer(const QVariant& propert
enabled = propertyMap["enabled"].toBool();
}
+ bool scaleWithAvatar = false;
+ if (propertyMap["scaleWithAvatar"].isValid()) {
+ scaleWithAvatar = propertyMap["scaleWithAvatar"].toBool();
+ }
+
LaserPointer::RenderStateMap renderStates;
if (propertyMap["renderStates"].isValid()) {
QList renderStateVariants = propertyMap["renderStates"].toList();
@@ -80,7 +85,7 @@ QUuid LaserPointerScriptingInterface::createLaserPointer(const QVariant& propert
}
}
- return qApp->getLaserPointerManager().createLaserPointer(properties, renderStates, defaultRenderStates, faceAvatar, centerEndY, lockEnd, distanceScaleEnd, enabled);
+ return qApp->getLaserPointerManager().createLaserPointer(properties, renderStates, defaultRenderStates, faceAvatar, centerEndY, lockEnd, distanceScaleEnd, scaleWithAvatar, enabled);
}
void LaserPointerScriptingInterface::editRenderState(const QUuid& uid, const QString& renderState, const QVariant& properties) const {
diff --git a/interface/src/scripting/SelectionScriptingInterface.cpp b/interface/src/scripting/SelectionScriptingInterface.cpp
index 808396c901..1adf5650dd 100644
--- a/interface/src/scripting/SelectionScriptingInterface.cpp
+++ b/interface/src/scripting/SelectionScriptingInterface.cpp
@@ -71,6 +71,12 @@ bool SelectionScriptingInterface::removeFromSelectedItemsList(const QString& lis
return false;
}
+bool SelectionScriptingInterface::clearSelectedItemsList(const QString& listName) {
+ _selectedItemsListMap.insert(listName, GameplayObjects());
+ emit selectedItemsListChanged(listName);
+ return true;
+}
+
template bool SelectionScriptingInterface::addToGameplayObjects(const QString& listName, T idToAdd) {
GameplayObjects currentList = _selectedItemsListMap.value(listName);
currentList.addToGameplayObjects(idToAdd);
diff --git a/interface/src/scripting/SelectionScriptingInterface.h b/interface/src/scripting/SelectionScriptingInterface.h
index d1a372c5c4..d9003c2c32 100644
--- a/interface/src/scripting/SelectionScriptingInterface.h
+++ b/interface/src/scripting/SelectionScriptingInterface.h
@@ -56,11 +56,45 @@ public:
GameplayObjects getList(const QString& listName);
+ /**jsdoc
+ * Prints out the list of avatars, entities and overlays stored in a particular selection.
+ * @function Selection.printList
+ * @param listName {string} name of the selection
+ */
Q_INVOKABLE void printList(const QString& listName);
+ /**jsdoc
+ * Removes a named selection from the list of selections.
+ * @function Selection.removeListFromMap
+ * @param listName {string} name of the selection
+ * @returns {bool} true if the selection existed and was successfully removed.
+ */
Q_INVOKABLE bool removeListFromMap(const QString& listName);
+ /**jsdoc
+ * Add an item in a selection.
+ * @function Selection.addToSelectedItemsList
+ * @param listName {string} name of the selection
+ * @param itemType {string} the type of the item (one of "avatar", "entity" or "overlay")
+ * @param id {EntityID} the Id of the item to add to the selection
+ * @returns {bool} true if the item was successfully added.
+ */
Q_INVOKABLE bool addToSelectedItemsList(const QString& listName, const QString& itemType, const QUuid& id);
+ /**jsdoc
+ * Remove an item from a selection.
+ * @function Selection.removeFromSelectedItemsList
+ * @param listName {string} name of the selection
+ * @param itemType {string} the type of the item (one of "avatar", "entity" or "overlay")
+ * @param id {EntityID} the Id of the item to remove
+ * @returns {bool} true if the item was successfully removed.
+ */
Q_INVOKABLE bool removeFromSelectedItemsList(const QString& listName, const QString& itemType, const QUuid& id);
+ /**jsdoc
+ * Remove all items from a selection.
+ * @function Selection.clearSelectedItemsList
+ * @param listName {string} name of the selection
+ * @returns {bool} true if the item was successfully cleared.
+ */
+ Q_INVOKABLE bool clearSelectedItemsList(const QString& listName);
signals:
void selectedItemsListChanged(const QString& listName);
diff --git a/interface/src/ui/AddressBarDialog.cpp b/interface/src/ui/AddressBarDialog.cpp
index 8b5e255b06..1a23674fa3 100644
--- a/interface/src/ui/AddressBarDialog.cpp
+++ b/interface/src/ui/AddressBarDialog.cpp
@@ -40,6 +40,10 @@ AddressBarDialog::AddressBarDialog(QQuickItem* parent) : OffscreenQmlDialog(pare
_backEnabled = !(DependencyManager::get()->getBackStack().isEmpty());
_forwardEnabled = !(DependencyManager::get()->getForwardStack().isEmpty());
connect(addressManager.data(), &AddressManager::hostChanged, this, &AddressBarDialog::hostChanged);
+ auto nodeList = DependencyManager::get();
+ const DomainHandler& domainHandler = nodeList->getDomainHandler();
+ connect(&domainHandler, &DomainHandler::connectedToDomain, this, &AddressBarDialog::hostChanged);
+ connect(&domainHandler, &DomainHandler::disconnectedFromDomain, this, &AddressBarDialog::hostChanged);
connect(DependencyManager::get().data(), &DialogsManager::setUseFeed, this, &AddressBarDialog::setUseFeed);
connect(qApp, &Application::receivedHifiSchemeURL, this, &AddressBarDialog::receivedHifiSchemeURL);
}
diff --git a/interface/src/ui/overlays/Base3DOverlay.cpp b/interface/src/ui/overlays/Base3DOverlay.cpp
index d157e29959..4412014eb1 100644
--- a/interface/src/ui/overlays/Base3DOverlay.cpp
+++ b/interface/src/ui/overlays/Base3DOverlay.cpp
@@ -16,13 +16,11 @@
#include "Application.h"
-const float DEFAULT_LINE_WIDTH = 1.0f;
const bool DEFAULT_IS_SOLID = false;
const bool DEFAULT_IS_DASHED_LINE = false;
Base3DOverlay::Base3DOverlay() :
SpatiallyNestable(NestableType::Overlay, QUuid::createUuid()),
- _lineWidth(DEFAULT_LINE_WIDTH),
_isSolid(DEFAULT_IS_SOLID),
_isDashedLine(DEFAULT_IS_DASHED_LINE),
_ignoreRayIntersection(false),
@@ -34,7 +32,6 @@ Base3DOverlay::Base3DOverlay() :
Base3DOverlay::Base3DOverlay(const Base3DOverlay* base3DOverlay) :
Overlay(base3DOverlay),
SpatiallyNestable(NestableType::Overlay, QUuid::createUuid()),
- _lineWidth(base3DOverlay->_lineWidth),
_isSolid(base3DOverlay->_isSolid),
_isDashedLine(base3DOverlay->_isDashedLine),
_ignoreRayIntersection(base3DOverlay->_ignoreRayIntersection),
@@ -153,12 +150,6 @@ void Base3DOverlay::setProperties(const QVariantMap& originalProperties) {
setLocalOrientation(quatFromVariant(properties["orientation"]));
needRenderItemUpdate = true;
}
-
- if (properties["lineWidth"].isValid()) {
- setLineWidth(properties["lineWidth"].toFloat());
- needRenderItemUpdate = true;
- }
-
if (properties["isSolid"].isValid()) {
setIsSolid(properties["isSolid"].toBool());
}
@@ -225,9 +216,6 @@ QVariant Base3DOverlay::getProperty(const QString& property) {
if (property == "localRotation" || property == "localOrientation") {
return quatToVariant(getLocalOrientation());
}
- if (property == "lineWidth") {
- return _lineWidth;
- }
if (property == "isSolid" || property == "isFilled" || property == "solid" || property == "filed") {
return _isSolid;
}
diff --git a/interface/src/ui/overlays/Base3DOverlay.h b/interface/src/ui/overlays/Base3DOverlay.h
index 83c5873260..2f6e6db9dc 100644
--- a/interface/src/ui/overlays/Base3DOverlay.h
+++ b/interface/src/ui/overlays/Base3DOverlay.h
@@ -38,7 +38,6 @@ public:
// TODO: consider implementing registration points in this class
glm::vec3 getCenter() const { return getPosition(); }
- float getLineWidth() const { return _lineWidth; }
bool getIsSolid() const { return _isSolid; }
bool getIsDashedLine() const { return _isDashedLine; }
bool getIsSolidLine() const { return !_isDashedLine; }
@@ -47,7 +46,6 @@ public:
bool getDrawHUDLayer() const { return _drawHUDLayer; }
bool getIsGrabbable() const { return _isGrabbable; }
- void setLineWidth(float lineWidth) { _lineWidth = lineWidth; }
void setIsSolid(bool isSolid) { _isSolid = isSolid; }
void setIsDashedLine(bool isDashedLine) { _isDashedLine = isDashedLine; }
void setIgnoreRayIntersection(bool value) { _ignoreRayIntersection = value; }
@@ -85,7 +83,6 @@ protected:
void setRenderVisible(bool visible);
const Transform& getRenderTransform() const { return _renderTransform; }
- float _lineWidth;
bool _isSolid;
bool _isDashedLine;
bool _ignoreRayIntersection;
diff --git a/interface/src/ui/overlays/ContextOverlayInterface.cpp b/interface/src/ui/overlays/ContextOverlayInterface.cpp
index b5af529f2b..d40c0972e9 100644
--- a/interface/src/ui/overlays/ContextOverlayInterface.cpp
+++ b/interface/src/ui/overlays/ContextOverlayInterface.cpp
@@ -38,8 +38,6 @@ ContextOverlayInterface::ContextOverlayInterface() {
_tabletScriptingInterface = DependencyManager::get();
_selectionScriptingInterface = DependencyManager::get();
- _selectionToSceneHandler.initialize("contextOverlayHighlightList");
-
_entityPropertyFlags += PROP_POSITION;
_entityPropertyFlags += PROP_ROTATION;
_entityPropertyFlags += PROP_MARKETPLACE_ID;
@@ -66,12 +64,20 @@ ContextOverlayInterface::ContextOverlayInterface() {
}
});
connect(entityScriptingInterface, &EntityScriptingInterface::deletingEntity, this, &ContextOverlayInterface::deletingEntity);
-
connect(&qApp->getOverlays(), &Overlays::mousePressOnOverlay, this, &ContextOverlayInterface::contextOverlays_mousePressOnOverlay);
connect(&qApp->getOverlays(), &Overlays::hoverEnterOverlay, this, &ContextOverlayInterface::contextOverlays_hoverEnterOverlay);
connect(&qApp->getOverlays(), &Overlays::hoverLeaveOverlay, this, &ContextOverlayInterface::contextOverlays_hoverLeaveOverlay);
- connect(_selectionScriptingInterface.data(), &SelectionScriptingInterface::selectedItemsListChanged, &_selectionToSceneHandler, &SelectionToSceneHandler::selectedItemsListChanged);
+ {
+ render::Transaction transaction;
+ initializeSelectionToSceneHandler(_selectionToSceneHandlers[0], "contextOverlayHighlightList", transaction);
+ for (auto i = 1; i < MAX_SELECTION_COUNT; i++) {
+ auto selectionName = QString("highlightList") + QString::number(i);
+ initializeSelectionToSceneHandler(_selectionToSceneHandlers[i], selectionName, transaction);
+ }
+ const render::ScenePointer& scene = qApp->getMain3DScene();
+ scene->enqueueTransaction(transaction);
+ }
auto nodeList = DependencyManager::get();
auto& packetReceiver = nodeList->getPacketReceiver();
@@ -79,6 +85,12 @@ ContextOverlayInterface::ContextOverlayInterface() {
_challengeOwnershipTimeoutTimer.setSingleShot(true);
}
+void ContextOverlayInterface::initializeSelectionToSceneHandler(SelectionToSceneHandler& handler, const QString& selectionName, render::Transaction& transaction) {
+ handler.initialize(selectionName);
+ connect(_selectionScriptingInterface.data(), &SelectionScriptingInterface::selectedItemsListChanged, &handler, &SelectionToSceneHandler::selectedItemsListChanged);
+ transaction.resetSelectionHighlight(selectionName.toStdString());
+}
+
static const uint32_t MOUSE_HW_ID = 0;
static const uint32_t LEFT_HAND_HW_ID = 1;
static const xColor CONTEXT_OVERLAY_COLOR = { 255, 255, 255 };
diff --git a/interface/src/ui/overlays/ContextOverlayInterface.h b/interface/src/ui/overlays/ContextOverlayInterface.h
index 8f0a40ef8e..81e398e15d 100644
--- a/interface/src/ui/overlays/ContextOverlayInterface.h
+++ b/interface/src/ui/overlays/ContextOverlayInterface.h
@@ -47,6 +47,7 @@ class ContextOverlayInterface : public QObject, public Dependency {
OverlayID _contextOverlayID { UNKNOWN_OVERLAY_ID };
std::shared_ptr _contextOverlay { nullptr };
public:
+
ContextOverlayInterface();
Q_INVOKABLE QUuid getCurrentEntityWithContextOverlay() { return _currentEntityWithContextOverlay; }
@@ -75,6 +76,11 @@ private slots:
void handleChallengeOwnershipReplyPacket(QSharedPointer packet, SharedNodePointer sendingNode);
private:
+
+ enum {
+ MAX_SELECTION_COUNT = 16
+ };
+
bool _verboseLogging { true };
bool _enabled { true };
EntityItemID _currentEntityWithContextOverlay{};
@@ -90,8 +96,9 @@ private:
void disableEntityHighlight(const EntityItemID& entityItemID);
void deletingEntity(const EntityItemID& entityItemID);
+ void initializeSelectionToSceneHandler(SelectionToSceneHandler& handler, const QString& selectionName, render::Transaction& transaction);
- SelectionToSceneHandler _selectionToSceneHandler;
+ SelectionToSceneHandler _selectionToSceneHandlers[MAX_SELECTION_COUNT];
Q_INVOKABLE void startChallengeOwnershipTimer();
QTimer _challengeOwnershipTimeoutTimer;
diff --git a/interface/src/ui/overlays/Line3DOverlay.cpp b/interface/src/ui/overlays/Line3DOverlay.cpp
index 5ef820b2e0..d59e382613 100644
--- a/interface/src/ui/overlays/Line3DOverlay.cpp
+++ b/interface/src/ui/overlays/Line3DOverlay.cpp
@@ -33,8 +33,8 @@ Line3DOverlay::Line3DOverlay(const Line3DOverlay* line3DOverlay) :
_length = line3DOverlay->getLength();
_endParentID = line3DOverlay->getEndParentID();
_endParentJointIndex = line3DOverlay->getEndJointIndex();
+ _lineWidth = line3DOverlay->getLineWidth();
_glow = line3DOverlay->getGlow();
- _glowWidth = line3DOverlay->getGlowWidth();
}
Line3DOverlay::~Line3DOverlay() {
@@ -145,7 +145,7 @@ void Line3DOverlay::render(RenderArgs* args) {
geometryCache->renderDashedLine(*batch, start, end, colorv4, _geometryCacheID);
} else {
// renderGlowLine handles both glow = 0 and glow > 0 cases
- geometryCache->renderGlowLine(*batch, start, end, colorv4, _glow, _glowWidth, _geometryCacheID);
+ geometryCache->renderGlowLine(*batch, start, end, colorv4, _glow, _lineWidth, _geometryCacheID);
}
}
}
@@ -239,11 +239,10 @@ void Line3DOverlay::setProperties(const QVariantMap& originalProperties) {
}
}
- auto glowWidth = properties["glowWidth"];
- if (glowWidth.isValid()) {
- setGlowWidth(glowWidth.toFloat());
+ auto lineWidth = properties["lineWidth"];
+ if (lineWidth.isValid()) {
+ setLineWidth(lineWidth.toFloat());
}
-
}
QVariant Line3DOverlay::getProperty(const QString& property) {
@@ -262,6 +261,9 @@ QVariant Line3DOverlay::getProperty(const QString& property) {
if (property == "length") {
return QVariant(getLength());
}
+ if (property == "lineWidth") {
+ return _lineWidth;
+ }
return Base3DOverlay::getProperty(property);
}
diff --git a/interface/src/ui/overlays/Line3DOverlay.h b/interface/src/ui/overlays/Line3DOverlay.h
index bcb65b1f1e..79af937f23 100644
--- a/interface/src/ui/overlays/Line3DOverlay.h
+++ b/interface/src/ui/overlays/Line3DOverlay.h
@@ -31,8 +31,8 @@ public:
// getters
glm::vec3 getStart() const;
glm::vec3 getEnd() const;
+ const float& getLineWidth() const { return _lineWidth; }
const float& getGlow() const { return _glow; }
- const float& getGlowWidth() const { return _glowWidth; }
// setters
void setStart(const glm::vec3& start);
@@ -41,8 +41,8 @@ public:
void setLocalStart(const glm::vec3& localStart) { setLocalPosition(localStart); }
void setLocalEnd(const glm::vec3& localEnd);
+ void setLineWidth(const float& lineWidth) { _lineWidth = lineWidth; }
void setGlow(const float& glow) { _glow = glow; }
- void setGlowWidth(const float& glowWidth) { _glowWidth = glowWidth; }
void setProperties(const QVariantMap& properties) override;
QVariant getProperty(const QString& property) override;
@@ -70,8 +70,9 @@ private:
glm::vec3 _direction; // in parent frame
float _length { 1.0 }; // in parent frame
+ const float DEFAULT_LINE_WIDTH = 0.02f;
+ float _lineWidth { DEFAULT_LINE_WIDTH };
float _glow { 0.0 };
- float _glowWidth { 0.0 };
int _geometryCacheID;
};
diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp
index acf7bf81d8..78aa1f4ba8 100644
--- a/libraries/animation/src/Rig.cpp
+++ b/libraries/animation/src/Rig.cpp
@@ -39,7 +39,12 @@ static std::mutex rigRegistryMutex;
static bool isEqual(const glm::vec3& u, const glm::vec3& v) {
const float EPSILON = 0.0001f;
- return glm::length(u - v) / glm::length(u) <= EPSILON;
+ float uLen = glm::length(u);
+ if (uLen == 0.0f) {
+ return glm::length(v) <= EPSILON;
+ } else {
+ return (glm::length(u - v) / uLen) <= EPSILON;
+ }
}
static bool isEqual(const glm::quat& p, const glm::quat& q) {
@@ -174,6 +179,11 @@ void Rig::restoreRoleAnimation(const QString& role) {
} else {
qCWarning(animation) << "Rig::restoreRoleAnimation could not find role " << role;
}
+
+ auto statesIter = _roleAnimStates.find(role);
+ if (statesIter != _roleAnimStates.end()) {
+ _roleAnimStates.erase(statesIter);
+ }
}
} else {
qCWarning(animation) << "Rig::overrideRoleAnimation avatar not ready yet";
@@ -303,7 +313,9 @@ void Rig::setModelOffset(const glm::mat4& modelOffsetMat) {
_rigToGeometryTransform = glm::inverse(_geometryToRigTransform);
// rebuild cached default poses
- buildAbsoluteRigPoses(_animSkeleton->getRelativeDefaultPoses(), _absoluteDefaultPoses);
+ if (_animSkeleton) {
+ buildAbsoluteRigPoses(_animSkeleton->getRelativeDefaultPoses(), _absoluteDefaultPoses);
+ }
}
}
diff --git a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp
index d2dc116e15..6bf9cc1666 100644
--- a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp
+++ b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp
@@ -453,7 +453,9 @@ void Avatar::applyPositionDelta(const glm::vec3& delta) {
void Avatar::measureMotionDerivatives(float deltaTime) {
PerformanceTimer perfTimer("derivatives");
// linear
- float invDeltaTime = 1.0f / deltaTime;
+ const float MIN_DELTA_TIME = 0.001f;
+ const float safeDeltaTime = glm::max(deltaTime, MIN_DELTA_TIME);
+ float invDeltaTime = 1.0f / safeDeltaTime;
// Floating point error prevents us from computing velocity in a naive way
// (e.g. vel = (pos - oldPos) / dt) so instead we use _positionOffsetAccumulator.
glm::vec3 velocity = _positionDeltaAccumulator * invDeltaTime;
@@ -1577,7 +1579,7 @@ float Avatar::getEyeHeight() const {
if (QThread::currentThread() != thread()) {
float result = DEFAULT_AVATAR_EYE_HEIGHT;
- BLOCKING_INVOKE_METHOD(const_cast(this), "getHeight", Q_RETURN_ARG(float, result));
+ BLOCKING_INVOKE_METHOD(const_cast(this), "getEyeHeight", Q_RETURN_ARG(float, result));
return result;
}
diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h
index fcfeaf7741..4dabbb3ff5 100644
--- a/libraries/avatars/src/AvatarData.h
+++ b/libraries/avatars/src/AvatarData.h
@@ -15,21 +15,7 @@
#include
#include
#include
-
-/* VS2010 defines stdint.h, but not inttypes.h */
-#if defined(_MSC_VER)
-typedef signed char int8_t;
-typedef signed short int16_t;
-typedef signed int int32_t;
-typedef unsigned char uint8_t;
-typedef unsigned short uint16_t;
-typedef unsigned int uint32_t;
-typedef signed long long int64_t;
-typedef unsigned long long quint64;
-#define PRId64 "I64d"
-#else
#include
-#endif
#include
#include
diff --git a/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp b/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp
index 7c96f00ede..54cc888c35 100644
--- a/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp
+++ b/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp
@@ -40,22 +40,26 @@ void ZoneEntityRenderer::onRemoveFromSceneTyped(const TypedEntityPointer& entity
if (_stage) {
if (!LightStage::isIndexInvalid(_sunIndex)) {
_stage->removeLight(_sunIndex);
+ _sunIndex = INVALID_INDEX;
+ _shadowIndex = INVALID_INDEX;
}
if (!LightStage::isIndexInvalid(_ambientIndex)) {
_stage->removeLight(_ambientIndex);
-
+ _ambientIndex = INVALID_INDEX;
}
}
if (_backgroundStage) {
if (!BackgroundStage::isIndexInvalid(_backgroundIndex)) {
_backgroundStage->removeBackground(_backgroundIndex);
+ _backgroundIndex = INVALID_INDEX;
}
}
if (_hazeStage) {
if (!HazeStage::isIndexInvalid(_hazeIndex)) {
_hazeStage->removeHaze(_hazeIndex);
+ _hazeIndex = INVALID_INDEX;
}
}
}
diff --git a/libraries/entities/src/ParticleEffectEntityItem.cpp b/libraries/entities/src/ParticleEffectEntityItem.cpp
index b216144ded..c2e29dd44f 100644
--- a/libraries/entities/src/ParticleEffectEntityItem.cpp
+++ b/libraries/entities/src/ParticleEffectEntityItem.cpp
@@ -97,7 +97,8 @@ bool operator==(const Properties& a, const Properties& b) {
(a.maxParticles == b.maxParticles) &&
(a.emission == b.emission) &&
(a.polar == b.polar) &&
- (a.azimuth == b.azimuth);
+ (a.azimuth == b.azimuth) &&
+ (a.textures == b.textures);
}
bool operator!=(const Properties& a, const Properties& b) {
diff --git a/libraries/entities/src/ShapeEntityItem.cpp b/libraries/entities/src/ShapeEntityItem.cpp
index 4115a606df..2771d22cdb 100644
--- a/libraries/entities/src/ShapeEntityItem.cpp
+++ b/libraries/entities/src/ShapeEntityItem.cpp
@@ -297,7 +297,7 @@ void ShapeEntityItem::computeShapeInfo(ShapeInfo& info) {
const float MIN_RELATIVE_SPHERICAL_ERROR = 0.001f;
if (diameter > MIN_DIAMETER
&& fabsf(diameter - entityDimensions.z) / diameter < MIN_RELATIVE_SPHERICAL_ERROR) {
- _collisionShapeType = SHAPE_TYPE_SPHERE;
+ _collisionShapeType = SHAPE_TYPE_CYLINDER_Y;
} else if (hullShapeCalculator) {
hullShapeCalculator(this, info);
_collisionShapeType = SHAPE_TYPE_SIMPLE_HULL;
diff --git a/libraries/gpu-gl/src/gpu/gl/GLShared.h b/libraries/gpu-gl/src/gpu/gl/GLShared.h
index 1b898e5c22..a1cf27afa6 100644
--- a/libraries/gpu-gl/src/gpu/gl/GLShared.h
+++ b/libraries/gpu-gl/src/gpu/gl/GLShared.h
@@ -110,6 +110,7 @@ static const GLenum ELEMENT_TYPE_TO_GL[gpu::NUM_TYPES] = {
GL_SHORT,
GL_UNSIGNED_SHORT,
GL_BYTE,
+ GL_UNSIGNED_BYTE,
GL_UNSIGNED_BYTE
};
diff --git a/libraries/gpu-gl/src/gpu/gl/GLTexelFormat.cpp b/libraries/gpu-gl/src/gpu/gl/GLTexelFormat.cpp
index 192a82dafc..528a2b524b 100644
--- a/libraries/gpu-gl/src/gpu/gl/GLTexelFormat.cpp
+++ b/libraries/gpu-gl/src/gpu/gl/GLTexelFormat.cpp
@@ -212,6 +212,9 @@ GLenum GLTexelFormat::evalGLTexelFormatInternal(const gpu::Element& dstFormat) {
case gpu::NUINT8:
result = GL_RGBA8;
break;
+ case gpu::NUINT2:
+ result = GL_RGBA2;
+ break;
case gpu::NINT8:
result = GL_RGBA8_SNORM;
break;
@@ -498,6 +501,7 @@ GLTexelFormat GLTexelFormat::evalGLTexelFormat(const Element& dstFormat, const E
break;
}
case gpu::COMPRESSED:
+ case gpu::NUINT2:
case gpu::NUM_TYPES: { // quiet compiler
Q_UNREACHABLE();
}
@@ -548,6 +552,7 @@ GLTexelFormat GLTexelFormat::evalGLTexelFormat(const Element& dstFormat, const E
break;
}
case gpu::COMPRESSED:
+ case gpu::NUINT2:
case gpu::NUM_TYPES: { // quiet compiler
Q_UNREACHABLE();
}
@@ -660,6 +665,10 @@ GLTexelFormat GLTexelFormat::evalGLTexelFormat(const Element& dstFormat, const E
texel.format = GL_RGBA;
texel.internalFormat = GL_RGBA8_SNORM;
break;
+ case gpu::NUINT2:
+ texel.format = GL_RGBA;
+ texel.internalFormat = GL_RGBA2;
+ break;
case gpu::NUINT32:
case gpu::NINT32:
case gpu::COMPRESSED:
diff --git a/libraries/gpu/src/gpu/DrawColor.slf b/libraries/gpu/src/gpu/DrawColor.slf
new file mode 100644
index 0000000000..c24d69d29f
--- /dev/null
+++ b/libraries/gpu/src/gpu/DrawColor.slf
@@ -0,0 +1,19 @@
+<@include gpu/Config.slh@>
+<$VERSION_HEADER$>
+// Generated on <$_SCRIBE_DATE$>
+//
+// Draw with color uniform
+//
+// Created by Olivier Prat on 25/10/2017
+// Copyright 2017 High Fidelity, Inc.
+//
+// Distributed under the Apache License, Version 2.0.
+// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
+//
+uniform vec4 color;
+
+out vec4 outFragColor;
+
+void main(void) {
+ outFragColor = color;
+}
diff --git a/libraries/gpu/src/gpu/Format.cpp b/libraries/gpu/src/gpu/Format.cpp
index 7efe4d3ed6..3b153097cf 100644
--- a/libraries/gpu/src/gpu/Format.cpp
+++ b/libraries/gpu/src/gpu/Format.cpp
@@ -19,6 +19,8 @@ const Element Element::COLOR_SRGBA_32{ VEC4, NUINT8, SRGBA };
const Element Element::COLOR_BGRA_32{ VEC4, NUINT8, BGRA };
const Element Element::COLOR_SBGRA_32{ VEC4, NUINT8, SBGRA };
+const Element Element::COLOR_RGBA_2{ VEC4, NUINT2, RGBA };
+
const Element Element::COLOR_COMPRESSED_RED{ TILE4x4, COMPRESSED, COMPRESSED_BC4_RED };
const Element Element::COLOR_COMPRESSED_SRGB { TILE4x4, COMPRESSED, COMPRESSED_BC1_SRGB };
const Element Element::COLOR_COMPRESSED_SRGBA_MASK { TILE4x4, COMPRESSED, COMPRESSED_BC1_SRGBA };
diff --git a/libraries/gpu/src/gpu/Format.h b/libraries/gpu/src/gpu/Format.h
index 0654b23581..9d5d2fc49d 100644
--- a/libraries/gpu/src/gpu/Format.h
+++ b/libraries/gpu/src/gpu/Format.h
@@ -38,6 +38,7 @@ enum Type : uint8_t {
NUINT16,
NINT8,
NUINT8,
+ NUINT2,
COMPRESSED,
@@ -309,6 +310,7 @@ public:
static const Element COLOR_SRGBA_32;
static const Element COLOR_BGRA_32;
static const Element COLOR_SBGRA_32;
+ static const Element COLOR_RGBA_2;
static const Element COLOR_R11G11B10;
static const Element COLOR_RGB9E5;
static const Element COLOR_COMPRESSED_RED;
diff --git a/libraries/gpu/src/gpu/Framebuffer.cpp b/libraries/gpu/src/gpu/Framebuffer.cpp
index f1257e7c83..4fcc9ab0f9 100755
--- a/libraries/gpu/src/gpu/Framebuffer.cpp
+++ b/libraries/gpu/src/gpu/Framebuffer.cpp
@@ -220,7 +220,7 @@ uint32 Framebuffer::getRenderBufferSubresource(uint32 slot) const {
}
}
-bool Framebuffer::setDepthStencilBuffer(const TexturePointer& texture, const Format& format, uint32 subresource) {
+bool Framebuffer::assignDepthStencilBuffer(const TexturePointer& texture, const Format& format, uint32 subresource) {
if (isSwapchain()) {
return false;
}
@@ -244,20 +244,59 @@ bool Framebuffer::setDepthStencilBuffer(const TexturePointer& texture, const For
// assign the new one
_depthStencilBuffer = TextureView(texture, subresource, format);
- _bufferMask = ( _bufferMask & ~BUFFER_DEPTHSTENCIL);
- if (texture) {
- if (format.getSemantic() == gpu::DEPTH) {
- _bufferMask |= BUFFER_DEPTH;
- } else if (format.getSemantic() == gpu::STENCIL) {
- _bufferMask |= BUFFER_STENCIL;
- } else if (format.getSemantic() == gpu::DEPTH_STENCIL) {
- _bufferMask |= BUFFER_DEPTHSTENCIL;
- }
- }
-
return true;
}
+bool Framebuffer::setDepthBuffer(const TexturePointer& texture, const Format& format, uint32 subresource) {
+ if (assignDepthStencilBuffer(texture, format, subresource)) {
+ _bufferMask = (_bufferMask & ~BUFFER_DEPTHSTENCIL);
+ if (texture) {
+ if (format.getSemantic() == gpu::DEPTH || format.getSemantic() == gpu::DEPTH_STENCIL) {
+ _bufferMask |= BUFFER_DEPTH;
+ } else {
+ return false;
+ }
+ }
+
+ return true;
+ }
+ return false;
+}
+
+bool Framebuffer::setStencilBuffer(const TexturePointer& texture, const Format& format, uint32 subresource) {
+ if (assignDepthStencilBuffer(texture, format, subresource)) {
+ _bufferMask = (_bufferMask & ~BUFFER_DEPTHSTENCIL);
+ if (texture) {
+ if (format.getSemantic() == gpu::STENCIL || format.getSemantic() == gpu::DEPTH_STENCIL) {
+ _bufferMask |= BUFFER_STENCIL;
+ } else {
+ return false;
+ }
+ }
+
+ return true;
+ }
+ return false;
+}
+
+bool Framebuffer::setDepthStencilBuffer(const TexturePointer& texture, const Format& format, uint32 subresource) {
+ if (assignDepthStencilBuffer(texture, format, subresource)) {
+ _bufferMask = (_bufferMask & ~BUFFER_DEPTHSTENCIL);
+ if (texture) {
+ if (format.getSemantic() == gpu::DEPTH) {
+ _bufferMask |= BUFFER_DEPTH;
+ } else if (format.getSemantic() == gpu::STENCIL) {
+ _bufferMask |= BUFFER_STENCIL;
+ } else if (format.getSemantic() == gpu::DEPTH_STENCIL) {
+ _bufferMask |= BUFFER_DEPTHSTENCIL;
+ }
+ }
+
+ return true;
+ }
+ return false;
+}
+
TexturePointer Framebuffer::getDepthStencilBuffer() const {
if (isSwapchain()) {
return TexturePointer();
diff --git a/libraries/gpu/src/gpu/Framebuffer.h b/libraries/gpu/src/gpu/Framebuffer.h
index b3a500d68f..b3cf0fbba3 100755
--- a/libraries/gpu/src/gpu/Framebuffer.h
+++ b/libraries/gpu/src/gpu/Framebuffer.h
@@ -107,6 +107,8 @@ public:
TexturePointer getRenderBuffer(uint32 slot) const;
uint32 getRenderBufferSubresource(uint32 slot) const;
+ bool setDepthBuffer(const TexturePointer& texture, const Format& format, uint32 subresource = 0);
+ bool setStencilBuffer(const TexturePointer& texture, const Format& format, uint32 subresource = 0);
bool setDepthStencilBuffer(const TexturePointer& texture, const Format& format, uint32 subresource = 0);
TexturePointer getDepthStencilBuffer() const;
uint32 getDepthStencilBufferSubresource() const;
@@ -168,6 +170,7 @@ protected:
uint16 _numSamples = 0;
void updateSize(const TexturePointer& texture);
+ bool assignDepthStencilBuffer(const TexturePointer& texture, const Format& format, uint32 subresource);
// Non exposed
Framebuffer(const Framebuffer& framebuffer) = delete;
diff --git a/libraries/gpu/src/gpu/StandardShaderLib.cpp b/libraries/gpu/src/gpu/StandardShaderLib.cpp
index 7143242618..0d8d131e0b 100755
--- a/libraries/gpu/src/gpu/StandardShaderLib.cpp
+++ b/libraries/gpu/src/gpu/StandardShaderLib.cpp
@@ -22,6 +22,7 @@
const char DrawNada_frag[] = "void main(void) {}"; // DrawNada is really simple...
#include "DrawWhite_frag.h"
+#include "DrawColor_frag.h"
#include "DrawTexture_frag.h"
#include "DrawTextureMirroredX_frag.h"
#include "DrawTextureOpaque_frag.h"
@@ -37,6 +38,7 @@ ShaderPointer StandardShaderLib::_drawVertexPositionVS;
ShaderPointer StandardShaderLib::_drawTransformVertexPositionVS;
ShaderPointer StandardShaderLib::_drawNadaPS;
ShaderPointer StandardShaderLib::_drawWhitePS;
+ShaderPointer StandardShaderLib::_drawColorPS;
ShaderPointer StandardShaderLib::_drawTexturePS;
ShaderPointer StandardShaderLib::_drawTextureMirroredXPS;
ShaderPointer StandardShaderLib::_drawTextureOpaquePS;
@@ -125,6 +127,13 @@ ShaderPointer StandardShaderLib::getDrawWhitePS() {
return _drawWhitePS;
}
+ShaderPointer StandardShaderLib::getDrawColorPS() {
+ if (!_drawColorPS) {
+ _drawColorPS = gpu::Shader::createPixel(std::string(DrawColor_frag));
+ }
+ return _drawColorPS;
+}
+
ShaderPointer StandardShaderLib::getDrawTexturePS() {
if (!_drawTexturePS) {
_drawTexturePS = gpu::Shader::createPixel(std::string(DrawTexture_frag));
diff --git a/libraries/gpu/src/gpu/StandardShaderLib.h b/libraries/gpu/src/gpu/StandardShaderLib.h
index 94885b8ca0..9c11f6cc3a 100755
--- a/libraries/gpu/src/gpu/StandardShaderLib.h
+++ b/libraries/gpu/src/gpu/StandardShaderLib.h
@@ -46,6 +46,7 @@ public:
static ShaderPointer getDrawNadaPS();
static ShaderPointer getDrawWhitePS();
+ static ShaderPointer getDrawColorPS();
static ShaderPointer getDrawTexturePS();
static ShaderPointer getDrawTextureMirroredXPS();
static ShaderPointer getDrawTextureOpaquePS();
@@ -67,6 +68,7 @@ protected:
static ShaderPointer _drawNadaPS;
static ShaderPointer _drawWhitePS;
+ static ShaderPointer _drawColorPS;
static ShaderPointer _drawTexturePS;
static ShaderPointer _drawTextureMirroredXPS;
static ShaderPointer _drawTextureOpaquePS;
diff --git a/libraries/gpu/src/gpu/Transform.slh b/libraries/gpu/src/gpu/Transform.slh
index 9feca4a3c9..b9b8544601 100644
--- a/libraries/gpu/src/gpu/Transform.slh
+++ b/libraries/gpu/src/gpu/Transform.slh
@@ -193,13 +193,17 @@ TransformObject getTransformObject() {
}
<@endfunc@>
-<@func transformModelToClipPos(cameraTransform, objectTransform, modelPos, clipPos)@>
- { // transformModelToClipPos
+<@func transformModelToMonoClipPos(cameraTransform, objectTransform, modelPos, clipPos)@>
+ { // transformModelToMonoClipPos
vec4 eyeWAPos;
<$transformModelToEyeWorldAlignedPos($cameraTransform$, $objectTransform$, $modelPos$, eyeWAPos)$>
-
<$clipPos$> = <$cameraTransform$>._projectionViewUntranslated * eyeWAPos;
-
+ }
+<@endfunc@>
+
+<@func transformModelToClipPos(cameraTransform, objectTransform, modelPos, clipPos)@>
+ { // transformModelToClipPos
+ <$transformModelToMonoClipPos($cameraTransform$, $objectTransform$, $modelPos$, $clipPos$)$>
<$transformStereoClipsSpace($cameraTransform$, $clipPos$)$>
}
<@endfunc@>
diff --git a/libraries/model/src/model/Haze.cpp b/libraries/model/src/model/Haze.cpp
index c9c73bcee9..b56932e131 100644
--- a/libraries/model/src/model/Haze.cpp
+++ b/libraries/model/src/model/Haze.cpp
@@ -8,8 +8,8 @@
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
-#include
+#include
#include "Haze.h"
using namespace model;
diff --git a/libraries/networking/src/LimitedNodeList.cpp b/libraries/networking/src/LimitedNodeList.cpp
index 300a445ebd..a9ebc5481d 100644
--- a/libraries/networking/src/LimitedNodeList.cpp
+++ b/libraries/networking/src/LimitedNodeList.cpp
@@ -27,7 +27,6 @@
#include
#include
#include
-#include
#include
#include "AccountManager.h"
@@ -1110,7 +1109,6 @@ void LimitedNodeList::setLocalSocket(const HifiSockAddr& sockAddr) {
qCInfo(networking) << "Local socket is" << sockAddr;
} else {
qCInfo(networking) << "Local socket has changed from" << _localSockAddr << "to" << sockAddr;
- DependencyManager::get()->incrementStat(LOCAL_SOCKET_CHANGE_STAT);
}
_localSockAddr = sockAddr;
diff --git a/libraries/networking/src/LimitedNodeList.h b/libraries/networking/src/LimitedNodeList.h
index 994f91db19..868e36f160 100644
--- a/libraries/networking/src/LimitedNodeList.h
+++ b/libraries/networking/src/LimitedNodeList.h
@@ -66,8 +66,6 @@ const QHostAddress DEFAULT_ASSIGNMENT_CLIENT_MONITOR_HOSTNAME = QHostAddress::Lo
const QString USERNAME_UUID_REPLACEMENT_STATS_KEY = "$username";
-const QString LOCAL_SOCKET_CHANGE_STAT = "LocalSocketChanges";
-
typedef std::pair UUIDNodePair;
typedef tbb::concurrent_unordered_map NodeHash;
diff --git a/libraries/octree/src/OctreeQuery.h b/libraries/octree/src/OctreeQuery.h
index 81a63a696c..bbd0350baf 100644
--- a/libraries/octree/src/OctreeQuery.h
+++ b/libraries/octree/src/OctreeQuery.h
@@ -12,21 +12,7 @@
#ifndef hifi_OctreeQuery_h
#define hifi_OctreeQuery_h
-/* VS2010 defines stdint.h, but not inttypes.h */
-#if defined(_MSC_VER)
-typedef signed char int8_t;
-typedef signed short int16_t;
-typedef signed int int32_t;
-typedef unsigned char uint8_t;
-typedef unsigned short uint16_t;
-typedef unsigned int uint32_t;
-typedef signed long long int64_t;
-typedef unsigned long long quint64;
-#define PRId64 "I64d"
-#else
#include
-#endif
-
#include
#include
@@ -45,7 +31,7 @@ public:
virtual ~OctreeQuery() {}
int getBroadcastData(unsigned char* destinationBuffer);
- int parseData(ReceivedMessage& message) override;
+ virtual int parseData(ReceivedMessage& message) override;
// getters for camera details
const glm::vec3& getCameraPosition() const { return _cameraPosition; }
diff --git a/libraries/octree/src/OctreeQueryNode.cpp b/libraries/octree/src/OctreeQueryNode.cpp
index c26b4ce77b..941bb6b536 100644
--- a/libraries/octree/src/OctreeQueryNode.cpp
+++ b/libraries/octree/src/OctreeQueryNode.cpp
@@ -18,6 +18,12 @@
#include
#include
+int OctreeQueryNode::parseData(ReceivedMessage& message) {
+ // set our flag to indicate that we've parsed for this query at least once
+ _hasReceivedFirstQuery = true;
+
+ return OctreeQuery::parseData(message);
+}
void OctreeQueryNode::nodeKilled() {
_isShuttingDown = true;
diff --git a/libraries/octree/src/OctreeQueryNode.h b/libraries/octree/src/OctreeQueryNode.h
index fd89a89949..fac118c628 100644
--- a/libraries/octree/src/OctreeQueryNode.h
+++ b/libraries/octree/src/OctreeQueryNode.h
@@ -35,6 +35,8 @@ public:
void init(); // called after creation to set up some virtual items
virtual PacketType getMyPacketType() const = 0;
+ virtual int parseData(ReceivedMessage& message) override;
+
void resetOctreePacket(); // resets octree packet to after "V" header
void writeToPacket(const unsigned char* buffer, unsigned int bytes); // writes to end of packet
@@ -106,6 +108,8 @@ public:
bool shouldForceFullScene() const { return _shouldForceFullScene; }
void setShouldForceFullScene(bool shouldForceFullScene) { _shouldForceFullScene = shouldForceFullScene; }
+ bool hasReceivedFirstQuery() const { return _hasReceivedFirstQuery; }
+
private:
OctreeQueryNode(const OctreeQueryNode &);
OctreeQueryNode& operator= (const OctreeQueryNode&);
@@ -153,6 +157,8 @@ private:
QJsonObject _lastCheckJSONParameters;
bool _shouldForceFullScene { false };
+
+ bool _hasReceivedFirstQuery { false };
};
#endif // hifi_OctreeQueryNode_h
diff --git a/libraries/physics/src/ShapeFactory.cpp b/libraries/physics/src/ShapeFactory.cpp
index cd0fba848a..f484f32fdf 100644
--- a/libraries/physics/src/ShapeFactory.cpp
+++ b/libraries/physics/src/ShapeFactory.cpp
@@ -16,6 +16,40 @@
#include "ShapeFactory.h"
#include "BulletUtil.h"
+
+class StaticMeshShape : public btBvhTriangleMeshShape {
+public:
+ StaticMeshShape() = delete;
+
+ StaticMeshShape(btTriangleIndexVertexArray* dataArray)
+ : btBvhTriangleMeshShape(dataArray, true), _dataArray(dataArray) {
+ assert(_dataArray);
+ }
+
+ ~StaticMeshShape() {
+ assert(_dataArray);
+ IndexedMeshArray& meshes = _dataArray->getIndexedMeshArray();
+ for (int32_t i = 0; i < meshes.size(); ++i) {
+ btIndexedMesh mesh = meshes[i];
+ mesh.m_numTriangles = 0;
+ delete [] mesh.m_triangleIndexBase;
+ mesh.m_triangleIndexBase = nullptr;
+ mesh.m_numVertices = 0;
+ delete [] mesh.m_vertexBase;
+ mesh.m_vertexBase = nullptr;
+ }
+ meshes.clear();
+ delete _dataArray;
+ _dataArray = nullptr;
+ }
+
+private:
+ // the StaticMeshShape owns its vertex/index data
+ btTriangleIndexVertexArray* _dataArray;
+};
+
+// the dataArray must be created before we create the StaticMeshShape
+
// These are the same normalized directions used by the btShapeHull class.
// 12 points for the face centers of a dodecahedron plus another 30 points
// for the midpoints the edges, for a total of 42.
@@ -230,23 +264,6 @@ btTriangleIndexVertexArray* createStaticMeshArray(const ShapeInfo& info) {
return dataArray;
}
-// util method
-void deleteStaticMeshArray(btTriangleIndexVertexArray* dataArray) {
- assert(dataArray);
- IndexedMeshArray& meshes = dataArray->getIndexedMeshArray();
- for (int32_t i = 0; i < meshes.size(); ++i) {
- btIndexedMesh mesh = meshes[i];
- mesh.m_numTriangles = 0;
- delete [] mesh.m_triangleIndexBase;
- mesh.m_triangleIndexBase = nullptr;
- mesh.m_numVertices = 0;
- delete [] mesh.m_vertexBase;
- mesh.m_vertexBase = nullptr;
- }
- meshes.clear();
- delete dataArray;
-}
-
const btCollisionShape* ShapeFactory::createShapeFromInfo(const ShapeInfo& info) {
btCollisionShape* shape = NULL;
int type = info.getType();
@@ -431,7 +448,6 @@ void ShapeFactory::deleteShape(const btCollisionShape* shape) {
assert(shape);
// ShapeFactory is responsible for deleting all shapes, even the const ones that are stored
// in the ShapeManager, so we must cast to non-const here when deleting.
- // so we cast to non-const here when deleting memory.
btCollisionShape* nonConstShape = const_cast(shape);
if (nonConstShape->getShapeType() == (int)COMPOUND_SHAPE_PROXYTYPE) {
btCompoundShape* compoundShape = static_cast(nonConstShape);
@@ -448,14 +464,3 @@ void ShapeFactory::deleteShape(const btCollisionShape* shape) {
}
delete nonConstShape;
}
-
-// the dataArray must be created before we create the StaticMeshShape
-ShapeFactory::StaticMeshShape::StaticMeshShape(btTriangleIndexVertexArray* dataArray)
-: btBvhTriangleMeshShape(dataArray, true), _dataArray(dataArray) {
- assert(dataArray);
-}
-
-ShapeFactory::StaticMeshShape::~StaticMeshShape() {
- deleteStaticMeshArray(_dataArray);
- _dataArray = nullptr;
-}
diff --git a/libraries/physics/src/ShapeFactory.h b/libraries/physics/src/ShapeFactory.h
index 2bf79f390c..704a7804b3 100644
--- a/libraries/physics/src/ShapeFactory.h
+++ b/libraries/physics/src/ShapeFactory.h
@@ -17,25 +17,11 @@
#include
-// translates between ShapeInfo and btShape
+// The ShapeFactory assembles and correctly disassembles btCollisionShapes.
namespace ShapeFactory {
const btCollisionShape* createShapeFromInfo(const ShapeInfo& info);
void deleteShape(const btCollisionShape* shape);
-
- //btTriangleIndexVertexArray* createStaticMeshArray(const ShapeInfo& info);
- //void deleteStaticMeshArray(btTriangleIndexVertexArray* dataArray);
-
- class StaticMeshShape : public btBvhTriangleMeshShape {
- public:
- StaticMeshShape() = delete;
- StaticMeshShape(btTriangleIndexVertexArray* dataArray);
- ~StaticMeshShape();
-
- private:
- // the StaticMeshShape owns its vertex/index data
- btTriangleIndexVertexArray* _dataArray;
- };
};
#endif // hifi_ShapeFactory_h
diff --git a/libraries/physics/src/ShapeManager.cpp b/libraries/physics/src/ShapeManager.cpp
index 77716f671b..97b9e5dab1 100644
--- a/libraries/physics/src/ShapeManager.cpp
+++ b/libraries/physics/src/ShapeManager.cpp
@@ -32,7 +32,7 @@ const btCollisionShape* ShapeManager::getShape(const ShapeInfo& info) {
if (info.getType() == SHAPE_TYPE_NONE) {
return nullptr;
}
- DoubleHashKey key = info.getHash();
+ HashKey key = info.getHash();
ShapeReference* shapeRef = _shapeMap.find(key);
if (shapeRef) {
shapeRef->refCount++;
@@ -50,7 +50,7 @@ const btCollisionShape* ShapeManager::getShape(const ShapeInfo& info) {
}
// private helper method
-bool ShapeManager::releaseShapeByKey(const DoubleHashKey& key) {
+bool ShapeManager::releaseShapeByKey(const HashKey& key) {
ShapeReference* shapeRef = _shapeMap.find(key);
if (shapeRef) {
if (shapeRef->refCount > 0) {
@@ -88,7 +88,7 @@ bool ShapeManager::releaseShape(const btCollisionShape* shape) {
void ShapeManager::collectGarbage() {
int numShapes = _pendingGarbage.size();
for (int i = 0; i < numShapes; ++i) {
- DoubleHashKey& key = _pendingGarbage[i];
+ HashKey& key = _pendingGarbage[i];
ShapeReference* shapeRef = _shapeMap.find(key);
if (shapeRef && shapeRef->refCount == 0) {
ShapeFactory::deleteShape(shapeRef->shape);
@@ -99,7 +99,7 @@ void ShapeManager::collectGarbage() {
}
int ShapeManager::getNumReferences(const ShapeInfo& info) const {
- DoubleHashKey key = info.getHash();
+ HashKey key = info.getHash();
const ShapeReference* shapeRef = _shapeMap.find(key);
if (shapeRef) {
return shapeRef->refCount;
diff --git a/libraries/physics/src/ShapeManager.h b/libraries/physics/src/ShapeManager.h
index ed81b5e8f8..d75bb1dc4a 100644
--- a/libraries/physics/src/ShapeManager.h
+++ b/libraries/physics/src/ShapeManager.h
@@ -17,7 +17,29 @@
#include
-#include "DoubleHashKey.h"
+#include "HashKey.h"
+
+// The ShapeManager handles the ref-counting on shared shapes:
+//
+// Each object added to the physics simulation gets a corresponding btRigidBody.
+// The body has a btCollisionShape that represents the contours of its collision
+// surface. Multiple bodies may have the same shape. Rather than create a unique
+// btCollisionShape instance for every body with a particular shape we can instead
+// use a single shape instance for all of the bodies. This is called "shape
+// sharing".
+//
+// When body needs a new shape a description of ths shape (ShapeInfo) is assembled
+// and a request is sent to the ShapeManager for a corresponding btCollisionShape
+// pointer. The ShapeManager will compute a hash of the ShapeInfo's data and use
+// that to find the shape in its map. If it finds one it increments the ref-count
+// and returns the pointer. If not it asks the ShapeFactory to create it, adds an
+// entry in the map with a ref-count of 1, and returns the pointer.
+//
+// When a body stops using a shape the ShapeManager must be informed so it can
+// decrement its ref-count. When a ref-count drops to zero the ShapeManager
+// doesn't delete it right away. Instead it puts the shape's key on a list delete
+// later. When that list grows big enough the ShapeManager will remove any matching
+// entries that still have zero ref-count.
class ShapeManager {
public:
@@ -41,18 +63,19 @@ public:
bool hasShape(const btCollisionShape* shape) const;
private:
- bool releaseShapeByKey(const DoubleHashKey& key);
+ bool releaseShapeByKey(const HashKey& key);
class ShapeReference {
public:
int refCount;
const btCollisionShape* shape;
- DoubleHashKey key;
+ HashKey key;
ShapeReference() : refCount(0), shape(nullptr) {}
};
- btHashMap _shapeMap;
- btAlignedObjectArray _pendingGarbage;
+ // btHashMap is required because it supports memory alignment of the btCollisionShapes
+ btHashMap _shapeMap;
+ btAlignedObjectArray _pendingGarbage;
};
#endif // hifi_ShapeManager_h
diff --git a/libraries/render-utils/src/BackgroundStage.cpp b/libraries/render-utils/src/BackgroundStage.cpp
index 2ea3683c4a..2d2c0ed150 100644
--- a/libraries/render-utils/src/BackgroundStage.cpp
+++ b/libraries/render-utils/src/BackgroundStage.cpp
@@ -14,6 +14,7 @@
#include
std::string BackgroundStage::_stageName { "BACKGROUND_STAGE"};
+const BackgroundStage::Index BackgroundStage::INVALID_INDEX { render::indexed_container::INVALID_INDEX };
BackgroundStage::Index BackgroundStage::findBackground(const BackgroundPointer& background) const {
auto found = _backgroundMap.find(background);
diff --git a/libraries/render-utils/src/BackgroundStage.h b/libraries/render-utils/src/BackgroundStage.h
index eab7c94f0d..4e0e09db5b 100644
--- a/libraries/render-utils/src/BackgroundStage.h
+++ b/libraries/render-utils/src/BackgroundStage.h
@@ -27,7 +27,7 @@ public:
static const std::string& getName() { return _stageName; }
using Index = render::indexed_container::Index;
- static const Index INVALID_INDEX { render::indexed_container::INVALID_INDEX };
+ static const Index INVALID_INDEX;
static bool isIndexInvalid(Index index) { return index == INVALID_INDEX; }
using BackgroundPointer = model::SunSkyStagePointer;
diff --git a/libraries/render-utils/src/BloomApply.slf b/libraries/render-utils/src/BloomApply.slf
new file mode 100644
index 0000000000..953258e8ab
--- /dev/null
+++ b/libraries/render-utils/src/BloomApply.slf
@@ -0,0 +1,27 @@
+<@include gpu/Config.slh@>
+<$VERSION_HEADER$>
+// BloomApply.slf
+// Mix the three gaussian blur textures.
+//
+// Created by Olivier Prat on 10/09/2017
+// Copyright 2017 High Fidelity, Inc.
+//
+// Distributed under the Apache License, Version 2.0.
+// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
+//
+
+uniform sampler2D blurMap0;
+uniform sampler2D blurMap1;
+uniform sampler2D blurMap2;
+uniform float intensity;
+
+in vec2 varTexCoord0;
+out vec4 outFragColor;
+
+void main(void) {
+ vec4 blur0 = texture(blurMap0, varTexCoord0);
+ vec4 blur1 = texture(blurMap1, varTexCoord0);
+ vec4 blur2 = texture(blurMap2, varTexCoord0);
+
+ outFragColor = vec4((blur0.rgb+blur1.rgb+blur2.rgb)*intensity, 1.0f);
+}
diff --git a/libraries/render-utils/src/BloomEffect.cpp b/libraries/render-utils/src/BloomEffect.cpp
new file mode 100644
index 0000000000..9d9367a6d5
--- /dev/null
+++ b/libraries/render-utils/src/BloomEffect.cpp
@@ -0,0 +1,359 @@
+//
+// BloomEffect.cpp
+// render-utils/src/
+//
+// Created by Olivier Prat on 09/25/17.
+// Copyright 2017 High Fidelity, Inc.
+//
+// Distributed under the Apache License, Version 2.0.
+// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
+//
+#include "BloomEffect.h"
+
+#include "gpu/Context.h"
+#include "gpu/StandardShaderLib.h"
+
+#include
+#include
+
+#include "BloomThreshold_frag.h"
+#include "BloomApply_frag.h"
+
+#define BLOOM_BLUR_LEVEL_COUNT 3
+
+BloomThreshold::BloomThreshold(unsigned int downsamplingFactor) :
+ _downsamplingFactor(downsamplingFactor) {
+ assert(downsamplingFactor > 0);
+}
+
+void BloomThreshold::configure(const Config& config) {
+ _threshold = config.threshold;
+}
+
+void BloomThreshold::run(const render::RenderContextPointer& renderContext, const Inputs& inputs, Outputs& outputs) {
+ assert(renderContext->args);
+ assert(renderContext->args->hasViewFrustum());
+
+ RenderArgs* args = renderContext->args;
+
+ const auto frameTransform = inputs.get0();
+ const auto inputFrameBuffer = inputs.get1();
+
+ assert(inputFrameBuffer->hasColor());
+
+ auto inputBuffer = inputFrameBuffer->getRenderBuffer(0);
+ auto bufferSize = gpu::Vec2u(inputBuffer->getDimensions());
+
+ // Downsample resolution
+ bufferSize.x /= _downsamplingFactor;
+ bufferSize.y /= _downsamplingFactor;
+
+ if (!_outputBuffer || _outputBuffer->getSize() != bufferSize) {
+ auto colorTexture = gpu::TexturePointer(gpu::Texture::createRenderBuffer(inputBuffer->getTexelFormat(), bufferSize.x, bufferSize.y,
+ gpu::Texture::SINGLE_MIP, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_LINEAR_MIP_POINT)));
+
+ _outputBuffer = gpu::FramebufferPointer(gpu::Framebuffer::create("BloomThreshold"));
+ _outputBuffer->setRenderBuffer(0, colorTexture);
+ }
+
+ static const int COLOR_MAP_SLOT = 0;
+ static const int THRESHOLD_SLOT = 1;
+
+ if (!_pipeline) {
+ auto vs = gpu::StandardShaderLib::getDrawTransformUnitQuadVS();
+ auto ps = gpu::Shader::createPixel(std::string(BloomThreshold_frag));
+ gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps);
+
+ gpu::Shader::BindingSet slotBindings;
+ slotBindings.insert(gpu::Shader::Binding("colorMap", COLOR_MAP_SLOT));
+ slotBindings.insert(gpu::Shader::Binding("threshold", THRESHOLD_SLOT));
+ gpu::Shader::makeProgram(*program, slotBindings);
+
+ gpu::StatePointer state = gpu::StatePointer(new gpu::State());
+ _pipeline = gpu::Pipeline::create(program, state);
+ }
+
+ glm::ivec4 viewport{ 0, 0, bufferSize.x, bufferSize.y };
+
+ gpu::doInBatch(args->_context, [&](gpu::Batch& batch) {
+ batch.enableStereo(false);
+
+ batch.setViewportTransform(viewport);
+ batch.setProjectionTransform(glm::mat4());
+ batch.resetViewTransform();
+ batch.setModelTransform(gpu::Framebuffer::evalSubregionTexcoordTransform(bufferSize, viewport));
+ batch.setPipeline(_pipeline);
+
+ batch.setFramebuffer(_outputBuffer);
+ batch.setResourceTexture(COLOR_MAP_SLOT, inputBuffer);
+ batch._glUniform1f(THRESHOLD_SLOT, _threshold);
+ batch.draw(gpu::TRIANGLE_STRIP, 4);
+ });
+
+ outputs = _outputBuffer;
+}
+
+BloomApply::BloomApply() {
+
+}
+
+void BloomApply::configure(const Config& config) {
+ _intensity = config.intensity;
+}
+
+void BloomApply::run(const render::RenderContextPointer& renderContext, const Inputs& inputs) {
+ assert(renderContext->args);
+ assert(renderContext->args->hasViewFrustum());
+ RenderArgs* args = renderContext->args;
+
+ static auto BLUR0_SLOT = 0;
+ static auto BLUR1_SLOT = 1;
+ static auto BLUR2_SLOT = 2;
+ static auto INTENSITY_SLOT = 3;
+
+ if (!_pipeline) {
+ auto vs = gpu::StandardShaderLib::getDrawTransformUnitQuadVS();
+ auto ps = gpu::Shader::createPixel(std::string(BloomApply_frag));
+ gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps);
+
+ gpu::Shader::BindingSet slotBindings;
+ slotBindings.insert(gpu::Shader::Binding("blurMap0", BLUR0_SLOT));
+ slotBindings.insert(gpu::Shader::Binding("blurMap1", BLUR1_SLOT));
+ slotBindings.insert(gpu::Shader::Binding("blurMap2", BLUR2_SLOT));
+ slotBindings.insert(gpu::Shader::Binding("intensity", INTENSITY_SLOT));
+ gpu::Shader::makeProgram(*program, slotBindings);
+
+ gpu::StatePointer state = gpu::StatePointer(new gpu::State());
+ state->setDepthTest(gpu::State::DepthTest(false, false));
+ _pipeline = gpu::Pipeline::create(program, state);
+ }
+
+ const auto frameBuffer = inputs.get0();
+ const auto framebufferSize = frameBuffer->getSize();
+ const auto blur0FB = inputs.get1();
+ const auto blur1FB = inputs.get2();
+ const auto blur2FB = inputs.get3();
+ const glm::ivec4 viewport{ 0, 0, framebufferSize.x, framebufferSize.y };
+
+ gpu::doInBatch(args->_context, [&](gpu::Batch& batch) {
+ batch.enableStereo(false);
+
+ batch.setFramebuffer(frameBuffer);
+
+ batch.setViewportTransform(viewport);
+ batch.setProjectionTransform(glm::mat4());
+ batch.resetViewTransform();
+ batch.setPipeline(_pipeline);
+
+ batch.setModelTransform(gpu::Framebuffer::evalSubregionTexcoordTransform(framebufferSize, viewport));
+ batch.setResourceTexture(BLUR0_SLOT, blur0FB->getRenderBuffer(0));
+ batch.setResourceTexture(BLUR1_SLOT, blur1FB->getRenderBuffer(0));
+ batch.setResourceTexture(BLUR2_SLOT, blur2FB->getRenderBuffer(0));
+ batch._glUniform1f(INTENSITY_SLOT, _intensity / 3.0f);
+ batch.draw(gpu::TRIANGLE_STRIP, 4);
+ });
+}
+
+void BloomDraw::run(const render::RenderContextPointer& renderContext, const Inputs& inputs) {
+ assert(renderContext->args);
+ assert(renderContext->args->hasViewFrustum());
+ RenderArgs* args = renderContext->args;
+
+ const auto frameBuffer = inputs.get0();
+ const auto bloomFrameBuffer = inputs.get1();
+
+ if (frameBuffer && bloomFrameBuffer) {
+ const auto framebufferSize = frameBuffer->getSize();
+
+ if (!_pipeline) {
+ auto vs = gpu::StandardShaderLib::getDrawTransformUnitQuadVS();
+ auto ps = gpu::StandardShaderLib::getDrawTextureOpaquePS();
+ gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps);
+
+ gpu::Shader::BindingSet slotBindings;
+ gpu::Shader::makeProgram(*program, slotBindings);
+
+ gpu::StatePointer state = gpu::StatePointer(new gpu::State());
+ state->setDepthTest(gpu::State::DepthTest(false, false));
+ state->setBlendFunction(true, gpu::State::ONE, gpu::State::BLEND_OP_ADD, gpu::State::ONE,
+ gpu::State::ZERO, gpu::State::BLEND_OP_ADD, gpu::State::ONE);
+ _pipeline = gpu::Pipeline::create(program, state);
+ }
+
+ gpu::doInBatch(args->_context, [&](gpu::Batch& batch) {
+ batch.enableStereo(false);
+
+ batch.setFramebuffer(frameBuffer);
+
+ batch.setViewportTransform(args->_viewport);
+ batch.setProjectionTransform(glm::mat4());
+ batch.resetViewTransform();
+ batch.setPipeline(_pipeline);
+
+ batch.setModelTransform(gpu::Framebuffer::evalSubregionTexcoordTransform(framebufferSize, args->_viewport));
+ batch.setResourceTexture(0, bloomFrameBuffer->getRenderBuffer(0));
+ batch.draw(gpu::TRIANGLE_STRIP, 4);
+ });
+ }
+}
+
+DebugBloom::DebugBloom() {
+}
+
+void DebugBloom::configure(const Config& config) {
+ _mode = static_cast(config.mode);
+ assert(_mode < DebugBloomConfig::MODE_COUNT);
+}
+
+void DebugBloom::run(const render::RenderContextPointer& renderContext, const Inputs& inputs) {
+ assert(renderContext->args);
+ assert(renderContext->args->hasViewFrustum());
+ RenderArgs* args = renderContext->args;
+
+ const auto frameBuffer = inputs.get0();
+ const auto combinedBlurBuffer = inputs.get4();
+ const auto framebufferSize = frameBuffer->getSize();
+ const auto level0FB = inputs.get1();
+ const auto level1FB = inputs.get2();
+ const auto level2FB = inputs.get3();
+ const gpu::TexturePointer levelTextures[BLOOM_BLUR_LEVEL_COUNT] = {
+ level0FB->getRenderBuffer(0),
+ level1FB->getRenderBuffer(0),
+ level2FB->getRenderBuffer(0)
+ };
+
+ static auto TEXCOORD_RECT_SLOT = 1;
+
+ if (!_pipeline) {
+ auto vs = gpu::StandardShaderLib::getDrawTexcoordRectTransformUnitQuadVS();
+ auto ps = gpu::StandardShaderLib::getDrawTextureOpaquePS();
+ gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps);
+
+ gpu::Shader::BindingSet slotBindings;
+ slotBindings.insert(gpu::Shader::Binding(std::string("texcoordRect"), TEXCOORD_RECT_SLOT));
+ gpu::Shader::makeProgram(*program, slotBindings);
+
+ gpu::StatePointer state = gpu::StatePointer(new gpu::State());
+ state->setDepthTest(gpu::State::DepthTest(false));
+ _pipeline = gpu::Pipeline::create(program, state);
+ }
+
+ gpu::doInBatch(args->_context, [&](gpu::Batch& batch) {
+ batch.enableStereo(false);
+
+ batch.setFramebuffer(frameBuffer);
+
+ batch.setViewportTransform(args->_viewport);
+ batch.setProjectionTransform(glm::mat4());
+ batch.resetViewTransform();
+ batch.setPipeline(_pipeline);
+
+ Transform modelTransform;
+ if (_mode == DebugBloomConfig::MODE_ALL_LEVELS) {
+ batch._glUniform4f(TEXCOORD_RECT_SLOT, 0.0f, 0.0f, 1.f, 1.f);
+
+ modelTransform = gpu::Framebuffer::evalSubregionTexcoordTransform(framebufferSize, args->_viewport / 2);
+ modelTransform.postTranslate(glm::vec3(-1.0f, 1.0f, 0.0f));
+ batch.setModelTransform(modelTransform);
+ batch.setResourceTexture(0, levelTextures[0]);
+ batch.draw(gpu::TRIANGLE_STRIP, 4);
+
+ modelTransform.postTranslate(glm::vec3(2.0f, 0.0f, 0.0f));
+ batch.setModelTransform(modelTransform);
+ batch.setResourceTexture(0, levelTextures[1]);
+ batch.draw(gpu::TRIANGLE_STRIP, 4);
+
+ modelTransform.postTranslate(glm::vec3(-2.0f, -2.0f, 0.0f));
+ batch.setModelTransform(modelTransform);
+ batch.setResourceTexture(0, levelTextures[2]);
+ batch.draw(gpu::TRIANGLE_STRIP, 4);
+
+ modelTransform.postTranslate(glm::vec3(2.0f, 0.0f, 0.0f));
+ batch.setModelTransform(modelTransform);
+ batch.setResourceTexture(0, combinedBlurBuffer->getRenderBuffer(0));
+ batch.draw(gpu::TRIANGLE_STRIP, 4);
+ } else {
+ auto viewport = args->_viewport;
+ auto blurLevel = _mode - DebugBloomConfig::MODE_LEVEL0;
+
+ viewport.z /= 2;
+
+ batch._glUniform4f(TEXCOORD_RECT_SLOT, 0.5f, 0.0f, 0.5f, 1.f);
+
+ modelTransform = gpu::Framebuffer::evalSubregionTexcoordTransform(framebufferSize, viewport);
+ modelTransform.postTranslate(glm::vec3(-1.0f, 0.0f, 0.0f));
+ batch.setModelTransform(modelTransform);
+ batch.setResourceTexture(0, levelTextures[blurLevel]);
+ batch.draw(gpu::TRIANGLE_STRIP, 4);
+ }
+ });
+}
+
+void BloomConfig::setIntensity(float value) {
+ auto task = static_cast(_task);
+ auto blurJobIt = task->editJob("BloomApply");
+ assert(blurJobIt != task->_jobs.end());
+ blurJobIt->getConfiguration()->setProperty("intensity", value);
+}
+
+float BloomConfig::getIntensity() const {
+ auto task = static_cast(_task);
+ auto blurJobIt = task->getJob("BloomApply");
+ assert(blurJobIt != task->_jobs.end());
+ return blurJobIt->getConfiguration()->property("intensity").toFloat();
+}
+
+void BloomConfig::setSize(float value) {
+ std::string blurName{ "BloomBlurN" };
+ auto sigma = 0.5f+value*3.5f;
+
+ for (auto i = 0; i < BLOOM_BLUR_LEVEL_COUNT; i++) {
+ blurName.back() = '0' + i;
+ auto task = static_cast(_task);
+ auto blurJobIt = task->editJob(blurName);
+ assert(blurJobIt != task->_jobs.end());
+ auto& gaussianBlur = blurJobIt->edit();
+ auto gaussianBlurParams = gaussianBlur.getParameters();
+ gaussianBlurParams->setFilterGaussianTaps(5, sigma);
+ // Gaussian blur increases at each level to have a slower rolloff on the edge
+ // of the response
+ sigma *= 1.5f;
+ }
+}
+
+Bloom::Bloom() {
+
+}
+
+void Bloom::configure(const Config& config) {
+ std::string blurName{ "BloomBlurN" };
+
+ for (auto i = 0; i < BLOOM_BLUR_LEVEL_COUNT; i++) {
+ blurName.back() = '0' + i;
+ auto blurConfig = config.getConfig(blurName);
+ blurConfig->setProperty("filterScale", 1.0f);
+ }
+}
+
+void Bloom::build(JobModel& task, const render::Varying& inputs, render::Varying& outputs) {
+ // Start by computing threshold of color buffer input at quarter resolution
+ const auto bloomInputBuffer = task.addJob("BloomThreshold", inputs, 4U);
+
+ // Multi-scale blur, each new blur is half resolution of the previous pass
+ const auto blurFB0 = task.addJob("BloomBlur0", bloomInputBuffer, true);
+ const auto blurFB1 = task.addJob("BloomBlur1", blurFB0, true, 2U);
+ const auto blurFB2 = task.addJob("BloomBlur2", blurFB1, true, 2U);
+
+ const auto& input = inputs.get();
+ const auto& frameBuffer = input[1];
+
+ // Mix all blur levels at quarter resolution
+ const auto applyInput = BloomApply::Inputs(bloomInputBuffer, blurFB0, blurFB1, blurFB2).asVarying();
+ task.addJob("BloomApply", applyInput);
+ // And them blend result in additive manner on top of final color buffer
+ const auto drawInput = BloomDraw::Inputs(frameBuffer, bloomInputBuffer).asVarying();
+ task.addJob("BloomDraw", drawInput);
+
+ const auto debugInput = DebugBloom::Inputs(frameBuffer, blurFB0, blurFB1, blurFB2, bloomInputBuffer).asVarying();
+ task.addJob("DebugBloom", debugInput);
+}
diff --git a/libraries/render-utils/src/BloomEffect.h b/libraries/render-utils/src/BloomEffect.h
new file mode 100644
index 0000000000..5352c65e4d
--- /dev/null
+++ b/libraries/render-utils/src/BloomEffect.h
@@ -0,0 +1,166 @@
+//
+// BloomEffect.h
+// render-utils/src/
+//
+// Created by Olivier Prat on 09/25/17.
+// Copyright 2017 High Fidelity, Inc.
+//
+// Distributed under the Apache License, Version 2.0.
+// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
+//
+
+#ifndef hifi_render_utils_BloomEffect_h
+#define hifi_render_utils_BloomEffect_h
+
+#include
+
+#include "DeferredFrameTransform.h"
+
+class BloomConfig : public render::Task::Config {
+ Q_OBJECT
+ Q_PROPERTY(float intensity READ getIntensity WRITE setIntensity NOTIFY dirty)
+ Q_PROPERTY(float size MEMBER size WRITE setSize NOTIFY dirty)
+
+public:
+
+ BloomConfig() : render::Task::Config(false) {}
+
+ float size{ 0.8f };
+
+ void setIntensity(float value);
+ float getIntensity() const;
+ void setSize(float value);
+
+signals:
+ void dirty();
+};
+
+class BloomThresholdConfig : public render::Job::Config {
+ Q_OBJECT
+ Q_PROPERTY(float threshold MEMBER threshold NOTIFY dirty)
+
+public:
+
+ float threshold{ 1.25f };
+
+signals:
+ void dirty();
+};
+
+class BloomThreshold {
+public:
+ using Inputs = render::VaryingSet2;
+ using Outputs = gpu::FramebufferPointer;
+ using Config = BloomThresholdConfig;
+ using JobModel = render::Job::ModelIO;
+
+ BloomThreshold(unsigned int downsamplingFactor);
+
+ void configure(const Config& config);
+ void run(const render::RenderContextPointer& renderContext, const Inputs& inputs, Outputs& outputs);
+
+private:
+
+ gpu::FramebufferPointer _outputBuffer;
+ gpu::PipelinePointer _pipeline;
+ float _threshold;
+ unsigned int _downsamplingFactor;
+};
+
+
+class BloomApplyConfig : public render::Job::Config {
+ Q_OBJECT
+ Q_PROPERTY(float intensity MEMBER intensity NOTIFY dirty)
+
+public:
+
+ float intensity{ 0.8f };
+
+signals:
+ void dirty();
+};
+
+class BloomApply {
+public:
+ using Inputs = render::VaryingSet4;
+ using Config = BloomApplyConfig;
+ using JobModel = render::Job::ModelI;
+
+ BloomApply();
+
+ void configure(const Config& config);
+ void run(const render::RenderContextPointer& renderContext, const Inputs& inputs);
+
+private:
+
+ gpu::PipelinePointer _pipeline;
+ float _intensity{ 1.0f };
+};
+
+class BloomDraw {
+public:
+ using Inputs = render::VaryingSet2;
+ using JobModel = render::Job::ModelI;
+
+ BloomDraw() {}
+
+ void run(const render::RenderContextPointer& renderContext, const Inputs& inputs);
+
+private:
+
+ gpu::PipelinePointer _pipeline;
+};
+
+class DebugBloomConfig : public render::Job::Config {
+ Q_OBJECT
+ Q_PROPERTY(int mode MEMBER mode NOTIFY dirty)
+
+public:
+
+ enum Mode {
+ MODE_LEVEL0 = 0,
+ MODE_LEVEL1,
+ MODE_LEVEL2,
+ MODE_ALL_LEVELS,
+
+ MODE_COUNT
+ };
+
+ DebugBloomConfig() : render::Job::Config(false) {}
+
+ int mode{ MODE_ALL_LEVELS };
+
+signals:
+ void dirty();
+};
+
+class DebugBloom {
+public:
+ using Inputs = render::VaryingSet5;
+ using Config = DebugBloomConfig;
+ using JobModel = render::Job::ModelI;
+
+ DebugBloom();
+
+ void configure(const Config& config);
+ void run(const render::RenderContextPointer& renderContext, const Inputs& inputs);
+
+private:
+ gpu::PipelinePointer _pipeline;
+ DebugBloomConfig::Mode _mode;
+};
+
+class Bloom {
+public:
+ using Inputs = render::VaryingSet2;
+ using Config = BloomConfig;
+ using JobModel = render::Task::ModelI;
+
+ Bloom();
+
+ void configure(const Config& config);
+ void build(JobModel& task, const render::Varying& inputs, render::Varying& outputs);
+
+};
+
+#endif // hifi_render_utils_BloomEffect_h
diff --git a/libraries/render-utils/src/BloomThreshold.slf b/libraries/render-utils/src/BloomThreshold.slf
new file mode 100644
index 0000000000..e4b96618df
--- /dev/null
+++ b/libraries/render-utils/src/BloomThreshold.slf
@@ -0,0 +1,45 @@
+<@include gpu/Config.slh@>
+<$VERSION_HEADER$>
+// BloomThreshold.slf
+// Perform a soft threshold on an input texture and downsample to half size in one go.
+//
+// Created by Olivier Prat on 09/26/2017
+// Copyright 2017 High Fidelity, Inc.
+//
+// Distributed under the Apache License, Version 2.0.
+// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
+//
+
+uniform sampler2D colorMap;
+uniform float threshold;
+
+in vec2 varTexCoord0;
+out vec4 outFragColor;
+
+#define DOWNSAMPLING_FACTOR 4
+#define SAMPLE_COUNT (DOWNSAMPLING_FACTOR/2)
+
+void main(void) {
+ vec2 deltaX = dFdx(varTexCoord0) / SAMPLE_COUNT;
+ vec2 deltaY = dFdy(varTexCoord0) / SAMPLE_COUNT;
+ vec2 startUv = varTexCoord0;
+ vec4 maskedColor = vec4(0,0,0,0);
+
+ for (int y=0 ; ysetDepthStencilBuffer(_primaryDepthTexture, depthFormat);
- auto smoothSampler = gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR);
+ auto smoothSampler = gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_LINEAR_MIP_POINT);
- _lightingTexture = gpu::Texture::createRenderBuffer(gpu::Element(gpu::SCALAR, gpu::FLOAT, gpu::R11G11B10), width, height, gpu::Texture::SINGLE_MIP, defaultSampler);
+ _lightingTexture = gpu::Texture::createRenderBuffer(gpu::Element(gpu::SCALAR, gpu::FLOAT, gpu::R11G11B10), width, height, gpu::Texture::SINGLE_MIP, smoothSampler);
_lightingFramebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create("lighting"));
_lightingFramebuffer->setRenderBuffer(0, _lightingTexture);
_lightingFramebuffer->setDepthStencilBuffer(_primaryDepthTexture, depthFormat);
diff --git a/libraries/render-utils/src/DeferredLightingEffect.cpp b/libraries/render-utils/src/DeferredLightingEffect.cpp
index 2187cb70b1..646b19198b 100644
--- a/libraries/render-utils/src/DeferredLightingEffect.cpp
+++ b/libraries/render-utils/src/DeferredLightingEffect.cpp
@@ -105,13 +105,13 @@ void DeferredLightingEffect::setupKeyLightBatch(const RenderArgs* args, gpu::Bat
PerformanceTimer perfTimer("DLE->setupBatch()");
model::LightPointer keySunLight;
auto lightStage = args->_scene->getStage();
- if (lightStage && lightStage->_currentFrame._sunLights.size()) {
- keySunLight = lightStage->getLight(lightStage->_currentFrame._sunLights.front());
+ if (lightStage) {
+ keySunLight = lightStage->getCurrentKeyLight();
}
model::LightPointer keyAmbiLight;
- if (lightStage && lightStage->_currentFrame._ambientLights.size()) {
- keyAmbiLight = lightStage->getLight(lightStage->_currentFrame._ambientLights.front());
+ if (lightStage) {
+ keyAmbiLight = lightStage->getCurrentAmbientLight();
}
if (keySunLight) {
@@ -620,7 +620,7 @@ void RenderDeferredLocals::run(const render::RenderContextPointer& renderContext
auto& lightIndices = lightClusters->_visibleLightIndices;
if (!lightIndices.empty() && lightIndices[0] > 0) {
// Bind the global list of lights and the visible lights this frame
- batch.setUniformBuffer(deferredLightingEffect->_localLightLocations->lightBufferUnit, lightClusters->_lightStage->_lightArrayBuffer);
+ batch.setUniformBuffer(deferredLightingEffect->_localLightLocations->lightBufferUnit, lightClusters->_lightStage->getLightArrayBuffer());
batch.setUniformBuffer(LIGHT_CLUSTER_GRID_FRUSTUM_GRID_SLOT, lightClusters->_frustumGridBuffer);
batch.setUniformBuffer(LIGHT_CLUSTER_GRID_CLUSTER_GRID_SLOT, lightClusters->_clusterGridBuffer);
diff --git a/libraries/render-utils/src/DrawHaze.cpp b/libraries/render-utils/src/DrawHaze.cpp
index f694a93033..4431c1bbc3 100644
--- a/libraries/render-utils/src/DrawHaze.cpp
+++ b/libraries/render-utils/src/DrawHaze.cpp
@@ -175,9 +175,9 @@ void DrawHaze::run(const render::RenderContextPointer& renderContext, const Inpu
batch.setUniformBuffer(HazeEffect_TransformBufferSlot, transformBuffer->getFrameTransformBuffer());
auto lightStage = args->_scene->getStage();
- if (lightStage && lightStage->_currentFrame._sunLights.size() > 0) {
- model::LightPointer keyLight;
- keyLight = lightStage->getLight(lightStage->_currentFrame._sunLights.front());
+ if (lightStage) {
+ model::LightPointer keyLight;
+ keyLight = lightStage->getCurrentKeyLight();
if (keyLight != nullptr) {
batch.setUniformBuffer(HazeEffect_LightingMapSlot, keyLight->getLightSchemaBuffer());
}
diff --git a/libraries/render-utils/src/GeometryCache.cpp b/libraries/render-utils/src/GeometryCache.cpp
index fa00737e3c..ebf0f13d97 100644
--- a/libraries/render-utils/src/GeometryCache.cpp
+++ b/libraries/render-utils/src/GeometryCache.cpp
@@ -1931,9 +1931,10 @@ void GeometryCache::renderGlowLine(gpu::Batch& batch, const glm::vec3& p1, const
vec4 p1;
vec4 p2;
vec4 color;
+ float width;
};
- LineData lineData { vec4(p1, 1.0f), vec4(p2, 1.0f), color };
+ LineData lineData { vec4(p1, 1.0f), vec4(p2, 1.0f), color, glowWidth };
details.uniformBuffer->resize(sizeof(LineData));
details.uniformBuffer->setSubData(0, lineData);
}
diff --git a/libraries/render-utils/src/HazeStage.cpp b/libraries/render-utils/src/HazeStage.cpp
index 7a12ee3c8a..016282d16f 100644
--- a/libraries/render-utils/src/HazeStage.cpp
+++ b/libraries/render-utils/src/HazeStage.cpp
@@ -14,6 +14,7 @@
#include
std::string HazeStage::_stageName { "HAZE_STAGE"};
+const HazeStage::Index HazeStage::INVALID_INDEX { render::indexed_container::INVALID_INDEX };
FetchHazeStage::FetchHazeStage() {
_haze = std::make_shared();
diff --git a/libraries/render-utils/src/HazeStage.h b/libraries/render-utils/src/HazeStage.h
index 102f299d8f..c355f06644 100644
--- a/libraries/render-utils/src/HazeStage.h
+++ b/libraries/render-utils/src/HazeStage.h
@@ -28,7 +28,7 @@ public:
static const std::string& getName() { return _stageName; }
using Index = render::indexed_container::Index;
- static const Index INVALID_INDEX { render::indexed_container::INVALID_INDEX };
+ static const Index INVALID_INDEX;
static bool isIndexInvalid(Index index) { return index == INVALID_INDEX; }
using HazePointer = model::HazePointer;
diff --git a/libraries/render-utils/src/Outline.slf b/libraries/render-utils/src/Highlight.slf
similarity index 72%
rename from libraries/render-utils/src/Outline.slf
rename to libraries/render-utils/src/Highlight.slf
index 68ef870cba..bf65f92613 100644
--- a/libraries/render-utils/src/Outline.slf
+++ b/libraries/render-utils/src/Highlight.slf
@@ -1,5 +1,5 @@
-// Outline.slf
-// Add outline effect based on two zbuffers : one containing the total scene z and another
+// Highlight.slf
+// Add highlight effect based on two zbuffers : one containing the total scene z and another
// with the z of only the objects to be outlined.
// This is the version without the fill effect inside the silhouette.
//
@@ -9,5 +9,5 @@
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
-<@include Outline.slh@>
+<@include Highlight.slh@>
<$main(0)$>
diff --git a/libraries/render-utils/src/Outline.slh b/libraries/render-utils/src/Highlight.slh
similarity index 67%
rename from libraries/render-utils/src/Outline.slh
rename to libraries/render-utils/src/Highlight.slh
index ac56e4c95c..2faa10682e 100644
--- a/libraries/render-utils/src/Outline.slh
+++ b/libraries/render-utils/src/Highlight.slh
@@ -1,7 +1,7 @@
<@include gpu/Config.slh@>
<$VERSION_HEADER$>
<$declareDeferredFrameTransform()$>
-<@include Outline_shared.slh@>
+<@include Highlight_shared.slh@>
-uniform outlineParamsBuffer {
- OutlineParameters params;
+uniform highlightParamsBuffer {
+ HighlightParameters params;
};
uniform sampler2D sceneDepthMap;
-uniform sampler2D outlinedDepthMap;
+uniform sampler2D highlightedDepthMap;
in vec2 varTexCoord0;
out vec4 outFragColor;
@@ -35,30 +35,26 @@ void main(void) {
// We offset by half a texel to be centered on the depth sample. If we don't do this
// the blur will have a different width between the left / right sides and top / bottom
// sides of the silhouette
- vec2 halfTexel = getInvWidthHeight() / 2;
- vec2 texCoord0 = varTexCoord0+halfTexel;
- float outlinedDepth = texture(outlinedDepthMap, texCoord0).x;
+ float highlightedDepth = texture(highlightedDepthMap, varTexCoord0).x;
float intensity = 0.0;
- if (outlinedDepth < FAR_Z) {
- // We're not on the far plane so we are on the outlined object, thus no outline to do!
+ if (highlightedDepth < FAR_Z) {
+ // We're not on the far plane so we are on the highlighted object, thus no outline to do!
<@if IS_FILLED@>
// But we need to fill the interior
- float sceneDepth = texture(sceneDepthMap, texCoord0).x;
+ float sceneDepth = texture(sceneDepthMap, varTexCoord0).x;
// Transform to linear depth for better precision
- outlinedDepth = -evalZeyeFromZdb(outlinedDepth);
+ highlightedDepth = -evalZeyeFromZdb(highlightedDepth);
sceneDepth = -evalZeyeFromZdb(sceneDepth);
// Are we occluded?
- if (sceneDepth < (outlinedDepth-LINEAR_DEPTH_BIAS)) {
- intensity = params._fillOpacityOccluded;
- } else {
- intensity = params._fillOpacityUnoccluded;
- }
+ intensity = sceneDepth < (highlightedDepth-LINEAR_DEPTH_BIAS) ? params._occludedFillOpacity : params._unoccludedFillOpacity;
<@else@>
discard;
<@endif@>
} else {
+ vec2 halfTexel = getInvWidthHeight() / 2;
+ vec2 texCoord0 = varTexCoord0+halfTexel;
float weight = 0.0;
vec2 deltaUv = params._size / params._blurKernelSize;
vec2 lineStartUv = texCoord0 - params._size / 2.0;
@@ -74,9 +70,9 @@ void main(void) {
for (x=0 ; x=0.0 && uv.x<=1.0)
{
- outlinedDepth = texture(outlinedDepthMap, uv).x;
- intensity += (outlinedDepth < FAR_Z) ? 1.0 : 0.0;
- weight += 1.f;
+ highlightedDepth = texture(highlightedDepthMap, uv).x;
+ intensity += (highlightedDepth < FAR_Z) ? 1.0 : 0.0;
+ weight += 1.0;
}
uv.x += deltaUv.x;
}
diff --git a/libraries/render-utils/src/HighlightEffect.cpp b/libraries/render-utils/src/HighlightEffect.cpp
new file mode 100644
index 0000000000..7c58e5ba66
--- /dev/null
+++ b/libraries/render-utils/src/HighlightEffect.cpp
@@ -0,0 +1,562 @@
+//
+// HighlightEffect.cpp
+// render-utils/src/
+//
+// Created by Olivier Prat on 08/08/17.
+// Copyright 2017 High Fidelity, Inc.
+//
+// Distributed under the Apache License, Version 2.0.
+// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
+//
+#include "HighlightEffect.h"
+
+#include "GeometryCache.h"
+
+#include "CubeProjectedPolygon.h"
+
+#include
+#include
+
+#include "gpu/Context.h"
+#include "gpu/StandardShaderLib.h"
+
+#include
+
+#include "surfaceGeometry_copyDepth_frag.h"
+#include "debug_deferred_buffer_vert.h"
+#include "debug_deferred_buffer_frag.h"
+#include "Highlight_frag.h"
+#include "Highlight_filled_frag.h"
+#include "Highlight_aabox_vert.h"
+#include "nop_frag.h"
+
+using namespace render;
+
+#define OUTLINE_STENCIL_MASK 1
+
+HighlightRessources::HighlightRessources() {
+}
+
+void HighlightRessources::update(const gpu::FramebufferPointer& primaryFrameBuffer) {
+ auto newFrameSize = glm::ivec2(primaryFrameBuffer->getSize());
+
+ // If the buffer size changed, we need to delete our FBOs and recreate them at the
+ // new correct dimensions.
+ if (_frameSize != newFrameSize) {
+ _frameSize = newFrameSize;
+ allocateDepthBuffer(primaryFrameBuffer);
+ allocateColorBuffer(primaryFrameBuffer);
+ } else {
+ if (!_depthFrameBuffer) {
+ allocateDepthBuffer(primaryFrameBuffer);
+ }
+ if (!_colorFrameBuffer) {
+ allocateColorBuffer(primaryFrameBuffer);
+ }
+ }
+}
+
+void HighlightRessources::allocateColorBuffer(const gpu::FramebufferPointer& primaryFrameBuffer) {
+ _colorFrameBuffer = gpu::FramebufferPointer(gpu::Framebuffer::create("primaryWithStencil"));
+ _colorFrameBuffer->setRenderBuffer(0, primaryFrameBuffer->getRenderBuffer(0));
+ _colorFrameBuffer->setStencilBuffer(_depthStencilTexture, _depthStencilTexture->getTexelFormat());
+}
+
+void HighlightRessources::allocateDepthBuffer(const gpu::FramebufferPointer& primaryFrameBuffer) {
+ auto depthFormat = gpu::Element(gpu::SCALAR, gpu::UINT32, gpu::DEPTH_STENCIL);
+ _depthStencilTexture = gpu::TexturePointer(gpu::Texture::createRenderBuffer(depthFormat, _frameSize.x, _frameSize.y));
+ _depthFrameBuffer = gpu::FramebufferPointer(gpu::Framebuffer::create("highlightDepth"));
+ _depthFrameBuffer->setDepthStencilBuffer(_depthStencilTexture, depthFormat);
+}
+
+gpu::FramebufferPointer HighlightRessources::getDepthFramebuffer() {
+ assert(_depthFrameBuffer);
+ return _depthFrameBuffer;
+}
+
+gpu::TexturePointer HighlightRessources::getDepthTexture() {
+ return _depthStencilTexture;
+}
+
+gpu::FramebufferPointer HighlightRessources::getColorFramebuffer() {
+ assert(_colorFrameBuffer);
+ return _colorFrameBuffer;
+}
+
+HighlightSharedParameters::HighlightSharedParameters() {
+ _highlightIds.fill(render::HighlightStage::INVALID_INDEX);
+}
+
+float HighlightSharedParameters::getBlurPixelWidth(const render::HighlightStyle& style, int frameBufferHeight) {
+ return ceilf(style.outlineWidth * frameBufferHeight / 400.0f);
+}
+
+PrepareDrawHighlight::PrepareDrawHighlight() {
+ _ressources = std::make_shared();
+}
+
+void PrepareDrawHighlight::run(const render::RenderContextPointer& renderContext, const Inputs& inputs, Outputs& outputs) {
+ auto destinationFrameBuffer = inputs;
+
+ _ressources->update(destinationFrameBuffer);
+ outputs = _ressources;
+}
+
+gpu::PipelinePointer DrawHighlightMask::_stencilMaskPipeline;
+gpu::PipelinePointer DrawHighlightMask::_stencilMaskFillPipeline;
+
+DrawHighlightMask::DrawHighlightMask(unsigned int highlightIndex,
+ render::ShapePlumberPointer shapePlumber, HighlightSharedParametersPointer parameters) :
+ _highlightPassIndex{ highlightIndex },
+ _shapePlumber { shapePlumber },
+ _sharedParameters{ parameters } {
+}
+
+void DrawHighlightMask::run(const render::RenderContextPointer& renderContext, const Inputs& inputs, Outputs& outputs) {
+ assert(renderContext->args);
+ assert(renderContext->args->hasViewFrustum());
+ auto& inShapes = inputs.get0();
+
+ if (!_stencilMaskPipeline || !_stencilMaskFillPipeline) {
+ gpu::StatePointer state = gpu::StatePointer(new gpu::State());
+ state->setDepthTest(true, false, gpu::LESS_EQUAL);
+ state->setStencilTest(true, 0xFF, gpu::State::StencilTest(OUTLINE_STENCIL_MASK, 0xFF, gpu::NOT_EQUAL, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_ZERO, gpu::State::STENCIL_OP_REPLACE));
+ state->setColorWriteMask(false, false, false, false);
+ state->setCullMode(gpu::State::CULL_FRONT);
+
+ gpu::StatePointer fillState = gpu::StatePointer(new gpu::State());
+ fillState->setDepthTest(false, false, gpu::LESS_EQUAL);
+ fillState->setStencilTest(true, 0xFF, gpu::State::StencilTest(OUTLINE_STENCIL_MASK, 0xFF, gpu::NOT_EQUAL, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_REPLACE));
+ fillState->setColorWriteMask(false, false, false, false);
+ fillState->setCullMode(gpu::State::CULL_FRONT);
+
+ auto vs = gpu::Shader::createVertex(std::string(Highlight_aabox_vert));
+ auto ps = gpu::Shader::createPixel(std::string(nop_frag));
+ gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps);
+
+ gpu::Shader::BindingSet slotBindings;
+ gpu::Shader::makeProgram(*program, slotBindings);
+
+ _stencilMaskPipeline = gpu::Pipeline::create(program, state);
+ _stencilMaskFillPipeline = gpu::Pipeline::create(program, fillState);
+ }
+
+ if (!_boundsBuffer) {
+ _boundsBuffer = std::make_shared(sizeof(render::ItemBound));
+ }
+
+ auto highlightStage = renderContext->_scene->getStage(render::HighlightStage::getName());
+ auto highlightId = _sharedParameters->_highlightIds[_highlightPassIndex];
+
+ if (!inShapes.empty() && !render::HighlightStage::isIndexInvalid(highlightId)) {
+ auto ressources = inputs.get1();
+ auto& highlight = highlightStage->getHighlight(highlightId);
+
+ RenderArgs* args = renderContext->args;
+ ShapeKey::Builder defaultKeyBuilder;
+
+ // Render full screen
+ outputs = args->_viewport;
+
+ // Clear the framebuffer without stereo
+ // Needs to be distinct from the other batch because using the clear call
+ // while stereo is enabled triggers a warning
+ gpu::doInBatch(args->_context, [&](gpu::Batch& batch) {
+ batch.enableStereo(false);
+ batch.setFramebuffer(ressources->getDepthFramebuffer());
+ batch.clearDepthStencilFramebuffer(1.0f, 0);
+ });
+
+ glm::mat4 projMat;
+ Transform viewMat;
+ args->getViewFrustum().evalProjectionMatrix(projMat);
+ args->getViewFrustum().evalViewTransform(viewMat);
+
+ render::ItemBounds itemBounds;
+
+ gpu::doInBatch(args->_context, [&](gpu::Batch& batch) {
+ args->_batch = &batch;
+
+ auto maskPipeline = _shapePlumber->pickPipeline(args, defaultKeyBuilder);
+ auto maskSkinnedPipeline = _shapePlumber->pickPipeline(args, defaultKeyBuilder.withSkinned());
+
+ // Setup camera, projection and viewport for all items
+ batch.setViewportTransform(args->_viewport);
+ batch.setProjectionTransform(projMat);
+ batch.setViewTransform(viewMat);
+
+ std::vector skinnedShapeKeys{};
+
+ // Iterate through all inShapes and render the unskinned
+ args->_shapePipeline = maskPipeline;
+ batch.setPipeline(maskPipeline->pipeline);
+ for (const auto& items : inShapes) {
+ itemBounds.insert(itemBounds.end(), items.second.begin(), items.second.end());
+ if (items.first.isSkinned()) {
+ skinnedShapeKeys.push_back(items.first);
+ } else {
+ renderItems(renderContext, items.second);
+ }
+ }
+
+ // Reiterate to render the skinned
+ args->_shapePipeline = maskSkinnedPipeline;
+ batch.setPipeline(maskSkinnedPipeline->pipeline);
+ for (const auto& key : skinnedShapeKeys) {
+ renderItems(renderContext, inShapes.at(key));
+ }
+
+ args->_shapePipeline = nullptr;
+ args->_batch = nullptr;
+ });
+
+ _boundsBuffer->setData(itemBounds.size() * sizeof(render::ItemBound), (const gpu::Byte*) itemBounds.data());
+
+ gpu::doInBatch(args->_context, [&](gpu::Batch& batch) {
+ // Setup camera, projection and viewport for all items
+ batch.setViewportTransform(args->_viewport);
+ batch.setProjectionTransform(projMat);
+ batch.setViewTransform(viewMat);
+
+ // Draw stencil mask with object bounding boxes
+ const auto highlightWidthLoc = _stencilMaskPipeline->getProgram()->getUniforms().findLocation("outlineWidth");
+ const auto securityMargin = 2.0f;
+ const float blurPixelWidth = 2.0f * securityMargin * HighlightSharedParameters::getBlurPixelWidth(highlight._style, args->_viewport.w);
+ const auto framebufferSize = ressources->getSourceFrameSize();
+
+ auto stencilPipeline = highlight._style.isFilled() ? _stencilMaskFillPipeline : _stencilMaskPipeline;
+ batch.setPipeline(stencilPipeline);
+ batch.setResourceBuffer(0, _boundsBuffer);
+ batch._glUniform2f(highlightWidthLoc, blurPixelWidth / framebufferSize.x, blurPixelWidth / framebufferSize.y);
+ static const int NUM_VERTICES_PER_CUBE = 36;
+ batch.draw(gpu::TRIANGLES, NUM_VERTICES_PER_CUBE * (gpu::uint32) itemBounds.size(), 0);
+ });
+ } else {
+ // Highlight rect should be null as there are no highlighted shapes
+ outputs = glm::ivec4(0, 0, 0, 0);
+ }
+}
+
+gpu::PipelinePointer DrawHighlight::_pipeline;
+gpu::PipelinePointer DrawHighlight::_pipelineFilled;
+
+DrawHighlight::DrawHighlight(unsigned int highlightIndex, HighlightSharedParametersPointer parameters) :
+ _highlightPassIndex{ highlightIndex },
+ _sharedParameters{ parameters } {
+}
+
+void DrawHighlight::run(const render::RenderContextPointer& renderContext, const Inputs& inputs) {
+ auto highlightFrameBuffer = inputs.get1();
+ auto highlightRect = inputs.get3();
+
+ if (highlightFrameBuffer && highlightRect.z>0 && highlightRect.w>0) {
+ auto sceneDepthBuffer = inputs.get2();
+ const auto frameTransform = inputs.get0();
+ auto highlightedDepthTexture = highlightFrameBuffer->getDepthTexture();
+ auto destinationFrameBuffer = highlightFrameBuffer->getColorFramebuffer();
+ auto framebufferSize = glm::ivec2(highlightedDepthTexture->getDimensions());
+
+ if (sceneDepthBuffer) {
+ auto args = renderContext->args;
+
+ auto highlightStage = renderContext->_scene->getStage(render::HighlightStage::getName());
+ auto highlightId = _sharedParameters->_highlightIds[_highlightPassIndex];
+ if (!render::HighlightStage::isIndexInvalid(highlightId)) {
+ auto& highlight = highlightStage->getHighlight(highlightId);
+ auto pipeline = getPipeline(highlight._style);
+ {
+ auto& shaderParameters = _configuration.edit();
+
+ shaderParameters._color = highlight._style.color;
+ shaderParameters._intensity = highlight._style.outlineIntensity * (highlight._style.isOutlineSmooth ? 2.0f : 1.0f);
+ shaderParameters._unoccludedFillOpacity = highlight._style.unoccludedFillOpacity;
+ shaderParameters._occludedFillOpacity = highlight._style.occludedFillOpacity;
+ shaderParameters._threshold = highlight._style.isOutlineSmooth ? 1.0f : 1e-3f;
+ shaderParameters._blurKernelSize = std::min(7, std::max(2, (int)floorf(highlight._style.outlineWidth * 3 + 0.5f)));
+ // Size is in normalized screen height. We decide that for highlight width = 1, this is equal to 1/400.
+ auto size = highlight._style.outlineWidth / 400.0f;
+ shaderParameters._size.x = (size * framebufferSize.y) / framebufferSize.x;
+ shaderParameters._size.y = size;
+ }
+
+ gpu::doInBatch(args->_context, [&](gpu::Batch& batch) {
+ batch.enableStereo(false);
+ batch.setFramebuffer(destinationFrameBuffer);
+
+ batch.setViewportTransform(args->_viewport);
+ batch.setProjectionTransform(glm::mat4());
+ batch.resetViewTransform();
+ batch.setModelTransform(gpu::Framebuffer::evalSubregionTexcoordTransform(framebufferSize, args->_viewport));
+ batch.setPipeline(pipeline);
+
+ batch.setUniformBuffer(HIGHLIGHT_PARAMS_SLOT, _configuration);
+ batch.setUniformBuffer(FRAME_TRANSFORM_SLOT, frameTransform->getFrameTransformBuffer());
+ batch.setResourceTexture(SCENE_DEPTH_MAP_SLOT, sceneDepthBuffer->getPrimaryDepthTexture());
+ batch.setResourceTexture(HIGHLIGHTED_DEPTH_MAP_SLOT, highlightedDepthTexture);
+ batch.draw(gpu::TRIANGLE_STRIP, 4);
+ });
+ }
+ }
+ }
+}
+
+const gpu::PipelinePointer& DrawHighlight::getPipeline(const render::HighlightStyle& style) {
+ if (!_pipeline) {
+ gpu::StatePointer state = gpu::StatePointer(new gpu::State());
+ state->setDepthTest(gpu::State::DepthTest(false, false));
+ state->setBlendFunction(true, gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA);
+ state->setStencilTest(true, 0, gpu::State::StencilTest(OUTLINE_STENCIL_MASK, 0xFF, gpu::EQUAL));
+
+ auto vs = gpu::StandardShaderLib::getDrawViewportQuadTransformTexcoordVS();
+ auto ps = gpu::Shader::createPixel(std::string(Highlight_frag));
+ gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps);
+
+ gpu::Shader::BindingSet slotBindings;
+ slotBindings.insert(gpu::Shader::Binding("highlightParamsBuffer", HIGHLIGHT_PARAMS_SLOT));
+ slotBindings.insert(gpu::Shader::Binding("deferredFrameTransformBuffer", FRAME_TRANSFORM_SLOT));
+ slotBindings.insert(gpu::Shader::Binding("sceneDepthMap", SCENE_DEPTH_MAP_SLOT));
+ slotBindings.insert(gpu::Shader::Binding("highlightedDepthMap", HIGHLIGHTED_DEPTH_MAP_SLOT));
+ gpu::Shader::makeProgram(*program, slotBindings);
+
+ _pipeline = gpu::Pipeline::create(program, state);
+
+ ps = gpu::Shader::createPixel(std::string(Highlight_filled_frag));
+ program = gpu::Shader::createProgram(vs, ps);
+ gpu::Shader::makeProgram(*program, slotBindings);
+ _pipelineFilled = gpu::Pipeline::create(program, state);
+ }
+ return style.isFilled() ? _pipelineFilled : _pipeline;
+}
+
+DebugHighlight::DebugHighlight() {
+ _geometryDepthId = DependencyManager::get()->allocateID();
+}
+
+DebugHighlight::~DebugHighlight() {
+ auto geometryCache = DependencyManager::get();
+ if (geometryCache) {
+ geometryCache->releaseID(_geometryDepthId);
+ }
+}
+
+void DebugHighlight::configure(const Config& config) {
+ _isDisplayEnabled = config.viewMask;
+}
+
+void DebugHighlight::run(const render::RenderContextPointer& renderContext, const Inputs& input) {
+ const auto highlightRessources = input.get0();
+ const auto highlightRect = input.get1();
+
+ if (_isDisplayEnabled && highlightRessources && highlightRect.z>0 && highlightRect.w>0) {
+ assert(renderContext->args);
+ assert(renderContext->args->hasViewFrustum());
+ RenderArgs* args = renderContext->args;
+
+ gpu::doInBatch(args->_context, [&](gpu::Batch& batch) {
+ batch.setViewportTransform(args->_viewport);
+ batch.setFramebuffer(highlightRessources->getColorFramebuffer());
+
+ const auto geometryBuffer = DependencyManager::get();
+
+ glm::mat4 projMat;
+ Transform viewMat;
+ args->getViewFrustum().evalProjectionMatrix(projMat);
+ args->getViewFrustum().evalViewTransform(viewMat);
+ batch.setProjectionTransform(projMat);
+ batch.setViewTransform(viewMat, true);
+ batch.setModelTransform(Transform());
+
+ const glm::vec4 color(1.0f, 1.0f, 1.0f, 1.0f);
+
+ batch.setPipeline(getDepthPipeline());
+ batch.setResourceTexture(0, highlightRessources->getDepthTexture());
+ const glm::vec2 bottomLeft(-1.0f, -1.0f);
+ const glm::vec2 topRight(1.0f, 1.0f);
+ geometryBuffer->renderQuad(batch, bottomLeft, topRight, color, _geometryDepthId);
+
+ batch.setResourceTexture(0, nullptr);
+ });
+ }
+}
+
+void DebugHighlight::initializePipelines() {
+ static const std::string VERTEX_SHADER{ debug_deferred_buffer_vert };
+ static const std::string FRAGMENT_SHADER{ debug_deferred_buffer_frag };
+ static const std::string SOURCE_PLACEHOLDER{ "//SOURCE_PLACEHOLDER" };
+ static const auto SOURCE_PLACEHOLDER_INDEX = FRAGMENT_SHADER.find(SOURCE_PLACEHOLDER);
+ Q_ASSERT_X(SOURCE_PLACEHOLDER_INDEX != std::string::npos, Q_FUNC_INFO,
+ "Could not find source placeholder");
+
+ auto state = std::make_shared();
+ state->setDepthTest(gpu::State::DepthTest(false, false));
+ state->setStencilTest(true, 0, gpu::State::StencilTest(OUTLINE_STENCIL_MASK, 0xFF, gpu::EQUAL));
+
+ const auto vs = gpu::Shader::createVertex(VERTEX_SHADER);
+
+ // Depth shader
+ {
+ static const std::string DEPTH_SHADER{
+ "vec4 getFragmentColor() {"
+ " float Zdb = texelFetch(depthMap, ivec2(gl_FragCoord.xy), 0).x;"
+ " Zdb = 1.0-(1.0-Zdb)*100;"
+ " return vec4(Zdb, Zdb, Zdb, 1.0); "
+ "}"
+ };
+
+ auto fragmentShader = FRAGMENT_SHADER;
+ fragmentShader.replace(SOURCE_PLACEHOLDER_INDEX, SOURCE_PLACEHOLDER.size(), DEPTH_SHADER);
+
+ const auto ps = gpu::Shader::createPixel(fragmentShader);
+ const auto program = gpu::Shader::createProgram(vs, ps);
+
+ gpu::Shader::BindingSet slotBindings;
+ slotBindings.insert(gpu::Shader::Binding("depthMap", 0));
+ gpu::Shader::makeProgram(*program, slotBindings);
+
+ _depthPipeline = gpu::Pipeline::create(program, state);
+ }
+}
+
+const gpu::PipelinePointer& DebugHighlight::getDepthPipeline() {
+ if (!_depthPipeline) {
+ initializePipelines();
+ }
+
+ return _depthPipeline;
+}
+
+void SelectionToHighlight::run(const render::RenderContextPointer& renderContext, Outputs& outputs) {
+ auto scene = renderContext->_scene;
+ auto highlightStage = scene->getStage(render::HighlightStage::getName());
+
+ outputs.clear();
+ _sharedParameters->_highlightIds.fill(render::HighlightStage::INVALID_INDEX);
+
+ for (auto i = 0; i < HighlightSharedParameters::MAX_PASS_COUNT; i++) {
+ std::ostringstream stream;
+ if (i > 0) {
+ stream << "highlightList" << i;
+ } else {
+ stream << "contextOverlayHighlightList";
+ }
+ auto selectionName = stream.str();
+ if (!scene->isSelectionEmpty(selectionName)) {
+ auto highlightId = highlightStage->getHighlightIdBySelection(selectionName);
+ if (!render::HighlightStage::isIndexInvalid(highlightId)) {
+ _sharedParameters->_highlightIds[outputs.size()] = highlightId;
+ outputs.emplace_back(selectionName);
+ }
+ }
+ }
+}
+
+void ExtractSelectionName::run(const render::RenderContextPointer& renderContext, const Inputs& inputs, Outputs& outputs) {
+ if (_highlightPassIndex < inputs.size()) {
+ outputs = inputs[_highlightPassIndex];
+ } else {
+ outputs.clear();
+ }
+}
+
+DrawHighlightTask::DrawHighlightTask() {
+
+}
+
+void DrawHighlightTask::configure(const Config& config) {
+
+}
+
+void DrawHighlightTask::build(JobModel& task, const render::Varying& inputs, render::Varying& outputs) {
+ const auto items = inputs.getN(0).get();
+ const auto sceneFrameBuffer = inputs.getN(1);
+ const auto primaryFramebuffer = inputs.getN(2);
+ const auto deferredFrameTransform = inputs.getN(3);
+
+ // Prepare the ShapePipeline
+ auto shapePlumber = std::make_shared();
+ {
+ auto state = std::make_shared();
+ state->setDepthTest(true, true, gpu::LESS_EQUAL);
+ state->setColorWriteMask(false, false, false, false);
+
+ initMaskPipelines(*shapePlumber, state);
+ }
+ auto sharedParameters = std::make_shared();
+
+ const auto highlightSelectionNames = task.addJob("SelectionToHighlight", sharedParameters);
+
+ // Prepare for highlight group rendering.
+ const auto highlightRessources = task.addJob("PrepareHighlight", primaryFramebuffer);
+ render::Varying highlight0Rect;
+
+ for (auto i = 0; i < HighlightSharedParameters::MAX_PASS_COUNT; i++) {
+ const auto selectionName = task.addJob("ExtractSelectionName", highlightSelectionNames, i);
+ const auto groupItems = addSelectItemJobs(task, selectionName, items);
+ const auto highlightedItemIDs = task.addJob("HighlightMetaToSubItemIDs", groupItems);
+ const auto highlightedItems = task.addJob("HighlightMetaToSubItems", highlightedItemIDs);
+
+ // Sort
+ const auto sortedPipelines = task.addJob("HighlightPipelineSort", highlightedItems);
+ const auto sortedBounds = task.addJob("HighlightDepthSort", sortedPipelines);
+
+ // Draw depth of highlighted objects in separate buffer
+ std::string name;
+ {
+ std::ostringstream stream;
+ stream << "HighlightMask" << i;
+ name = stream.str();
+ }
+ const auto drawMaskInputs = DrawHighlightMask::Inputs(sortedBounds, highlightRessources).asVarying();
+ const auto highlightedRect = task.addJob(name, drawMaskInputs, i, shapePlumber, sharedParameters);
+ if (i == 0) {
+ highlight0Rect = highlightedRect;
+ }
+
+ // Draw highlight
+ {
+ std::ostringstream stream;
+ stream << "HighlightEffect" << i;
+ name = stream.str();
+ }
+ const auto drawHighlightInputs = DrawHighlight::Inputs(deferredFrameTransform, highlightRessources, sceneFrameBuffer, highlightedRect).asVarying();
+ task.addJob(name, drawHighlightInputs, i, sharedParameters);
+ }
+
+ // Debug highlight
+ const auto debugInputs = DebugHighlight::Inputs(highlightRessources, const_cast(highlight0Rect)).asVarying();
+ task.addJob("HighlightDebug", debugInputs);
+}
+
+const render::Varying DrawHighlightTask::addSelectItemJobs(JobModel& task, const render::Varying& selectionName,
+ const RenderFetchCullSortTask::BucketList& items) {
+ const auto& opaques = items[RenderFetchCullSortTask::OPAQUE_SHAPE];
+ const auto& transparents = items[RenderFetchCullSortTask::TRANSPARENT_SHAPE];
+ const auto& metas = items[RenderFetchCullSortTask::META];
+
+ const auto selectMetaInput = SelectItems::Inputs(metas, Varying(), selectionName).asVarying();
+ const auto selectedMetas = task.addJob("MetaSelection", selectMetaInput);
+ const auto selectMetaAndOpaqueInput = SelectItems::Inputs(opaques, selectedMetas, selectionName).asVarying();
+ const auto selectedMetasAndOpaques = task.addJob("OpaqueSelection", selectMetaAndOpaqueInput);
+ const auto selectItemInput = SelectItems::Inputs(transparents, selectedMetasAndOpaques, selectionName).asVarying();
+ return task.addJob("TransparentSelection", selectItemInput);
+}
+
+#include "model_shadow_vert.h"
+#include "skin_model_shadow_vert.h"
+
+#include "model_shadow_frag.h"
+
+void DrawHighlightTask::initMaskPipelines(render::ShapePlumber& shapePlumber, gpu::StatePointer state) {
+ auto modelVertex = gpu::Shader::createVertex(std::string(model_shadow_vert));
+ auto modelPixel = gpu::Shader::createPixel(std::string(model_shadow_frag));
+ gpu::ShaderPointer modelProgram = gpu::Shader::createProgram(modelVertex, modelPixel);
+ shapePlumber.addPipeline(
+ ShapeKey::Filter::Builder().withoutSkinned(),
+ modelProgram, state);
+
+ auto skinVertex = gpu::Shader::createVertex(std::string(skin_model_shadow_vert));
+ gpu::ShaderPointer skinProgram = gpu::Shader::createProgram(skinVertex, modelPixel);
+ shapePlumber.addPipeline(
+ ShapeKey::Filter::Builder().withSkinned(),
+ skinProgram, state);
+}
diff --git a/libraries/render-utils/src/HighlightEffect.h b/libraries/render-utils/src/HighlightEffect.h
new file mode 100644
index 0000000000..90a8e730ce
--- /dev/null
+++ b/libraries/render-utils/src/HighlightEffect.h
@@ -0,0 +1,226 @@
+//
+// HighlightEffect.h
+// render-utils/src/
+//
+// Created by Olivier Prat on 08/08/17.
+// Copyright 2017 High Fidelity, Inc.
+//
+// Distributed under the Apache License, Version 2.0.
+// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
+//
+
+#ifndef hifi_render_utils_HighlightEffect_h
+#define hifi_render_utils_HighlightEffect_h
+
+#include
+#include
+#include
+
+#include "DeferredFramebuffer.h"
+#include "DeferredFrameTransform.h"
+
+class HighlightRessources {
+public:
+ HighlightRessources();
+
+ gpu::FramebufferPointer getDepthFramebuffer();
+ gpu::TexturePointer getDepthTexture();
+
+ gpu::FramebufferPointer getColorFramebuffer();
+
+ // Update the source framebuffer size which will drive the allocation of all the other resources.
+ void update(const gpu::FramebufferPointer& primaryFrameBuffer);
+ const glm::ivec2& getSourceFrameSize() const { return _frameSize; }
+
+protected:
+
+ gpu::FramebufferPointer _depthFrameBuffer;
+ gpu::FramebufferPointer _colorFrameBuffer;
+ gpu::TexturePointer _depthStencilTexture;
+
+ glm::ivec2 _frameSize;
+
+ void allocateColorBuffer(const gpu::FramebufferPointer& primaryFrameBuffer);
+ void allocateDepthBuffer(const gpu::FramebufferPointer& primaryFrameBuffer);
+};
+
+using HighlightRessourcesPointer = std::shared_ptr;
+
+class HighlightSharedParameters {
+public:
+
+ enum {
+ MAX_PASS_COUNT = 8
+ };
+
+ HighlightSharedParameters();
+
+ std::array _highlightIds;
+
+ static float getBlurPixelWidth(const render::HighlightStyle& style, int frameBufferHeight);
+};
+
+using HighlightSharedParametersPointer = std::shared_ptr;
+
+class PrepareDrawHighlight {
+public:
+ using Inputs = gpu::FramebufferPointer;
+ using Outputs = HighlightRessourcesPointer;
+ using JobModel = render::Job::ModelIO;
+
+ PrepareDrawHighlight();
+
+ void run(const render::RenderContextPointer& renderContext, const Inputs& inputs, Outputs& outputs);
+
+private:
+
+ HighlightRessourcesPointer _ressources;
+
+};
+
+class SelectionToHighlight {
+public:
+
+ using Outputs = std::vector;
+ using JobModel = render::Job::ModelO;
+
+ SelectionToHighlight(HighlightSharedParametersPointer parameters) : _sharedParameters{ parameters } {}
+
+ void run(const render::RenderContextPointer& renderContext, Outputs& outputs);
+
+private:
+
+ HighlightSharedParametersPointer _sharedParameters;
+};
+
+class ExtractSelectionName {
+public:
+
+ using Inputs = SelectionToHighlight::Outputs;
+ using Outputs = std::string;
+ using JobModel = render::Job::ModelIO;
+
+ ExtractSelectionName(unsigned int highlightIndex) : _highlightPassIndex{ highlightIndex } {}
+
+ void run(const render::RenderContextPointer& renderContext, const Inputs& inputs, Outputs& outputs);
+
+private:
+
+ unsigned int _highlightPassIndex;
+
+};
+
+class DrawHighlightMask {
+public:
+
+ using Inputs = render::VaryingSet2;
+ using Outputs = glm::ivec4;
+ using JobModel = render::Job::ModelIO;
+
+ DrawHighlightMask(unsigned int highlightIndex, render::ShapePlumberPointer shapePlumber, HighlightSharedParametersPointer parameters);
+
+ void run(const render::RenderContextPointer& renderContext, const Inputs& inputs, Outputs& outputs);
+
+protected:
+
+ unsigned int _highlightPassIndex;
+ render::ShapePlumberPointer _shapePlumber;
+ HighlightSharedParametersPointer _sharedParameters;
+ gpu::BufferPointer _boundsBuffer;
+
+ static gpu::PipelinePointer _stencilMaskPipeline;
+ static gpu::PipelinePointer _stencilMaskFillPipeline;
+};
+
+class DrawHighlight {
+public:
+
+ using Inputs = render::VaryingSet4;
+ using Config = render::Job::Config;
+ using JobModel = render::Job::ModelI;
+
+ DrawHighlight(unsigned int highlightIndex, HighlightSharedParametersPointer parameters);
+
+ void run(const render::RenderContextPointer& renderContext, const Inputs& inputs);
+
+private:
+
+#include "Highlight_shared.slh"
+
+ enum {
+ SCENE_DEPTH_MAP_SLOT = 0,
+ HIGHLIGHTED_DEPTH_MAP_SLOT,
+
+ HIGHLIGHT_PARAMS_SLOT = 0,
+ FRAME_TRANSFORM_SLOT,
+ };
+
+ using HighlightConfigurationBuffer = gpu::StructBuffer;
+
+ static const gpu::PipelinePointer& getPipeline(const render::HighlightStyle& style);
+
+ static gpu::PipelinePointer _pipeline;
+ static gpu::PipelinePointer _pipelineFilled;
+
+ unsigned int _highlightPassIndex;
+ HighlightParameters _parameters;
+ HighlightSharedParametersPointer _sharedParameters;
+ HighlightConfigurationBuffer _configuration;
+};
+
+class DebugHighlightConfig : public render::Job::Config {
+ Q_OBJECT
+ Q_PROPERTY(bool viewMask MEMBER viewMask NOTIFY dirty)
+
+public:
+
+ bool viewMask{ false };
+
+signals:
+ void dirty();
+};
+
+class DebugHighlight {
+public:
+ using Inputs = render::VaryingSet2;
+ using Config = DebugHighlightConfig;
+ using JobModel = render::Job::ModelI;
+
+ DebugHighlight();
+ ~DebugHighlight();
+
+ void configure(const Config& config);
+ void run(const render::RenderContextPointer& renderContext, const Inputs& inputs);
+
+private:
+
+ gpu::PipelinePointer _depthPipeline;
+ int _geometryDepthId{ 0 };
+ bool _isDisplayEnabled{ false };
+
+ const gpu::PipelinePointer& getDepthPipeline();
+ void initializePipelines();
+};
+
+class DrawHighlightTask {
+public:
+
+ using Inputs = render::VaryingSet4;
+ using Config = render::Task::Config;
+ using JobModel = render::Task::ModelI;
+
+ DrawHighlightTask();
+
+ void configure(const Config& config);
+ void build(JobModel& task, const render::Varying& inputs, render::Varying& outputs);
+
+private:
+
+ static void initMaskPipelines(render::ShapePlumber& plumber, gpu::StatePointer state);
+ static const render::Varying addSelectItemJobs(JobModel& task, const render::Varying& selectionName, const RenderFetchCullSortTask::BucketList& items);
+
+};
+
+#endif // hifi_render_utils_HighlightEffect_h
+
+
diff --git a/libraries/render-utils/src/Highlight_aabox.slv b/libraries/render-utils/src/Highlight_aabox.slv
new file mode 100644
index 0000000000..4927db9610
--- /dev/null
+++ b/libraries/render-utils/src/Highlight_aabox.slv
@@ -0,0 +1,104 @@
+<@include gpu/Config.slh@>
+<$VERSION_HEADER$>
+// Generated on <$_SCRIBE_DATE$>
+//
+// Draw and transform the fed vertex position with the standard MVP stack
+// and offset the vertices by a certain amount in the vertex direction
+//
+// Created by Olivier Prat on 11/02/2017
+// Copyright 2017 High Fidelity, Inc.
+//
+// Distributed under the Apache License, Version 2.0.
+// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
+//
+
+<@include gpu/Transform.slh@>
+
+<$declareStandardTransform()$>
+
+struct ItemBound {
+ vec4 id_boundPos;
+ vec4 boundDim_s;
+};
+
+#if defined(GPU_GL410)
+uniform samplerBuffer ssbo0Buffer;
+ItemBound getItemBound(int i) {
+ int offset = 2 * i;
+ ItemBound bound;
+ bound.id_boundPos = texelFetch(ssbo0Buffer, offset);
+ bound.boundDim_s = texelFetch(ssbo0Buffer, offset + 1);
+ return bound;
+}
+#else
+layout(std140) buffer ssbo0Buffer {
+ ItemBound bounds[];
+};
+ItemBound getItemBound(int i) {
+ ItemBound bound = bounds[i];
+ return bound;
+}
+#endif
+
+uniform vec2 outlineWidth;
+
+void main(void) {
+ const vec3 UNIT_BOX_VERTICES[8] = vec3[8](
+ vec3(0.0, 1.0, 0.0),
+ vec3(1.0, 1.0, 0.0),
+ vec3(1.0, 0.0, 0.0),
+ vec3(0.0, 0.0, 0.0),
+ vec3(0.0, 1.0, 1.0),
+ vec3(1.0, 1.0, 1.0),
+ vec3(1.0, 0.0, 1.0),
+ vec3(0.0, 0.0, 1.0)
+ );
+ const vec3 UNIT_BOX_NORMALS[8] = vec3[8](
+ vec3(-1.0, 1.0, -1.0),
+ vec3(1.0, 1.0, -1.0),
+ vec3(1.0, -1.0, -1.0),
+ vec3(-1.0, -1.0, -1.0),
+ vec3(-1.0, 1.0, 1.0),
+ vec3(1.0, 1.0, 1.0),
+ vec3(1.0, -1.0, 1.0),
+ vec3(-1.0, -1.0, 1.0)
+ );
+ const int NUM_VERTICES_PER_CUBE = 36;
+ const int UNIT_BOX_TRIANGLE_INDICES[NUM_VERTICES_PER_CUBE] = int[NUM_VERTICES_PER_CUBE](
+ 0, 1, 2,
+ 0, 2, 3,
+ 3, 2, 6,
+ 3, 6, 7,
+ 7, 6, 5,
+ 7, 5, 4,
+ 4, 5, 1,
+ 4, 1, 0,
+ 1, 5, 6,
+ 1, 6, 2,
+ 4, 0, 3,
+ 4, 3, 7
+ );
+
+ int boundID = gl_VertexID / NUM_VERTICES_PER_CUBE;
+ int vertexID = gl_VertexID - boundID * NUM_VERTICES_PER_CUBE;
+ int triangleIndex = UNIT_BOX_TRIANGLE_INDICES[vertexID];
+ vec3 cubeVec = UNIT_BOX_VERTICES[triangleIndex];
+
+ ItemBound bound = getItemBound(boundID);
+ vec3 boundPos = bound.id_boundPos.yzw;
+ vec3 boundDim = bound.boundDim_s.xyz;
+
+ vec4 pos = vec4(boundPos + boundDim * cubeVec.xyz, 1.0);
+
+ // standard transform
+ TransformCamera cam = getTransformCamera();
+ TransformObject obj = getTransformObject();
+ <$transformModelToMonoClipPos(cam, obj, pos, gl_Position)$>
+
+ // Offset the vertex to take into account the outline width
+ pos.xyz += UNIT_BOX_NORMALS[triangleIndex];
+ vec4 offsetPosition;
+ <$transformModelToMonoClipPos(cam, obj, pos, offsetPosition)$>
+ gl_Position.xy += normalize(offsetPosition.xy-gl_Position.xy) * outlineWidth * gl_Position.w;
+ <$transformStereoClipsSpace(cam, gl_Position)$>
+}
diff --git a/libraries/render-utils/src/Outline_filled.slf b/libraries/render-utils/src/Highlight_filled.slf
similarity index 71%
rename from libraries/render-utils/src/Outline_filled.slf
rename to libraries/render-utils/src/Highlight_filled.slf
index aaa3396bac..53530746f0 100644
--- a/libraries/render-utils/src/Outline_filled.slf
+++ b/libraries/render-utils/src/Highlight_filled.slf
@@ -1,5 +1,5 @@
-// Outline_filled.slf
-// Add outline effect based on two zbuffers : one containing the total scene z and another
+// Highlight_filled.slf
+// Add highlight effect based on two zbuffers : one containing the total scene z and another
// with the z of only the objects to be outlined.
// This is the version with the fill effect inside the silhouette.
//
@@ -9,5 +9,5 @@
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
-<@include Outline.slh@>
+<@include Highlight.slh@>
<$main(1)$>
diff --git a/libraries/render-utils/src/Highlight_shared.slh b/libraries/render-utils/src/Highlight_shared.slh
new file mode 100644
index 0000000000..5efbde4d52
--- /dev/null
+++ b/libraries/render-utils/src/Highlight_shared.slh
@@ -0,0 +1,30 @@
+// glsl / C++ compatible source as interface for highlight
+#ifdef __cplusplus
+# define TVEC2 glm::vec2
+# define TVEC3 glm::vec3
+# define TVEC4 glm::vec4
+#else
+# define TVEC2 vec2
+# define TVEC3 vec3
+# define TVEC4 vec4
+#endif
+
+struct HighlightParameters
+{
+ TVEC3 _color;
+ float _intensity;
+
+ TVEC2 _size;
+ float _unoccludedFillOpacity;
+ float _occludedFillOpacity;
+
+ float _threshold;
+ int _blurKernelSize;
+ float padding2;
+ float padding3;
+};
+
+// <@if 1@>
+// Trigger Scribe include
+// <@endif@>
+//
diff --git a/libraries/render-utils/src/LightClusters.cpp b/libraries/render-utils/src/LightClusters.cpp
index ab1e194498..eedb9053c7 100644
--- a/libraries/render-utils/src/LightClusters.cpp
+++ b/libraries/render-utils/src/LightClusters.cpp
@@ -727,7 +727,7 @@ void DebugLightClusters::run(const render::RenderContextPointer& renderContext,
batch.setModelTransform(Transform());
// Bind the Light CLuster data strucutre
- batch.setUniformBuffer(LIGHT_GPU_SLOT, lightClusters->_lightStage->_lightArrayBuffer);
+ batch.setUniformBuffer(LIGHT_GPU_SLOT, lightClusters->_lightStage->getLightArrayBuffer());
batch.setUniformBuffer(LIGHT_CLUSTER_GRID_FRUSTUM_GRID_SLOT, lightClusters->_frustumGridBuffer);
batch.setUniformBuffer(LIGHT_CLUSTER_GRID_CLUSTER_GRID_SLOT, lightClusters->_clusterGridBuffer);
batch.setUniformBuffer(LIGHT_CLUSTER_GRID_CLUSTER_CONTENT_SLOT, lightClusters->_clusterContentBuffer);
diff --git a/libraries/render-utils/src/LightStage.cpp b/libraries/render-utils/src/LightStage.cpp
index 079c63f367..ba705e56cb 100644
--- a/libraries/render-utils/src/LightStage.cpp
+++ b/libraries/render-utils/src/LightStage.cpp
@@ -14,18 +14,34 @@
#include "LightStage.h"
std::string LightStage::_stageName { "LIGHT_STAGE"};
+const LightStage::Index LightStage::INVALID_INDEX { render::indexed_container::INVALID_INDEX };
LightStage::LightStage() {
}
-LightStage::Shadow::Shadow(model::LightPointer light) : _light{ light}, _frustum{ std::make_shared() } {
- framebuffer = gpu::FramebufferPointer(gpu::Framebuffer::createShadowmap(MAP_SIZE));
- map = framebuffer->getDepthStencilBuffer();
- Schema schema;
- _schemaBuffer = std::make_shared(sizeof(Schema), (const gpu::Byte*) &schema);
+LightStage::Shadow::Schema::Schema() :
+ bias{ 0.005f },
+ scale{ 1.0f / MAP_SIZE } {
+
}
-void LightStage::Shadow::setKeylightFrustum(const ViewFrustum& viewFrustum, float nearDepth, float farDepth) {
+gpu::FramebufferPointer LightStage::Shadow::framebuffer;
+gpu::TexturePointer LightStage::Shadow::map;
+
+LightStage::Shadow::Shadow(model::LightPointer light) : _light{ light}, _frustum{ std::make_shared() } {
+ Schema schema;
+ _schemaBuffer = std::make_shared(sizeof(Schema), (const gpu::Byte*) &schema);
+
+ if (!framebuffer) {
+ framebuffer = gpu::FramebufferPointer(gpu::Framebuffer::createShadowmap(MAP_SIZE));
+ map = framebuffer->getDepthStencilBuffer();
+ }
+}
+
+void LightStage::Shadow::setKeylightFrustum(const ViewFrustum& viewFrustum,
+ float viewMinShadowDistance, float viewMaxShadowDistance,
+ float nearDepth, float farDepth) {
+ assert(viewMinShadowDistance < viewMaxShadowDistance);
assert(nearDepth < farDepth);
// Orient the keylight frustum
@@ -48,8 +64,8 @@ void LightStage::Shadow::setKeylightFrustum(const ViewFrustum& viewFrustum, floa
const Transform view{ _frustum->getView()};
const Transform viewInverse{ view.getInverseMatrix() };
- auto nearCorners = viewFrustum.getCorners(nearDepth);
- auto farCorners = viewFrustum.getCorners(farDepth);
+ auto nearCorners = viewFrustum.getCorners(viewMinShadowDistance);
+ auto farCorners = viewFrustum.getCorners(viewMaxShadowDistance);
vec3 min{ viewInverse.transform(nearCorners.bottomLeft) };
vec3 max{ min };
@@ -73,7 +89,10 @@ void LightStage::Shadow::setKeylightFrustum(const ViewFrustum& viewFrustum, floa
fitFrustum(farCorners.topLeft);
fitFrustum(farCorners.topRight);
- glm::mat4 ortho = glm::ortho(min.x, max.x, min.y, max.y, -max.z, -min.z);
+ // Re-adjust near shadow distance
+ auto near = glm::max(max.z, -nearDepth);
+ auto far = -min.z;
+ glm::mat4 ortho = glm::ortho(min.x, max.x, min.y, max.y, near, far);
_frustum->setProjection(ortho);
// Calculate the frustum's internal state
@@ -84,6 +103,16 @@ void LightStage::Shadow::setKeylightFrustum(const ViewFrustum& viewFrustum, floa
_schemaBuffer.edit().viewInverse = viewInverse.getMatrix();
}
+void LightStage::Shadow::setFrustum(const ViewFrustum& shadowFrustum) {
+ const Transform view{ shadowFrustum.getView() };
+ const Transform viewInverse{ view.getInverseMatrix() };
+
+ *_frustum = shadowFrustum;
+ // Update the buffer
+ _schemaBuffer.edit().projection = shadowFrustum.getProjection();
+ _schemaBuffer.edit().viewInverse = viewInverse.getMatrix();
+}
+
const glm::mat4& LightStage::Shadow::getView() const {
return _frustum->getView();
}
@@ -99,11 +128,9 @@ LightStage::Index LightStage::findLight(const LightPointer& light) const {
} else {
return (*found).second;
}
-
}
LightStage::Index LightStage::addLight(const LightPointer& light) {
-
auto found = _lightMap.find(light);
if (found == _lightMap.end()) {
auto lightId = _lights.newElement(light);
@@ -114,6 +141,7 @@ LightStage::Index LightStage::addLight(const LightPointer& light) {
if (lightId >= (Index) _descs.size()) {
_descs.emplace_back(Desc());
} else {
+ assert(_descs[lightId].shadowId == INVALID_INDEX);
_descs.emplace(_descs.begin() + lightId, Desc());
}
@@ -132,6 +160,7 @@ LightStage::Index LightStage::addShadow(Index lightIndex) {
auto light = getLight(lightIndex);
Index shadowId = INVALID_INDEX;
if (light) {
+ assert(_descs[lightIndex].shadowId == INVALID_INDEX);
shadowId = _shadows.newElement(std::make_shared(light));
_descs[lightIndex].shadowId = shadowId;
}
@@ -139,18 +168,65 @@ LightStage::Index LightStage::addShadow(Index lightIndex) {
}
LightStage::LightPointer LightStage::removeLight(Index index) {
- LightPointer removed = _lights.freeElement(index);
-
- if (removed) {
+ LightPointer removedLight = _lights.freeElement(index);
+ if (removedLight) {
auto shadowId = _descs[index].shadowId;
// Remove shadow if one exists for this light
if (shadowId != INVALID_INDEX) {
- _shadows.freeElement(shadowId);
+ auto removedShadow = _shadows.freeElement(shadowId);
+ assert(removedShadow);
+ assert(removedShadow->getLight() == removedLight);
}
- _lightMap.erase(removed);
+ _lightMap.erase(removedLight);
_descs[index] = Desc();
}
- return removed;
+ assert(_descs.size() <= index || _descs[index].shadowId == INVALID_INDEX);
+ return removedLight;
+}
+
+LightStage::LightPointer LightStage::getCurrentKeyLight() const {
+ Index keyLightId{ 0 };
+ if (!_currentFrame._sunLights.empty()) {
+ keyLightId = _currentFrame._sunLights.front();
+ }
+ return _lights.get(keyLightId);
+}
+
+LightStage::LightPointer LightStage::getCurrentAmbientLight() const {
+ Index keyLightId{ 0 };
+ if (!_currentFrame._ambientLights.empty()) {
+ keyLightId = _currentFrame._ambientLights.front();
+ }
+ return _lights.get(keyLightId);
+}
+
+LightStage::ShadowPointer LightStage::getCurrentKeyShadow() const {
+ Index keyLightId{ 0 };
+ if (!_currentFrame._sunLights.empty()) {
+ keyLightId = _currentFrame._sunLights.front();
+ }
+ auto shadow = getShadow(keyLightId);
+ assert(shadow == nullptr || shadow->getLight() == getLight(keyLightId));
+ return shadow;
+}
+
+LightStage::LightAndShadow LightStage::getCurrentKeyLightAndShadow() const {
+ Index keyLightId{ 0 };
+ if (!_currentFrame._sunLights.empty()) {
+ keyLightId = _currentFrame._sunLights.front();
+ }
+ auto shadow = getShadow(keyLightId);
+ auto light = getLight(keyLightId);
+ assert(shadow == nullptr || shadow->getLight() == light);
+ return LightAndShadow(light, shadow);
+}
+
+LightStage::Index LightStage::getShadowId(Index lightId) const {
+ if (checkLightId(lightId)) {
+ return _descs[lightId].shadowId;
+ } else {
+ return INVALID_INDEX;
+ }
}
void LightStage::updateLightArrayBuffer(Index lightId) {
diff --git a/libraries/render-utils/src/LightStage.h b/libraries/render-utils/src/LightStage.h
index 66d73c9a6e..fa581c8315 100644
--- a/libraries/render-utils/src/LightStage.h
+++ b/libraries/render-utils/src/LightStage.h
@@ -32,7 +32,7 @@ public:
static const std::string& getName() { return _stageName; }
using Index = render::indexed_container::Index;
- static const Index INVALID_INDEX { render::indexed_container::INVALID_INDEX };
+ static const Index INVALID_INDEX;
static bool isIndexInvalid(Index index) { return index == INVALID_INDEX; }
using LightPointer = model::LightPointer;
@@ -48,8 +48,9 @@ public:
Shadow(model::LightPointer light);
- void setKeylightFrustum(const ViewFrustum& viewFrustum, float nearDepth, float farDepth);
+ void setKeylightFrustum(const ViewFrustum& viewFrustum, float viewMinShadowDistance, float viewMaxShadowDistance, float nearDepth = 1.0f, float farDepth = 1000.0f);
+ void setFrustum(const ViewFrustum& shadowFrustum);
const std::shared_ptr getFrustum() const { return _frustum; }
const glm::mat4& getView() const;
@@ -57,32 +58,36 @@ public:
const UniformBufferView& getBuffer() const { return _schemaBuffer; }
- gpu::FramebufferPointer framebuffer;
- gpu::TexturePointer map;
+ // Shadow maps are shared among all lights for the moment as only one key light
+ // is used.
+ static gpu::FramebufferPointer framebuffer;
+ static gpu::TexturePointer map;
+
+ const model::LightPointer& getLight() const { return _light; }
+
protected:
+
model::LightPointer _light;
std::shared_ptr _frustum;
class Schema {
public:
+
+ Schema();
+
glm::mat4 projection;
glm::mat4 viewInverse;
- glm::float32 bias = 0.005f;
- glm::float32 scale = 1 / MAP_SIZE;
+ glm::float32 bias;
+ glm::float32 scale;
};
UniformBufferView _schemaBuffer = nullptr;
- friend class Light;
};
+
using ShadowPointer = std::shared_ptr;
using Shadows = render::indexed_container::IndexedPointerVector;
- struct Desc {
- Index shadowId { INVALID_INDEX };
- };
- using Descs = std::vector;
-
Index findLight(const LightPointer& light) const;
Index addLight(const LightPointer& light);
@@ -100,50 +105,29 @@ public:
return _lights.get(lightId);
}
- Index getShadowId(Index lightId) const {
- if (checkLightId(lightId)) {
- return _descs[lightId].shadowId;
- } else {
- return INVALID_INDEX;
- }
- }
+ Index getShadowId(Index lightId) const;
+
ShadowPointer getShadow(Index lightId) const {
return _shadows.get(getShadowId(lightId));
}
using LightAndShadow = std::pair;
LightAndShadow getLightAndShadow(Index lightId) const {
- return LightAndShadow(getLight(lightId), getShadow(lightId));
+ auto light = getLight(lightId);
+ auto shadow = getShadow(lightId);
+ assert(shadow == nullptr || shadow->getLight() == light);
+ return LightAndShadow(light, shadow);
}
- LightPointer getCurrentKeyLight() const {
- Index keyLightId{ 0 };
- if (!_currentFrame._sunLights.empty()) {
- keyLightId = _currentFrame._sunLights.front();
- }
- return _lights.get(keyLightId);
- }
-
- ShadowPointer getCurrentKeyShadow() const {
- Index keyLightId{ 0 };
- if (!_currentFrame._sunLights.empty()) {
- keyLightId = _currentFrame._sunLights.front();
- }
- return getShadow(keyLightId);
- }
-
- LightAndShadow getCurrentKeyLightAndShadow() const {
- Index keyLightId{ 0 };
- if (!_currentFrame._sunLights.empty()) {
- keyLightId = _currentFrame._sunLights.front();
- }
- return LightAndShadow(getLight(keyLightId), getShadow(keyLightId));
- }
+ LightPointer getCurrentKeyLight() const;
+ LightPointer getCurrentAmbientLight() const;
+ ShadowPointer getCurrentKeyShadow() const;
+ LightAndShadow getCurrentKeyLightAndShadow() const;
LightStage();
- Lights _lights;
- LightMap _lightMap;
- Descs _descs;
+
+ gpu::BufferPointer getLightArrayBuffer() const { return _lightArrayBuffer; }
+ void updateLightArrayBuffer(Index lightId);
class Frame {
public:
@@ -172,15 +156,24 @@ public:
Frame _currentFrame;
- gpu::BufferPointer _lightArrayBuffer;
- void updateLightArrayBuffer(Index lightId);
+protected:
+ struct Desc {
+ Index shadowId{ INVALID_INDEX };
+ };
+ using Descs = std::vector;
+
+ gpu::BufferPointer _lightArrayBuffer;
+
+ Lights _lights;
Shadows _shadows;
+ Descs _descs;
+ LightMap _lightMap;
+
};
using LightStagePointer = std::shared_ptr;
-
class LightStageSetup {
public:
using JobModel = render::Job::Model;
diff --git a/libraries/render-utils/src/OutlineEffect.cpp b/libraries/render-utils/src/OutlineEffect.cpp
deleted file mode 100644
index d5b3b1c3bb..0000000000
--- a/libraries/render-utils/src/OutlineEffect.cpp
+++ /dev/null
@@ -1,371 +0,0 @@
-//
-// OutlineEffect.cpp
-// render-utils/src/
-//
-// Created by Olivier Prat on 08/08/17.
-// Copyright 2017 High Fidelity, Inc.
-//
-// Distributed under the Apache License, Version 2.0.
-// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
-//
-#include "OutlineEffect.h"
-
-#include "GeometryCache.h"
-
-#include
-#include
-
-#include "gpu/Context.h"
-#include "gpu/StandardShaderLib.h"
-
-
-#include "surfaceGeometry_copyDepth_frag.h"
-#include "debug_deferred_buffer_vert.h"
-#include "debug_deferred_buffer_frag.h"
-#include "Outline_frag.h"
-#include "Outline_filled_frag.h"
-
-using namespace render;
-
-extern void initZPassPipelines(ShapePlumber& plumber, gpu::StatePointer state);
-
-OutlineFramebuffer::OutlineFramebuffer() {
-}
-
-void OutlineFramebuffer::update(const gpu::TexturePointer& colorBuffer) {
- // If the depth buffer or size changed, we need to delete our FBOs and recreate them at the
- // new correct dimensions.
- if (_depthTexture) {
- auto newFrameSize = glm::ivec2(colorBuffer->getDimensions());
- if (_frameSize != newFrameSize) {
- _frameSize = newFrameSize;
- clear();
- }
- }
-}
-
-void OutlineFramebuffer::clear() {
- _depthFramebuffer.reset();
- _depthTexture.reset();
-}
-
-void OutlineFramebuffer::allocate() {
-
- auto width = _frameSize.x;
- auto height = _frameSize.y;
- auto format = gpu::Element(gpu::SCALAR, gpu::FLOAT, gpu::DEPTH);
-
- _depthTexture = gpu::TexturePointer(gpu::Texture::createRenderBuffer(format, width, height));
- _depthFramebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create("outlineDepth"));
- _depthFramebuffer->setDepthStencilBuffer(_depthTexture, format);
-}
-
-gpu::FramebufferPointer OutlineFramebuffer::getDepthFramebuffer() {
- if (!_depthFramebuffer) {
- allocate();
- }
- return _depthFramebuffer;
-}
-
-gpu::TexturePointer OutlineFramebuffer::getDepthTexture() {
- if (!_depthTexture) {
- allocate();
- }
- return _depthTexture;
-}
-
-void DrawOutlineDepth::run(const render::RenderContextPointer& renderContext, const Inputs& inputs, Outputs& output) {
- assert(renderContext->args);
- assert(renderContext->args->hasViewFrustum());
- auto& inShapes = inputs.get0();
- auto& deferredFrameBuffer = inputs.get1();
-
- if (!inShapes.empty()) {
- RenderArgs* args = renderContext->args;
- ShapeKey::Builder defaultKeyBuilder;
-
- if (!_outlineFramebuffer) {
- _outlineFramebuffer = std::make_shared();
- }
- _outlineFramebuffer->update(deferredFrameBuffer->getDeferredColorTexture());
-
- gpu::doInBatch(args->_context, [&](gpu::Batch& batch) {
- args->_batch = &batch;
-
- batch.setFramebuffer(_outlineFramebuffer->getDepthFramebuffer());
- // Clear it
- batch.clearFramebuffer(
- gpu::Framebuffer::BUFFER_DEPTH,
- vec4(vec3(1.0, 1.0, 1.0), 0.0), 1.0, 0, false);
-
- // Setup camera, projection and viewport for all items
- batch.setViewportTransform(args->_viewport);
- batch.setStateScissorRect(args->_viewport);
-
- glm::mat4 projMat;
- Transform viewMat;
- args->getViewFrustum().evalProjectionMatrix(projMat);
- args->getViewFrustum().evalViewTransform(viewMat);
-
- batch.setProjectionTransform(projMat);
- batch.setViewTransform(viewMat);
-
- auto shadowPipeline = _shapePlumber->pickPipeline(args, defaultKeyBuilder);
- auto shadowSkinnedPipeline = _shapePlumber->pickPipeline(args, defaultKeyBuilder.withSkinned());
-
- std::vector skinnedShapeKeys{};
-
- // Iterate through all inShapes and render the unskinned
- args->_shapePipeline = shadowPipeline;
- batch.setPipeline(shadowPipeline->pipeline);
- for (auto items : inShapes) {
- if (items.first.isSkinned()) {
- skinnedShapeKeys.push_back(items.first);
- }
- else {
- renderItems(renderContext, items.second);
- }
- }
-
- // Reiterate to render the skinned
- args->_shapePipeline = shadowSkinnedPipeline;
- batch.setPipeline(shadowSkinnedPipeline->pipeline);
- for (const auto& key : skinnedShapeKeys) {
- renderItems(renderContext, inShapes.at(key));
- }
-
- args->_shapePipeline = nullptr;
- args->_batch = nullptr;
- });
-
- output = _outlineFramebuffer;
- } else {
- output = nullptr;
- }
-}
-
-DrawOutline::DrawOutline() {
-}
-
-void DrawOutline::configure(const Config& config) {
- _color = config.color;
- _blurKernelSize = std::min(10, std::max(2, (int)floorf(config.width*2 + 0.5f)));
- // Size is in normalized screen height. We decide that for outline width = 1, this is equal to 1/400.
- _size = config.width / 400.f;
- _fillOpacityUnoccluded = config.fillOpacityUnoccluded;
- _fillOpacityOccluded = config.fillOpacityOccluded;
- _threshold = config.glow ? 1.f : 1e-3f;
- _intensity = config.intensity * (config.glow ? 2.f : 1.f);
-}
-
-void DrawOutline::run(const render::RenderContextPointer& renderContext, const Inputs& inputs) {
- auto outlineFrameBuffer = inputs.get1();
-
- if (outlineFrameBuffer) {
- auto sceneDepthBuffer = inputs.get2();
- const auto frameTransform = inputs.get0();
- auto outlinedDepthTexture = outlineFrameBuffer->getDepthTexture();
- auto destinationFrameBuffer = inputs.get3();
- auto framebufferSize = glm::ivec2(outlinedDepthTexture->getDimensions());
-
- if (!_primaryWithoutDepthBuffer || framebufferSize!=_frameBufferSize) {
- // Failing to recreate this frame buffer when the screen has been resized creates a bug on Mac
- _primaryWithoutDepthBuffer = gpu::FramebufferPointer(gpu::Framebuffer::create("primaryWithoutDepth"));
- _primaryWithoutDepthBuffer->setRenderBuffer(0, destinationFrameBuffer->getRenderBuffer(0));
- _frameBufferSize = framebufferSize;
- }
-
- if (sceneDepthBuffer) {
- const auto OPACITY_EPSILON = 5e-3f;
- auto pipeline = getPipeline(_fillOpacityUnoccluded>OPACITY_EPSILON || _fillOpacityOccluded>OPACITY_EPSILON);
- auto args = renderContext->args;
- {
- auto& configuration = _configuration.edit();
- configuration._color = _color;
- configuration._intensity = _intensity;
- configuration._fillOpacityUnoccluded = _fillOpacityUnoccluded;
- configuration._fillOpacityOccluded = _fillOpacityOccluded;
- configuration._threshold = _threshold;
- configuration._blurKernelSize = _blurKernelSize;
- configuration._size.x = _size * _frameBufferSize.y / _frameBufferSize.x;
- configuration._size.y = _size;
- }
-
- gpu::doInBatch(args->_context, [&](gpu::Batch& batch) {
- batch.enableStereo(false);
- batch.setFramebuffer(_primaryWithoutDepthBuffer);
-
- batch.setViewportTransform(args->_viewport);
- batch.setProjectionTransform(glm::mat4());
- batch.resetViewTransform();
- batch.setModelTransform(gpu::Framebuffer::evalSubregionTexcoordTransform(_frameBufferSize, args->_viewport));
- batch.setPipeline(pipeline);
-
- batch.setUniformBuffer(OUTLINE_PARAMS_SLOT, _configuration);
- batch.setUniformBuffer(FRAME_TRANSFORM_SLOT, frameTransform->getFrameTransformBuffer());
- batch.setResourceTexture(SCENE_DEPTH_SLOT, sceneDepthBuffer->getPrimaryDepthTexture());
- batch.setResourceTexture(OUTLINED_DEPTH_SLOT, outlinedDepthTexture);
- batch.draw(gpu::TRIANGLE_STRIP, 4);
-
- // Restore previous frame buffer
- batch.setFramebuffer(destinationFrameBuffer);
- });
- }
- }
-}
-
-const gpu::PipelinePointer& DrawOutline::getPipeline(bool isFilled) {
- if (!_pipeline) {
- auto vs = gpu::StandardShaderLib::getDrawViewportQuadTransformTexcoordVS();
- auto ps = gpu::Shader::createPixel(std::string(Outline_frag));
- gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps);
-
- gpu::Shader::BindingSet slotBindings;
- slotBindings.insert(gpu::Shader::Binding("outlineParamsBuffer", OUTLINE_PARAMS_SLOT));
- slotBindings.insert(gpu::Shader::Binding("deferredFrameTransformBuffer", FRAME_TRANSFORM_SLOT));
- slotBindings.insert(gpu::Shader::Binding("sceneDepthMap", SCENE_DEPTH_SLOT));
- slotBindings.insert(gpu::Shader::Binding("outlinedDepthMap", OUTLINED_DEPTH_SLOT));
- gpu::Shader::makeProgram(*program, slotBindings);
-
- gpu::StatePointer state = gpu::StatePointer(new gpu::State());
- state->setDepthTest(gpu::State::DepthTest(false, false));
- state->setBlendFunction(true, gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA);
- _pipeline = gpu::Pipeline::create(program, state);
-
- ps = gpu::Shader::createPixel(std::string(Outline_filled_frag));
- program = gpu::Shader::createProgram(vs, ps);
- gpu::Shader::makeProgram(*program, slotBindings);
- _pipelineFilled = gpu::Pipeline::create(program, state);
- }
- return isFilled ? _pipelineFilled : _pipeline;
-}
-
-DebugOutline::DebugOutline() {
- _geometryId = DependencyManager::get()->allocateID();
-}
-
-DebugOutline::~DebugOutline() {
- auto geometryCache = DependencyManager::get();
- if (geometryCache) {
- geometryCache->releaseID(_geometryId);
- }
-}
-
-void DebugOutline::configure(const Config& config) {
- _isDisplayDepthEnabled = config.viewOutlinedDepth;
-}
-
-void DebugOutline::run(const render::RenderContextPointer& renderContext, const Inputs& input) {
- const auto outlineFramebuffer = input;
-
- if (_isDisplayDepthEnabled && outlineFramebuffer) {
- assert(renderContext->args);
- assert(renderContext->args->hasViewFrustum());
- RenderArgs* args = renderContext->args;
-
- gpu::doInBatch(args->_context, [&](gpu::Batch& batch) {
- batch.enableStereo(false);
- batch.setViewportTransform(args->_viewport);
-
- const auto geometryBuffer = DependencyManager::get();
-
- glm::mat4 projMat;
- Transform viewMat;
- args->getViewFrustum().evalProjectionMatrix(projMat);
- args->getViewFrustum().evalViewTransform(viewMat);
- batch.setProjectionTransform(projMat);
- batch.setViewTransform(viewMat, true);
- batch.setModelTransform(Transform());
-
- batch.setPipeline(getDebugPipeline());
- batch.setResourceTexture(0, outlineFramebuffer->getDepthTexture());
-
- const glm::vec4 color(1.0f, 0.5f, 0.2f, 1.0f);
- const glm::vec2 bottomLeft(-1.0f, -1.0f);
- const glm::vec2 topRight(1.0f, 1.0f);
- geometryBuffer->renderQuad(batch, bottomLeft, topRight, color, _geometryId);
-
- batch.setResourceTexture(0, nullptr);
- });
- }
-}
-
-const gpu::PipelinePointer& DebugOutline::getDebugPipeline() {
- if (!_debugPipeline) {
- static const std::string VERTEX_SHADER{ debug_deferred_buffer_vert };
- static const std::string FRAGMENT_SHADER{ debug_deferred_buffer_frag };
- static const std::string SOURCE_PLACEHOLDER{ "//SOURCE_PLACEHOLDER" };
- static const auto SOURCE_PLACEHOLDER_INDEX = FRAGMENT_SHADER.find(SOURCE_PLACEHOLDER);
- Q_ASSERT_X(SOURCE_PLACEHOLDER_INDEX != std::string::npos, Q_FUNC_INFO,
- "Could not find source placeholder");
- static const std::string DEFAULT_DEPTH_SHADER{
- "vec4 getFragmentColor() {"
- " float Zdb = texelFetch(depthMap, ivec2(gl_FragCoord.xy), 0).x;"
- " Zdb = 1.0-(1.0-Zdb)*100;"
- " return vec4(Zdb, Zdb, Zdb, 1.0);"
- " }"
- };
-
- auto bakedFragmentShader = FRAGMENT_SHADER;
- bakedFragmentShader.replace(SOURCE_PLACEHOLDER_INDEX, SOURCE_PLACEHOLDER.size(), DEFAULT_DEPTH_SHADER);
-
- static const auto vs = gpu::Shader::createVertex(VERTEX_SHADER);
- const auto ps = gpu::Shader::createPixel(bakedFragmentShader);
- const auto program = gpu::Shader::createProgram(vs, ps);
-
- gpu::Shader::BindingSet slotBindings;
- slotBindings.insert(gpu::Shader::Binding("depthMap", 0));
- gpu::Shader::makeProgram(*program, slotBindings);
-
- auto state = std::make_shared();
- state->setDepthTest(gpu::State::DepthTest(false));
- _debugPipeline = gpu::Pipeline::create(program, state);
- }
-
- return _debugPipeline;
-}
-
-DrawOutlineTask::DrawOutlineTask() {
-
-}
-
-void DrawOutlineTask::configure(const Config& config) {
-
-}
-
-void DrawOutlineTask::build(JobModel& task, const render::Varying& inputs, render::Varying& outputs) {
- const auto input = inputs.get();
- const auto selectedMetas = inputs.getN(0);
- const auto shapePlumber = input.get1();
- const auto sceneFrameBuffer = inputs.getN(2);
- const auto primaryFramebuffer = inputs.getN(3);
- const auto deferredFrameTransform = inputs.getN(4);
-
- // Prepare the ShapePipeline
- ShapePlumberPointer shapePlumberZPass = std::make_shared();
- {
- auto state = std::make_shared();
- state->setDepthTest(true, true, gpu::LESS_EQUAL);
- state->setColorWriteMask(false, false, false, false);
-
- initZPassPipelines(*shapePlumberZPass, state);
- }
-
- const auto outlinedItemIDs = task.addJob("OutlineMetaToSubItemIDs", selectedMetas);
- const auto outlinedItems = task.addJob("OutlineMetaToSubItems", outlinedItemIDs, true);
-
- // Sort
- const auto sortedPipelines = task.addJob("OutlinePipelineSort", outlinedItems);
- const auto sortedShapes = task.addJob("OutlineDepthSort", sortedPipelines);
-
- // Draw depth of outlined objects in separate buffer
- const auto drawOutlineDepthInputs = DrawOutlineDepth::Inputs(sortedShapes, sceneFrameBuffer).asVarying();
- const auto outlinedFrameBuffer = task.addJob("OutlineDepth", drawOutlineDepthInputs, shapePlumberZPass);
-
- // Draw outline
- const auto drawOutlineInputs = DrawOutline::Inputs(deferredFrameTransform, outlinedFrameBuffer, sceneFrameBuffer, primaryFramebuffer).asVarying();
- task.addJob("OutlineEffect", drawOutlineInputs);
-
- // Debug outline
- task.addJob("OutlineDebug", outlinedFrameBuffer);
-}
diff --git a/libraries/render-utils/src/OutlineEffect.h b/libraries/render-utils/src/OutlineEffect.h
deleted file mode 100644
index f88092429f..0000000000
--- a/libraries/render-utils/src/OutlineEffect.h
+++ /dev/null
@@ -1,183 +0,0 @@
-//
-// OutlineEffect.h
-// render-utils/src/
-//
-// Created by Olivier Prat on 08/08/17.
-// Copyright 2017 High Fidelity, Inc.
-//
-// Distributed under the Apache License, Version 2.0.
-// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
-//
-
-#ifndef hifi_render_utils_OutlineEffect_h
-#define hifi_render_utils_OutlineEffect_h
-
-#include
-#include "DeferredFramebuffer.h"
-#include "DeferredFrameTransform.h"
-
-class OutlineFramebuffer {
-public:
- OutlineFramebuffer();
-
- gpu::FramebufferPointer getDepthFramebuffer();
- gpu::TexturePointer getDepthTexture();
-
- // Update the source framebuffer size which will drive the allocation of all the other resources.
- void update(const gpu::TexturePointer& colorBuffer);
- const glm::ivec2& getSourceFrameSize() const { return _frameSize; }
-
-protected:
-
- void clear();
- void allocate();
-
- gpu::FramebufferPointer _depthFramebuffer;
- gpu::TexturePointer _depthTexture;
-
- glm::ivec2 _frameSize;
-};
-
-using OutlineFramebufferPointer = std::shared_ptr;
-
-class DrawOutlineDepth {
-public:
-
- using Inputs = render::VaryingSet2;
- // Output will contain outlined objects only z-depth texture and the input primary buffer but without the primary depth buffer
- using Outputs = OutlineFramebufferPointer;
- using JobModel = render::Job::ModelIO;
-
- DrawOutlineDepth(render::ShapePlumberPointer shapePlumber) : _shapePlumber{ shapePlumber } {}
-
- void run(const render::RenderContextPointer& renderContext, const Inputs& inputs, Outputs& output);
-
-protected:
-
- render::ShapePlumberPointer _shapePlumber;
- OutlineFramebufferPointer _outlineFramebuffer;
-};
-
-class DrawOutlineConfig : public render::Job::Config {
- Q_OBJECT
- Q_PROPERTY(bool glow MEMBER glow NOTIFY dirty)
- Q_PROPERTY(float width MEMBER width NOTIFY dirty)
- Q_PROPERTY(float intensity MEMBER intensity NOTIFY dirty)
- Q_PROPERTY(float colorR READ getColorR WRITE setColorR NOTIFY dirty)
- Q_PROPERTY(float colorG READ getColorG WRITE setColorG NOTIFY dirty)
- Q_PROPERTY(float colorB READ getColorB WRITE setColorB NOTIFY dirty)
- Q_PROPERTY(float fillOpacityUnoccluded MEMBER fillOpacityUnoccluded NOTIFY dirty)
- Q_PROPERTY(float fillOpacityOccluded MEMBER fillOpacityOccluded NOTIFY dirty)
-
-public:
-
- void setColorR(float value) { color.r = value; emit dirty(); }
- float getColorR() const { return color.r; }
-
- void setColorG(float value) { color.g = value; emit dirty(); }
- float getColorG() const { return color.g; }
-
- void setColorB(float value) { color.b = value; emit dirty(); }
- float getColorB() const { return color.b; }
-
- glm::vec3 color{ 1.f, 0.7f, 0.2f };
- float width{ 2.0f };
- float intensity{ 0.9f };
- float fillOpacityUnoccluded{ 0.0f };
- float fillOpacityOccluded{ 0.0f };
- bool glow{ false };
-
-signals:
- void dirty();
-};
-
-class DrawOutline {
-public:
- using Inputs = render::VaryingSet4;
- using Config = DrawOutlineConfig;
- using JobModel = render::Job::ModelI;
-
- DrawOutline();
-
- void configure(const Config& config);
- void run(const render::RenderContextPointer& renderContext, const Inputs& inputs);
-
-private:
-
- enum {
- SCENE_DEPTH_SLOT = 0,
- OUTLINED_DEPTH_SLOT,
-
- OUTLINE_PARAMS_SLOT = 0,
- FRAME_TRANSFORM_SLOT
- };
-
-#include "Outline_shared.slh"
-
- using OutlineConfigurationBuffer = gpu::StructBuffer;
-
- const gpu::PipelinePointer& getPipeline(bool isFilled);
-
- gpu::FramebufferPointer _primaryWithoutDepthBuffer;
- glm::ivec2 _frameBufferSize {0, 0};
- gpu::PipelinePointer _pipeline;
- gpu::PipelinePointer _pipelineFilled;
- OutlineConfigurationBuffer _configuration;
- glm::vec3 _color;
- float _size;
- int _blurKernelSize;
- float _intensity;
- float _fillOpacityUnoccluded;
- float _fillOpacityOccluded;
- float _threshold;
-};
-
-class DebugOutlineConfig : public render::Job::Config {
- Q_OBJECT
- Q_PROPERTY(bool viewOutlinedDepth MEMBER viewOutlinedDepth NOTIFY dirty)
-
-public:
-
- bool viewOutlinedDepth{ false };
-
-signals:
- void dirty();
-};
-
-class DebugOutline {
-public:
- using Inputs = OutlineFramebufferPointer;
- using Config = DebugOutlineConfig;
- using JobModel = render::Job::ModelI;
-
- DebugOutline();
- ~DebugOutline();
-
- void configure(const Config& config);
- void run(const render::RenderContextPointer& renderContext, const Inputs& inputs);
-
-private:
-
- const gpu::PipelinePointer& getDebugPipeline();
-
- gpu::PipelinePointer _debugPipeline;
- int _geometryId{ 0 };
- bool _isDisplayDepthEnabled{ false };
-};
-
-class DrawOutlineTask {
-public:
- using Inputs = render::VaryingSet5;
- using Config = render::Task::Config;
- using JobModel = render::Task::ModelI;
-
- DrawOutlineTask();
-
- void configure(const Config& config);
- void build(JobModel& task, const render::Varying& inputs, render::Varying& outputs);
-
-};
-
-#endif // hifi_render_utils_OutlineEffect_h
-
-
diff --git a/libraries/render-utils/src/Outline_shared.slh b/libraries/render-utils/src/Outline_shared.slh
deleted file mode 100644
index 902bbd20ad..0000000000
--- a/libraries/render-utils/src/Outline_shared.slh
+++ /dev/null
@@ -1,28 +0,0 @@
-// glsl / C++ compatible source as interface for Outline
-#ifdef __cplusplus
-# define VEC2 glm::vec2
-# define VEC3 glm::vec3
-#else
-# define VEC2 vec2
-# define VEC3 vec3
-#endif
-
-struct OutlineParameters
-{
- VEC3 _color;
- float _intensity;
-
- VEC2 _size;
- float _fillOpacityUnoccluded;
- float _fillOpacityOccluded;
-
- float _threshold;
- int _blurKernelSize;
- float padding2;
- float padding3;
-};
-
-// <@if 1@>
-// Trigger Scribe include
-// <@endif@>
-//
diff --git a/libraries/render-utils/src/RenderDeferredTask.cpp b/libraries/render-utils/src/RenderDeferredTask.cpp
index 6a3b560167..a395136978 100644
--- a/libraries/render-utils/src/RenderDeferredTask.cpp
+++ b/libraries/render-utils/src/RenderDeferredTask.cpp
@@ -35,16 +35,17 @@
#include "TextureCache.h"
#include "ZoneRenderer.h"
#include "FadeEffect.h"
+#include "RenderUtilsLogging.h"
#include "AmbientOcclusionEffect.h"
#include "AntialiasingEffect.h"
#include "ToneMappingEffect.h"
#include "SubsurfaceScattering.h"
#include "DrawHaze.h"
-#include "OutlineEffect.h"
-
-#include
+#include "BloomEffect.h"
+#include "HighlightEffect.h"
+#include
using namespace render;
extern void initOverlay3DPipelines(render::ShapePlumber& plumber, bool depthTest = false);
@@ -58,6 +59,18 @@ void RenderDeferredTask::configure(const Config& config)
{
}
+const render::Varying RenderDeferredTask::addSelectItemJobs(JobModel& task, const char* selectionName,
+ const render::Varying& metas,
+ const render::Varying& opaques,
+ const render::Varying& transparents) {
+ const auto selectMetaInput = SelectItems::Inputs(metas, Varying(), std::string()).asVarying();
+ const auto selectedMetas = task.addJob("MetaSelection", selectMetaInput, selectionName);
+ const auto selectMetaAndOpaqueInput = SelectItems::Inputs(opaques, selectedMetas, std::string()).asVarying();
+ const auto selectedMetasAndOpaques = task.addJob("OpaqueSelection", selectMetaAndOpaqueInput, selectionName);
+ const auto selectItemInput = SelectItems::Inputs(transparents, selectedMetasAndOpaques, std::string()).asVarying();
+ return task.addJob("TransparentSelection", selectItemInput, selectionName);
+}
+
void RenderDeferredTask::build(JobModel& task, const render::Varying& input, render::Varying& output) {
const auto& items = input.get();
auto fadeEffect = DependencyManager::get();
@@ -95,15 +108,6 @@ void RenderDeferredTask::build(JobModel& task, const render::Varying& input, ren
// draw a stencil mask in hidden regions of the framebuffer.
task.addJob("PrepareStencil", primaryFramebuffer);
- // Select items that need to be outlined
- const auto selectionName = "contextOverlayHighlightList";
- const auto selectMetaInput = SelectItems::Inputs(metas, Varying()).asVarying();
- const auto selectedMetas = task.addJob("PassTestMetaSelection", selectMetaInput, selectionName);
- const auto selectMetaAndOpaqueInput = SelectItems::Inputs(opaques, selectedMetas).asVarying();
- const auto selectedMetasAndOpaques = task.addJob("PassTestOpaqueSelection", selectMetaAndOpaqueInput, selectionName);
- const auto selectItemInput = SelectItems::Inputs(transparents, selectedMetasAndOpaques).asVarying();
- const auto selectedItems = task.addJob("PassTestTransparentSelection", selectItemInput, selectionName);
-
// Render opaque objects in DeferredBuffer
const auto opaqueInputs = DrawStateSortDeferred::Inputs(opaques, lightingModel).asVarying();
task.addJob("DrawOpaqueDeferred", opaqueInputs, shapePlumber);
@@ -163,7 +167,7 @@ void RenderDeferredTask::build(JobModel& task, const render::Varying& input, ren
const auto transparentsInputs = DrawDeferred::Inputs(transparents, lightingModel).asVarying();
task.addJob("DrawTransparentDeferred", transparentsInputs, shapePlumber);
- // LIght Cluster Grid Debuging job
+ // Light Cluster Grid Debuging job
{
const auto debugLightClustersInputs = DebugLightClusters::Inputs(deferredFrameTransform, deferredFramebuffer, lightingModel, linearDepthTarget, lightClusters).asVarying();
task.addJob("DebugLightClusters", debugLightClustersInputs);
@@ -174,22 +178,39 @@ void RenderDeferredTask::build(JobModel& task, const render::Varying& input, ren
const auto toneAndPostRangeTimer = task.addJob("BeginToneAndPostRangeTimer", "PostToneOverlaysAntialiasing");
+ // Add bloom
+ const auto bloomInputs = Bloom::Inputs(deferredFrameTransform, lightingFramebuffer).asVarying();
+ task.addJob("Bloom", bloomInputs);
+
// Lighting Buffer ready for tone mapping
const auto toneMappingInputs = ToneMappingDeferred::Inputs(lightingFramebuffer, primaryFramebuffer).asVarying();
task.addJob("ToneMapping", toneMappingInputs);
- const auto outlineRangeTimer = task.addJob("BeginOutlineRangeTimer", "Outline");
- const auto outlineInputs = DrawOutlineTask::Inputs(selectedItems, shapePlumber, deferredFramebuffer, primaryFramebuffer, deferredFrameTransform).asVarying();
- task.addJob("DrawOutline", outlineInputs);
- task.addJob("EndOutlineRangeTimer", outlineRangeTimer);
+ const auto outlineRangeTimer = task.addJob("BeginHighlightRangeTimer", "Highlight");
+ // Select items that need to be outlined
+ const auto selectionBaseName = "contextOverlayHighlightList";
+ const auto selectedItems = addSelectItemJobs(task, selectionBaseName, metas, opaques, transparents);
- { // DEbug the bounds of the rendered items, still look at the zbuffer
+ const auto outlineInputs = DrawHighlightTask::Inputs(items.get0(), deferredFramebuffer, primaryFramebuffer, deferredFrameTransform).asVarying();
+ task.addJob("DrawHighlight", outlineInputs);
+
+ task.addJob("HighlightRangeTimer", outlineRangeTimer);
+
+ { // Debug the bounds of the rendered items, still look at the zbuffer
task.addJob("DrawMetaBounds", metas);
task.addJob("DrawOpaqueBounds", opaques);
task.addJob("DrawTransparentBounds", transparents);
task.addJob("DrawLightBounds", lights);
task.addJob("DrawZones", zones);
+ const auto frustums = task.addJob("ExtractFrustums");
+ const auto viewFrustum = frustums.getN(ExtractFrustums::VIEW_FRUSTUM);
+ const auto shadowFrustum = frustums.getN(ExtractFrustums::SHADOW_FRUSTUM);
+ task.addJob("DrawViewFrustum", viewFrustum, glm::vec3(1.0f, 1.0f, 0.0f));
+ task.addJob("DrawShadowFrustum", shadowFrustum, glm::vec3(0.0f, 0.0f, 1.0f));
+
+ // Render.getConfig("RenderMainView.DrawSelectionBounds").enabled = true
+ task.addJob("DrawSelectionBounds", selectedItems);
}
// Layered Overlays
@@ -236,9 +257,6 @@ void RenderDeferredTask::build(JobModel& task, const render::Varying& input, ren
}
task.addJob("DrawZoneStack", deferredFrameTransform);
-
- // Render.getConfig("RenderMainView.DrawSelectionBounds").enabled = true
- task.addJob("DrawSelectionBounds", selectedItems);
}
// AA job to be revisited
@@ -438,6 +456,11 @@ void CompositeHUD::run(const RenderContextPointer& renderContext) {
assert(renderContext->args);
assert(renderContext->args->_context);
+ // We do not want to render HUD elements in secondary camera
+ if (renderContext->args->_renderMode == RenderArgs::RenderMode::SECONDARY_CAMERA_RENDER_MODE) {
+ return;
+ }
+
// Grab the HUD texture
gpu::doInBatch(renderContext->args->_context, [&](gpu::Batch& batch) {
if (renderContext->args->_hudOperator) {
@@ -454,6 +477,7 @@ void Blit::run(const RenderContextPointer& renderContext, const gpu::Framebuffer
auto blitFbo = renderArgs->_blitFramebuffer;
if (!blitFbo) {
+ qCWarning(renderutils) << "Blit::run - no blit frame buffer.";
return;
}
@@ -516,3 +540,32 @@ void Blit::run(const RenderContextPointer& renderContext, const gpu::Framebuffer
});
}
+void ExtractFrustums::run(const render::RenderContextPointer& renderContext, Output& output) {
+ assert(renderContext->args);
+ assert(renderContext->args->_context);
+
+ RenderArgs* args = renderContext->args;
+
+ // Return view frustum
+ auto& viewFrustum = output[VIEW_FRUSTUM].edit();
+ if (!viewFrustum) {
+ viewFrustum = std::make_shared(args->getViewFrustum());
+ } else {
+ *viewFrustum = args->getViewFrustum();
+ }
+
+ // Return shadow frustum
+ auto& shadowFrustum = output[SHADOW_FRUSTUM].edit();
+ auto lightStage = args->_scene->getStage(LightStage::getName());
+ if (lightStage) {
+ auto globalShadow = lightStage->getCurrentKeyShadow();
+
+ if (globalShadow) {
+ shadowFrustum = globalShadow->getFrustum();
+ } else {
+ shadowFrustum.reset();
+ }
+ } else {
+ shadowFrustum.reset();
+ }
+}
diff --git a/libraries/render-utils/src/RenderDeferredTask.h b/libraries/render-utils/src/RenderDeferredTask.h
index 452420589b..40ae503fb7 100644
--- a/libraries/render-utils/src/RenderDeferredTask.h
+++ b/libraries/render-utils/src/RenderDeferredTask.h
@@ -170,6 +170,22 @@ public:
void run(const render::RenderContextPointer& renderContext, const gpu::FramebufferPointer& srcFramebuffer);
};
+class ExtractFrustums {
+public:
+
+ enum Frustum {
+ VIEW_FRUSTUM,
+ SHADOW_FRUSTUM,
+
+ FRUSTUM_COUNT
+ };
+
+ using Output = render::VaryingArray;
+ using JobModel = render::Job::ModelO;
+
+ void run(const render::RenderContextPointer& renderContext, Output& output);
+};
+
class RenderDeferredTaskConfig : public render::Task::Config {
Q_OBJECT
Q_PROPERTY(float fadeScale MEMBER fadeScale NOTIFY dirty)
@@ -198,6 +214,10 @@ public:
void configure(const Config& config);
void build(JobModel& task, const render::Varying& inputs, render::Varying& outputs);
+private:
+
+ static const render::Varying addSelectItemJobs(JobModel& task, const char* selectionName,
+ const render::Varying& metas, const render::Varying& opaques, const render::Varying& transparents);
};
#endif // hifi_RenderDeferredTask_h
diff --git a/libraries/render-utils/src/RenderShadowTask.cpp b/libraries/render-utils/src/RenderShadowTask.cpp
index 7171543abc..7a6e3dc74f 100644
--- a/libraries/render-utils/src/RenderShadowTask.cpp
+++ b/libraries/render-utils/src/RenderShadowTask.cpp
@@ -22,25 +22,136 @@
#include "DeferredLightingEffect.h"
#include "FramebufferCache.h"
+// These values are used for culling the objects rendered in the shadow map
+// but are readjusted afterwards
+#define SHADOW_FRUSTUM_NEAR 1.0f
+#define SHADOW_FRUSTUM_FAR 500.0f
+
using namespace render;
extern void initZPassPipelines(ShapePlumber& plumber, gpu::StatePointer state);
-void RenderShadowMap::run(const render::RenderContextPointer& renderContext,
- const render::ShapeBounds& inShapes) {
+static void computeNearFar(const Triangle& triangle, const Plane shadowClipPlanes[4], float& near, float& far) {
+ static const int MAX_TRIANGLE_COUNT = 16;
+ Triangle clippedTriangles[MAX_TRIANGLE_COUNT];
+ auto clippedTriangleCount = clipTriangleWithPlanes(triangle, shadowClipPlanes, 4, clippedTriangles, MAX_TRIANGLE_COUNT);
+
+ for (auto i = 0; i < clippedTriangleCount; i++) {
+ const auto& clippedTriangle = clippedTriangles[i];
+
+ near = glm::min(near, -clippedTriangle.v0.z);
+ near = glm::min(near, -clippedTriangle.v1.z);
+ near = glm::min(near, -clippedTriangle.v2.z);
+
+ far = glm::max(far, -clippedTriangle.v0.z);
+ far = glm::max(far, -clippedTriangle.v1.z);
+ far = glm::max(far, -clippedTriangle.v2.z);
+ }
+}
+
+static void computeNearFar(const glm::vec3 sceneBoundVertices[8], const Plane shadowClipPlanes[4], float& near, float& far) {
+ // This code is inspired from Microsoft's CascadedShadowMaps11 sample which is under MIT licence.
+ // See https://code.msdn.microsoft.com/windowsdesktop/Direct3D-Shadow-Win32-2d72a4f2/sourcecode?fileId=121915&pathId=1645833187
+ // Basically it decomposes the object bounding box in triangles and clips each triangle with the shadow
+ // frustum planes. Finally it computes the minimum and maximum depth of the clipped triangle vertices
+ // in shadow space to extract the near and far distances of the shadow frustum.
+ static const std::array boxQuadVertexIndices = { {
+ { TOP_LEFT_FAR, BOTTOM_LEFT_FAR, BOTTOM_RIGHT_FAR, TOP_RIGHT_FAR },
+ { TOP_LEFT_NEAR, BOTTOM_LEFT_NEAR, BOTTOM_RIGHT_NEAR, TOP_RIGHT_NEAR },
+ { TOP_RIGHT_FAR, BOTTOM_RIGHT_FAR, BOTTOM_RIGHT_NEAR, TOP_RIGHT_NEAR },
+ { TOP_LEFT_FAR, BOTTOM_LEFT_FAR, BOTTOM_LEFT_NEAR, TOP_LEFT_NEAR },
+ { BOTTOM_LEFT_FAR, BOTTOM_RIGHT_FAR, BOTTOM_RIGHT_NEAR, BOTTOM_LEFT_NEAR },
+ { TOP_LEFT_FAR, TOP_RIGHT_FAR, TOP_RIGHT_NEAR, TOP_LEFT_NEAR }
+ } };
+ Triangle triangle;
+
+ for (auto quadVertexIndices : boxQuadVertexIndices) {
+ triangle.v0 = sceneBoundVertices[quadVertexIndices[0]];
+ triangle.v1 = sceneBoundVertices[quadVertexIndices[1]];
+ triangle.v2 = sceneBoundVertices[quadVertexIndices[2]];
+ computeNearFar(triangle, shadowClipPlanes, near, far);
+ triangle.v1 = sceneBoundVertices[quadVertexIndices[3]];
+ computeNearFar(triangle, shadowClipPlanes, near, far);
+ }
+}
+
+static void adjustNearFar(const AABox& inShapeBounds, ViewFrustum& shadowFrustum) {
+ const Transform shadowView{ shadowFrustum.getView() };
+ const Transform shadowViewInverse{ shadowView.getInverseMatrix() };
+
+ glm::vec3 sceneBoundVertices[8];
+ // Keep only the left, right, top and bottom shadow frustum planes as we wish to determine
+ // the near and far
+ Plane shadowClipPlanes[4];
+ int i;
+
+ // The vertices of the scene bounding box are expressed in the shadow frustum's local space
+ for (i = 0; i < 8; i++) {
+ sceneBoundVertices[i] = shadowViewInverse.transform(inShapeBounds.getVertex(static_cast(i)));
+ }
+ // This indirection array is just a protection in case the ViewFrustum::PlaneIndex enum
+ // changes order especially as we don't need to test the NEAR and FAR planes.
+ static const ViewFrustum::PlaneIndex planeIndices[4] = {
+ ViewFrustum::TOP_PLANE,
+ ViewFrustum::BOTTOM_PLANE,
+ ViewFrustum::LEFT_PLANE,
+ ViewFrustum::RIGHT_PLANE
+ };
+ // Same goes for the shadow frustum planes.
+ for (i = 0; i < 4; i++) {
+ const auto& worldPlane = shadowFrustum.getPlanes()[planeIndices[i]];
+ // We assume the transform doesn't have a non uniform scale component to apply the
+ // transform to the normal without using the correct transpose of inverse, which should be the
+ // case for a view matrix.
+ auto planeNormal = shadowViewInverse.transformDirection(worldPlane.getNormal());
+ auto planePoint = shadowViewInverse.transform(worldPlane.getPoint());
+ shadowClipPlanes[i].setNormalAndPoint(planeNormal, planePoint);
+ }
+
+ float near = std::numeric_limits::max();
+ float far = 0.0f;
+
+ computeNearFar(sceneBoundVertices, shadowClipPlanes, near, far);
+ // Limit the far range to the one used originally. There's no point in rendering objects
+ // that are not in the view frustum.
+ far = glm::min(far, shadowFrustum.getFarClip());
+
+ const auto depthEpsilon = 0.1f;
+ auto projMatrix = glm::ortho(-1.0f, 1.0f, -1.0f, 1.0f, near - depthEpsilon, far + depthEpsilon);
+ auto shadowProjection = shadowFrustum.getProjection();
+
+ shadowProjection[2][2] = projMatrix[2][2];
+ shadowProjection[3][2] = projMatrix[3][2];
+ shadowFrustum.setProjection(shadowProjection);
+ shadowFrustum.calculate();
+}
+
+void RenderShadowMap::run(const render::RenderContextPointer& renderContext, const Inputs& inputs) {
assert(renderContext->args);
assert(renderContext->args->hasViewFrustum());
+ const auto& inShapes = inputs.get0();
+ const auto& inShapeBounds = inputs.get1();
+
auto lightStage = renderContext->_scene->getStage();
assert(lightStage);
- const auto shadow = lightStage->getCurrentKeyShadow();
+ auto shadow = lightStage->getCurrentKeyShadow();
if (!shadow) return;
const auto& fbo = shadow->framebuffer;
RenderArgs* args = renderContext->args;
ShapeKey::Builder defaultKeyBuilder;
+ auto adjustedShadowFrustum = args->getViewFrustum();
+
+ // Adjust the frustum near and far depths based on the rendered items bounding box to have
+ // the minimal Z range.
+ adjustNearFar(inShapeBounds, adjustedShadowFrustum);
+ // Reapply the frustum as it has been adjusted
+ shadow->setFrustum(adjustedShadowFrustum);
+ args->popViewFrustum();
+ args->pushViewFrustum(adjustedShadowFrustum);
gpu::doInBatch(args->_context, [&](gpu::Batch& batch) {
args->_batch = &batch;
@@ -55,8 +166,13 @@ void RenderShadowMap::run(const render::RenderContextPointer& renderContext,
gpu::Framebuffer::BUFFER_COLOR0 | gpu::Framebuffer::BUFFER_DEPTH,
vec4(vec3(1.0, 1.0, 1.0), 0.0), 1.0, 0, true);
- batch.setProjectionTransform(shadow->getProjection());
- batch.setViewTransform(shadow->getView(), false);
+ glm::mat4 projMat;
+ Transform viewMat;
+ args->getViewFrustum().evalProjectionMatrix(projMat);
+ args->getViewFrustum().evalViewTransform(viewMat);
+
+ batch.setProjectionTransform(projMat);
+ batch.setViewTransform(viewMat, false);
auto shadowPipeline = _shapePlumber->pickPipeline(args, defaultKeyBuilder);
auto shadowSkinnedPipeline = _shapePlumber->pickPipeline(args, defaultKeyBuilder.withSkinned());
@@ -87,7 +203,7 @@ void RenderShadowMap::run(const render::RenderContextPointer& renderContext,
}
void RenderShadowTask::build(JobModel& task, const render::Varying& input, render::Varying& output, CullFunctor cullFunctor) {
- cullFunctor = cullFunctor ? cullFunctor : [](const RenderArgs*, const AABox&){ return true; };
+ cullFunctor = cullFunctor ? cullFunctor : [](const RenderArgs*, const AABox&) { return true; };
// Prepare the ShapePipeline
ShapePlumberPointer shapePlumber = std::make_shared();
@@ -109,10 +225,10 @@ void RenderShadowTask::build(JobModel& task, const render::Varying& input, rende
// Sort
const auto sortedPipelines = task.addJob("PipelineSortShadowSort", culledShadowSelection);
- const auto sortedShapes = task.addJob("DepthSortShadowMap", sortedPipelines);
+ const auto sortedShapesAndBounds = task.addJob("DepthSortShadowMap", sortedPipelines, true);
// GPU jobs: Render to shadow map
- task.addJob