mirror of
https://github.com/lubosz/overte.git
synced 2025-04-23 19:34:02 +02:00
Merge branch 'feature/qml_whitelist_tablet' of https://github.com/jherico/hifi into commerce_QmlWhitelist
This commit is contained in:
commit
a3332e15b7
203 changed files with 3213 additions and 1937 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -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
|
||||
|
|
76
cmake/externals/hifiAudioCodec/CMakeLists.txt
vendored
76
cmake/externals/hifiAudioCodec/CMakeLists.txt
vendored
|
@ -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()
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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; })
|
||||
|
|
|
@ -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; })
|
||||
|
|
|
@ -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; })
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
});
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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"});
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
|
|
|
@ -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: {
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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();
|
||||
}
|
|
@ -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 {
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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";
|
||||
|
|
159
interface/resources/qml/hifi/tablet/TabletHome.qml
Normal file
159
interface/resources/qml/hifi/tablet/TabletHome.qml
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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.
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
}
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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 };
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -44,7 +44,7 @@ Cube3DOverlay::~Cube3DOverlay() {
|
|||
}
|
||||
|
||||
void Cube3DOverlay::render(RenderArgs* args) {
|
||||
if (!_visible) {
|
||||
if (!_renderVisible) {
|
||||
return; // do nothing if we're not visible
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
@ -29,9 +29,6 @@ public:
|
|||
|
||||
virtual Rectangle3DOverlay* createClone() const override;
|
||||
|
||||
protected:
|
||||
Transform evalRenderTransform() override;
|
||||
|
||||
private:
|
||||
int _geometryCacheID;
|
||||
std::array<int, 4> _rectGeometryIds;
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
//
|
||||
|
|
|
@ -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 };
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
};
|
||||
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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 };
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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@>
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -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>();
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)$>
|
|
@ -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;
|
||||
}
|
562
libraries/render-utils/src/HighlightEffect.cpp
Normal file
562
libraries/render-utils/src/HighlightEffect.cpp
Normal 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);
|
||||
}
|
226
libraries/render-utils/src/HighlightEffect.h
Normal file
226
libraries/render-utils/src/HighlightEffect.h
Normal 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
|
||||
|
||||
|
104
libraries/render-utils/src/Highlight_aabox.slv
Normal file
104
libraries/render-utils/src/Highlight_aabox.slv
Normal 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)$>
|
||||
}
|
|
@ -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)$>
|
30
libraries/render-utils/src/Highlight_shared.slh
Normal file
30
libraries/render-utils/src/Highlight_shared.slh
Normal 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 !>
|
||||
//
|
|
@ -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() {
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
Loading…
Reference in a new issue