Merge branch 'feature/qml_whitelist_tablet' of https://github.com/jherico/hifi into commerce_QmlWhitelist

This commit is contained in:
Zach Fox 2017-11-13 15:01:26 -08:00
commit a3332e15b7
203 changed files with 3213 additions and 1937 deletions

1
.gitignore vendored
View file

@ -64,6 +64,7 @@ gvr-interface/libs/*
# ignore files for various dev environments
TAGS
*.sw[po]
*.qmlc
# ignore node files for the console
node_modules

View file

@ -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()

View file

@ -19,12 +19,13 @@
Upload an entities file (e.g.: models.json.gz) to replace the content of this domain.<br>
Note: <strong>Your domain's content will be replaced by the content you upload</strong>, but the backup files of your domain's content will not immediately be changed.
</p>
<p>
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:<br>
<pre>C:/Users/[username]/AppData/Roaming/High Fidelity/assignment-client/entities/models.json.gz</pre>
<pre>/Users/[username]/Library/Application Support/High Fidelity/assignment-client/entities/models.json.gz</pre>
<pre>/home/[username]/.local/share/High Fidelity/assignment-client/entities/models.json.gz</pre>
</p>
<p>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:</p>
<label class="control-label">Windows</label>
<pre>C:/Users/[username]/AppData/Roaming/High Fidelity/assignment-client/entities/models.json.gz</pre>
<label class="control-label">OSX</label>
<pre>/Users/[username]/Library/Application Support/High Fidelity/assignment-client/entities/models.json.gz</pre>
<label class="control-label">Linux</label>
<pre>/home/[username]/.local/share/High Fidelity/assignment-client/entities/models.json.gz</pre>
<br>
<input type="file" name="entities-file" class="form-control-file" accept=".json, .gz">
<br>

View file

@ -12,8 +12,10 @@ function(JOIN VALUES GLUE OUTPUT)
endfunction()
set(INTERFACE_QML_QRC ${CMAKE_CURRENT_BINARY_DIR}/qml.qrc)
generate_qrc(OUTPUT ${INTERFACE_QML_QRC} PATH ${CMAKE_CURRENT_SOURCE_DIR}/resources GLOBS *.qml *.qss *.js *.html *.ttf *.gif *.svg *.png *.jpg)
if (NOT DEV_BUILD)
set(INTERFACE_QML_QRC ${CMAKE_CURRENT_BINARY_DIR}/qml.qrc)
generate_qrc(OUTPUT ${INTERFACE_QML_QRC} PATH ${CMAKE_CURRENT_SOURCE_DIR}/resources GLOBS *.qml *.qss *.js *.html *.ttf *.gif *.svg *.png *.jpg)
endif()
# set a default root dir for each of our optional externals if it was not passed
set(OPTIONAL_EXTERNALS "LeapMotion")
@ -80,7 +82,9 @@ qt5_wrap_ui(QT_UI_HEADERS "${QT_UI_FILES}")
# add them to the interface source files
set(INTERFACE_SRCS ${INTERFACE_SRCS} "${QT_UI_HEADERS}" "${QT_RESOURCES}")
if (NOT DEV_BUILD)
list(APPEND INTERFACE_SRCS ${INTERFACE_QML_QRC})
endif()
if (UNIX)
install(

View file

@ -248,7 +248,7 @@ Item {
//dont rise local keyboard
keyboardEnabled = !root.isTablet && HMD.active;
//but rise Tablet's one instead for Tablet interface
//but rise Tablet's one instead for Tablets interface
if (root.isTablet) {
root.keyboardEnabled = HMD.active;
root.keyboardRaised = Qt.binding( function() { return keyboardRaised; })

View file

@ -221,7 +221,7 @@ Item {
root.iconText = "<"
//dont rise local keyboard
keyboardEnabled = !root.isTablet && HMD.active;
//but rise Tablet's one instead for Tablet interface
//but rise Tablet's one instead for Tablets interface
if (root.isTablet) {
root.keyboardEnabled = HMD.active;
root.keyboardRaised = Qt.binding( function() { return keyboardRaised; })

View file

@ -131,7 +131,7 @@ Item {
root.iconText = "<"
//dont rise local keyboard
keyboardEnabled = !root.isTablet && HMD.active;
//but rise Tablet's one instead for Tablet interface
//but rise Tablet's one instead for Tablets interface
if (root.isTablet) {
root.keyboardEnabled = HMD.active;
root.keyboardRaised = Qt.binding( function() { return keyboardRaised; })

View file

@ -29,12 +29,12 @@ Original.Button {
onHoveredChanged: {
if (hovered) {
tabletInterface.playSound(TabletEnums.ButtonHover);
Tablets.playSound(TabletEnums.ButtonHover);
}
}
onClicked: {
tabletInterface.playSound(TabletEnums.ButtonClick);
Tablets.playSound(TabletEnums.ButtonClick);
}
style: ButtonStyle {

View file

@ -31,12 +31,12 @@ Original.CheckBox {
activeFocusOnPress: true
onClicked: {
tabletInterface.playSound(TabletEnums.ButtonClick);
Tablets.playSound(TabletEnums.ButtonClick);
}
// TODO: doesnt works for QQC1. check with QQC2
// onHovered: {
// tabletInterface.playSound(TabletEnums.ButtonHover);
// Tablets.playSound(TabletEnums.ButtonHover);
// }
style: CheckBoxStyle {

View file

@ -36,12 +36,12 @@ CheckBox {
hoverEnabled: true
onClicked: {
tabletInterface.playSound(TabletEnums.ButtonClick);
Tablets.playSound(TabletEnums.ButtonClick);
}
onHoveredChanged: {
if (hovered) {
tabletInterface.playSound(TabletEnums.ButtonHover);
Tablets.playSound(TabletEnums.ButtonHover);
}
}

View file

@ -27,12 +27,12 @@ Original.Button {
onHoveredChanged: {
if (hovered) {
tabletInterface.playSound(TabletEnums.ButtonHover);
Tablets.playSound(TabletEnums.ButtonHover);
}
}
onClicked: {
tabletInterface.playSound(TabletEnums.ButtonClick);
Tablets.playSound(TabletEnums.ButtonClick);
}
style: ButtonStyle {

View file

@ -35,13 +35,13 @@ Item {
onContainsMouseChanged: {
if (containsMouse) {
tabletInterface.playSound(TabletEnums.ButtonHover);
Tablets.playSound(TabletEnums.ButtonHover);
}
}
onClicked: {
mouse.accepted = true;
tabletInterface.playSound(TabletEnums.ButtonClick);
Tablets.playSound(TabletEnums.ButtonClick);
webEntity.synthesizeKeyPress(glyph);
webEntity.synthesizeKeyPress(glyph, mirrorText);

View file

@ -30,12 +30,12 @@ Original.RadioButton {
readonly property int checkRadius: 2
onClicked: {
tabletInterface.playSound(TabletEnums.ButtonClick);
Tablets.playSound(TabletEnums.ButtonClick);
}
// TODO: doesnt works for QQC1. check with QQC2
// onHovered: {
// tabletInterface.playSound(TabletEnums.ButtonHover);
// Tablets.playSound(TabletEnums.ButtonHover);
// }
style: RadioButtonStyle {

View file

@ -41,7 +41,7 @@ Item {
}
if (WebEngineView.LoadFailedStatus === loadRequest.status) {
console.log(" Tablet WebEngineView failed to load url: " + loadRequest.url.toString());
console.log("Tablet WebEngineView failed to load url: " + loadRequest.url.toString());
}
if (WebEngineView.LoadSucceededStatus === loadRequest.status) {

View file

@ -24,7 +24,7 @@ Item {
});
object.selected.connect(function(button) {
if (button === OriginalDialogs.StandardButton.Ok) {
var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system");
var tablet = Tablets.getTablet("com.highfidelity.interface.tablet.system");
tablet.gotoHomeScreen()
}
});

View file

@ -89,10 +89,10 @@ TabletModalWindow {
//bodyLoader.active = false
}
if (gotoPreviousApp) {
var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system");
var tablet = Tablets.getTablet("com.highfidelity.interface.tablet.system");
tablet.returnToPreviousApp();
} else {
Tablet.getTablet("com.highfidelity.interface.tablet.system").gotoHomeScreen();
Tablets.getTablet("com.highfidelity.interface.tablet.system").gotoHomeScreen();
}
}

View file

@ -25,13 +25,13 @@ Preference {
id: button
onHoveredChanged: {
if (hovered) {
tabletInterface.playSound(TabletEnums.ButtonHover);
Tablets.playSound(TabletEnums.ButtonHover);
}
}
onClicked: {
preference.trigger();
tabletInterface.playSound(TabletEnums.ButtonClick);
Tablets.playSound(TabletEnums.ButtonClick);
}
width: 180
anchors.bottom: parent.bottom

View file

@ -41,12 +41,12 @@ Preference {
id: checkBox
onHoveredChanged: {
if (hovered) {
tabletInterface.playSound(TabletEnums.ButtonHover);
Tablets.playSound(TabletEnums.ButtonHover);
}
}
onClicked: {
tabletInterface.playSound(TabletEnums.ButtonClick);
Tablets.playSound(TabletEnums.ButtonClick);
}
anchors {

View file

@ -246,12 +246,12 @@ Item {
anchors.fill: parent;
acceptedButtons: Qt.LeftButton;
onClicked: {
tabletInterface.playSound(TabletEnums.ButtonClick);
Tablets.playSound(TabletEnums.ButtonClick);
goFunction("hifi://" + hifiUrl);
}
hoverEnabled: true;
onEntered: {
tabletInterface.playSound(TabletEnums.ButtonHover);
Tablets.playSound(TabletEnums.ButtonHover);
hoverThunk();
}
onExited: unhoverThunk();
@ -269,7 +269,7 @@ Item {
}
}
function go() {
tabletInterface.playSound(TabletEnums.ButtonClick);
Tablets.playSound(TabletEnums.ButtonClick);
goFunction(drillDownToPlace ? ("/places/" + placeName) : ("/user_stories/" + storyId));
}
MouseArea {

View file

@ -61,12 +61,12 @@ Rectangle {
scrollGestureEnabled: false;
onClicked: {
Audio.muted = !Audio.muted;
tabletInterface.playSound(TabletEnums.ButtonClick);
Tablets.playSound(TabletEnums.ButtonClick);
}
drag.target: dragTarget;
onContainsMouseChanged: {
if (containsMouse) {
tabletInterface.playSound(TabletEnums.ButtonHover);
Tablets.playSound(TabletEnums.ButtonHover);
}
}
}

View file

@ -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: "<b>The Wallet app is in closed Beta.</b><br><br>To request entry and <b>receive free HFC</b>, please contact " +
"<b>info@highfidelity.com</b> 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"});
}
}
//

View file

@ -123,11 +123,11 @@ Item {
hoverEnabled: true
enabled: true
onClicked: {
tabletInterface.playSound(TabletEnums.ButtonClick);
Tablets.playSound(TabletEnums.ButtonClick);
newEntityButton.clicked();
}
onEntered: {
tabletInterface.playSound(TabletEnums.ButtonHover);
Tablets.playSound(TabletEnums.ButtonHover);
newEntityButton.state = "hover state";
}
onExited: {

View file

@ -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")

View file

@ -1,280 +0,0 @@
import QtQuick 2.5
import QtGraphicalEffects 1.0
import QtQuick.Layouts 1.3
import "."
import "../../styles-uit"
import "../audio" as HifiAudio
Item {
id: tablet
objectName: "tablet"
property int rowIndex: 0
property int columnIndex: 0
property int count: (flowMain.children.length - 1)
Component {
id: buttonComponent
TabletButton { }
}
Component.onCompleted: {
tablet.populateButtons();
}
function createClickedHandler(proxy) {
return function() { proxy.clicked(); }
}
function populateButtons() {
var tabletProxy = Tablet.getTablet("com.highfidelity.interface.tablet.system");
var buttons = tabletProxy.getButtons();
for (var i = 0; i < buttons.length; i++) {
var proxy = buttons[i];
var button = tablet.addButtonProxy(proxy.getProperties());
button.clicked.connect(createClickedHandler(proxy));
proxy.setQmlButton(button);
}
sortButtons();
}
// used to look up a button by its uuid
function findButtonIndex(uuid) {
if (!uuid) {
return -1;
}
for (var i in flowMain.children) {
var child = flowMain.children[i];
if (child.uuid === uuid) {
return i;
}
}
return -1;
}
function sortButtons() {
var children = [];
for (var i = 0; i < flowMain.children.length; i++) {
children[i] = flowMain.children[i];
}
children.sort(function (a, b) {
if (a.sortOrder === b.sortOrder) {
// subsort by stableOrder, because JS sort is not stable in qml.
return a.stableOrder - b.stableOrder;
} else {
return a.sortOrder - b.sortOrder;
}
});
flowMain.children = children;
}
// called by C++ code when a button should be added to the tablet
function addButtonProxy(properties) {
var button = buttonComponent.createObject(flowMain);
// copy all properites to button
var keys = Object.keys(properties).forEach(function (key) {
button[key] = properties[key];
});
// pass a reference to the tabletRoot object to the button.
if (tabletRoot) {
button.tabletRoot = tabletRoot;
} else {
button.tabletRoot = parent.parent;
}
return button;
}
// called by C++ code when a button should be removed from the tablet
function removeButtonProxy(properties) {
var index = findButtonIndex(properties.uuid);
if (index < 0) {
console.log("Warning: Tablet.qml could not find button with uuid = " + properties.uuid);
} else {
flowMain.children[index].destroy();
}
}
Rectangle {
id: bgTopBar
height: 90
anchors {
top: parent.top
left: parent.left
right: parent.right
}
gradient: Gradient {
GradientStop {
position: 0
color: "#2b2b2b"
}
GradientStop {
position: 1
color: "#1e1e1e"
}
}
HifiAudio.MicBar {
anchors {
left: parent.left
leftMargin: 30
verticalCenter: parent.verticalCenter
}
}
Item {
width: 150
height: 50
anchors.right: parent.right
anchors.rightMargin: 30
anchors.verticalCenter: parent.verticalCenter
ColumnLayout {
anchors.fill: parent
RalewaySemiBold {
text: Account.loggedIn ? qsTr("Log out") : qsTr("Log in")
horizontalAlignment: Text.AlignRight
anchors.right: parent.right
font.pixelSize: 20
color: "#afafaf"
}
RalewaySemiBold {
visible: Account.loggedIn
height: Account.loggedIn ? parent.height/2 - parent.spacing/2 : 0
text: Account.loggedIn ? "[" + tabletRoot.usernameShort + "]" : ""
horizontalAlignment: Text.AlignRight
anchors.right: parent.right
font.pixelSize: 20
color: "#afafaf"
}
}
MouseArea {
anchors.fill: parent
onClicked: {
if (!Account.loggedIn) {
DialogsManager.showLoginDialog()
} else {
Account.logOut()
}
}
}
}
}
Rectangle {
id: bgMain
gradient: Gradient {
GradientStop {
position: 0
color: "#2b2b2b"
}
GradientStop {
position: 1
color: "#0f212e"
}
}
anchors.bottom: parent.bottom
anchors.bottomMargin: 0
anchors.right: parent.right
anchors.rightMargin: 0
anchors.left: parent.left
anchors.leftMargin: 0
anchors.top: bgTopBar.bottom
anchors.topMargin: 0
Flickable {
id: flickable
width: parent.width
height: parent.height
contentWidth: parent.width
contentHeight: flowMain.childrenRect.height + flowMain.anchors.topMargin + flowMain.anchors.bottomMargin + flowMain.spacing
clip: true
Flow {
id: flowMain
spacing: 16
anchors.right: parent.right
anchors.rightMargin: 30
anchors.left: parent.left
anchors.leftMargin: 30
anchors.bottom: parent.bottom
anchors.bottomMargin: 30
anchors.top: parent.top
anchors.topMargin: 30
}
}
}
function setCurrentItemState(state) {
var index = rowIndex + columnIndex;
if (index >= 0 && index <= count ) {
flowMain.children[index].state = state;
}
}
function nextItem() {
setCurrentItemState("base state");
var nextColumnIndex = (columnIndex + 3 + 1) % 3;
var nextIndex = rowIndex + nextColumnIndex;
if(nextIndex <= count) {
columnIndex = nextColumnIndex;
};
setCurrentItemState("hover state");
}
function previousItem() {
setCurrentItemState("base state");
var prevIndex = (columnIndex + 3 - 1) % 3;
if((rowIndex + prevIndex) <= count){
columnIndex = prevIndex;
}
setCurrentItemState("hover state");
}
function upItem() {
setCurrentItemState("base state");
rowIndex = rowIndex - 3;
if (rowIndex < 0 ) {
rowIndex = (count - (count % 3));
var index = rowIndex + columnIndex;
if(index > count) {
rowIndex = rowIndex - 3;
}
}
setCurrentItemState("hover state");
}
function downItem() {
setCurrentItemState("base state");
rowIndex = rowIndex + 3;
var index = rowIndex + columnIndex;
if (index > count ) {
rowIndex = 0;
}
setCurrentItemState("hover state");
}
function selectItem() {
flowMain.children[rowIndex + columnIndex].clicked();
if (tabletRoot) {
tabletRoot.playButtonClickSound();
}
}
Keys.onRightPressed: nextItem();
Keys.onLeftPressed: previousItem();
Keys.onDownPressed: downItem();
Keys.onUpPressed: upItem();
Keys.onReturnPressed: selectItem();
}

View file

@ -72,7 +72,7 @@ StackView {
addressLine.focus = !HMD.active;
root.parentChanged.connect(center);
center();
tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system");
tablet = Tablets.getTablet("com.highfidelity.interface.tablet.system");
}
Component.onDestruction: {
root.parentChanged.disconnect(center);
@ -149,7 +149,7 @@ StackView {
onClicked: {
addressBarDialog.loadHome();
tabletRoot.shown = false;
tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system");
tablet = Tablets.getTablet("com.highfidelity.interface.tablet.system");
tablet.gotoHomeScreen();
}
anchors {

View file

@ -37,7 +37,7 @@ Item {
}
function closeDialog() {
Tablet.getTablet("com.highfidelity.interface.tablet.system").gotoHomeScreen();
Tablets.getTablet("com.highfidelity.interface.tablet.system").gotoHomeScreen();
}
anchors.topMargin: hifi.dimensions.tabletMenuHeader // Space for header.

View file

@ -121,7 +121,6 @@ Item {
enabled: true
preventStealing: true
onClicked: {
console.log("Tablet Button Clicked!");
if (tabletButton.inDebugMode) {
if (tabletButton.isActive) {
tabletButton.isActive = false;
@ -131,12 +130,12 @@ Item {
}
tabletButton.clicked();
if (tabletRoot) {
tabletInterface.playSound(TabletEnums.ButtonClick);
Tablets.playSound(TabletEnums.ButtonClick);
}
}
onEntered: {
tabletButton.isEntered = true;
tabletInterface.playSound(TabletEnums.ButtonHover);
Tablets.playSound(TabletEnums.ButtonHover);
if (tabletButton.isActive) {
tabletButton.state = "hover active state";

View file

@ -0,0 +1,159 @@
import QtQuick 2.5
import QtGraphicalEffects 1.0
import QtQuick.Layouts 1.3
import "."
import "../../styles-uit"
import "../audio" as HifiAudio
Item {
id: tablet
objectName: "tablet"
property var tabletProxy: Tablets.getTablet("com.highfidelity.interface.tablet.system");
Rectangle {
id: bgTopBar
height: 90
anchors {
top: parent.top
left: parent.left
right: parent.right
}
gradient: Gradient {
GradientStop {
position: 0
color: "#2b2b2b"
}
GradientStop {
position: 1
color: "#1e1e1e"
}
}
HifiAudio.MicBar {
anchors {
left: parent.left
leftMargin: 30
verticalCenter: parent.verticalCenter
}
}
Item {
width: 150
height: 50
anchors.right: parent.right
anchors.rightMargin: 30
anchors.verticalCenter: parent.verticalCenter
ColumnLayout {
anchors.fill: parent
RalewaySemiBold {
text: Account.loggedIn ? qsTr("Log out") : qsTr("Log in")
horizontalAlignment: Text.AlignRight
anchors.right: parent.right
font.pixelSize: 20
color: "#afafaf"
}
RalewaySemiBold {
visible: Account.loggedIn
height: Account.loggedIn ? parent.height/2 - parent.spacing/2 : 0
text: Account.loggedIn ? "[" + tabletRoot.usernameShort + "]" : ""
horizontalAlignment: Text.AlignRight
anchors.right: parent.right
font.pixelSize: 20
color: "#afafaf"
}
}
MouseArea {
anchors.fill: parent
onClicked: {
if (!Account.loggedIn) {
DialogsManager.showLoginDialog()
} else {
Account.logOut()
}
}
}
}
}
Rectangle {
id: bgMain
clip: true
gradient: Gradient {
GradientStop {
position: 0
color: "#2b2b2b"
}
GradientStop {
position: 1
color: "#0f212e"
}
}
anchors.bottom: parent.bottom
anchors.right: parent.right
anchors.left: parent.left
anchors.top: bgTopBar.bottom
GridView {
id: flickable
anchors.top: parent.top
anchors.topMargin: 15
anchors.bottom: parent.bottom
anchors.horizontalCenter: parent.horizontalCenter
width: cellWidth * 3
cellHeight: 145
cellWidth: 145
model: tabletProxy.buttons
delegate: Item {
width: flickable.cellWidth
height: flickable.cellHeight
property var proxy: modelData
TabletButton {
id: tabletButton
anchors.centerIn: parent
onClicked: modelData.clicked()
state: wrapper.GridView.isCurrentItem ? "hover state" : "base state"
}
Connections {
target: modelData;
onPropertiesChanged: {
updateProperties();
}
}
Component.onCompleted: updateProperties()
function updateProperties() {
var keys = Object.keys(modelData.properties).forEach(function (key) {
if (tabletButton[key] !== modelData.properties[key]) {
tabletButton[key] = modelData.properties[key];
}
});
}
}
}
}
Keys.onRightPressed: flickable.moveCurrentIndexRight();
Keys.onLeftPressed: flickable.moveCurrentIndexLeft();
Keys.onDownPressed: flickable.moveCurrentIndexDown();
Keys.onUpPressed: flickable.moveCurrentIndexUp();
Keys.onReturnPressed: {
if (flickable.currentItem) {
flickable.currentItem.proxy.clicked();
if (tabletRoot) {
tabletRoot.playButtonClickSound();
}
}
}
}

View file

@ -77,12 +77,12 @@ FocusScope {
anchors.fill: parent
hoverEnabled: true
onEntered: {
tabletInterface.playSound(TabletEnums.ButtonHover);
Tablets.playSound(TabletEnums.ButtonHover);
listView.currentIndex = index
}
onClicked: {
tabletInterface.playSound(TabletEnums.ButtonClick);
Tablets.playSound(TabletEnums.ButtonClick);
root.selected(item);
}
}

View file

@ -68,8 +68,8 @@ Item {
function loadSource(url) {
tabletApps.clear();
loader.load(url)
tabletApps.append({"appUrl": url, "isWebUrl": false, "scriptUrl": "", "appWebUrl": ""});
tabletApps.append({"appUrl": url, "isWebUrl": false, "scriptUrl": "", "appWebUrl": ""});
loader.load(url)
}
function loadQMLOnTop(url) {
@ -84,8 +84,8 @@ Item {
function loadWebContent(source, url, injectJavaScriptUrl) {
tabletApps.append({"appUrl": source, "isWebUrl": true, "scriptUrl": injectJavaScriptUrl, "appWebUrl": url});
loader.load(source, function() {
loader.item.url = tabletApps.get(currentApp).appWebUrl;
loader.item.scriptUrl = tabletApps.get(currentApp).scriptUrl;
loader.item.scriptURL = injectJavaScriptUrl;
loader.item.url = url;
if (loader.item.hasOwnProperty("gotoPreviousApp")) {
loader.item.gotoPreviousApp = true;
}

View file

@ -62,20 +62,22 @@ Windows.ScrollingWindow {
loader.load(url)
}
function loadWebBase() {
loader.load("hifi/tablet/TabletWebView.qml");
function loadWebContent(source, url, injectJavaScriptUrl) {
loader.load(source, function() {
loader.item.scriptURL = injectJavaScriptUrl;
loader.item.url = url;
if (loader.item.hasOwnProperty("closeButtonVisible")) {
loader.item.closeButtonVisible = false;
}
});
}
function loadTabletWebBase() {
loader.load("hifi/tablet/BlocksWebView.qml");
function loadWebBase(url, injectJavaScriptUrl) {
loadWebContent("hifi/tablet/TabletWebView.qml", url, injectJavaScriptUrl);
}
function loadWebUrl(url, injectedJavaScriptUrl) {
loader.item.url = url;
loader.item.scriptURL = injectedJavaScriptUrl;
if (loader.item.hasOwnProperty("closeButtonVisible")) {
loader.item.closeButtonVisible = false;
}
function loadTabletWebBase(url, injectJavaScriptUrl) {
loadWebContent("hifi/tablet/BlocksWebView.qml", url, injectJavaScriptUrl);
}
// used to send a message from qml to interface script.
@ -144,11 +146,6 @@ Windows.ScrollingWindow {
}
function load(newSource, callback) {
if (loader.source == newSource) {
loader.loaded();
return;
}
if (loader.item) {
loader.item.destroy();
loader.item = null;

View file

@ -56,7 +56,7 @@ Item {
}
function closeDialog() {
var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system");
var tablet = Tablets.getTablet("com.highfidelity.interface.tablet.system");
if (gotoPreviousApp) {
tablet.returnToPreviousApp();
@ -206,7 +206,7 @@ Item {
}
Component.onCompleted: {
tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system");
tablet = Tablets.getTablet("com.highfidelity.interface.tablet.system");
keyboardEnabled = HMD.active;
}

View file

@ -2214,12 +2214,12 @@ extern void setupPreferences();
void Application::initializeUi() {
// Make sure all QML surfaces share the main thread GL context
OffscreenQmlSurface::setSharedContext(_offscreenContext->getContext());
OffscreenQmlSurface::addWhitelistContextHandler(QUrl{ "qrc:///qml/OverlayWindowTest.qml" },
OffscreenQmlSurface::addWhitelistContextHandler(QUrl{ "OverlayWindowTest.qml" },
[](QQmlContext* context) {
qDebug() << "Whitelist OverlayWindow worked";
context->setContextProperty("OverlayWindowTestString", "TestWorked");
});
OffscreenQmlSurface::addWhitelistContextHandler(QUrl{ "qrc:///qml/hifi/audio/Audio.qml" },
OffscreenQmlSurface::addWhitelistContextHandler(QUrl{ "hifi/audio/Audio.qml" },
[](QQmlContext* context) {
qDebug() << "QQQ" << __FUNCTION__ << "Whitelist Audio worked";
});
@ -2335,9 +2335,6 @@ void Application::initializeUi() {
surfaceContext->setContextProperty("InputConfiguration", DependencyManager::get<InputConfiguration>().data());
surfaceContext->setContextProperty("Account", AccountScriptingInterface::getInstance());
surfaceContext->setContextProperty("Tablet", DependencyManager::get<TabletScriptingInterface>().data());
// Tablet inteference with Tablet.qml. Need to avoid this in QML space
surfaceContext->setContextProperty("tabletInterface", DependencyManager::get<TabletScriptingInterface>().data());
surfaceContext->setContextProperty("DialogsManager", _dialogsManagerScriptingInterface);
surfaceContext->setContextProperty("GlobalServices", GlobalServicesScriptingInterface::getInstance());
surfaceContext->setContextProperty("FaceTracker", DependencyManager::get<DdeFaceTracker>().data());
@ -2727,7 +2724,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);
@ -4475,7 +4472,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
}
@ -5832,7 +5829,8 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEnginePointe
qScriptRegisterMetaType(scriptEngine.data(), wrapperToScriptValue<TabletProxy>, wrapperFromScriptValue<TabletProxy>);
qScriptRegisterMetaType(scriptEngine.data(),
wrapperToScriptValue<TabletButtonProxy>, wrapperFromScriptValue<TabletButtonProxy>);
// Tablet inteference with Tablet.qml. Need to avoid this in QML space
scriptEngine->registerGlobalObject("Tablets", DependencyManager::get<TabletScriptingInterface>().data());
// FIXME remove these deprecated names for the tablet scripting interface
scriptEngine->registerGlobalObject("tabletInterface", DependencyManager::get<TabletScriptingInterface>().data());
scriptEngine->registerGlobalObject("Tablet", DependencyManager::get<TabletScriptingInterface>().data());
@ -6269,7 +6267,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");
@ -6304,7 +6302,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");
@ -7308,6 +7306,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");
}

View file

@ -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;
}

View file

@ -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();
@ -2189,6 +2189,7 @@ void MyAvatar::clampScaleChangeToDomainLimits(float desiredScale) {
setTargetScale(clampedTargetScale);
qCDebug(interfaceapp, "Changed scale to %f", (double)_targetScale);
emit(scaleChanged());
}
float MyAvatar::getDomainMinScale() {
@ -2278,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;

View file

@ -620,6 +620,10 @@ signals:
void dominantHandChanged(const QString& hand);
void sensorToWorldScaleChanged(float sensorToWorldScale);
void attachmentsChanged();
void scaleChanged();
private slots:
void leaveDomain();
private:
@ -637,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;

View file

@ -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<AccountManager>();
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.
}

View file

@ -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);

View file

@ -16,7 +16,6 @@
#include "ui/ImageProvider.h"
#include "scripting/HMDScriptingInterface.h"
#include <FingerprintUtils.h>
#include <PathUtils.h>
#include <OffscreenUi.h>
#include <AccountManager.h>
@ -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<Ledger>();
QString machineFingerprint = uuidStringWithoutCurlyBraces(FingerprintUtils::getMachineFingerprint());
return ledger->receiveAt(key, oldKey, machineFingerprint);
return ledger->receiveAt(key, oldKey);
}
QStringList Wallet::listPublicKeys() {

View file

@ -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 <class T> bool SelectionScriptingInterface::addToGameplayObjects(const QString& listName, T idToAdd) {
GameplayObjects currentList = _selectedItemsListMap.value(listName);
currentList.addToGameplayObjects(idToAdd);

View file

@ -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);

View file

@ -265,7 +265,7 @@ void Base3DOverlay::locationChanged(bool tellPhysics) {
SpatiallyNestable::locationChanged(tellPhysics);
// Force the actual update of the render transform through the notify call
notifyRenderTransformChange();
notifyRenderVariableChange();
}
void Base3DOverlay::parentDeleted() {
@ -275,18 +275,21 @@ void Base3DOverlay::parentDeleted() {
void Base3DOverlay::update(float duration) {
// In Base3DOverlay, if its location or bound changed, the renderTrasnformDirty flag is true.
// then the correct transform used for rendering is computed in the update transaction and assigned.
if (_renderTransformDirty) {
if (_renderVariableDirty) {
auto itemID = getRenderItemID();
if (render::Item::isValidID(itemID)) {
// Capture the render transform value in game loop before
auto latestTransform = evalRenderTransform();
_renderTransformDirty = false;
bool latestVisible = getVisible();
_renderVariableDirty = false;
render::ScenePointer scene = qApp->getMain3DScene();
render::Transaction transaction;
transaction.updateItem<Overlay>(itemID, [latestTransform](Overlay& data) {
transaction.updateItem<Overlay>(itemID, [latestTransform, latestVisible](Overlay& data) {
auto overlay3D = dynamic_cast<Base3DOverlay*>(&data);
if (overlay3D) {
// TODO: overlays need to communicate all relavent render properties through transactions
overlay3D->setRenderTransform(latestTransform);
overlay3D->setRenderVisible(latestVisible);
}
});
scene->enqueueTransaction(transaction);
@ -294,8 +297,8 @@ void Base3DOverlay::update(float duration) {
}
}
void Base3DOverlay::notifyRenderTransformChange() const {
_renderTransformDirty = true;
void Base3DOverlay::notifyRenderVariableChange() const {
_renderVariableDirty = true;
}
Transform Base3DOverlay::evalRenderTransform() {
@ -306,8 +309,17 @@ void Base3DOverlay::setRenderTransform(const Transform& transform) {
_renderTransform = transform;
}
void Base3DOverlay::setRenderVisible(bool visible) {
_renderVisible = visible;
}
SpatialParentTree* Base3DOverlay::getParentTree() const {
auto entityTreeRenderer = qApp->getEntities();
EntityTreePointer entityTree = entityTreeRenderer ? entityTreeRenderer->getTree() : nullptr;
return entityTree.get();
}
void Base3DOverlay::setVisible(bool visible) {
Parent::setVisible(visible);
notifyRenderVariableChange();
}

View file

@ -18,11 +18,14 @@
class Base3DOverlay : public Overlay, public SpatiallyNestable {
Q_OBJECT
using Parent = Overlay;
public:
Base3DOverlay();
Base3DOverlay(const Base3DOverlay* base3DOverlay);
void setVisible(bool visible) override;
virtual OverlayID getOverlayID() const override { return OverlayID(getID().toString()); }
void setOverlayID(OverlayID overlayID) override { setID(overlayID); }
@ -56,7 +59,7 @@ public:
void update(float deltatime) override;
void notifyRenderTransformChange() const;
void notifyRenderVariableChange() const;
void setProperties(const QVariantMap& properties) override;
QVariant getProperty(const QString& property) override;
@ -76,8 +79,10 @@ protected:
virtual void parentDeleted() override;
mutable Transform _renderTransform;
mutable bool _renderVisible;
virtual Transform evalRenderTransform();
virtual void setRenderTransform(const Transform& transform);
void setRenderVisible(bool visible);
const Transform& getRenderTransform() const { return _renderTransform; }
float _lineWidth;
@ -87,7 +92,7 @@ protected:
bool _drawInFront;
bool _drawHUDLayer;
bool _isGrabbable { false };
mutable bool _renderTransformDirty{ true };
mutable bool _renderVariableDirty { true };
QString _name;
};

View file

@ -59,7 +59,7 @@ Circle3DOverlay::~Circle3DOverlay() {
}
}
void Circle3DOverlay::render(RenderArgs* args) {
if (!_visible) {
if (!_renderVisible) {
return; // do nothing if we're not visible
}
@ -259,7 +259,7 @@ void Circle3DOverlay::render(RenderArgs* args) {
}
const render::ShapeKey Circle3DOverlay::getShapeKey() {
auto builder = render::ShapeKey::Builder().withoutCullFace().withUnlit();
auto builder = render::ShapeKey::Builder().withoutCullFace();
if (isTransparent()) {
builder.withTranslucent();
}

View file

@ -38,8 +38,6 @@ ContextOverlayInterface::ContextOverlayInterface() {
_tabletScriptingInterface = DependencyManager::get<TabletScriptingInterface>();
_selectionScriptingInterface = DependencyManager::get<SelectionScriptingInterface>();
_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<NodeList>();
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 };

View file

@ -47,6 +47,7 @@ class ContextOverlayInterface : public QObject, public Dependency {
OverlayID _contextOverlayID { UNKNOWN_OVERLAY_ID };
std::shared_ptr<Image3DOverlay> _contextOverlay { nullptr };
public:
ContextOverlayInterface();
Q_INVOKABLE QUuid getCurrentEntityWithContextOverlay() { return _currentEntityWithContextOverlay; }
@ -75,6 +76,11 @@ private slots:
void handleChallengeOwnershipReplyPacket(QSharedPointer<ReceivedMessage> 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;

View file

@ -44,7 +44,7 @@ Cube3DOverlay::~Cube3DOverlay() {
}
void Cube3DOverlay::render(RenderArgs* args) {
if (!_visible) {
if (!_renderVisible) {
return; // do nothing if we're not visible
}

View file

@ -53,7 +53,7 @@ AABox Grid3DOverlay::getBounds() const {
}
void Grid3DOverlay::render(RenderArgs* args) {
if (!_visible) {
if (!_renderVisible) {
return; // do nothing if we're not visible
}

View file

@ -60,7 +60,7 @@ void Image3DOverlay::update(float deltatime) {
}
void Image3DOverlay::render(RenderArgs* args) {
if (!_visible || !getParentVisible() || !_texture || !_texture->isLoaded()) {
if (!_renderVisible || !getParentVisible() || !_texture || !_texture->isLoaded()) {
return;
}

View file

@ -96,7 +96,7 @@ void Line3DOverlay::setEnd(const glm::vec3& end) {
} else {
_direction = glm::vec3(0.0f);
}
notifyRenderTransformChange();
notifyRenderVariableChange();
}
void Line3DOverlay::setLocalEnd(const glm::vec3& localEnd) {
@ -123,7 +123,7 @@ AABox Line3DOverlay::getBounds() const {
}
void Line3DOverlay::render(RenderArgs* args) {
if (!_visible) {
if (!_renderVisible) {
return; // do nothing if we're not visible
}

View file

@ -37,7 +37,7 @@ AABox Planar3DOverlay::getBounds() const {
void Planar3DOverlay::setDimensions(const glm::vec2& value) {
_dimensions = value;
notifyRenderTransformChange();
notifyRenderVariableChange();
}
void Planar3DOverlay::setProperties(const QVariantMap& properties) {

View file

@ -23,7 +23,6 @@ Rectangle3DOverlay::Rectangle3DOverlay() :
for (size_t i = 0; i < _rectGeometryIds.size(); ++i) {
_rectGeometryIds[i] = geometryCache->allocateID();
}
qDebug() << "Building rect3d overlay";
}
Rectangle3DOverlay::Rectangle3DOverlay(const Rectangle3DOverlay* rectangle3DOverlay) :
@ -34,11 +33,9 @@ Rectangle3DOverlay::Rectangle3DOverlay(const Rectangle3DOverlay* rectangle3DOver
for (size_t i = 0; i < _rectGeometryIds.size(); ++i) {
_rectGeometryIds[i] = geometryCache->allocateID();
}
qDebug() << "Building rect3d overlay";
}
Rectangle3DOverlay::~Rectangle3DOverlay() {
qDebug() << "Destryoing rect3d overlay";
auto geometryCache = DependencyManager::get<GeometryCache>();
if (geometryCache) {
geometryCache->releaseID(_geometryCacheID);
@ -49,7 +46,7 @@ Rectangle3DOverlay::~Rectangle3DOverlay() {
}
void Rectangle3DOverlay::render(RenderArgs* args) {
if (!_visible) {
if (!_renderVisible) {
return; // do nothing if we're not visible
}
@ -58,18 +55,11 @@ void Rectangle3DOverlay::render(RenderArgs* args) {
const float MAX_COLOR = 255.0f;
glm::vec4 rectangleColor(color.red / MAX_COLOR, color.green / MAX_COLOR, color.blue / MAX_COLOR, alpha);
glm::vec3 position = getPosition();
glm::vec2 dimensions = getDimensions();
glm::vec2 halfDimensions = dimensions * 0.5f;
glm::quat rotation = getRotation();
auto batch = args->_batch;
if (batch) {
// FIXME Start using the _renderTransform instead of calling for Transform and Dimensions from here, do the custom things needed in evalRenderTransform()
Transform transform;
transform.setTranslation(position);
transform.setRotation(rotation);
Transform transform = getRenderTransform();
glm::vec2 halfDimensions = transform.getScale() * 0.5f;
transform.setScale(1.0f);
batch->setModelTransform(transform);
auto geometryCache = DependencyManager::get<GeometryCache>();
@ -124,8 +114,3 @@ void Rectangle3DOverlay::setProperties(const QVariantMap& properties) {
Rectangle3DOverlay* Rectangle3DOverlay::createClone() const {
return new Rectangle3DOverlay(this);
}
Transform Rectangle3DOverlay::evalRenderTransform() {
return getTransform();
}

View file

@ -29,9 +29,6 @@ public:
virtual Rectangle3DOverlay* createClone() const override;
protected:
Transform evalRenderTransform() override;
private:
int _geometryCacheID;
std::array<int, 4> _rectGeometryIds;

View file

@ -24,7 +24,7 @@ Shape3DOverlay::Shape3DOverlay(const Shape3DOverlay* Shape3DOverlay) :
}
void Shape3DOverlay::render(RenderArgs* args) {
if (!_visible) {
if (!_renderVisible) {
return; // do nothing if we're not visible
}

View file

@ -27,7 +27,7 @@ Sphere3DOverlay::Sphere3DOverlay(const Sphere3DOverlay* Sphere3DOverlay) :
}
void Sphere3DOverlay::render(RenderArgs* args) {
if (!_visible) {
if (!_renderVisible) {
return; // do nothing if we're not visible
}

View file

@ -92,7 +92,7 @@ void Text3DOverlay::update(float deltatime) {
}
void Text3DOverlay::render(RenderArgs* args) {
if (!_visible || !getParentVisible()) {
if (!_renderVisible || !getParentVisible()) {
return; // do nothing if we're not visible
}

View file

@ -28,7 +28,7 @@ AABox Volume3DOverlay::getBounds() const {
void Volume3DOverlay::setDimensions(const glm::vec3& value) {
_localBoundingBox.setBox(-value / 2.0f, value);
notifyRenderTransformChange();
notifyRenderVariableChange();
}
void Volume3DOverlay::setProperties(const QVariantMap& properties) {

View file

@ -209,7 +209,6 @@ void Web3DOverlay::setupQmlSurface() {
_webSurface->getSurfaceContext()->setContextProperty("fileDialogHelper", new FileDialogHelper());
_webSurface->getSurfaceContext()->setContextProperty("MyAvatar", DependencyManager::get<AvatarManager>()->getMyAvatar().get());
_webSurface->getSurfaceContext()->setContextProperty("ScriptDiscoveryService", DependencyManager::get<ScriptEngines>().data());
_webSurface->getSurfaceContext()->setContextProperty("Tablet", DependencyManager::get<TabletScriptingInterface>().data());
_webSurface->getSurfaceContext()->setContextProperty("Assets", DependencyManager::get<AssetMappingsScriptingInterface>().data());
_webSurface->getSurfaceContext()->setContextProperty("LODManager", DependencyManager::get<LODManager>().data());
_webSurface->getSurfaceContext()->setContextProperty("OctreeStats", DependencyManager::get<OctreeStatsProvider>().data());
@ -228,9 +227,6 @@ void Web3DOverlay::setupQmlSurface() {
_webSurface->getSurfaceContext()->setContextProperty("pathToFonts", "../../");
// Tablet inteference with Tablet.qml. Need to avoid this in QML space
_webSurface->getSurfaceContext()->setContextProperty("tabletInterface", DependencyManager::get<TabletScriptingInterface>().data());
tabletScriptingInterface->setQmlTabletRoot("com.highfidelity.interface.tablet.system", _webSurface.data());
// mark the TabletProxy object as cpp ownership.
QObject* tablet = tabletScriptingInterface->getTablet("com.highfidelity.interface.tablet.system");
@ -268,7 +264,7 @@ Q_INVOKABLE int Web3DOverlay::deviceIdByTouchPoint(qreal x, qreal y) {
}
void Web3DOverlay::render(RenderArgs* args) {
if (!_visible || !getParentVisible()) {
if (!_renderVisible || !getParentVisible()) {
return;
}

View file

@ -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) {
@ -303,7 +308,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);
}
}
}

View file

@ -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;
@ -728,19 +730,19 @@ void Avatar::simulateAttachments(float deltaTime) {
glm::quat jointRotation;
if (attachment.isSoft) {
// soft attachments do not have transform offsets
model->setTranslation(getPosition());
model->setRotation(getOrientation() * Quaternions::Y_180);
model->setTransformNoUpdateRenderItems(Transform(getOrientation() * Quaternions::Y_180, glm::vec3(1.0), getPosition()));
model->simulate(deltaTime);
model->updateRenderItems();
} else {
if (_skeletonModel->getJointPositionInWorldFrame(jointIndex, jointPosition) &&
_skeletonModel->getJointRotationInWorldFrame(jointIndex, jointRotation)) {
model->setTranslation(jointPosition + jointRotation * attachment.translation * getModelScale());
model->setRotation(jointRotation * attachment.rotation);
model->setTransformNoUpdateRenderItems(Transform(jointRotation * attachment.rotation, glm::vec3(1.0), jointPosition + jointRotation * attachment.translation * getModelScale()));
float scale = getModelScale() * attachment.scale;
model->setScaleToFit(true, model->getNaturalDimensions() * scale, true); // hack to force rescale
model->setSnapModelToCenter(false); // hack to force resnap
model->setSnapModelToCenter(true);
model->simulate(deltaTime);
model->updateRenderItems();
}
}
}

View file

@ -2223,11 +2223,13 @@ glm::vec3 variantToVec3(const QVariant& var) {
return result;
}
void AttachmentData::fromVariant(const QVariant& variant) {
bool AttachmentData::fromVariant(const QVariant& variant) {
bool isValid = false;
auto map = variant.toMap();
if (map.contains("modelUrl")) {
auto urlString = map["modelUrl"].toString();
modelURL = urlString;
isValid = true;
}
if (map.contains("jointName")) {
jointName = map["jointName"].toString();
@ -2244,6 +2246,7 @@ void AttachmentData::fromVariant(const QVariant& variant) {
if (map.contains("soft")) {
isSoft = map["soft"].toBool();
}
return isValid;
}
QVariantList AvatarData::getAttachmentsVariant() const {
@ -2259,8 +2262,7 @@ void AvatarData::setAttachmentsVariant(const QVariantList& variant) {
newAttachments.reserve(variant.size());
for (const auto& attachmentVar : variant) {
AttachmentData attachment;
attachment.fromVariant(attachmentVar);
if (!attachment.modelURL.isEmpty()) {
if (attachment.fromVariant(attachmentVar)) {
newAttachments.append(attachment);
}
}

View file

@ -15,21 +15,7 @@
#include <string>
#include <memory>
#include <queue>
/* 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 <inttypes.h>
#endif
#include <vector>
#include <glm/glm.hpp>
@ -903,7 +889,7 @@ public:
void fromJson(const QJsonObject& json);
QVariant toVariant() const;
void fromVariant(const QVariant& variant);
bool fromVariant(const QVariant& variant);
};
QDataStream& operator<<(QDataStream& out, const AttachmentData& attachment);

View file

@ -295,6 +295,14 @@ void EntityRenderer::updateInScene(const ScenePointer& scene, Transaction& trans
});
}
void EntityRenderer::clearSubRenderItemIDs() {
_subRenderItemIDs.clear();
}
void EntityRenderer::setSubRenderItemIDs(const render::ItemIDs& ids) {
_subRenderItemIDs = ids;
}
//
// Internal methods
//

View file

@ -49,6 +49,9 @@ public:
virtual bool addToScene(const ScenePointer& scene, Transaction& transaction) final;
virtual void removeFromScene(const ScenePointer& scene, Transaction& transaction);
void clearSubRenderItemIDs();
void setSubRenderItemIDs(const render::ItemIDs& ids);
protected:
virtual bool needsRenderUpdateFromEntity() const final { return needsRenderUpdateFromEntity(_entity); }
virtual void onAddToScene(const EntityItemPointer& entity);
@ -113,6 +116,7 @@ protected:
SharedSoundPointer _collisionSound;
QUuid _changeHandlerId;
ItemID _renderItemID{ Item::INVALID_ITEM_ID };
ItemIDs _subRenderItemIDs;
quint64 _fadeStartTime{ usecTimestampNow() };
bool _isFading{ _entitiesShouldFadeFunction() };
bool _prevIsTransparent { false };

View file

@ -953,7 +953,7 @@ ItemKey ModelEntityRenderer::getKey() {
uint32_t ModelEntityRenderer::metaFetchMetaSubItems(ItemIDs& subItems) {
if (_model) {
auto metaSubItems = _model->fetchRenderItemIDs();
auto metaSubItems = _subRenderItemIDs;
subItems.insert(subItems.end(), metaSubItems.begin(), metaSubItems.end());
return (uint32_t)metaSubItems.size();
}
@ -1202,6 +1202,10 @@ void ModelEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& sce
if ((bool)model) {
model->removeFromScene(scene, transaction);
withWriteLock([&] { _model.reset(); });
transaction.updateItem<PayloadProxyInterface>(getRenderItemID(), [](PayloadProxyInterface& data) {
auto entityRenderer = static_cast<EntityRenderer*>(&data);
entityRenderer->clearSubRenderItemIDs();
});
}
return;
}
@ -1297,6 +1301,12 @@ void ModelEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& sce
render::Item::Status::Getters statusGetters;
makeStatusGetters(entity, statusGetters);
model->addToScene(scene, transaction, statusGetters);
auto newRenderItemIDs{ model->fetchRenderItemIDs() };
transaction.updateItem<PayloadProxyInterface>(getRenderItemID(), [newRenderItemIDs](PayloadProxyInterface& data) {
auto entityRenderer = static_cast<EntityRenderer*>(&data);
entityRenderer->setSubRenderItemIDs(newRenderItemIDs);
});
}
}

View file

@ -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
};

View file

@ -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:

View file

@ -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 };

View file

@ -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;

View file

@ -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();

View file

@ -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;

View file

@ -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@>

View file

@ -217,8 +217,6 @@ gpu::TexturePointer TextureCache::cacheTextureByHash(const std::string& hash, co
if (!result) {
_texturesByHashes[hash] = texture;
result = texture;
} else {
qCWarning(modelnetworking) << "QQQ Swapping out texture with previous live texture in hash " << hash.c_str();
}
}
return result;

View file

@ -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 <memory>
#include <memory>
#include "Haze.h"
using namespace model;

View file

@ -9,10 +9,6 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#ifdef _WIN32
#define _USE_MATH_DEFINES
#endif
#include <cstring>
#include <cstdio>
#include <cmath>

View file

@ -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 <inttypes.h>
#endif
#include <glm/glm.hpp>
#include <glm/gtc/quaternion.hpp>

View file

@ -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<btCollisionShape*>(shape);
if (nonConstShape->getShapeType() == (int)COMPOUND_SHAPE_PROXYTYPE) {
btCompoundShape* compoundShape = static_cast<btCompoundShape*>(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;
}

View file

@ -17,25 +17,11 @@
#include <ShapeInfo.h>
// 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

View file

@ -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;

View file

@ -17,7 +17,29 @@
#include <ShapeInfo.h>
#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<DoubleHashKey, ShapeReference> _shapeMap;
btAlignedObjectArray<DoubleHashKey> _pendingGarbage;
// btHashMap is required because it supports memory alignment of the btCollisionShapes
btHashMap<HashKey, ShapeReference> _shapeMap;
btAlignedObjectArray<HashKey> _pendingGarbage;
};
#endif // hifi_ShapeManager_h

View file

@ -14,6 +14,7 @@
#include <gpu/Context.h>
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);

View file

@ -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;

View file

@ -20,11 +20,22 @@ using namespace render;
CauterizedMeshPartPayload::CauterizedMeshPartPayload(ModelPointer model, int meshIndex, int partIndex, int shapeIndex, const Transform& transform, const Transform& offsetTransform)
: ModelMeshPartPayload(model, meshIndex, partIndex, shapeIndex, transform, offsetTransform) {}
void CauterizedMeshPartPayload::updateTransformForCauterizedMesh(
const Transform& renderTransform,
const gpu::BufferPointer& buffer) {
void CauterizedMeshPartPayload::updateClusterBuffer(const std::vector<glm::mat4>& clusterMatrices, const std::vector<glm::mat4>& cauterizedClusterMatrices) {
ModelMeshPartPayload::updateClusterBuffer(clusterMatrices);
if (cauterizedClusterMatrices.size() > 1) {
if (!_cauterizedClusterBuffer) {
_cauterizedClusterBuffer = std::make_shared<gpu::Buffer>(cauterizedClusterMatrices.size() * sizeof(glm::mat4),
(const gpu::Byte*) cauterizedClusterMatrices.data());
} else {
_cauterizedClusterBuffer->setSubData(0, cauterizedClusterMatrices.size() * sizeof(glm::mat4),
(const gpu::Byte*) cauterizedClusterMatrices.data());
}
}
}
void CauterizedMeshPartPayload::updateTransformForCauterizedMesh(const Transform& renderTransform) {
_cauterizedTransform = renderTransform;
_cauterizedClusterBuffer = buffer;
}
void CauterizedMeshPartPayload::bindTransform(gpu::Batch& batch, const render::ShapePipeline::LocationsPointer locations, RenderArgs::RenderMode renderMode) const {

View file

@ -15,7 +15,9 @@ class CauterizedMeshPartPayload : public ModelMeshPartPayload {
public:
CauterizedMeshPartPayload(ModelPointer model, int meshIndex, int partIndex, int shapeIndex, const Transform& transform, const Transform& offsetTransform);
void updateTransformForCauterizedMesh(const Transform& renderTransform, const gpu::BufferPointer& buffer);
void updateClusterBuffer(const std::vector<glm::mat4>& clusterMatrices, const std::vector<glm::mat4>& cauterizedClusterMatrices);
void updateTransformForCauterizedMesh(const Transform& renderTransform);
void bindTransform(gpu::Batch& batch, const render::ShapePipeline::LocationsPointer locations, RenderArgs::RenderMode renderMode) const override;

View file

@ -48,7 +48,7 @@ void CauterizedModel::createVisibleRenderItemSet() {
const auto& meshes = _renderGeometry->getMeshes();
// all of our mesh vectors must match in size
if ((int)meshes.size() != _meshStates.size()) {
if (meshes.size() != _meshStates.size()) {
qCDebug(renderutils) << "WARNING!!!! Mesh Sizes don't match! We will not segregate mesh groups yet.";
return;
}
@ -57,6 +57,7 @@ void CauterizedModel::createVisibleRenderItemSet() {
Q_ASSERT(_modelMeshRenderItems.isEmpty());
_modelMeshRenderItems.clear();
_modelMeshRenderItemShapes.clear();
Transform transform;
transform.setTranslation(_translation);
@ -80,6 +81,7 @@ void CauterizedModel::createVisibleRenderItemSet() {
for (int partIndex = 0; partIndex < numParts; partIndex++) {
auto ptr = std::make_shared<CauterizedMeshPartPayload>(shared_from_this(), i, partIndex, shapeID, transform, offset);
_modelMeshRenderItems << std::static_pointer_cast<ModelMeshPartPayload>(ptr);
_modelMeshRenderItemShapes.emplace_back(ShapeInfo{ (int)i });
shapeID++;
}
}
@ -102,7 +104,7 @@ void CauterizedModel::updateClusterMatrices() {
_needsUpdateClusterMatrices = false;
const FBXGeometry& geometry = getFBXGeometry();
for (int i = 0; i < _meshStates.size(); i++) {
for (int i = 0; i < (int)_meshStates.size(); i++) {
Model::MeshState& state = _meshStates[i];
const FBXMesh& mesh = geometry.meshes.at(i);
for (int j = 0; j < mesh.clusters.size(); j++) {
@ -110,17 +112,6 @@ void CauterizedModel::updateClusterMatrices() {
auto jointMatrix = _rig.getJointTransform(cluster.jointIndex);
glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, state.clusterMatrices[j]);
}
// Once computed the cluster matrices, update the buffer(s)
if (mesh.clusters.size() > 1) {
if (!state.clusterBuffer) {
state.clusterBuffer = std::make_shared<gpu::Buffer>(state.clusterMatrices.size() * sizeof(glm::mat4),
(const gpu::Byte*) state.clusterMatrices.constData());
} else {
state.clusterBuffer->setSubData(0, state.clusterMatrices.size() * sizeof(glm::mat4),
(const gpu::Byte*) state.clusterMatrices.constData());
}
}
}
// as an optimization, don't build cautrizedClusterMatrices if the boneSet is empty.
@ -143,17 +134,6 @@ void CauterizedModel::updateClusterMatrices() {
}
glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, state.clusterMatrices[j]);
}
if (!_cauterizeBoneSet.empty() && (state.clusterMatrices.size() > 1)) {
if (!state.clusterBuffer) {
state.clusterBuffer =
std::make_shared<gpu::Buffer>(state.clusterMatrices.size() * sizeof(glm::mat4),
(const gpu::Byte*) state.clusterMatrices.constData());
} else {
state.clusterBuffer->setSubData(0, state.clusterMatrices.size() * sizeof(glm::mat4),
(const gpu::Byte*) state.clusterMatrices.constData());
}
}
}
}
@ -181,11 +161,11 @@ void CauterizedModel::updateRenderItems() {
// queue up this work for later processing, at the end of update and just before rendering.
// the application will ensure only the last lambda is actually invoked.
void* key = (void*)this;
std::weak_ptr<Model> weakSelf = shared_from_this();
AbstractViewStateInterface::instance()->pushPostUpdateLambda(key, [weakSelf, scale]() {
std::weak_ptr<CauterizedModel> weakSelf = std::dynamic_pointer_cast<CauterizedModel>(shared_from_this());
AbstractViewStateInterface::instance()->pushPostUpdateLambda(key, [weakSelf]() {
// do nothing, if the model has already been destroyed.
auto self = weakSelf.lock();
if (!self) {
if (!self || !self->isLoaded()) {
return;
}
@ -198,37 +178,28 @@ void CauterizedModel::updateRenderItems() {
modelTransform.setTranslation(self->getTranslation());
modelTransform.setRotation(self->getRotation());
Transform scaledModelTransform(modelTransform);
scaledModelTransform.setScale(scale);
uint32_t deleteGeometryCounter = self->getGeometryCounter();
render::Transaction transaction;
QList<render::ItemID> keys = self->getRenderItems().keys();
foreach (auto itemID, keys) {
transaction.updateItem<CauterizedMeshPartPayload>(itemID, [modelTransform, deleteGeometryCounter](CauterizedMeshPartPayload& data) {
ModelPointer model = data._model.lock();
if (model && model->isLoaded()) {
// Ensure the model geometry was not reset between frames
if (deleteGeometryCounter == model->getGeometryCounter()) {
// this stuff identical to what happens in regular Model
const Model::MeshState& state = model->getMeshState(data._meshIndex);
Transform renderTransform = modelTransform;
if (state.clusterMatrices.size() == 1) {
renderTransform = modelTransform.worldTransform(Transform(state.clusterMatrices[0]));
}
data.updateTransformForSkinnedMesh(renderTransform, modelTransform, state.clusterBuffer);
for (int i = 0; i < (int)self->_modelMeshRenderItemIDs.size(); i++) {
// this stuff for cauterized mesh
CauterizedModel* cModel = static_cast<CauterizedModel*>(model.get());
const Model::MeshState& cState = cModel->getCauterizeMeshState(data._meshIndex);
renderTransform = modelTransform;
if (cState.clusterMatrices.size() == 1) {
renderTransform = modelTransform.worldTransform(Transform(cState.clusterMatrices[0]));
}
data.updateTransformForCauterizedMesh(renderTransform, cState.clusterBuffer);
}
auto itemID = self->_modelMeshRenderItemIDs[i];
auto meshIndex = self->_modelMeshRenderItemShapes[i].meshIndex;
auto clusterMatrices(self->getMeshState(meshIndex).clusterMatrices);
auto clusterMatricesCauterized(self->getCauterizeMeshState(meshIndex).clusterMatrices);
transaction.updateItem<CauterizedMeshPartPayload>(itemID, [modelTransform, clusterMatrices, clusterMatricesCauterized](CauterizedMeshPartPayload& data) {
data.updateClusterBuffer(clusterMatrices, clusterMatricesCauterized);
Transform renderTransform = modelTransform;
if (clusterMatrices.size() == 1) {
renderTransform = modelTransform.worldTransform(Transform(clusterMatrices[0]));
}
data.updateTransformForSkinnedMesh(renderTransform, modelTransform);
renderTransform = modelTransform;
if (clusterMatricesCauterized.size() == 1) {
renderTransform = modelTransform.worldTransform(Transform(clusterMatricesCauterized[0]));
}
data.updateTransformForCauterizedMesh(renderTransform);
});
}

View file

@ -14,6 +14,7 @@
#include <gpu/Context.h>
std::string HazeStage::_stageName { "HAZE_STAGE"};
const HazeStage::Index HazeStage::INVALID_INDEX { render::indexed_container::INVALID_INDEX };
FetchHazeStage::FetchHazeStage() {
_haze = std::make_shared<model::Haze>();

View file

@ -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;

View file

@ -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)$>

View file

@ -1,7 +1,7 @@
<@include gpu/Config.slh@>
<$VERSION_HEADER$>
<!
// Outline.slh
// Highlight.slh
// fragment shader
//
// Created by Olivier Prat on 9/7/17.
@ -13,14 +13,14 @@
<@include DeferredTransform.slh@>
<$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<params._blurKernelSize ; x++) {
if (uv.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;
}

View file

@ -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 <render/FilterTask.h>
#include <render/SortTask.h>
#include "gpu/Context.h"
#include "gpu/StandardShaderLib.h"
#include <sstream>
#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<HighlightRessources>();
}
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<gpu::Buffer>(sizeof(render::ItemBound));
}
auto highlightStage = renderContext->_scene->getStage<render::HighlightStage>(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<ShapeKey> 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>(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<GeometryCache>()->allocateID();
}
DebugHighlight::~DebugHighlight() {
auto geometryCache = DependencyManager::get<GeometryCache>();
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<GeometryCache>();
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<gpu::State>();
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>(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<Inputs>(0).get<RenderFetchCullSortTask::BucketList>();
const auto sceneFrameBuffer = inputs.getN<Inputs>(1);
const auto primaryFramebuffer = inputs.getN<Inputs>(2);
const auto deferredFrameTransform = inputs.getN<Inputs>(3);
// Prepare the ShapePipeline
auto shapePlumber = std::make_shared<ShapePlumber>();
{
auto state = std::make_shared<gpu::State>();
state->setDepthTest(true, true, gpu::LESS_EQUAL);
state->setColorWriteMask(false, false, false, false);
initMaskPipelines(*shapePlumber, state);
}
auto sharedParameters = std::make_shared<HighlightSharedParameters>();
const auto highlightSelectionNames = task.addJob<SelectionToHighlight>("SelectionToHighlight", sharedParameters);
// Prepare for highlight group rendering.
const auto highlightRessources = task.addJob<PrepareDrawHighlight>("PrepareHighlight", primaryFramebuffer);
render::Varying highlight0Rect;
for (auto i = 0; i < HighlightSharedParameters::MAX_PASS_COUNT; i++) {
const auto selectionName = task.addJob<ExtractSelectionName>("ExtractSelectionName", highlightSelectionNames, i);
const auto groupItems = addSelectItemJobs(task, selectionName, items);
const auto highlightedItemIDs = task.addJob<render::MetaToSubItems>("HighlightMetaToSubItemIDs", groupItems);
const auto highlightedItems = task.addJob<render::IDsToBounds>("HighlightMetaToSubItems", highlightedItemIDs);
// Sort
const auto sortedPipelines = task.addJob<render::PipelineSortShapes>("HighlightPipelineSort", highlightedItems);
const auto sortedBounds = task.addJob<render::DepthSortShapes>("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<DrawHighlightMask>(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<DrawHighlight>(name, drawHighlightInputs, i, sharedParameters);
}
// Debug highlight
const auto debugInputs = DebugHighlight::Inputs(highlightRessources, const_cast<const render::Varying&>(highlight0Rect)).asVarying();
task.addJob<DebugHighlight>("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<SelectItems>("MetaSelection", selectMetaInput);
const auto selectMetaAndOpaqueInput = SelectItems::Inputs(opaques, selectedMetas, selectionName).asVarying();
const auto selectedMetasAndOpaques = task.addJob<SelectItems>("OpaqueSelection", selectMetaAndOpaqueInput);
const auto selectItemInput = SelectItems::Inputs(transparents, selectedMetasAndOpaques, selectionName).asVarying();
return task.addJob<SelectItems>("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);
}

View file

@ -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 <render/Engine.h>
#include <render/HighlightStage.h>
#include <render/RenderFetchCullSortTask.h>
#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<HighlightRessources>;
class HighlightSharedParameters {
public:
enum {
MAX_PASS_COUNT = 8
};
HighlightSharedParameters();
std::array<render::HighlightStage::Index, MAX_PASS_COUNT> _highlightIds;
static float getBlurPixelWidth(const render::HighlightStyle& style, int frameBufferHeight);
};
using HighlightSharedParametersPointer = std::shared_ptr<HighlightSharedParameters>;
class PrepareDrawHighlight {
public:
using Inputs = gpu::FramebufferPointer;
using Outputs = HighlightRessourcesPointer;
using JobModel = render::Job::ModelIO<PrepareDrawHighlight, Inputs, Outputs>;
PrepareDrawHighlight();
void run(const render::RenderContextPointer& renderContext, const Inputs& inputs, Outputs& outputs);
private:
HighlightRessourcesPointer _ressources;
};
class SelectionToHighlight {
public:
using Outputs = std::vector<std::string>;
using JobModel = render::Job::ModelO<SelectionToHighlight, Outputs>;
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, Inputs, Outputs>;
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<render::ShapeBounds, HighlightRessourcesPointer>;
using Outputs = glm::ivec4;
using JobModel = render::Job::ModelIO<DrawHighlightMask, Inputs, Outputs>;
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<DeferredFrameTransformPointer, HighlightRessourcesPointer, DeferredFramebufferPointer, glm::ivec4>;
using Config = render::Job::Config;
using JobModel = render::Job::ModelI<DrawHighlight, Inputs, Config>;
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<HighlightParameters>;
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<HighlightRessourcesPointer, glm::ivec4>;
using Config = DebugHighlightConfig;
using JobModel = render::Job::ModelI<DebugHighlight, Inputs, Config>;
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<RenderFetchCullSortTask::BucketList, DeferredFramebufferPointer, gpu::FramebufferPointer, DeferredFrameTransformPointer>;
using Config = render::Task::Config;
using JobModel = render::Task::ModelI<DrawHighlightTask, Inputs, Config>;
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

View file

@ -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)$>
}

View file

@ -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)$>

View file

@ -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@> <!def that !>
//

View file

@ -14,6 +14,7 @@
#include "LightStage.h"
std::string LightStage::_stageName { "LIGHT_STAGE"};
const LightStage::Index LightStage::INVALID_INDEX { render::indexed_container::INVALID_INDEX };
LightStage::LightStage() {
}

View file

@ -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;

View file

@ -337,7 +337,7 @@ ModelMeshPartPayload::ModelMeshPartPayload(ModelPointer model, int meshIndex, in
if (state.clusterMatrices.size() == 1) {
renderTransform = transform.worldTransform(Transform(state.clusterMatrices[0]));
}
updateTransformForSkinnedMesh(renderTransform, transform, state.clusterBuffer);
updateTransformForSkinnedMesh(renderTransform, transform);
initCache();
}
@ -367,12 +367,25 @@ void ModelMeshPartPayload::notifyLocationChanged() {
}
void ModelMeshPartPayload::updateTransformForSkinnedMesh(const Transform& renderTransform, const Transform& boundTransform,
const gpu::BufferPointer& buffer) {
void ModelMeshPartPayload::updateClusterBuffer(const std::vector<glm::mat4>& clusterMatrices) {
// Once computed the cluster matrices, update the buffer(s)
if (clusterMatrices.size() > 1) {
if (!_clusterBuffer) {
_clusterBuffer = std::make_shared<gpu::Buffer>(clusterMatrices.size() * sizeof(glm::mat4),
(const gpu::Byte*) clusterMatrices.data());
}
else {
_clusterBuffer->setSubData(0, clusterMatrices.size() * sizeof(glm::mat4),
(const gpu::Byte*) clusterMatrices.data());
}
}
}
void ModelMeshPartPayload::updateTransformForSkinnedMesh(const Transform& renderTransform, const Transform& boundTransform) {
_transform = renderTransform;
_worldBound = _adjustedLocalBound;
_worldBound.transform(boundTransform);
_clusterBuffer = buffer;
}
ItemKey ModelMeshPartPayload::getKey() const {
@ -416,7 +429,6 @@ int ModelMeshPartPayload::getLayer() const {
}
ShapeKey ModelMeshPartPayload::getShapeKey() const {
// guard against partially loaded meshes
ModelPointer model = _model.lock();
if (!model || !model->isLoaded() || !model->getGeometry()) {
@ -582,11 +594,11 @@ void ModelMeshPartPayload::render(RenderArgs* args) {
args->_details._trianglesRendered += _drawPart._numIndices / INDICES_PER_TRIANGLE;
}
void ModelMeshPartPayload::computeAdjustedLocalBound(const QVector<glm::mat4>& clusterMatrices) {
void ModelMeshPartPayload::computeAdjustedLocalBound(const std::vector<glm::mat4>& clusterMatrices) {
_adjustedLocalBound = _localBound;
if (clusterMatrices.size() > 0) {
_adjustedLocalBound.transform(clusterMatrices[0]);
for (int i = 1; i < clusterMatrices.size(); ++i) {
for (int i = 1; i < (int)clusterMatrices.size(); ++i) {
AABox clusterBound = _localBound;
clusterBound.transform(clusterMatrices[i]);
_adjustedLocalBound += clusterBound;

View file

@ -87,9 +87,8 @@ public:
typedef Payload::DataPointer Pointer;
void notifyLocationChanged() override;
void updateTransformForSkinnedMesh(const Transform& renderTransform,
const Transform& boundTransform,
const gpu::BufferPointer& buffer);
void updateClusterBuffer(const std::vector<glm::mat4>& clusterMatrices);
void updateTransformForSkinnedMesh(const Transform& renderTransform, const Transform& boundTransform);
// Render Item interface
render::ItemKey getKey() const override;
@ -103,7 +102,7 @@ public:
void initCache();
void computeAdjustedLocalBound(const QVector<glm::mat4>& clusterMatrices);
void computeAdjustedLocalBound(const std::vector<glm::mat4>& clusterMatrices);
gpu::BufferPointer _clusterBuffer;
ModelWeakPointer _model;

View file

@ -226,7 +226,7 @@ void Model::updateRenderItems() {
// do nothing, if the model has already been destroyed.
auto self = weakSelf.lock();
if (!self) {
if (!self || !self->isLoaded()) {
return;
}
@ -237,24 +237,20 @@ void Model::updateRenderItems() {
Transform modelTransform = self->getTransform();
modelTransform.setScale(glm::vec3(1.0f));
uint32_t deleteGeometryCounter = self->_deleteGeometryCounter;
render::Transaction transaction;
foreach (auto itemID, self->_modelMeshRenderItemsMap.keys()) {
transaction.updateItem<ModelMeshPartPayload>(itemID, [deleteGeometryCounter, modelTransform](ModelMeshPartPayload& data) {
ModelPointer model = data._model.lock();
if (model && model->isLoaded()) {
// Ensure the model geometry was not reset between frames
if (deleteGeometryCounter == model->_deleteGeometryCounter) {
for (int i = 0; i < (int) self->_modelMeshRenderItemIDs.size(); i++) {
const Model::MeshState& state = model->getMeshState(data._meshIndex);
Transform renderTransform = modelTransform;
if (state.clusterMatrices.size() == 1) {
renderTransform = modelTransform.worldTransform(Transform(state.clusterMatrices[0]));
}
data.updateTransformForSkinnedMesh(renderTransform, modelTransform, state.clusterBuffer);
}
auto itemID = self->_modelMeshRenderItemIDs[i];
auto meshIndex = self->_modelMeshRenderItemShapes[i].meshIndex;
auto clusterMatrices(self->getMeshState(meshIndex).clusterMatrices);
transaction.updateItem<ModelMeshPartPayload>(itemID, [modelTransform, clusterMatrices](ModelMeshPartPayload& data) {
data.updateClusterBuffer(clusterMatrices);
Transform renderTransform = modelTransform;
if (clusterMatrices.size() == 1) {
renderTransform = modelTransform.worldTransform(Transform(clusterMatrices[0]));
}
data.updateTransformForSkinnedMesh(renderTransform, modelTransform);
});
}
@ -311,7 +307,7 @@ bool Model::updateGeometry() {
foreach (const FBXMesh& mesh, fbxGeometry.meshes) {
MeshState state;
state.clusterMatrices.resize(mesh.clusters.size());
_meshStates.append(state);
_meshStates.push_back(state);
// Note: we add empty buffers for meshes that lack blendshapes so we can access the buffers by index
// later in ModelMeshPayload, however the vast majority of meshes will not have them.
@ -686,6 +682,7 @@ void Model::removeFromScene(const render::ScenePointer& scene, render::Transacti
_modelMeshRenderItemIDs.clear();
_modelMeshRenderItemsMap.clear();
_modelMeshRenderItems.clear();
_modelMeshRenderItemShapes.clear();
foreach(auto item, _collisionRenderItemsMap.keys()) {
transaction.removeItem(item);
@ -1127,7 +1124,7 @@ void Model::updateClusterMatrices() {
}
_needsUpdateClusterMatrices = false;
const FBXGeometry& geometry = getFBXGeometry();
for (int i = 0; i < _meshStates.size(); i++) {
for (int i = 0; i < (int) _meshStates.size(); i++) {
MeshState& state = _meshStates[i];
const FBXMesh& mesh = geometry.meshes.at(i);
for (int j = 0; j < mesh.clusters.size(); j++) {
@ -1135,17 +1132,6 @@ void Model::updateClusterMatrices() {
auto jointMatrix = _rig.getJointTransform(cluster.jointIndex);
glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, state.clusterMatrices[j]);
}
// Once computed the cluster matrices, update the buffer(s)
if (mesh.clusters.size() > 1) {
if (!state.clusterBuffer) {
state.clusterBuffer = std::make_shared<gpu::Buffer>(state.clusterMatrices.size() * sizeof(glm::mat4),
(const gpu::Byte*) state.clusterMatrices.constData());
} else {
state.clusterBuffer->setSubData(0, state.clusterMatrices.size() * sizeof(glm::mat4),
(const gpu::Byte*) state.clusterMatrices.constData());
}
}
}
// post the blender if we're not currently waiting for one to finish
@ -1255,7 +1241,7 @@ void Model::createVisibleRenderItemSet() {
const auto& meshes = _renderGeometry->getMeshes();
// all of our mesh vectors must match in size
if ((int)meshes.size() != _meshStates.size()) {
if (meshes.size() != _meshStates.size()) {
qCDebug(renderutils) << "WARNING!!!! Mesh Sizes don't match! We will not segregate mesh groups yet.";
return;
}
@ -1264,6 +1250,7 @@ void Model::createVisibleRenderItemSet() {
Q_ASSERT(_modelMeshRenderItems.isEmpty());
_modelMeshRenderItems.clear();
_modelMeshRenderItemShapes.clear();
Transform transform;
transform.setTranslation(_translation);
@ -1286,6 +1273,7 @@ void Model::createVisibleRenderItemSet() {
int numParts = (int)mesh->getNumParts();
for (int partIndex = 0; partIndex < numParts; partIndex++) {
_modelMeshRenderItems << std::make_shared<ModelMeshPartPayload>(shared_from_this(), i, partIndex, shapeID, transform, offset);
_modelMeshRenderItemShapes.emplace_back(ShapeInfo{ (int)i });
shapeID++;
}
}
@ -1327,7 +1315,7 @@ void Model::createCollisionRenderItemSet() {
}
bool Model::isRenderable() const {
return !_meshStates.isEmpty() || (isLoaded() && _renderGeometry->getMeshes().empty());
return !_meshStates.empty() || (isLoaded() && _renderGeometry->getMeshes().empty());
}
class CollisionRenderGeometry : public Geometry {

View file

@ -246,8 +246,7 @@ public:
class MeshState {
public:
QVector<glm::mat4> clusterMatrices;
gpu::BufferPointer clusterBuffer;
std::vector<glm::mat4> clusterMatrices;
};
const MeshState& getMeshState(int index) { return _meshStates.at(index); }
@ -317,7 +316,7 @@ protected:
bool _snappedToRegistrationPoint; /// are we currently snapped to a registration point
glm::vec3 _registrationPoint = glm::vec3(0.5f); /// the point in model space our center is snapped to
QVector<MeshState> _meshStates;
std::vector<MeshState> _meshStates;
virtual void initJointStates();
@ -388,8 +387,9 @@ protected:
QVector<std::shared_ptr<ModelMeshPartPayload>> _modelMeshRenderItems;
QMap<render::ItemID, render::PayloadPointer> _modelMeshRenderItemsMap;
render::ItemIDs _modelMeshRenderItemIDs;
using ShapeInfo = struct { int meshIndex; };
std::vector<ShapeInfo> _modelMeshRenderItemShapes;
bool _addedToScene { false }; // has been added to scene
bool _needsFixupInScene { true }; // needs to be removed/re-added to scene

Some files were not shown because too many files have changed in this diff Show more