mirror of
https://github.com/overte-org/overte.git
synced 2025-08-05 04:10:11 +02:00
fixing merge conflicts
This commit is contained in:
commit
f6a74cc80e
118 changed files with 2539 additions and 335 deletions
|
@ -68,7 +68,7 @@ module.exports = {
|
|||
"eqeqeq": ["error", "always"],
|
||||
"indent": ["error", 4, { "SwitchCase": 1 }],
|
||||
"keyword-spacing": ["error", { "before": true, "after": true }],
|
||||
"max-len": ["error", 192, 4],
|
||||
"max-len": ["error", 128, 4],
|
||||
"new-cap": ["error"],
|
||||
"no-floating-decimal": ["error"],
|
||||
//"no-magic-numbers": ["error", { "ignore": [0, 1], "ignoreArrayIndexes": true }],
|
||||
|
|
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -66,6 +66,9 @@ TAGS
|
|||
*.sw[po]
|
||||
*.qmlc
|
||||
|
||||
# ignore QML compilation output
|
||||
*.qmlc
|
||||
|
||||
# ignore node files for the console
|
||||
node_modules
|
||||
npm-debug.log
|
||||
|
|
|
@ -28,6 +28,10 @@
|
|||
const QString ASSIGNMENT_CLIENT_MONITOR_TARGET_NAME = "assignment-client-monitor";
|
||||
const int WAIT_FOR_CHILD_MSECS = 1000;
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
HANDLE PROCESS_GROUP = createProcessGroup();
|
||||
#endif
|
||||
|
||||
AssignmentClientMonitor::AssignmentClientMonitor(const unsigned int numAssignmentClientForks,
|
||||
const unsigned int minAssignmentClientForks,
|
||||
const unsigned int maxAssignmentClientForks,
|
||||
|
@ -202,6 +206,10 @@ void AssignmentClientMonitor::spawnChildClient() {
|
|||
assignmentClient->setProcessChannelMode(QProcess::ForwardedChannels);
|
||||
assignmentClient->start(QCoreApplication::applicationFilePath(), _childArguments);
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
addProcessToGroup(PROCESS_GROUP, assignmentClient->processId());
|
||||
#endif
|
||||
|
||||
QString stdoutPath, stderrPath;
|
||||
|
||||
if (_wantsChildFileLogging) {
|
||||
|
|
Binary file not shown.
BIN
interface/resources/images/lowerKeyboard.png
Normal file
BIN
interface/resources/images/lowerKeyboard.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1 KiB |
|
@ -5,10 +5,16 @@ Item {
|
|||
id: keyItem
|
||||
width: 45
|
||||
height: 50
|
||||
|
||||
property int contentPadding: 4
|
||||
property string glyph: "a"
|
||||
property bool toggle: false // does this button have the toggle behaivor?
|
||||
property bool toggled: false // is this button currently toggled?
|
||||
property alias mouseArea: mouseArea1
|
||||
property alias fontFamily: letter.font.family;
|
||||
property alias fontPixelSize: letter.font.pixelSize
|
||||
property alias verticalAlignment: letter.verticalAlignment
|
||||
property alias letterAnchors: letter.anchors
|
||||
|
||||
function resetToggledMode(mode) {
|
||||
toggled = mode;
|
||||
|
@ -106,14 +112,8 @@ Item {
|
|||
color: "#121212"
|
||||
radius: 2
|
||||
border.color: "#00000000"
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: 4
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: 4
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.bottomMargin: 4
|
||||
anchors.top: parent.top
|
||||
anchors.topMargin: 4
|
||||
anchors.fill: parent
|
||||
anchors.margins: contentPadding
|
||||
}
|
||||
|
||||
Text {
|
||||
|
|
|
@ -8,7 +8,8 @@
|
|||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
import QtQuick 2.0
|
||||
import QtQuick 2.7
|
||||
import QtGraphicalEffects 1.0
|
||||
import "."
|
||||
|
||||
Rectangle {
|
||||
|
@ -55,6 +56,8 @@ Rectangle {
|
|||
return ">";
|
||||
} else if (str === "/") {
|
||||
return "?";
|
||||
} else if (str === "-") {
|
||||
return "_";
|
||||
} else {
|
||||
return str.toUpperCase(str);
|
||||
}
|
||||
|
@ -67,6 +70,8 @@ Rectangle {
|
|||
return ".";
|
||||
} else if (str === "?") {
|
||||
return "/";
|
||||
} else if (str === "_") {
|
||||
return "-";
|
||||
} else {
|
||||
return str.toLowerCase(str);
|
||||
}
|
||||
|
@ -85,7 +90,7 @@ Rectangle {
|
|||
|
||||
onShiftModeChanged: {
|
||||
forEachKey(function (key) {
|
||||
if (/[a-z]/i.test(key.glyph)) {
|
||||
if (/[a-z-_]/i.test(key.glyph)) {
|
||||
if (shiftMode) {
|
||||
key.glyph = keyboardBase.toUpper(key.glyph);
|
||||
} else {
|
||||
|
@ -112,8 +117,6 @@ Rectangle {
|
|||
}
|
||||
|
||||
Rectangle {
|
||||
y: 0
|
||||
x: 0
|
||||
height: showMirrorText ? mirrorTextHeight : 0
|
||||
width: keyboardWidth
|
||||
color: "#252525"
|
||||
|
@ -122,13 +125,18 @@ Rectangle {
|
|||
TextInput {
|
||||
id: mirrorText
|
||||
visible: showMirrorText
|
||||
FontLoader { id: ralewaySemiBold; source: "../../fonts/Raleway-SemiBold.ttf"; }
|
||||
font.family: ralewaySemiBold.name
|
||||
font.pointSize: 13.5
|
||||
FontLoader { id: font; source: "../../fonts/FiraSans-Regular.ttf"; }
|
||||
font.family: font.name
|
||||
font.pixelSize: 20
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
color: "#FFFFFF";
|
||||
anchors.fill: parent
|
||||
color: "#00B4EF";
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: 10
|
||||
anchors.right: parent.right
|
||||
anchors.top: parent.top
|
||||
anchors.bottom: parent.bottom
|
||||
|
||||
wrapMode: Text.WordWrap
|
||||
readOnly: false // we need this to allow control to accept QKeyEvent
|
||||
selectByMouse: false
|
||||
|
@ -140,16 +148,15 @@ Rectangle {
|
|||
event.accepted = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea { // ... and we need this mouse area to prevent mirrorText from getting mouse events to ensure it will never get focus
|
||||
anchors.fill: parent
|
||||
MouseArea { // ... and we need this mouse area to prevent mirrorText from getting mouse events to ensure it will never get focus
|
||||
anchors.fill: parent
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: keyboardRect
|
||||
x: 0
|
||||
y: showMirrorText ? mirrorTextHeight : 0
|
||||
width: keyboardWidth
|
||||
height: raisedHeight
|
||||
|
@ -158,6 +165,8 @@ Rectangle {
|
|||
anchors.bottom: parent.bottom
|
||||
anchors.bottomMargin: 0
|
||||
|
||||
FontLoader { id: hiFiGlyphs; source: pathToFonts + "fonts/hifi-glyphs.ttf"; }
|
||||
|
||||
Column {
|
||||
id: columnAlpha
|
||||
width: keyboardWidth
|
||||
|
@ -221,7 +230,7 @@ Rectangle {
|
|||
Key { width: 43; glyph: "b"; }
|
||||
Key { width: 43; glyph: "n"; }
|
||||
Key { width: 43; glyph: "m"; }
|
||||
Key { width: 43; glyph: "_"; }
|
||||
Key { width: 43; glyph: "-"; }
|
||||
Key { width: 43; glyph: "/"; }
|
||||
Key { width: 43; glyph: "?"; }
|
||||
}
|
||||
|
@ -240,8 +249,13 @@ Rectangle {
|
|||
Key { width: 231; glyph: " "; }
|
||||
Key { width: 43; glyph: ","; }
|
||||
Key { width: 43; glyph: "."; }
|
||||
Key { width: 43; glyph: "\u276C"; }
|
||||
Key { width: 43; glyph: "\u276D"; }
|
||||
Key {
|
||||
fontFamily: hiFiGlyphs.name;
|
||||
fontPixelSize: 48;
|
||||
letterAnchors.topMargin: -4;
|
||||
verticalAlignment: Text.AlignVCenter;
|
||||
width: 86; glyph: "\ue02b";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -328,8 +342,13 @@ Rectangle {
|
|||
Key { width: 231; glyph: " "; }
|
||||
Key { width: 43; glyph: ","; }
|
||||
Key { width: 43; glyph: "."; }
|
||||
Key { width: 43; glyph: "\u276C"; }
|
||||
Key { width: 43; glyph: "\u276D"; }
|
||||
Key {
|
||||
fontFamily: hiFiGlyphs.name;
|
||||
fontPixelSize: 48;
|
||||
letterAnchors.topMargin: -4;
|
||||
verticalAlignment: Text.AlignVCenter;
|
||||
width: 86; glyph: "\ue02b";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,6 +27,12 @@ Item {
|
|||
id: hifi
|
||||
}
|
||||
|
||||
function unfocus() {
|
||||
webViewCore.runJavaScript("if (document.activeElement) document.activeElement.blur();", function(result) {
|
||||
console.log('unfocus completed: ', result);
|
||||
});
|
||||
}
|
||||
|
||||
function onLoadingChanged(loadRequest) {
|
||||
if (WebEngineView.LoadStartedStatus === loadRequest.status) {
|
||||
|
||||
|
|
|
@ -10,6 +10,11 @@ Item {
|
|||
property alias urlTag: webroot.urlTag
|
||||
property bool keyboardEnabled: true // FIXME - Keyboard HMD only: Default to false
|
||||
property bool keyboardRaised: false
|
||||
onKeyboardRaisedChanged: {
|
||||
if(!keyboardRaised) {
|
||||
webroot.unfocus();
|
||||
}
|
||||
}
|
||||
property bool punctuationMode: false
|
||||
|
||||
// FIXME - Keyboard HMD only: Make Interface either set keyboardRaised property directly in OffscreenQmlSurface
|
||||
|
|
|
@ -15,6 +15,11 @@ Item {
|
|||
property string scriptURL
|
||||
property bool keyboardEnabled: false
|
||||
property bool keyboardRaised: false
|
||||
onKeyboardRaisedChanged: {
|
||||
if(!keyboardRaised) {
|
||||
webroot.unfocus();
|
||||
}
|
||||
}
|
||||
property bool punctuationMode: false
|
||||
property bool passwordField: false
|
||||
property bool isDesktop: false
|
||||
|
|
|
@ -12,6 +12,11 @@ Item {
|
|||
property alias urlTag: webroot.urlTag
|
||||
property bool keyboardEnabled: true // FIXME - Keyboard HMD only: Default to false
|
||||
property bool keyboardRaised: false
|
||||
onKeyboardRaisedChanged: {
|
||||
if(!keyboardRaised) {
|
||||
webroot.unfocus();
|
||||
}
|
||||
}
|
||||
property bool punctuationMode: false
|
||||
property bool passwordField: false
|
||||
property alias flickable: webroot.interactive
|
||||
|
|
|
@ -596,9 +596,7 @@ Rectangle {
|
|||
anchors.right: parent.right;
|
||||
text: root.isWearable ? "Wear It" : "Rez It"
|
||||
onClicked: {
|
||||
if (urlHandler.canHandleUrl(root.itemHref)) {
|
||||
urlHandler.handleUrl(root.itemHref);
|
||||
}
|
||||
sendToScript({method: 'checkout_rezClicked', itemHref: root.itemHref, isWearable: root.isWearable});
|
||||
rezzedNotifContainer.visible = true;
|
||||
rezzedNotifContainerTimer.start();
|
||||
}
|
||||
|
|
|
@ -346,9 +346,7 @@ Item {
|
|||
enabled: (root.canRezCertifiedItems || root.isWearable) && root.purchaseStatus !== "invalidated";
|
||||
|
||||
onClicked: {
|
||||
if (urlHandler.canHandleUrl(root.itemHref)) {
|
||||
urlHandler.handleUrl(root.itemHref);
|
||||
}
|
||||
sendToPurchases({method: 'purchases_rezClicked', itemHref: root.itemHref, isWearable: root.isWearable});
|
||||
rezzedNotifContainer.visible = true;
|
||||
rezzedNotifContainerTimer.start();
|
||||
}
|
||||
|
|
|
@ -442,6 +442,8 @@ Rectangle {
|
|||
onSendToPurchases: {
|
||||
if (msg.method === 'purchases_itemInfoClicked') {
|
||||
sendToScript({method: 'purchases_itemInfoClicked', itemId: itemId});
|
||||
} else if (msg.method === "purchases_rezClicked") {
|
||||
sendToScript({method: 'purchases_rezClicked', itemHref: itemHref, isWearable: isWearable});
|
||||
} else if (msg.method === 'purchases_itemCertificateClicked') {
|
||||
inspectionCertificate.visible = true;
|
||||
inspectionCertificate.isLightbox = true;
|
||||
|
|
|
@ -32,6 +32,8 @@ Rectangle {
|
|||
|
||||
color: hifi.colors.baseGray
|
||||
|
||||
property bool keyboardEnabled: HMD.active
|
||||
property bool keyboardRaised: false
|
||||
|
||||
LetterboxMessage {
|
||||
id: letterBoxMessage
|
||||
|
@ -380,7 +382,7 @@ Rectangle {
|
|||
Component.onCompleted: scriptsModel.filterRegExp = new RegExp("^.*$", "i")
|
||||
onActiveFocusChanged: {
|
||||
// raise the keyboard
|
||||
keyboard.raised = activeFocus;
|
||||
root.keyboardRaised = activeFocus;
|
||||
|
||||
// scroll to the bottom of the content area.
|
||||
if (activeFocus) {
|
||||
|
@ -481,7 +483,7 @@ Rectangle {
|
|||
|
||||
HifiControls.Keyboard {
|
||||
id: keyboard
|
||||
raised: false
|
||||
raised: parent.keyboardEnabled && parent.keyboardRaised
|
||||
numeric: false
|
||||
anchors {
|
||||
bottom: parent.bottom
|
||||
|
|
|
@ -8,8 +8,8 @@ import "../audio" as HifiAudio
|
|||
Item {
|
||||
id: tablet
|
||||
objectName: "tablet"
|
||||
property int rowIndex: 0
|
||||
property int columnIndex: 0
|
||||
property int rowIndex: 6 // by default
|
||||
property int columnIndex: 1 // point to 'go to location'
|
||||
property int count: (flowMain.children.length - 1)
|
||||
|
||||
// used to look up a button by its uuid
|
||||
|
|
|
@ -366,7 +366,7 @@ StackView {
|
|||
|
||||
HifiControls.Keyboard {
|
||||
id: keyboard
|
||||
raised: parent.keyboardEnabled
|
||||
raised: parent.keyboardEnabled && parent.keyboardRaised
|
||||
numeric: parent.punctuationMode
|
||||
anchors {
|
||||
bottom: parent.bottom
|
||||
|
|
|
@ -194,8 +194,8 @@
|
|||
#include <EntityScriptClient.h>
|
||||
#include <ModelScriptingInterface.h>
|
||||
|
||||
#include <pointers/PickManager.h>
|
||||
#include <pointers/PointerManager.h>
|
||||
#include <PickManager.h>
|
||||
#include <PointerManager.h>
|
||||
#include <raypick/RayPickScriptingInterface.h>
|
||||
#include <raypick/LaserPointerScriptingInterface.h>
|
||||
#include <raypick/PickScriptingInterface.h>
|
||||
|
@ -1710,8 +1710,6 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
|
|||
lastLeftHandPose = leftHandPose;
|
||||
lastRightHandPose = rightHandPose;
|
||||
|
||||
properties["local_socket_changes"] = DependencyManager::get<StatTracker>()->getStat(LOCAL_SOCKET_CHANGE_STAT).toInt();
|
||||
|
||||
UserActivityLogger::getInstance().logAction("stats", properties);
|
||||
});
|
||||
sendStatsTimer->start();
|
||||
|
|
|
@ -14,12 +14,12 @@
|
|||
#include "avatar/AvatarManager.h"
|
||||
|
||||
#include <DependencyManager.h>
|
||||
#include <pointers/PickManager.h>
|
||||
#include <PickManager.h>
|
||||
#include "PickScriptingInterface.h"
|
||||
#include "RayPick.h"
|
||||
|
||||
LaserPointer::LaserPointer(const QVariant& rayProps, const RenderStateMap& renderStates, const DefaultRenderStateMap& defaultRenderStates, bool hover,
|
||||
const PointerTriggers& triggers, bool faceAvatar, bool centerEndY, bool lockEnd, bool distanceScaleEnd, bool enabled) :
|
||||
const PointerTriggers& triggers, bool faceAvatar, bool centerEndY, bool lockEnd, bool distanceScaleEnd, bool scaleWithAvatar, bool enabled) :
|
||||
Pointer(DependencyManager::get<PickScriptingInterface>()->createRayPick(rayProps), enabled, hover),
|
||||
_triggers(triggers),
|
||||
_renderStates(renderStates),
|
||||
|
@ -27,7 +27,8 @@ LaserPointer::LaserPointer(const QVariant& rayProps, const RenderStateMap& rende
|
|||
_faceAvatar(faceAvatar),
|
||||
_centerEndY(centerEndY),
|
||||
_lockEnd(lockEnd),
|
||||
_distanceScaleEnd(distanceScaleEnd)
|
||||
_distanceScaleEnd(distanceScaleEnd),
|
||||
_scaleWithAvatar(scaleWithAvatar)
|
||||
{
|
||||
for (auto& state : _renderStates) {
|
||||
if (!enabled || state.first != _currentRenderState) {
|
||||
|
@ -73,6 +74,10 @@ void LaserPointer::editRenderState(const std::string& state, const QVariant& sta
|
|||
if (endDim.isValid()) {
|
||||
_renderStates[state].setEndDim(vec3FromVariant(endDim));
|
||||
}
|
||||
QVariant lineWidth = pathProps.toMap()["lineWidth"];
|
||||
if (lineWidth.isValid()) {
|
||||
_renderStates[state].setLineWidth(lineWidth.toFloat());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -127,6 +132,7 @@ void LaserPointer::updateRenderState(const RenderState& renderState, const Inter
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
QVariant end = vec3toVariant(endVec);
|
||||
if (!renderState.getPathID().isNull()) {
|
||||
QVariantMap pathProps;
|
||||
|
@ -134,6 +140,9 @@ void LaserPointer::updateRenderState(const RenderState& renderState, const Inter
|
|||
pathProps.insert("end", end);
|
||||
pathProps.insert("visible", true);
|
||||
pathProps.insert("ignoreRayIntersection", renderState.doesPathIgnoreRays());
|
||||
if (_scaleWithAvatar) {
|
||||
pathProps.insert("lineWidth", renderState.getLineWidth() * DependencyManager::get<AvatarManager>()->getMyAvatar()->getSensorToWorldScale());
|
||||
}
|
||||
qApp->getOverlays().editOverlay(renderState.getPathID(), pathProps);
|
||||
}
|
||||
if (!renderState.getEndID().isNull()) {
|
||||
|
@ -141,7 +150,7 @@ void LaserPointer::updateRenderState(const RenderState& renderState, const Inter
|
|||
glm::quat faceAvatarRotation = DependencyManager::get<AvatarManager>()->getMyAvatar()->getOrientation() * glm::quat(glm::radians(glm::vec3(0.0f, 180.0f, 0.0f)));
|
||||
glm::vec3 dim = vec3FromVariant(qApp->getOverlays().getProperty(renderState.getEndID(), "dimensions").value);
|
||||
if (_distanceScaleEnd) {
|
||||
dim = renderState.getEndDim() * glm::distance(pickRay.origin, endVec) * DependencyManager::get<AvatarManager>()->getMyAvatar()->getSensorToWorldScale();
|
||||
dim = renderState.getEndDim() * glm::distance(pickRay.origin, endVec);
|
||||
endProps.insert("dimensions", vec3toVariant(dim));
|
||||
}
|
||||
if (_centerEndY) {
|
||||
|
@ -242,6 +251,7 @@ RenderState::RenderState(const OverlayID& startID, const OverlayID& pathID, cons
|
|||
}
|
||||
if (!_pathID.isNull()) {
|
||||
_pathIgnoreRays = qApp->getOverlays().getProperty(_pathID, "ignoreRayIntersection").value.toBool();
|
||||
_lineWidth = qApp->getOverlays().getProperty(_pathID, "lineWidth").value.toFloat();
|
||||
}
|
||||
if (!_endID.isNull()) {
|
||||
_endDim = vec3FromVariant(qApp->getOverlays().getProperty(_endID, "dimensions").value);
|
||||
|
|
|
@ -16,8 +16,8 @@
|
|||
|
||||
#include "ui/overlays/Overlay.h"
|
||||
|
||||
#include <pointers/Pointer.h>
|
||||
#include <pointers/Pick.h>
|
||||
#include <Pointer.h>
|
||||
#include <Pick.h>
|
||||
|
||||
struct LockEndObject {
|
||||
QUuid id { QUuid() };
|
||||
|
@ -41,6 +41,9 @@ public:
|
|||
void setEndDim(const glm::vec3& endDim) { _endDim = endDim; }
|
||||
const glm::vec3& getEndDim() const { return _endDim; }
|
||||
|
||||
void setLineWidth(const float& lineWidth) { _lineWidth = lineWidth; }
|
||||
const float& getLineWidth() const { return _lineWidth; }
|
||||
|
||||
void deleteOverlays();
|
||||
|
||||
private:
|
||||
|
@ -52,6 +55,7 @@ private:
|
|||
bool _endIgnoreRays;
|
||||
|
||||
glm::vec3 _endDim;
|
||||
float _lineWidth;
|
||||
};
|
||||
|
||||
class LaserPointer : public Pointer {
|
||||
|
@ -61,7 +65,7 @@ public:
|
|||
typedef std::unordered_map<std::string, std::pair<float, RenderState>> DefaultRenderStateMap;
|
||||
|
||||
LaserPointer(const QVariant& rayProps, const RenderStateMap& renderStates, const DefaultRenderStateMap& defaultRenderStates, bool hover, const PointerTriggers& triggers,
|
||||
bool faceAvatar, bool centerEndY, bool lockEnd, bool distanceScaleEnd, bool enabled);
|
||||
bool faceAvatar, bool centerEndY, bool lockEnd, bool distanceScaleEnd, bool scaleWithAvatar, bool enabled);
|
||||
~LaserPointer();
|
||||
|
||||
void setRenderState(const std::string& state) override;
|
||||
|
@ -94,6 +98,7 @@ private:
|
|||
bool _centerEndY;
|
||||
bool _lockEnd;
|
||||
bool _distanceScaleEnd;
|
||||
bool _scaleWithAvatar;
|
||||
LockEndObject _lockEndObject;
|
||||
|
||||
void updateRenderStateOverlay(const OverlayID& id, const QVariant& props);
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
#include <QtCore/QObject>
|
||||
|
||||
#include "DependencyManager.h"
|
||||
#include <pointers/PointerManager.h>
|
||||
#include <PointerManager.h>
|
||||
|
||||
class LaserPointerScriptingInterface : public QObject, public Dependency {
|
||||
Q_OBJECT
|
||||
|
|
|
@ -11,14 +11,13 @@
|
|||
#include <QVariant>
|
||||
#include "GLMHelpers.h"
|
||||
|
||||
#include <pointers/PickManager.h>
|
||||
#include <PickManager.h>
|
||||
|
||||
#include "StaticRayPick.h"
|
||||
#include "JointRayPick.h"
|
||||
#include "MouseRayPick.h"
|
||||
#include "StylusPick.h"
|
||||
|
||||
#include <pointers/Pick.h>
|
||||
#include <ScriptEngine.h>
|
||||
|
||||
unsigned int PickScriptingInterface::createPick(const PickQuery::PickType type, const QVariant& properties) {
|
||||
|
|
|
@ -12,7 +12,26 @@
|
|||
|
||||
#include <RegisteredMetaTypes.h>
|
||||
#include <DependencyManager.h>
|
||||
#include <pointers/Pick.h>
|
||||
#include <Pick.h>
|
||||
|
||||
/**jsdoc
|
||||
* The Picks API lets you create and manage objects for repeatedly calculating intersections in different ways.
|
||||
*
|
||||
* @namespace Picks
|
||||
* @property PICK_NOTHING {number} A filter flag. Don't intersect with anything.
|
||||
* @property PICK_ENTITIES {number} A filter flag. Include entities when intersecting.
|
||||
* @property PICK_OVERLAYS {number} A filter flag. Include overlays when intersecting.
|
||||
* @property PICK_AVATARS {number} A filter flag. Include avatars when intersecting.
|
||||
* @property PICK_HUD {number} A filter flag. Include the HUD sphere when intersecting in HMD mode.
|
||||
* @property PICK_COARSE {number} A filter flag. Pick against coarse meshes, instead of exact meshes.
|
||||
* @property PICK_INCLUDE_INVISIBLE {number} A filter flag. Include invisible objects when intersecting.
|
||||
* @property PICK_INCLUDE_NONCOLLIDABLE {number} A filter flag. Include non-collidable objects when intersecting.
|
||||
* @property INTERSECTED_NONE {number} An intersection type. Intersected nothing with the given filter flags.
|
||||
* @property INTERSECTED_ENTITY {number} An intersection type. Intersected an entity.
|
||||
* @property INTERSECTED_OVERLAY {number} An intersection type. Intersected an overlay.
|
||||
* @property INTERSECTED_AVATAR {number} An intersection type. Intersected an avatar.
|
||||
* @property INTERSECTED_HUD {number} An intersection type. Intersected the HUD sphere.
|
||||
*/
|
||||
|
||||
class PickScriptingInterface : public QObject, public Dependency {
|
||||
Q_OBJECT
|
||||
|
@ -38,18 +57,130 @@ public:
|
|||
|
||||
void registerMetaTypes(QScriptEngine* engine);
|
||||
|
||||
/**jsdoc
|
||||
* A set of properties that can be passed to {@link Picks.createPick} to create a new Pick.
|
||||
*
|
||||
* Different {@link Picks.PickType}s use different properties, and within one PickType, the properties you choose can lead to a wide range of behaviors. For example,
|
||||
* with PickType.Ray, depending on which optional parameters you pass, you could create a Static Ray Pick, a Mouse Ray Pick, or a Joint Ray Pick.
|
||||
*
|
||||
* @typedef {Object} Picks.PickProperties
|
||||
* @property {boolean} [enabled=false] If this Pick should start enabled or not. Disabled Picks do not updated their pick results.
|
||||
* @property {number} [filter=Picks.PICK_NOTHING] The filter for this Pick to use, constructed using filter flags combined using bitwise OR.
|
||||
* @property {float} [maxDistance=0.0] The max distance at which this Pick will intersect. 0.0 = no max. < 0.0 is invalid.
|
||||
* @property {string} [joint] Only for Joint or Mouse Ray Picks. If "Mouse", it will create a Ray Pick that follows the system mouse, in desktop or HMD.
|
||||
* If "Avatar", it will create a Joint Ray Pick that follows your avatar's head. Otherwise, it will create a Joint Ray Pick that follows the given joint, if it
|
||||
* exists on your current avatar.
|
||||
* @property {Vec3} [posOffset=Vec3.ZERO] Only for Joint Ray Picks. A local joint position offset, in meters. x = upward, y = forward, z = lateral
|
||||
* @property {Vec3} [dirOffset=Vec3.UP] Only for Joint Ray Picks. A local joint direction offset. x = upward, y = forward, z = lateral
|
||||
* @property {Vec3} [position] Only for Static Ray Picks. The world-space origin of the ray.
|
||||
* @property {Vec3} [direction=-Vec3.UP] Only for Static Ray Picks. The world-space direction of the ray.
|
||||
* @property {number} [hand=-1] Only for Stylus Picks. An integer. 0 == left, 1 == right. Invalid otherwise.
|
||||
*/
|
||||
|
||||
/**jsdoc
|
||||
* Adds a new Pick.
|
||||
* @function Picks.createPick
|
||||
* @param {Picks.PickType} type A PickType that specifies the method of picking to use
|
||||
* @param {Picks.PickProperties} properties A PickProperties object, containing all the properties for initializing this Pick
|
||||
* @returns {number} The ID of the created Pick. Used for managing the Pick. 0 if invalid.
|
||||
*/
|
||||
Q_INVOKABLE unsigned int createPick(const PickQuery::PickType type, const QVariant& properties);
|
||||
/**jsdoc
|
||||
* Enables a Pick.
|
||||
* @function Picks.enablePick
|
||||
* @param {number} uid The ID of the Pick, as returned by {@link Picks.createPick}.
|
||||
*/
|
||||
Q_INVOKABLE void enablePick(unsigned int uid);
|
||||
/**jsdoc
|
||||
* Disables a Pick.
|
||||
* @function Picks.disablePick
|
||||
* @param {number} uid The ID of the Pick, as returned by {@link Picks.createPick}.
|
||||
*/
|
||||
Q_INVOKABLE void disablePick(unsigned int uid);
|
||||
/**jsdoc
|
||||
* Removes a Pick.
|
||||
* @function Picks.removePick
|
||||
* @param {number} uid The ID of the Pick, as returned by {@link Picks.createPick}.
|
||||
*/
|
||||
Q_INVOKABLE void removePick(unsigned int uid);
|
||||
|
||||
/**jsdoc
|
||||
* An intersection result for a Ray Pick.
|
||||
*
|
||||
* @typedef {Object} Picks.RayPickResult
|
||||
* @property {number} type The intersection type.
|
||||
* @property {bool} intersects If there was a valid intersection (type != INTERSECTED_NONE)
|
||||
* @property {Uuid} objectID The ID of the intersected object. Uuid.NULL for the HUD or invalid intersections.
|
||||
* @property {float} distance The distance to the intersection point from the origin of the ray.
|
||||
* @property {Vec3} intersection The intersection point in world-space.
|
||||
* @property {Vec3} surfaceNormal The surface normal at the intersected point. All NANs if type == INTERSECTED_HUD.
|
||||
* @property {PickRay} searchRay The PickRay that was used. Valid even if there was no intersection.
|
||||
*/
|
||||
|
||||
/**jsdoc
|
||||
* An intersection result for a Stylus Pick.
|
||||
*
|
||||
* @typedef {Object} Picks.StylusPickResult
|
||||
* @property {number} type The intersection type.
|
||||
* @property {bool} intersects If there was a valid intersection (type != INTERSECTED_NONE)
|
||||
* @property {Uuid} objectID The ID of the intersected object. Uuid.NULL for the HUD or invalid intersections.
|
||||
* @property {float} distance The distance to the intersection point from the origin of the ray.
|
||||
* @property {Vec3} intersection The intersection point in world-space.
|
||||
* @property {Vec3} surfaceNormal The surface normal at the intersected point. All NANs if type == INTERSECTED_HUD.
|
||||
* @property {StylusTip} stylusTip The StylusTip that was used. Valid even if there was no intersection.
|
||||
*/
|
||||
|
||||
/**jsdoc
|
||||
* Get the most recent pick result from this Pick. This will be updated as long as the Pick is enabled.
|
||||
* @function Picks.getPrevPickResult
|
||||
* @param {number} uid The ID of the Pick, as returned by {@link Picks.createPick}.
|
||||
* @returns {PickResult} The most recent intersection result. This will be slightly different for different PickTypes. See {@link Picks.RayPickResult} and {@link Picks.StylusPickResult}.
|
||||
*/
|
||||
Q_INVOKABLE QVariantMap getPrevPickResult(unsigned int uid);
|
||||
|
||||
/**jsdoc
|
||||
* Sets whether or not to use precision picking.
|
||||
* @function Picks.setPrecisionPicking
|
||||
* @param {number} uid The ID of the Pick, as returned by {@link Picks.createPick}.
|
||||
* @param {boolean} precisionPicking Whether or not to use precision picking
|
||||
*/
|
||||
Q_INVOKABLE void setPrecisionPicking(unsigned int uid, bool precisionPicking);
|
||||
Q_INVOKABLE void setIgnoreItems(unsigned int uid, const QScriptValue& ignoreEntities);
|
||||
Q_INVOKABLE void setIncludeItems(unsigned int uid, const QScriptValue& includeEntities);
|
||||
/**jsdoc
|
||||
* Sets a list of Entity IDs, Overlay IDs, and/or Avatar IDs to ignore during intersection. Not used by Stylus Picks.
|
||||
* @function Picks.setIgnoreItems
|
||||
* @param {number} uid The ID of the Pick, as returned by {@link Picks.createPick}.
|
||||
* @param {Uuid[]} ignoreItems A list of IDs to ignore.
|
||||
*/
|
||||
Q_INVOKABLE void setIgnoreItems(unsigned int uid, const QScriptValue& ignoreItems);
|
||||
/**jsdoc
|
||||
* Sets a list of Entity IDs, Overlay IDs, and/or Avatar IDs to include during intersection, instead of intersecting with everything. Stylus
|
||||
* Picks <b>only</b> intersect with objects in their include list.
|
||||
* @function Picks.setIncludeItems
|
||||
* @param {number} uid The ID of the Pick, as returned by {@link Picks.createPick}.
|
||||
* @param {Uuid[]} includeItems A list of IDs to include.
|
||||
*/
|
||||
Q_INVOKABLE void setIncludeItems(unsigned int uid, const QScriptValue& includeItems);
|
||||
|
||||
/**jsdoc
|
||||
* Check if a Pick is associated with the left hand.
|
||||
* @function Picks.isLeftHand
|
||||
* @param {number} uid The ID of the Pick, as returned by {@link Picks.createPick}.
|
||||
* @returns {boolean} True if the Pick is a Joint Ray Pick with joint == "_CONTROLLER_LEFTHAND" or "_CAMERA_RELATIVE_CONTROLLER_LEFTHAND", or a Stylus Pick with hand == 0.
|
||||
*/
|
||||
Q_INVOKABLE bool isLeftHand(unsigned int uid);
|
||||
/**jsdoc
|
||||
* Check if a Pick is associated with the right hand.
|
||||
* @function Picks.isRightHand
|
||||
* @param {number} uid The ID of the Pick, as returned by {@link Picks.createPick}.
|
||||
* @returns {boolean} True if the Pick is a Joint Ray Pick with joint == "_CONTROLLER_RIGHTHAND" or "_CAMERA_RELATIVE_CONTROLLER_RIGHTHAND", or a Stylus Pick with hand == 1.
|
||||
*/
|
||||
Q_INVOKABLE bool isRightHand(unsigned int uid);
|
||||
/**jsdoc
|
||||
* Check if a Pick is associated with the system mouse.
|
||||
* @function Picks.isMouse
|
||||
* @param {number} uid The ID of the Pick, as returned by {@link Picks.createPick}.
|
||||
* @returns {boolean} True if the Pick is a Mouse Ray Pick, false otherwise.
|
||||
*/
|
||||
Q_INVOKABLE bool isMouse(unsigned int uid);
|
||||
|
||||
public slots:
|
||||
|
|
|
@ -81,6 +81,11 @@ unsigned int PointerScriptingInterface::createLaserPointer(const QVariant& prope
|
|||
distanceScaleEnd = propertyMap["distanceScaleEnd"].toBool();
|
||||
}
|
||||
|
||||
bool scaleWithAvatar = false;
|
||||
if (propertyMap["scaleWithAvatar"].isValid()) {
|
||||
scaleWithAvatar = propertyMap["scaleWithAvatar"].toBool();
|
||||
}
|
||||
|
||||
bool enabled = false;
|
||||
if (propertyMap["enabled"].isValid()) {
|
||||
enabled = propertyMap["enabled"].toBool();
|
||||
|
@ -139,7 +144,7 @@ unsigned int PointerScriptingInterface::createLaserPointer(const QVariant& prope
|
|||
}
|
||||
|
||||
return DependencyManager::get<PointerManager>()->addPointer(std::make_shared<LaserPointer>(properties, renderStates, defaultRenderStates, hover, triggers,
|
||||
faceAvatar, centerEndY, lockEnd, distanceScaleEnd, enabled));
|
||||
faceAvatar, centerEndY, lockEnd, distanceScaleEnd, scaleWithAvatar, enabled));
|
||||
}
|
||||
|
||||
void PointerScriptingInterface::editRenderState(unsigned int uid, const QString& renderState, const QVariant& properties) const {
|
||||
|
|
|
@ -11,8 +11,15 @@
|
|||
#include <QtCore/QObject>
|
||||
|
||||
#include "DependencyManager.h"
|
||||
#include <pointers/PointerManager.h>
|
||||
#include <pointers/Pick.h>
|
||||
#include <PointerManager.h>
|
||||
#include <Pick.h>
|
||||
|
||||
/**jsdoc
|
||||
* The Pointers API lets you create and manage objects for repeatedly calculating intersections in different ways, as well as the visual representation of those objects.
|
||||
* Pointers can also be configured to automatically generate PointerEvents.
|
||||
*
|
||||
* @namespace Pointers
|
||||
*/
|
||||
|
||||
class PointerScriptingInterface : public QObject, public Dependency {
|
||||
Q_OBJECT
|
||||
|
@ -22,33 +29,201 @@ public:
|
|||
unsigned int createLaserPointer(const QVariant& properties) const;
|
||||
unsigned int createStylus(const QVariant& properties) const;
|
||||
|
||||
/**jsdoc
|
||||
* A set of properties that can be passed to {@link Pointers.createPointer} to create a new Pointer. Also contains the relevant {@link Picks.PickProperties} to define the underlying Pick.
|
||||
*
|
||||
* Different {@link PickType}s use different properties, and within one PickType, the properties you choose can lead to a wide range of behaviors. For example,
|
||||
* with PickType.Ray, depending on which optional parameters you pass, you could create a Static Ray Pointer, a Mouse Ray Pointer, or a Joint Ray Pointer.
|
||||
*
|
||||
* @typedef {Object} Pointers.PointerProperties
|
||||
* @property {boolean} [hover=false] If this Pointer should generate hover events.
|
||||
* @property {boolean} [faceAvatar=false] Ray Pointers only. If true, the end of the Pointer will always rotate to face the avatar.
|
||||
* @property {boolean} [centerEndY=true] Ray Pointers only. If false, the end of the Pointer will be moved up by half of its height.
|
||||
* @property {boolean} [lockEnd=false] Ray Pointers only. If true, the end of the Pointer will lock on to the center of the object at which the laser is pointing.
|
||||
* @property {boolean} [distanceScaleEnd=false] Ray Pointers only. If true, the dimensions of the end of the Pointer will scale linearly with distance.
|
||||
* @property {boolean} [scaleWithAvatar=false] Ray Pointers only. If true, the width of the Pointer's path will scale linearly with your avatar's scale.
|
||||
* @property {Pointers.RayPointerRenderState[]} [renderStates] Ray Pointers only. A list of different visual states to switch between.
|
||||
* @property {Pointers.DefaultRayPointerRenderState[]} [defaultRenderStates] Ray Pointers only. A list of different visual states to use if there is no intersection.
|
||||
* @property {Pointers.Trigger[]} [triggers] Ray Pointers only. A list of different triggers mechanisms that control this Pointer's click event generation.
|
||||
*/
|
||||
|
||||
/**jsdoc
|
||||
* A set of properties used to define the visual aspect of a Ray Pointer in the case that the Pointer is intersecting something.
|
||||
*
|
||||
* @typedef {Object} Pointers.RayPointerRenderState
|
||||
* @property {string} name The name of this render state, used by {@link Pointers.setRenderState} and {@link Pointers.editRenderState}
|
||||
* @property {OverlayProperties} [start] All of the properties you would normally pass to {@Overlays.addOverlay}, plus the type (as a <code>type</code> field).
|
||||
* An overlay to represent the beginning of the Ray Pointer, if desired.
|
||||
* @property {OverlayProperties} [path] All of the properties you would normally pass to {@Overlays.addOverlay}, plus the type (as a <code>type</code> field), which <b>must</b> be "line3d".
|
||||
* An overlay to represent the path of the Ray Pointer, if desired.
|
||||
* @property {OverlayProperties} [end] All of the properties you would normally pass to {@Overlays.addOverlay}, plus the type (as a <code>type</code> field).
|
||||
* An overlay to represent the end of the Ray Pointer, if desired.
|
||||
*/
|
||||
|
||||
/**jsdoc
|
||||
* A set of properties used to define the visual aspect of a Ray Pointer in the case that the Pointer is not intersecting something. Same as a {@link Pointers.RayPointerRenderState},
|
||||
* but with an additional distance field.
|
||||
*
|
||||
* @typedef {Object} Pointers.DefaultRayPointerRenderState
|
||||
* @augments Pointers.RayPointerRenderState
|
||||
* @property {number} distance The distance at which to render the end of this Ray Pointer, if one is defined.
|
||||
*/
|
||||
|
||||
/**jsdoc
|
||||
* A trigger mechanism for Ray Pointers.
|
||||
*
|
||||
* @typedef {Object} Pointers.Trigger
|
||||
* @property {Controller.Action} action This can be a built-in Controller action, like Controller.Standard.LTClick, or a function that evaluates to >= 1.0 when you want to trigger <code>button</code>.
|
||||
* @property {string} button Which button to trigger. "Primary", "Secondary", "Tertiary", and "Focus" are currently supported. Only "Primary" will trigger clicks on web surfaces. If "Focus" is triggered,
|
||||
* it will try to set the entity or overlay focus to the object at which the Pointer is aimed. Buttons besides the first three will still trigger events, but event.button will be "None".
|
||||
*/
|
||||
|
||||
/**jsdoc
|
||||
* Adds a new Pointer
|
||||
* @function Pointers.createPointer
|
||||
* @param {Picks.PickType} type A PickType that specifies the method of picking to use
|
||||
* @param {Pointers.PointerProperties} properties A PointerProperties object, containing all the properties for initializing this Pointer <b>and</b> the {@link Picks.PickProperties} for the Pick that
|
||||
* this Pointer will use to do its picking.
|
||||
* @returns {number} The ID of the created Pointer. Used for managing the Pointer. 0 if invalid.
|
||||
*
|
||||
* @example <caption>Create a left hand Ray Pointer that triggers events on left controller trigger click and changes color when it's intersecting something.</caption>
|
||||
*
|
||||
* var end = {
|
||||
* type: "sphere",
|
||||
* dimensions: {x:0.5, y:0.5, z:0.5},
|
||||
* solid: true,
|
||||
* color: {red:0, green:255, blue:0},
|
||||
* ignoreRayIntersection: true
|
||||
* };
|
||||
* var end2 = {
|
||||
* type: "sphere",
|
||||
* dimensions: {x:0.5, y:0.5, z:0.5},
|
||||
* solid: true,
|
||||
* color: {red:255, green:0, blue:0},
|
||||
* ignoreRayIntersection: true
|
||||
* };
|
||||
*
|
||||
* var renderStates = [ {name: "test", end: end} ];
|
||||
* var defaultRenderStates = [ {name: "test", distance: 10.0, end: end2} ];
|
||||
* var pointer = Pointers.createPointer(PickType.Ray, {
|
||||
* joint: "_CAMERA_RELATIVE_CONTROLLER_LEFTHAND",
|
||||
* filter: Picks.PICK_OVERLAYS | Picks.PICK_ENTITIES | Picks.PICK_INCLUDE_NONCOLLIDABLE,
|
||||
* renderStates: renderStates,
|
||||
* defaultRenderStates: defaultRenderStates,
|
||||
* distanceScaleEnd: true,
|
||||
* triggers: [ {action: Controller.Standard.LTClick, button: "Focus"}, {action: Controller.Standard.LTClick, button: "Primary"} ],
|
||||
* hover: true,
|
||||
* enabled: true
|
||||
* });
|
||||
* Pointers.setRenderState(pointer, "test");
|
||||
*/
|
||||
Q_INVOKABLE unsigned int createPointer(const PickQuery::PickType& type, const QVariant& properties);
|
||||
/**jsdoc
|
||||
* Enables a Pointer.
|
||||
* @function Pointers.enablePointer
|
||||
* @param {number} uid The ID of the Pointer, as returned by {@link Pointers.createPointer}.
|
||||
*/
|
||||
Q_INVOKABLE void enablePointer(unsigned int uid) const { DependencyManager::get<PointerManager>()->enablePointer(uid); }
|
||||
/**jsdoc
|
||||
* Disables a Pointer.
|
||||
* @function Pointers.disablePointer
|
||||
* @param {number} uid The ID of the Pointer, as returned by {@link Pointers.createPointer}.
|
||||
*/
|
||||
Q_INVOKABLE void disablePointer(unsigned int uid) const { DependencyManager::get<PointerManager>()->disablePointer(uid); }
|
||||
/**jsdoc
|
||||
* Removes a Pointer.
|
||||
* @function Pointers.removePointer
|
||||
* @param {number} uid The ID of the Pointer, as returned by {@link Pointers.createPointer}.
|
||||
*/
|
||||
Q_INVOKABLE void removePointer(unsigned int uid) const { DependencyManager::get<PointerManager>()->removePointer(uid); }
|
||||
/**jsdoc
|
||||
* Edit some visual aspect of a Pointer. Currently only supported for Ray Pointers.
|
||||
* @function Pointers.editRenderState
|
||||
* @param {number} uid The ID of the Pointer, as returned by {@link Pointers.createPointer}.
|
||||
* @param {string} renderState The name of the render state you want to edit.
|
||||
* @param {RenderState} properties The new properties for <code>renderState</code>. For Ray Pointers, a {@link Pointers.RayPointerRenderState}.
|
||||
*/
|
||||
Q_INVOKABLE void editRenderState(unsigned int uid, const QString& renderState, const QVariant& properties) const;
|
||||
/**jsdoc
|
||||
* Set the render state of a Pointer. For Ray Pointers, this means switching between their {@link Pointers.RayPointerRenderState}s, or "" to turn off rendering and hover/trigger events.
|
||||
* For Stylus Pointers, there are three built-in options: "events on" (render and send events, the default), "events off" (render but don't send events), and "disabled" (don't render, don't send events).
|
||||
* @function Pointers.setRenderState
|
||||
* @param {number} uid The ID of the Pointer, as returned by {@link Pointers.createPointer}.
|
||||
* @param {string} renderState The name of the render state to which you want to switch.
|
||||
*/
|
||||
Q_INVOKABLE void setRenderState(unsigned int uid, const QString& renderState) const { DependencyManager::get<PointerManager>()->setRenderState(uid, renderState.toStdString()); }
|
||||
|
||||
/**jsdoc
|
||||
* Get the most recent pick result from this Pointer. This will be updated as long as the Pointer is enabled, regardless of the render state.
|
||||
* @function Pointers.getPrevPickResult
|
||||
* @param {number} uid The ID of the Pointer, as returned by {@link Pointers.createPointer}.
|
||||
* @returns {PickResult} The most recent intersection result. This will be slightly different for different PickTypes. See {@link Picks.RayPickResult} and {@link Picks.StylusPickResult}.
|
||||
*/
|
||||
Q_INVOKABLE QVariantMap getPrevPickResult(unsigned int uid) const;
|
||||
|
||||
/**jsdoc
|
||||
* Sets whether or not to use precision picking.
|
||||
* @function Pointers.setPrecisionPicking
|
||||
* @param {number} uid The ID of the Pointer, as returned by {@link Pointers.createPointer}.
|
||||
* @param {boolean} precisionPicking Whether or not to use precision picking
|
||||
*/
|
||||
Q_INVOKABLE void setPrecisionPicking(unsigned int uid, bool precisionPicking) const { DependencyManager::get<PointerManager>()->setPrecisionPicking(uid, precisionPicking); }
|
||||
/**jsdoc
|
||||
* Sets the length of this Pointer. No effect on Stylus Pointers.
|
||||
* @function Pointers.setLength
|
||||
* @param {number} uid The ID of the Pointer, as returned by {@link Pointers.createPointer}.
|
||||
* @param {float} length The desired length of the Pointer.
|
||||
*/
|
||||
Q_INVOKABLE void setLength(unsigned int uid, float length) const { DependencyManager::get<PointerManager>()->setLength(uid, length); }
|
||||
/**jsdoc
|
||||
* Sets a list of Entity IDs, Overlay IDs, and/or Avatar IDs to ignore during intersection. Not used by Stylus Pointers.
|
||||
* @function Pointers.setIgnoreItems
|
||||
* @param {number} uid The ID of the Pointer, as returned by {@link Pointers.createPointer}.
|
||||
* @param {Uuid[]} ignoreItems A list of IDs to ignore.
|
||||
*/
|
||||
Q_INVOKABLE void setIgnoreItems(unsigned int uid, const QScriptValue& ignoreEntities) const;
|
||||
/**jsdoc
|
||||
* Sets a list of Entity IDs, Overlay IDs, and/or Avatar IDs to include during intersection, instead of intersecting with everything. Stylus
|
||||
* Pointers <b>only</b> intersect with objects in their include list.
|
||||
* @function Pointers.setIncludeItems
|
||||
* @param {number} uid The ID of the Pointer, as returned by {@link Pointers.createPointer}.
|
||||
* @param {Uuid[]} includeItems A list of IDs to include.
|
||||
*/
|
||||
Q_INVOKABLE void setIncludeItems(unsigned int uid, const QScriptValue& includeEntities) const;
|
||||
|
||||
/**jsdoc
|
||||
* Lock a Pointer onto a specific object (overlay, entity, or avatar). Optionally, provide an offset in object-space, otherwise the Pointer will lock on to the center of the object.
|
||||
* Not used by Stylus Pointers.
|
||||
* @function Pointers.setLockEndUUID
|
||||
* @param {number} uid The ID of the Pointer, as returned by {@link Pointers.createPointer}.
|
||||
* @param {QUuid} objectID The ID of the object to which to lock on.
|
||||
* @param {boolean} isOverlay False for entities or avatars, true for overlays
|
||||
* @param {Mat4} [offsetMat] The offset matrix to use if you do not want to lock on to the center of the object.
|
||||
*/
|
||||
Q_INVOKABLE void setLockEndUUID(unsigned int uid, const QUuid& objectID, bool isOverlay, const glm::mat4& offsetMat = glm::mat4()) const { DependencyManager::get<PointerManager>()->setLockEndUUID(uid, objectID, isOverlay, offsetMat); }
|
||||
|
||||
/**jsdoc
|
||||
* Check if a Pointer is associated with the left hand.
|
||||
* @function Pointers.isLeftHand
|
||||
* @param {number} uid The ID of the Pointer, as returned by {@link Pointers.createPointer}.
|
||||
* @returns {boolean} True if the Pointer is a Joint Ray Pointer with joint == "_CONTROLLER_LEFTHAND" or "_CAMERA_RELATIVE_CONTROLLER_LEFTHAND", or a Stylus Pointer with hand == 0
|
||||
*/
|
||||
Q_INVOKABLE bool isLeftHand(unsigned int uid) { return DependencyManager::get<PointerManager>()->isLeftHand(uid); }
|
||||
/**jsdoc
|
||||
* Check if a Pointer is associated with the right hand.
|
||||
* @function Pointers.isRightHand
|
||||
* @param {number} uid The ID of the Pointer, as returned by {@link Pointers.createPointer}.
|
||||
* @returns {boolean} True if the Pointer is a Joint Ray Pointer with joint == "_CONTROLLER_RIGHTHAND" or "_CAMERA_RELATIVE_CONTROLLER_RIGHTHAND", or a Stylus Pointer with hand == 1
|
||||
*/
|
||||
Q_INVOKABLE bool isRightHand(unsigned int uid) { return DependencyManager::get<PointerManager>()->isRightHand(uid); }
|
||||
/**jsdoc
|
||||
* Check if a Pointer is associated with the system mouse.
|
||||
* @function Pointers.isMouse
|
||||
* @param {number} uid The ID of the Pointer, as returned by {@link Pointers.createPointer}.
|
||||
* @returns {boolean} True if the Pointer is a Mouse Ray Pointer, false otherwise.
|
||||
*/
|
||||
Q_INVOKABLE bool isMouse(unsigned int uid) { return DependencyManager::get<PointerManager>()->isMouse(uid); }
|
||||
|
||||
signals:
|
||||
void triggerBegin(const QUuid& id, const PointerEvent& pointerEvent);
|
||||
void triggerContinue(const QUuid& id, const PointerEvent& pointerEvent);
|
||||
void triggerEnd(const QUuid& id, const PointerEvent& pointerEvent);
|
||||
void hoverBegin(const QUuid& id, const PointerEvent& pointerEvent);
|
||||
void hoverContinue(const QUuid& id, const PointerEvent& pointerEvent);
|
||||
void hoverEnd(const QUuid& id, const PointerEvent& pointerEvent);
|
||||
|
||||
};
|
||||
|
||||
#endif // hifi_PointerScriptingInterface_h
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
#define hifi_RayPick_h
|
||||
|
||||
#include <RegisteredMetaTypes.h>
|
||||
#include <pointers/Pick.h>
|
||||
#include <Pick.h>
|
||||
|
||||
class EntityItemID;
|
||||
class OverlayID;
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
#include <QVariant>
|
||||
#include "GLMHelpers.h"
|
||||
|
||||
#include <pointers/PickManager.h>
|
||||
#include <PickManager.h>
|
||||
|
||||
#include "StaticRayPick.h"
|
||||
#include "JointRayPick.h"
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
#ifndef hifi_StylusPick_h
|
||||
#define hifi_StylusPick_h
|
||||
|
||||
#include "pointers/Pick.h"
|
||||
#include <Pick.h>
|
||||
#include "RegisteredMetaTypes.h"
|
||||
|
||||
class StylusPickResult : public PickResult {
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
|
||||
#include <DependencyManager.h>
|
||||
#include "PickScriptingInterface.h"
|
||||
#include <pointers/PickManager.h>
|
||||
#include <PickManager.h>
|
||||
|
||||
// TODO: make these configurable per pointer
|
||||
static const float WEB_STYLUS_LENGTH = 0.2f;
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
#ifndef hifi_StylusPointer_h
|
||||
#define hifi_StylusPointer_h
|
||||
|
||||
#include <pointers/Pointer.h>
|
||||
#include <Pointer.h>
|
||||
#include <shared/Bilateral.h>
|
||||
#include <RegisteredMetaTypes.h>
|
||||
|
||||
|
|
|
@ -16,13 +16,11 @@
|
|||
#include "Application.h"
|
||||
|
||||
|
||||
const float DEFAULT_LINE_WIDTH = 1.0f;
|
||||
const bool DEFAULT_IS_SOLID = false;
|
||||
const bool DEFAULT_IS_DASHED_LINE = false;
|
||||
|
||||
Base3DOverlay::Base3DOverlay() :
|
||||
SpatiallyNestable(NestableType::Overlay, QUuid::createUuid()),
|
||||
_lineWidth(DEFAULT_LINE_WIDTH),
|
||||
_isSolid(DEFAULT_IS_SOLID),
|
||||
_isDashedLine(DEFAULT_IS_DASHED_LINE),
|
||||
_ignoreRayIntersection(false),
|
||||
|
@ -34,7 +32,6 @@ Base3DOverlay::Base3DOverlay() :
|
|||
Base3DOverlay::Base3DOverlay(const Base3DOverlay* base3DOverlay) :
|
||||
Overlay(base3DOverlay),
|
||||
SpatiallyNestable(NestableType::Overlay, QUuid::createUuid()),
|
||||
_lineWidth(base3DOverlay->_lineWidth),
|
||||
_isSolid(base3DOverlay->_isSolid),
|
||||
_isDashedLine(base3DOverlay->_isDashedLine),
|
||||
_ignoreRayIntersection(base3DOverlay->_ignoreRayIntersection),
|
||||
|
@ -153,12 +150,6 @@ void Base3DOverlay::setProperties(const QVariantMap& originalProperties) {
|
|||
setLocalOrientation(quatFromVariant(properties["orientation"]));
|
||||
needRenderItemUpdate = true;
|
||||
}
|
||||
|
||||
if (properties["lineWidth"].isValid()) {
|
||||
setLineWidth(properties["lineWidth"].toFloat());
|
||||
needRenderItemUpdate = true;
|
||||
}
|
||||
|
||||
if (properties["isSolid"].isValid()) {
|
||||
setIsSolid(properties["isSolid"].toBool());
|
||||
}
|
||||
|
@ -225,9 +216,6 @@ QVariant Base3DOverlay::getProperty(const QString& property) {
|
|||
if (property == "localRotation" || property == "localOrientation") {
|
||||
return quatToVariant(getLocalOrientation());
|
||||
}
|
||||
if (property == "lineWidth") {
|
||||
return _lineWidth;
|
||||
}
|
||||
if (property == "isSolid" || property == "isFilled" || property == "solid" || property == "filed") {
|
||||
return _isSolid;
|
||||
}
|
||||
|
|
|
@ -38,7 +38,6 @@ public:
|
|||
// TODO: consider implementing registration points in this class
|
||||
glm::vec3 getCenter() const { return getPosition(); }
|
||||
|
||||
float getLineWidth() const { return _lineWidth; }
|
||||
bool getIsSolid() const { return _isSolid; }
|
||||
bool getIsDashedLine() const { return _isDashedLine; }
|
||||
bool getIsSolidLine() const { return !_isDashedLine; }
|
||||
|
@ -47,7 +46,6 @@ public:
|
|||
bool getDrawHUDLayer() const { return _drawHUDLayer; }
|
||||
bool getIsGrabbable() const { return _isGrabbable; }
|
||||
|
||||
void setLineWidth(float lineWidth) { _lineWidth = lineWidth; }
|
||||
void setIsSolid(bool isSolid) { _isSolid = isSolid; }
|
||||
void setIsDashedLine(bool isDashedLine) { _isDashedLine = isDashedLine; }
|
||||
void setIgnoreRayIntersection(bool value) { _ignoreRayIntersection = value; }
|
||||
|
@ -85,7 +83,6 @@ protected:
|
|||
void setRenderVisible(bool visible);
|
||||
const Transform& getRenderTransform() const { return _renderTransform; }
|
||||
|
||||
float _lineWidth;
|
||||
bool _isSolid;
|
||||
bool _isDashedLine;
|
||||
bool _ignoreRayIntersection;
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
#include <QtNetwork/QNetworkReply>
|
||||
#include <commerce/Ledger.h>
|
||||
|
||||
#include <pointers/PointerManager.h>
|
||||
#include <PointerManager.h>
|
||||
|
||||
#ifndef MIN
|
||||
#define MIN(a,b) ((a) < (b) ? (a) : (b))
|
||||
|
|
|
@ -33,8 +33,8 @@ Line3DOverlay::Line3DOverlay(const Line3DOverlay* line3DOverlay) :
|
|||
_length = line3DOverlay->getLength();
|
||||
_endParentID = line3DOverlay->getEndParentID();
|
||||
_endParentJointIndex = line3DOverlay->getEndJointIndex();
|
||||
_lineWidth = line3DOverlay->getLineWidth();
|
||||
_glow = line3DOverlay->getGlow();
|
||||
_glowWidth = line3DOverlay->getGlowWidth();
|
||||
}
|
||||
|
||||
Line3DOverlay::~Line3DOverlay() {
|
||||
|
@ -145,7 +145,7 @@ void Line3DOverlay::render(RenderArgs* args) {
|
|||
geometryCache->renderDashedLine(*batch, start, end, colorv4, _geometryCacheID);
|
||||
} else {
|
||||
// renderGlowLine handles both glow = 0 and glow > 0 cases
|
||||
geometryCache->renderGlowLine(*batch, start, end, colorv4, _glow, _glowWidth, _geometryCacheID);
|
||||
geometryCache->renderGlowLine(*batch, start, end, colorv4, _glow, _lineWidth, _geometryCacheID);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -239,11 +239,10 @@ void Line3DOverlay::setProperties(const QVariantMap& originalProperties) {
|
|||
}
|
||||
}
|
||||
|
||||
auto glowWidth = properties["glowWidth"];
|
||||
if (glowWidth.isValid()) {
|
||||
setGlowWidth(glowWidth.toFloat());
|
||||
auto lineWidth = properties["lineWidth"];
|
||||
if (lineWidth.isValid()) {
|
||||
setLineWidth(lineWidth.toFloat());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
QVariant Line3DOverlay::getProperty(const QString& property) {
|
||||
|
@ -262,6 +261,9 @@ QVariant Line3DOverlay::getProperty(const QString& property) {
|
|||
if (property == "length") {
|
||||
return QVariant(getLength());
|
||||
}
|
||||
if (property == "lineWidth") {
|
||||
return _lineWidth;
|
||||
}
|
||||
|
||||
return Base3DOverlay::getProperty(property);
|
||||
}
|
||||
|
|
|
@ -31,8 +31,8 @@ public:
|
|||
// getters
|
||||
glm::vec3 getStart() const;
|
||||
glm::vec3 getEnd() const;
|
||||
const float& getLineWidth() const { return _lineWidth; }
|
||||
const float& getGlow() const { return _glow; }
|
||||
const float& getGlowWidth() const { return _glowWidth; }
|
||||
|
||||
// setters
|
||||
void setStart(const glm::vec3& start);
|
||||
|
@ -41,8 +41,8 @@ public:
|
|||
void setLocalStart(const glm::vec3& localStart) { setLocalPosition(localStart); }
|
||||
void setLocalEnd(const glm::vec3& localEnd);
|
||||
|
||||
void setLineWidth(const float& lineWidth) { _lineWidth = lineWidth; }
|
||||
void setGlow(const float& glow) { _glow = glow; }
|
||||
void setGlowWidth(const float& glowWidth) { _glowWidth = glowWidth; }
|
||||
|
||||
void setProperties(const QVariantMap& properties) override;
|
||||
QVariant getProperty(const QString& property) override;
|
||||
|
@ -70,8 +70,9 @@ private:
|
|||
glm::vec3 _direction; // in parent frame
|
||||
float _length { 1.0 }; // in parent frame
|
||||
|
||||
const float DEFAULT_LINE_WIDTH = 0.02f;
|
||||
float _lineWidth { DEFAULT_LINE_WIDTH };
|
||||
float _glow { 0.0 };
|
||||
float _glowWidth { 0.0 };
|
||||
int _geometryCacheID;
|
||||
};
|
||||
|
||||
|
|
|
@ -37,7 +37,7 @@
|
|||
#include "Web3DOverlay.h"
|
||||
#include <QtQuick/QQuickWindow>
|
||||
|
||||
#include <pointers/PointerManager.h>
|
||||
#include <PointerManager.h>
|
||||
|
||||
Q_LOGGING_CATEGORY(trace_render_overlays, "trace.render.overlays")
|
||||
|
||||
|
|
|
@ -179,6 +179,11 @@ void Rig::restoreRoleAnimation(const QString& role) {
|
|||
} else {
|
||||
qCWarning(animation) << "Rig::restoreRoleAnimation could not find role " << role;
|
||||
}
|
||||
|
||||
auto statesIter = _roleAnimStates.find(role);
|
||||
if (statesIter != _roleAnimStates.end()) {
|
||||
_roleAnimStates.erase(statesIter);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
qCWarning(animation) << "Rig::overrideRoleAnimation avatar not ready yet";
|
||||
|
|
|
@ -1579,7 +1579,7 @@ float Avatar::getEyeHeight() const {
|
|||
|
||||
if (QThread::currentThread() != thread()) {
|
||||
float result = DEFAULT_AVATAR_EYE_HEIGHT;
|
||||
BLOCKING_INVOKE_METHOD(const_cast<Avatar*>(this), "getHeight", Q_RETURN_ARG(float, result));
|
||||
BLOCKING_INVOKE_METHOD(const_cast<Avatar*>(this), "getEyeHeight", Q_RETURN_ARG(float, result));
|
||||
return result;
|
||||
}
|
||||
|
||||
|
|
|
@ -37,7 +37,7 @@
|
|||
|
||||
#include "RenderableWebEntityItem.h"
|
||||
|
||||
#include <pointers/PointerManager.h>
|
||||
#include <PointerManager.h>
|
||||
|
||||
size_t std::hash<EntityItemID>::operator()(const EntityItemID& id) const { return qHash(id); }
|
||||
std::function<bool()> EntityTreeRenderer::_entitiesShouldFadeFunction;
|
||||
|
|
|
@ -40,22 +40,26 @@ void ZoneEntityRenderer::onRemoveFromSceneTyped(const TypedEntityPointer& entity
|
|||
if (_stage) {
|
||||
if (!LightStage::isIndexInvalid(_sunIndex)) {
|
||||
_stage->removeLight(_sunIndex);
|
||||
_sunIndex = INVALID_INDEX;
|
||||
_shadowIndex = INVALID_INDEX;
|
||||
}
|
||||
if (!LightStage::isIndexInvalid(_ambientIndex)) {
|
||||
_stage->removeLight(_ambientIndex);
|
||||
|
||||
_ambientIndex = INVALID_INDEX;
|
||||
}
|
||||
}
|
||||
|
||||
if (_backgroundStage) {
|
||||
if (!BackgroundStage::isIndexInvalid(_backgroundIndex)) {
|
||||
_backgroundStage->removeBackground(_backgroundIndex);
|
||||
_backgroundIndex = INVALID_INDEX;
|
||||
}
|
||||
}
|
||||
|
||||
if (_hazeStage) {
|
||||
if (!HazeStage::isIndexInvalid(_hazeIndex)) {
|
||||
_hazeStage->removeHaze(_hazeIndex);
|
||||
_hazeIndex = INVALID_INDEX;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -297,7 +297,7 @@ void ShapeEntityItem::computeShapeInfo(ShapeInfo& info) {
|
|||
const float MIN_RELATIVE_SPHERICAL_ERROR = 0.001f;
|
||||
if (diameter > MIN_DIAMETER
|
||||
&& fabsf(diameter - entityDimensions.z) / diameter < MIN_RELATIVE_SPHERICAL_ERROR) {
|
||||
_collisionShapeType = SHAPE_TYPE_SPHERE;
|
||||
_collisionShapeType = SHAPE_TYPE_CYLINDER_Y;
|
||||
} else if (hullShapeCalculator) {
|
||||
hullShapeCalculator(this, info);
|
||||
_collisionShapeType = SHAPE_TYPE_SIMPLE_HULL;
|
||||
|
|
19
libraries/gpu/src/gpu/DrawColor.slf
Normal file
19
libraries/gpu/src/gpu/DrawColor.slf
Normal file
|
@ -0,0 +1,19 @@
|
|||
<@include gpu/Config.slh@>
|
||||
<$VERSION_HEADER$>
|
||||
// Generated on <$_SCRIBE_DATE$>
|
||||
//
|
||||
// Draw with color uniform
|
||||
//
|
||||
// Created by Olivier Prat on 25/10/2017
|
||||
// Copyright 2017 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
uniform vec4 color;
|
||||
|
||||
out vec4 outFragColor;
|
||||
|
||||
void main(void) {
|
||||
outFragColor = color;
|
||||
}
|
|
@ -22,6 +22,7 @@
|
|||
const char DrawNada_frag[] = "void main(void) {}"; // DrawNada is really simple...
|
||||
|
||||
#include "DrawWhite_frag.h"
|
||||
#include "DrawColor_frag.h"
|
||||
#include "DrawTexture_frag.h"
|
||||
#include "DrawTextureMirroredX_frag.h"
|
||||
#include "DrawTextureOpaque_frag.h"
|
||||
|
@ -37,6 +38,7 @@ ShaderPointer StandardShaderLib::_drawVertexPositionVS;
|
|||
ShaderPointer StandardShaderLib::_drawTransformVertexPositionVS;
|
||||
ShaderPointer StandardShaderLib::_drawNadaPS;
|
||||
ShaderPointer StandardShaderLib::_drawWhitePS;
|
||||
ShaderPointer StandardShaderLib::_drawColorPS;
|
||||
ShaderPointer StandardShaderLib::_drawTexturePS;
|
||||
ShaderPointer StandardShaderLib::_drawTextureMirroredXPS;
|
||||
ShaderPointer StandardShaderLib::_drawTextureOpaquePS;
|
||||
|
@ -125,6 +127,13 @@ ShaderPointer StandardShaderLib::getDrawWhitePS() {
|
|||
return _drawWhitePS;
|
||||
}
|
||||
|
||||
ShaderPointer StandardShaderLib::getDrawColorPS() {
|
||||
if (!_drawColorPS) {
|
||||
_drawColorPS = gpu::Shader::createPixel(std::string(DrawColor_frag));
|
||||
}
|
||||
return _drawColorPS;
|
||||
}
|
||||
|
||||
ShaderPointer StandardShaderLib::getDrawTexturePS() {
|
||||
if (!_drawTexturePS) {
|
||||
_drawTexturePS = gpu::Shader::createPixel(std::string(DrawTexture_frag));
|
||||
|
|
|
@ -46,6 +46,7 @@ public:
|
|||
static ShaderPointer getDrawNadaPS();
|
||||
|
||||
static ShaderPointer getDrawWhitePS();
|
||||
static ShaderPointer getDrawColorPS();
|
||||
static ShaderPointer getDrawTexturePS();
|
||||
static ShaderPointer getDrawTextureMirroredXPS();
|
||||
static ShaderPointer getDrawTextureOpaquePS();
|
||||
|
@ -67,6 +68,7 @@ protected:
|
|||
|
||||
static ShaderPointer _drawNadaPS;
|
||||
static ShaderPointer _drawWhitePS;
|
||||
static ShaderPointer _drawColorPS;
|
||||
static ShaderPointer _drawTexturePS;
|
||||
static ShaderPointer _drawTextureMirroredXPS;
|
||||
static ShaderPointer _drawTextureOpaquePS;
|
||||
|
|
|
@ -27,7 +27,6 @@
|
|||
#include <NumericalConstants.h>
|
||||
#include <SettingHandle.h>
|
||||
#include <SharedUtil.h>
|
||||
#include <StatTracker.h>
|
||||
#include <UUID.h>
|
||||
|
||||
#include "AccountManager.h"
|
||||
|
@ -1110,7 +1109,6 @@ void LimitedNodeList::setLocalSocket(const HifiSockAddr& sockAddr) {
|
|||
qCInfo(networking) << "Local socket is" << sockAddr;
|
||||
} else {
|
||||
qCInfo(networking) << "Local socket has changed from" << _localSockAddr << "to" << sockAddr;
|
||||
DependencyManager::get<StatTracker>()->incrementStat(LOCAL_SOCKET_CHANGE_STAT);
|
||||
}
|
||||
|
||||
_localSockAddr = sockAddr;
|
||||
|
|
|
@ -66,8 +66,6 @@ const QHostAddress DEFAULT_ASSIGNMENT_CLIENT_MONITOR_HOSTNAME = QHostAddress::Lo
|
|||
|
||||
const QString USERNAME_UUID_REPLACEMENT_STATS_KEY = "$username";
|
||||
|
||||
const QString LOCAL_SOCKET_CHANGE_STAT = "LocalSocketChanges";
|
||||
|
||||
typedef std::pair<QUuid, SharedNodePointer> UUIDNodePair;
|
||||
typedef tbb::concurrent_unordered_map<QUuid, SharedNodePointer, UUIDHasher> NodeHash;
|
||||
|
||||
|
|
|
@ -134,6 +134,16 @@ class PickQuery : protected ReadWriteLockable {
|
|||
public:
|
||||
PickQuery(const PickFilter& filter, const float maxDistance, const bool enabled);
|
||||
|
||||
/**jsdoc
|
||||
* @namespace
|
||||
* @augments Picks
|
||||
*
|
||||
* Enum for different types of Picks and Pointers.
|
||||
*
|
||||
* @typedef {enum} Picks.PickType
|
||||
* @property {number} Ray Ray Picks intersect a ray with the nearest object in front of them, along a given direction.
|
||||
* @property {number} Stylus Stylus Picks provide "tapping" functionality on/into flat surfaces.
|
||||
*/
|
||||
enum PickType {
|
||||
Ray = 0,
|
||||
Stylus,
|
27
libraries/render-utils/src/BloomApply.slf
Normal file
27
libraries/render-utils/src/BloomApply.slf
Normal file
|
@ -0,0 +1,27 @@
|
|||
<@include gpu/Config.slh@>
|
||||
<$VERSION_HEADER$>
|
||||
// BloomApply.slf
|
||||
// Mix the three gaussian blur textures.
|
||||
//
|
||||
// Created by Olivier Prat on 10/09/2017
|
||||
// Copyright 2017 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
uniform sampler2D blurMap0;
|
||||
uniform sampler2D blurMap1;
|
||||
uniform sampler2D blurMap2;
|
||||
uniform float intensity;
|
||||
|
||||
in vec2 varTexCoord0;
|
||||
out vec4 outFragColor;
|
||||
|
||||
void main(void) {
|
||||
vec4 blur0 = texture(blurMap0, varTexCoord0);
|
||||
vec4 blur1 = texture(blurMap1, varTexCoord0);
|
||||
vec4 blur2 = texture(blurMap2, varTexCoord0);
|
||||
|
||||
outFragColor = vec4((blur0.rgb+blur1.rgb+blur2.rgb)*intensity, 1.0f);
|
||||
}
|
359
libraries/render-utils/src/BloomEffect.cpp
Normal file
359
libraries/render-utils/src/BloomEffect.cpp
Normal file
|
@ -0,0 +1,359 @@
|
|||
//
|
||||
// BloomEffect.cpp
|
||||
// render-utils/src/
|
||||
//
|
||||
// Created by Olivier Prat on 09/25/17.
|
||||
// Copyright 2017 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
#include "BloomEffect.h"
|
||||
|
||||
#include "gpu/Context.h"
|
||||
#include "gpu/StandardShaderLib.h"
|
||||
|
||||
#include <render/BlurTask.h>
|
||||
#include <render/ResampleTask.h>
|
||||
|
||||
#include "BloomThreshold_frag.h"
|
||||
#include "BloomApply_frag.h"
|
||||
|
||||
#define BLOOM_BLUR_LEVEL_COUNT 3
|
||||
|
||||
BloomThreshold::BloomThreshold(unsigned int downsamplingFactor) :
|
||||
_downsamplingFactor(downsamplingFactor) {
|
||||
assert(downsamplingFactor > 0);
|
||||
}
|
||||
|
||||
void BloomThreshold::configure(const Config& config) {
|
||||
_threshold = config.threshold;
|
||||
}
|
||||
|
||||
void BloomThreshold::run(const render::RenderContextPointer& renderContext, const Inputs& inputs, Outputs& outputs) {
|
||||
assert(renderContext->args);
|
||||
assert(renderContext->args->hasViewFrustum());
|
||||
|
||||
RenderArgs* args = renderContext->args;
|
||||
|
||||
const auto frameTransform = inputs.get0();
|
||||
const auto inputFrameBuffer = inputs.get1();
|
||||
|
||||
assert(inputFrameBuffer->hasColor());
|
||||
|
||||
auto inputBuffer = inputFrameBuffer->getRenderBuffer(0);
|
||||
auto bufferSize = gpu::Vec2u(inputBuffer->getDimensions());
|
||||
|
||||
// Downsample resolution
|
||||
bufferSize.x /= _downsamplingFactor;
|
||||
bufferSize.y /= _downsamplingFactor;
|
||||
|
||||
if (!_outputBuffer || _outputBuffer->getSize() != bufferSize) {
|
||||
auto colorTexture = gpu::TexturePointer(gpu::Texture::createRenderBuffer(inputBuffer->getTexelFormat(), bufferSize.x, bufferSize.y,
|
||||
gpu::Texture::SINGLE_MIP, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_LINEAR_MIP_POINT)));
|
||||
|
||||
_outputBuffer = gpu::FramebufferPointer(gpu::Framebuffer::create("BloomThreshold"));
|
||||
_outputBuffer->setRenderBuffer(0, colorTexture);
|
||||
}
|
||||
|
||||
static const int COLOR_MAP_SLOT = 0;
|
||||
static const int THRESHOLD_SLOT = 1;
|
||||
|
||||
if (!_pipeline) {
|
||||
auto vs = gpu::StandardShaderLib::getDrawTransformUnitQuadVS();
|
||||
auto ps = gpu::Shader::createPixel(std::string(BloomThreshold_frag));
|
||||
gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps);
|
||||
|
||||
gpu::Shader::BindingSet slotBindings;
|
||||
slotBindings.insert(gpu::Shader::Binding("colorMap", COLOR_MAP_SLOT));
|
||||
slotBindings.insert(gpu::Shader::Binding("threshold", THRESHOLD_SLOT));
|
||||
gpu::Shader::makeProgram(*program, slotBindings);
|
||||
|
||||
gpu::StatePointer state = gpu::StatePointer(new gpu::State());
|
||||
_pipeline = gpu::Pipeline::create(program, state);
|
||||
}
|
||||
|
||||
glm::ivec4 viewport{ 0, 0, bufferSize.x, bufferSize.y };
|
||||
|
||||
gpu::doInBatch(args->_context, [&](gpu::Batch& batch) {
|
||||
batch.enableStereo(false);
|
||||
|
||||
batch.setViewportTransform(viewport);
|
||||
batch.setProjectionTransform(glm::mat4());
|
||||
batch.resetViewTransform();
|
||||
batch.setModelTransform(gpu::Framebuffer::evalSubregionTexcoordTransform(bufferSize, viewport));
|
||||
batch.setPipeline(_pipeline);
|
||||
|
||||
batch.setFramebuffer(_outputBuffer);
|
||||
batch.setResourceTexture(COLOR_MAP_SLOT, inputBuffer);
|
||||
batch._glUniform1f(THRESHOLD_SLOT, _threshold);
|
||||
batch.draw(gpu::TRIANGLE_STRIP, 4);
|
||||
});
|
||||
|
||||
outputs = _outputBuffer;
|
||||
}
|
||||
|
||||
BloomApply::BloomApply() {
|
||||
|
||||
}
|
||||
|
||||
void BloomApply::configure(const Config& config) {
|
||||
_intensity = config.intensity;
|
||||
}
|
||||
|
||||
void BloomApply::run(const render::RenderContextPointer& renderContext, const Inputs& inputs) {
|
||||
assert(renderContext->args);
|
||||
assert(renderContext->args->hasViewFrustum());
|
||||
RenderArgs* args = renderContext->args;
|
||||
|
||||
static auto BLUR0_SLOT = 0;
|
||||
static auto BLUR1_SLOT = 1;
|
||||
static auto BLUR2_SLOT = 2;
|
||||
static auto INTENSITY_SLOT = 3;
|
||||
|
||||
if (!_pipeline) {
|
||||
auto vs = gpu::StandardShaderLib::getDrawTransformUnitQuadVS();
|
||||
auto ps = gpu::Shader::createPixel(std::string(BloomApply_frag));
|
||||
gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps);
|
||||
|
||||
gpu::Shader::BindingSet slotBindings;
|
||||
slotBindings.insert(gpu::Shader::Binding("blurMap0", BLUR0_SLOT));
|
||||
slotBindings.insert(gpu::Shader::Binding("blurMap1", BLUR1_SLOT));
|
||||
slotBindings.insert(gpu::Shader::Binding("blurMap2", BLUR2_SLOT));
|
||||
slotBindings.insert(gpu::Shader::Binding("intensity", INTENSITY_SLOT));
|
||||
gpu::Shader::makeProgram(*program, slotBindings);
|
||||
|
||||
gpu::StatePointer state = gpu::StatePointer(new gpu::State());
|
||||
state->setDepthTest(gpu::State::DepthTest(false, false));
|
||||
_pipeline = gpu::Pipeline::create(program, state);
|
||||
}
|
||||
|
||||
const auto frameBuffer = inputs.get0();
|
||||
const auto framebufferSize = frameBuffer->getSize();
|
||||
const auto blur0FB = inputs.get1();
|
||||
const auto blur1FB = inputs.get2();
|
||||
const auto blur2FB = inputs.get3();
|
||||
const glm::ivec4 viewport{ 0, 0, framebufferSize.x, framebufferSize.y };
|
||||
|
||||
gpu::doInBatch(args->_context, [&](gpu::Batch& batch) {
|
||||
batch.enableStereo(false);
|
||||
|
||||
batch.setFramebuffer(frameBuffer);
|
||||
|
||||
batch.setViewportTransform(viewport);
|
||||
batch.setProjectionTransform(glm::mat4());
|
||||
batch.resetViewTransform();
|
||||
batch.setPipeline(_pipeline);
|
||||
|
||||
batch.setModelTransform(gpu::Framebuffer::evalSubregionTexcoordTransform(framebufferSize, viewport));
|
||||
batch.setResourceTexture(BLUR0_SLOT, blur0FB->getRenderBuffer(0));
|
||||
batch.setResourceTexture(BLUR1_SLOT, blur1FB->getRenderBuffer(0));
|
||||
batch.setResourceTexture(BLUR2_SLOT, blur2FB->getRenderBuffer(0));
|
||||
batch._glUniform1f(INTENSITY_SLOT, _intensity / 3.0f);
|
||||
batch.draw(gpu::TRIANGLE_STRIP, 4);
|
||||
});
|
||||
}
|
||||
|
||||
void BloomDraw::run(const render::RenderContextPointer& renderContext, const Inputs& inputs) {
|
||||
assert(renderContext->args);
|
||||
assert(renderContext->args->hasViewFrustum());
|
||||
RenderArgs* args = renderContext->args;
|
||||
|
||||
const auto frameBuffer = inputs.get0();
|
||||
const auto bloomFrameBuffer = inputs.get1();
|
||||
|
||||
if (frameBuffer && bloomFrameBuffer) {
|
||||
const auto framebufferSize = frameBuffer->getSize();
|
||||
|
||||
if (!_pipeline) {
|
||||
auto vs = gpu::StandardShaderLib::getDrawTransformUnitQuadVS();
|
||||
auto ps = gpu::StandardShaderLib::getDrawTextureOpaquePS();
|
||||
gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps);
|
||||
|
||||
gpu::Shader::BindingSet slotBindings;
|
||||
gpu::Shader::makeProgram(*program, slotBindings);
|
||||
|
||||
gpu::StatePointer state = gpu::StatePointer(new gpu::State());
|
||||
state->setDepthTest(gpu::State::DepthTest(false, false));
|
||||
state->setBlendFunction(true, gpu::State::ONE, gpu::State::BLEND_OP_ADD, gpu::State::ONE,
|
||||
gpu::State::ZERO, gpu::State::BLEND_OP_ADD, gpu::State::ONE);
|
||||
_pipeline = gpu::Pipeline::create(program, state);
|
||||
}
|
||||
|
||||
gpu::doInBatch(args->_context, [&](gpu::Batch& batch) {
|
||||
batch.enableStereo(false);
|
||||
|
||||
batch.setFramebuffer(frameBuffer);
|
||||
|
||||
batch.setViewportTransform(args->_viewport);
|
||||
batch.setProjectionTransform(glm::mat4());
|
||||
batch.resetViewTransform();
|
||||
batch.setPipeline(_pipeline);
|
||||
|
||||
batch.setModelTransform(gpu::Framebuffer::evalSubregionTexcoordTransform(framebufferSize, args->_viewport));
|
||||
batch.setResourceTexture(0, bloomFrameBuffer->getRenderBuffer(0));
|
||||
batch.draw(gpu::TRIANGLE_STRIP, 4);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
DebugBloom::DebugBloom() {
|
||||
}
|
||||
|
||||
void DebugBloom::configure(const Config& config) {
|
||||
_mode = static_cast<DebugBloomConfig::Mode>(config.mode);
|
||||
assert(_mode < DebugBloomConfig::MODE_COUNT);
|
||||
}
|
||||
|
||||
void DebugBloom::run(const render::RenderContextPointer& renderContext, const Inputs& inputs) {
|
||||
assert(renderContext->args);
|
||||
assert(renderContext->args->hasViewFrustum());
|
||||
RenderArgs* args = renderContext->args;
|
||||
|
||||
const auto frameBuffer = inputs.get0();
|
||||
const auto combinedBlurBuffer = inputs.get4();
|
||||
const auto framebufferSize = frameBuffer->getSize();
|
||||
const auto level0FB = inputs.get1();
|
||||
const auto level1FB = inputs.get2();
|
||||
const auto level2FB = inputs.get3();
|
||||
const gpu::TexturePointer levelTextures[BLOOM_BLUR_LEVEL_COUNT] = {
|
||||
level0FB->getRenderBuffer(0),
|
||||
level1FB->getRenderBuffer(0),
|
||||
level2FB->getRenderBuffer(0)
|
||||
};
|
||||
|
||||
static auto TEXCOORD_RECT_SLOT = 1;
|
||||
|
||||
if (!_pipeline) {
|
||||
auto vs = gpu::StandardShaderLib::getDrawTexcoordRectTransformUnitQuadVS();
|
||||
auto ps = gpu::StandardShaderLib::getDrawTextureOpaquePS();
|
||||
gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps);
|
||||
|
||||
gpu::Shader::BindingSet slotBindings;
|
||||
slotBindings.insert(gpu::Shader::Binding(std::string("texcoordRect"), TEXCOORD_RECT_SLOT));
|
||||
gpu::Shader::makeProgram(*program, slotBindings);
|
||||
|
||||
gpu::StatePointer state = gpu::StatePointer(new gpu::State());
|
||||
state->setDepthTest(gpu::State::DepthTest(false));
|
||||
_pipeline = gpu::Pipeline::create(program, state);
|
||||
}
|
||||
|
||||
gpu::doInBatch(args->_context, [&](gpu::Batch& batch) {
|
||||
batch.enableStereo(false);
|
||||
|
||||
batch.setFramebuffer(frameBuffer);
|
||||
|
||||
batch.setViewportTransform(args->_viewport);
|
||||
batch.setProjectionTransform(glm::mat4());
|
||||
batch.resetViewTransform();
|
||||
batch.setPipeline(_pipeline);
|
||||
|
||||
Transform modelTransform;
|
||||
if (_mode == DebugBloomConfig::MODE_ALL_LEVELS) {
|
||||
batch._glUniform4f(TEXCOORD_RECT_SLOT, 0.0f, 0.0f, 1.f, 1.f);
|
||||
|
||||
modelTransform = gpu::Framebuffer::evalSubregionTexcoordTransform(framebufferSize, args->_viewport / 2);
|
||||
modelTransform.postTranslate(glm::vec3(-1.0f, 1.0f, 0.0f));
|
||||
batch.setModelTransform(modelTransform);
|
||||
batch.setResourceTexture(0, levelTextures[0]);
|
||||
batch.draw(gpu::TRIANGLE_STRIP, 4);
|
||||
|
||||
modelTransform.postTranslate(glm::vec3(2.0f, 0.0f, 0.0f));
|
||||
batch.setModelTransform(modelTransform);
|
||||
batch.setResourceTexture(0, levelTextures[1]);
|
||||
batch.draw(gpu::TRIANGLE_STRIP, 4);
|
||||
|
||||
modelTransform.postTranslate(glm::vec3(-2.0f, -2.0f, 0.0f));
|
||||
batch.setModelTransform(modelTransform);
|
||||
batch.setResourceTexture(0, levelTextures[2]);
|
||||
batch.draw(gpu::TRIANGLE_STRIP, 4);
|
||||
|
||||
modelTransform.postTranslate(glm::vec3(2.0f, 0.0f, 0.0f));
|
||||
batch.setModelTransform(modelTransform);
|
||||
batch.setResourceTexture(0, combinedBlurBuffer->getRenderBuffer(0));
|
||||
batch.draw(gpu::TRIANGLE_STRIP, 4);
|
||||
} else {
|
||||
auto viewport = args->_viewport;
|
||||
auto blurLevel = _mode - DebugBloomConfig::MODE_LEVEL0;
|
||||
|
||||
viewport.z /= 2;
|
||||
|
||||
batch._glUniform4f(TEXCOORD_RECT_SLOT, 0.5f, 0.0f, 0.5f, 1.f);
|
||||
|
||||
modelTransform = gpu::Framebuffer::evalSubregionTexcoordTransform(framebufferSize, viewport);
|
||||
modelTransform.postTranslate(glm::vec3(-1.0f, 0.0f, 0.0f));
|
||||
batch.setModelTransform(modelTransform);
|
||||
batch.setResourceTexture(0, levelTextures[blurLevel]);
|
||||
batch.draw(gpu::TRIANGLE_STRIP, 4);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void BloomConfig::setIntensity(float value) {
|
||||
auto task = static_cast<render::Task::TaskConcept*>(_task);
|
||||
auto blurJobIt = task->editJob("BloomApply");
|
||||
assert(blurJobIt != task->_jobs.end());
|
||||
blurJobIt->getConfiguration()->setProperty("intensity", value);
|
||||
}
|
||||
|
||||
float BloomConfig::getIntensity() const {
|
||||
auto task = static_cast<render::Task::TaskConcept*>(_task);
|
||||
auto blurJobIt = task->getJob("BloomApply");
|
||||
assert(blurJobIt != task->_jobs.end());
|
||||
return blurJobIt->getConfiguration()->property("intensity").toFloat();
|
||||
}
|
||||
|
||||
void BloomConfig::setSize(float value) {
|
||||
std::string blurName{ "BloomBlurN" };
|
||||
auto sigma = 0.5f+value*3.5f;
|
||||
|
||||
for (auto i = 0; i < BLOOM_BLUR_LEVEL_COUNT; i++) {
|
||||
blurName.back() = '0' + i;
|
||||
auto task = static_cast<render::Task::TaskConcept*>(_task);
|
||||
auto blurJobIt = task->editJob(blurName);
|
||||
assert(blurJobIt != task->_jobs.end());
|
||||
auto& gaussianBlur = blurJobIt->edit<render::BlurGaussian>();
|
||||
auto gaussianBlurParams = gaussianBlur.getParameters();
|
||||
gaussianBlurParams->setFilterGaussianTaps(5, sigma);
|
||||
// Gaussian blur increases at each level to have a slower rolloff on the edge
|
||||
// of the response
|
||||
sigma *= 1.5f;
|
||||
}
|
||||
}
|
||||
|
||||
Bloom::Bloom() {
|
||||
|
||||
}
|
||||
|
||||
void Bloom::configure(const Config& config) {
|
||||
std::string blurName{ "BloomBlurN" };
|
||||
|
||||
for (auto i = 0; i < BLOOM_BLUR_LEVEL_COUNT; i++) {
|
||||
blurName.back() = '0' + i;
|
||||
auto blurConfig = config.getConfig<render::BlurGaussian>(blurName);
|
||||
blurConfig->setProperty("filterScale", 1.0f);
|
||||
}
|
||||
}
|
||||
|
||||
void Bloom::build(JobModel& task, const render::Varying& inputs, render::Varying& outputs) {
|
||||
// Start by computing threshold of color buffer input at quarter resolution
|
||||
const auto bloomInputBuffer = task.addJob<BloomThreshold>("BloomThreshold", inputs, 4U);
|
||||
|
||||
// Multi-scale blur, each new blur is half resolution of the previous pass
|
||||
const auto blurFB0 = task.addJob<render::BlurGaussian>("BloomBlur0", bloomInputBuffer, true);
|
||||
const auto blurFB1 = task.addJob<render::BlurGaussian>("BloomBlur1", blurFB0, true, 2U);
|
||||
const auto blurFB2 = task.addJob<render::BlurGaussian>("BloomBlur2", blurFB1, true, 2U);
|
||||
|
||||
const auto& input = inputs.get<Inputs>();
|
||||
const auto& frameBuffer = input[1];
|
||||
|
||||
// Mix all blur levels at quarter resolution
|
||||
const auto applyInput = BloomApply::Inputs(bloomInputBuffer, blurFB0, blurFB1, blurFB2).asVarying();
|
||||
task.addJob<BloomApply>("BloomApply", applyInput);
|
||||
// And them blend result in additive manner on top of final color buffer
|
||||
const auto drawInput = BloomDraw::Inputs(frameBuffer, bloomInputBuffer).asVarying();
|
||||
task.addJob<BloomDraw>("BloomDraw", drawInput);
|
||||
|
||||
const auto debugInput = DebugBloom::Inputs(frameBuffer, blurFB0, blurFB1, blurFB2, bloomInputBuffer).asVarying();
|
||||
task.addJob<DebugBloom>("DebugBloom", debugInput);
|
||||
}
|
166
libraries/render-utils/src/BloomEffect.h
Normal file
166
libraries/render-utils/src/BloomEffect.h
Normal file
|
@ -0,0 +1,166 @@
|
|||
//
|
||||
// BloomEffect.h
|
||||
// render-utils/src/
|
||||
//
|
||||
// Created by Olivier Prat on 09/25/17.
|
||||
// Copyright 2017 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#ifndef hifi_render_utils_BloomEffect_h
|
||||
#define hifi_render_utils_BloomEffect_h
|
||||
|
||||
#include <render/Engine.h>
|
||||
|
||||
#include "DeferredFrameTransform.h"
|
||||
|
||||
class BloomConfig : public render::Task::Config {
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(float intensity READ getIntensity WRITE setIntensity NOTIFY dirty)
|
||||
Q_PROPERTY(float size MEMBER size WRITE setSize NOTIFY dirty)
|
||||
|
||||
public:
|
||||
|
||||
BloomConfig() : render::Task::Config(false) {}
|
||||
|
||||
float size{ 0.8f };
|
||||
|
||||
void setIntensity(float value);
|
||||
float getIntensity() const;
|
||||
void setSize(float value);
|
||||
|
||||
signals:
|
||||
void dirty();
|
||||
};
|
||||
|
||||
class BloomThresholdConfig : public render::Job::Config {
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(float threshold MEMBER threshold NOTIFY dirty)
|
||||
|
||||
public:
|
||||
|
||||
float threshold{ 1.25f };
|
||||
|
||||
signals:
|
||||
void dirty();
|
||||
};
|
||||
|
||||
class BloomThreshold {
|
||||
public:
|
||||
using Inputs = render::VaryingSet2<DeferredFrameTransformPointer, gpu::FramebufferPointer>;
|
||||
using Outputs = gpu::FramebufferPointer;
|
||||
using Config = BloomThresholdConfig;
|
||||
using JobModel = render::Job::ModelIO<BloomThreshold, Inputs, Outputs, Config>;
|
||||
|
||||
BloomThreshold(unsigned int downsamplingFactor);
|
||||
|
||||
void configure(const Config& config);
|
||||
void run(const render::RenderContextPointer& renderContext, const Inputs& inputs, Outputs& outputs);
|
||||
|
||||
private:
|
||||
|
||||
gpu::FramebufferPointer _outputBuffer;
|
||||
gpu::PipelinePointer _pipeline;
|
||||
float _threshold;
|
||||
unsigned int _downsamplingFactor;
|
||||
};
|
||||
|
||||
|
||||
class BloomApplyConfig : public render::Job::Config {
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(float intensity MEMBER intensity NOTIFY dirty)
|
||||
|
||||
public:
|
||||
|
||||
float intensity{ 0.8f };
|
||||
|
||||
signals:
|
||||
void dirty();
|
||||
};
|
||||
|
||||
class BloomApply {
|
||||
public:
|
||||
using Inputs = render::VaryingSet4<gpu::FramebufferPointer, gpu::FramebufferPointer, gpu::FramebufferPointer, gpu::FramebufferPointer>;
|
||||
using Config = BloomApplyConfig;
|
||||
using JobModel = render::Job::ModelI<BloomApply, Inputs, Config>;
|
||||
|
||||
BloomApply();
|
||||
|
||||
void configure(const Config& config);
|
||||
void run(const render::RenderContextPointer& renderContext, const Inputs& inputs);
|
||||
|
||||
private:
|
||||
|
||||
gpu::PipelinePointer _pipeline;
|
||||
float _intensity{ 1.0f };
|
||||
};
|
||||
|
||||
class BloomDraw {
|
||||
public:
|
||||
using Inputs = render::VaryingSet2<gpu::FramebufferPointer, gpu::FramebufferPointer>;
|
||||
using JobModel = render::Job::ModelI<BloomDraw, Inputs>;
|
||||
|
||||
BloomDraw() {}
|
||||
|
||||
void run(const render::RenderContextPointer& renderContext, const Inputs& inputs);
|
||||
|
||||
private:
|
||||
|
||||
gpu::PipelinePointer _pipeline;
|
||||
};
|
||||
|
||||
class DebugBloomConfig : public render::Job::Config {
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(int mode MEMBER mode NOTIFY dirty)
|
||||
|
||||
public:
|
||||
|
||||
enum Mode {
|
||||
MODE_LEVEL0 = 0,
|
||||
MODE_LEVEL1,
|
||||
MODE_LEVEL2,
|
||||
MODE_ALL_LEVELS,
|
||||
|
||||
MODE_COUNT
|
||||
};
|
||||
|
||||
DebugBloomConfig() : render::Job::Config(false) {}
|
||||
|
||||
int mode{ MODE_ALL_LEVELS };
|
||||
|
||||
signals:
|
||||
void dirty();
|
||||
};
|
||||
|
||||
class DebugBloom {
|
||||
public:
|
||||
using Inputs = render::VaryingSet5<gpu::FramebufferPointer, gpu::FramebufferPointer, gpu::FramebufferPointer, gpu::FramebufferPointer, gpu::FramebufferPointer>;
|
||||
using Config = DebugBloomConfig;
|
||||
using JobModel = render::Job::ModelI<DebugBloom, Inputs, Config>;
|
||||
|
||||
DebugBloom();
|
||||
|
||||
void configure(const Config& config);
|
||||
void run(const render::RenderContextPointer& renderContext, const Inputs& inputs);
|
||||
|
||||
private:
|
||||
gpu::PipelinePointer _pipeline;
|
||||
DebugBloomConfig::Mode _mode;
|
||||
};
|
||||
|
||||
class Bloom {
|
||||
public:
|
||||
using Inputs = render::VaryingSet2<DeferredFrameTransformPointer, gpu::FramebufferPointer>;
|
||||
using Config = BloomConfig;
|
||||
using JobModel = render::Task::ModelI<Bloom, Inputs, Config>;
|
||||
|
||||
Bloom();
|
||||
|
||||
void configure(const Config& config);
|
||||
void build(JobModel& task, const render::Varying& inputs, render::Varying& outputs);
|
||||
|
||||
};
|
||||
|
||||
#endif // hifi_render_utils_BloomEffect_h
|
45
libraries/render-utils/src/BloomThreshold.slf
Normal file
45
libraries/render-utils/src/BloomThreshold.slf
Normal file
|
@ -0,0 +1,45 @@
|
|||
<@include gpu/Config.slh@>
|
||||
<$VERSION_HEADER$>
|
||||
// BloomThreshold.slf
|
||||
// Perform a soft threshold on an input texture and downsample to half size in one go.
|
||||
//
|
||||
// Created by Olivier Prat on 09/26/2017
|
||||
// Copyright 2017 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
uniform sampler2D colorMap;
|
||||
uniform float threshold;
|
||||
|
||||
in vec2 varTexCoord0;
|
||||
out vec4 outFragColor;
|
||||
|
||||
#define DOWNSAMPLING_FACTOR 4
|
||||
#define SAMPLE_COUNT (DOWNSAMPLING_FACTOR/2)
|
||||
|
||||
void main(void) {
|
||||
vec2 deltaX = dFdx(varTexCoord0) / SAMPLE_COUNT;
|
||||
vec2 deltaY = dFdy(varTexCoord0) / SAMPLE_COUNT;
|
||||
vec2 startUv = varTexCoord0;
|
||||
vec4 maskedColor = vec4(0,0,0,0);
|
||||
|
||||
for (int y=0 ; y<SAMPLE_COUNT ; y++) {
|
||||
vec2 uv = startUv;
|
||||
|
||||
for (int x=0 ; x<SAMPLE_COUNT ; x++) {
|
||||
vec4 color = texture(colorMap, uv);
|
||||
float luminance = (color.r+color.g+color.b) / 3.0;
|
||||
float mask = clamp((luminance-threshold)*0.25, 0, 1);
|
||||
|
||||
color *= mask;
|
||||
maskedColor += color;
|
||||
uv += deltaX;
|
||||
}
|
||||
|
||||
startUv += deltaY;
|
||||
}
|
||||
maskedColor /= SAMPLE_COUNT*SAMPLE_COUNT;
|
||||
outFragColor = vec4(maskedColor.rgb, 1.0);
|
||||
}
|
|
@ -73,9 +73,9 @@ void DeferredFramebuffer::allocate() {
|
|||
_deferredFramebufferDepthColor->setDepthStencilBuffer(_primaryDepthTexture, depthFormat);
|
||||
|
||||
|
||||
auto smoothSampler = gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR);
|
||||
auto smoothSampler = gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_LINEAR_MIP_POINT);
|
||||
|
||||
_lightingTexture = gpu::Texture::createRenderBuffer(gpu::Element(gpu::SCALAR, gpu::FLOAT, gpu::R11G11B10), width, height, gpu::Texture::SINGLE_MIP, defaultSampler);
|
||||
_lightingTexture = gpu::Texture::createRenderBuffer(gpu::Element(gpu::SCALAR, gpu::FLOAT, gpu::R11G11B10), width, height, gpu::Texture::SINGLE_MIP, smoothSampler);
|
||||
_lightingFramebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create("lighting"));
|
||||
_lightingFramebuffer->setRenderBuffer(0, _lightingTexture);
|
||||
_lightingFramebuffer->setDepthStencilBuffer(_primaryDepthTexture, depthFormat);
|
||||
|
|
|
@ -105,13 +105,13 @@ void DeferredLightingEffect::setupKeyLightBatch(const RenderArgs* args, gpu::Bat
|
|||
PerformanceTimer perfTimer("DLE->setupBatch()");
|
||||
model::LightPointer keySunLight;
|
||||
auto lightStage = args->_scene->getStage<LightStage>();
|
||||
if (lightStage && lightStage->_currentFrame._sunLights.size()) {
|
||||
keySunLight = lightStage->getLight(lightStage->_currentFrame._sunLights.front());
|
||||
if (lightStage) {
|
||||
keySunLight = lightStage->getCurrentKeyLight();
|
||||
}
|
||||
|
||||
model::LightPointer keyAmbiLight;
|
||||
if (lightStage && lightStage->_currentFrame._ambientLights.size()) {
|
||||
keyAmbiLight = lightStage->getLight(lightStage->_currentFrame._ambientLights.front());
|
||||
if (lightStage) {
|
||||
keyAmbiLight = lightStage->getCurrentAmbientLight();
|
||||
}
|
||||
|
||||
if (keySunLight) {
|
||||
|
@ -620,7 +620,7 @@ void RenderDeferredLocals::run(const render::RenderContextPointer& renderContext
|
|||
auto& lightIndices = lightClusters->_visibleLightIndices;
|
||||
if (!lightIndices.empty() && lightIndices[0] > 0) {
|
||||
// Bind the global list of lights and the visible lights this frame
|
||||
batch.setUniformBuffer(deferredLightingEffect->_localLightLocations->lightBufferUnit, lightClusters->_lightStage->_lightArrayBuffer);
|
||||
batch.setUniformBuffer(deferredLightingEffect->_localLightLocations->lightBufferUnit, lightClusters->_lightStage->getLightArrayBuffer());
|
||||
|
||||
batch.setUniformBuffer(LIGHT_CLUSTER_GRID_FRUSTUM_GRID_SLOT, lightClusters->_frustumGridBuffer);
|
||||
batch.setUniformBuffer(LIGHT_CLUSTER_GRID_CLUSTER_GRID_SLOT, lightClusters->_clusterGridBuffer);
|
||||
|
|
|
@ -175,9 +175,9 @@ void DrawHaze::run(const render::RenderContextPointer& renderContext, const Inpu
|
|||
batch.setUniformBuffer(HazeEffect_TransformBufferSlot, transformBuffer->getFrameTransformBuffer());
|
||||
|
||||
auto lightStage = args->_scene->getStage<LightStage>();
|
||||
if (lightStage && lightStage->_currentFrame._sunLights.size() > 0) {
|
||||
model::LightPointer keyLight;
|
||||
keyLight = lightStage->getLight(lightStage->_currentFrame._sunLights.front());
|
||||
if (lightStage) {
|
||||
model::LightPointer keyLight;
|
||||
keyLight = lightStage->getCurrentKeyLight();
|
||||
if (keyLight != nullptr) {
|
||||
batch.setUniformBuffer(HazeEffect_LightingMapSlot, keyLight->getLightSchemaBuffer());
|
||||
}
|
||||
|
|
|
@ -1931,9 +1931,10 @@ void GeometryCache::renderGlowLine(gpu::Batch& batch, const glm::vec3& p1, const
|
|||
vec4 p1;
|
||||
vec4 p2;
|
||||
vec4 color;
|
||||
float width;
|
||||
};
|
||||
|
||||
LineData lineData { vec4(p1, 1.0f), vec4(p2, 1.0f), color };
|
||||
LineData lineData { vec4(p1, 1.0f), vec4(p2, 1.0f), color, glowWidth };
|
||||
details.uniformBuffer->resize(sizeof(LineData));
|
||||
details.uniformBuffer->setSubData(0, lineData);
|
||||
}
|
||||
|
|
|
@ -727,7 +727,7 @@ void DebugLightClusters::run(const render::RenderContextPointer& renderContext,
|
|||
batch.setModelTransform(Transform());
|
||||
|
||||
// Bind the Light CLuster data strucutre
|
||||
batch.setUniformBuffer(LIGHT_GPU_SLOT, lightClusters->_lightStage->_lightArrayBuffer);
|
||||
batch.setUniformBuffer(LIGHT_GPU_SLOT, lightClusters->_lightStage->getLightArrayBuffer());
|
||||
batch.setUniformBuffer(LIGHT_CLUSTER_GRID_FRUSTUM_GRID_SLOT, lightClusters->_frustumGridBuffer);
|
||||
batch.setUniformBuffer(LIGHT_CLUSTER_GRID_CLUSTER_GRID_SLOT, lightClusters->_clusterGridBuffer);
|
||||
batch.setUniformBuffer(LIGHT_CLUSTER_GRID_CLUSTER_CONTENT_SLOT, lightClusters->_clusterContentBuffer);
|
||||
|
|
|
@ -19,14 +19,29 @@ const LightStage::Index LightStage::INVALID_INDEX { render::indexed_container::I
|
|||
LightStage::LightStage() {
|
||||
}
|
||||
|
||||
LightStage::Shadow::Shadow(model::LightPointer light) : _light{ light}, _frustum{ std::make_shared<ViewFrustum>() } {
|
||||
framebuffer = gpu::FramebufferPointer(gpu::Framebuffer::createShadowmap(MAP_SIZE));
|
||||
map = framebuffer->getDepthStencilBuffer();
|
||||
Schema schema;
|
||||
_schemaBuffer = std::make_shared<gpu::Buffer>(sizeof(Schema), (const gpu::Byte*) &schema);
|
||||
LightStage::Shadow::Schema::Schema() :
|
||||
bias{ 0.005f },
|
||||
scale{ 1.0f / MAP_SIZE } {
|
||||
|
||||
}
|
||||
|
||||
void LightStage::Shadow::setKeylightFrustum(const ViewFrustum& viewFrustum, float nearDepth, float farDepth) {
|
||||
gpu::FramebufferPointer LightStage::Shadow::framebuffer;
|
||||
gpu::TexturePointer LightStage::Shadow::map;
|
||||
|
||||
LightStage::Shadow::Shadow(model::LightPointer light) : _light{ light}, _frustum{ std::make_shared<ViewFrustum>() } {
|
||||
Schema schema;
|
||||
_schemaBuffer = std::make_shared<gpu::Buffer>(sizeof(Schema), (const gpu::Byte*) &schema);
|
||||
|
||||
if (!framebuffer) {
|
||||
framebuffer = gpu::FramebufferPointer(gpu::Framebuffer::createShadowmap(MAP_SIZE));
|
||||
map = framebuffer->getDepthStencilBuffer();
|
||||
}
|
||||
}
|
||||
|
||||
void LightStage::Shadow::setKeylightFrustum(const ViewFrustum& viewFrustum,
|
||||
float viewMinShadowDistance, float viewMaxShadowDistance,
|
||||
float nearDepth, float farDepth) {
|
||||
assert(viewMinShadowDistance < viewMaxShadowDistance);
|
||||
assert(nearDepth < farDepth);
|
||||
|
||||
// Orient the keylight frustum
|
||||
|
@ -49,8 +64,8 @@ void LightStage::Shadow::setKeylightFrustum(const ViewFrustum& viewFrustum, floa
|
|||
const Transform view{ _frustum->getView()};
|
||||
const Transform viewInverse{ view.getInverseMatrix() };
|
||||
|
||||
auto nearCorners = viewFrustum.getCorners(nearDepth);
|
||||
auto farCorners = viewFrustum.getCorners(farDepth);
|
||||
auto nearCorners = viewFrustum.getCorners(viewMinShadowDistance);
|
||||
auto farCorners = viewFrustum.getCorners(viewMaxShadowDistance);
|
||||
|
||||
vec3 min{ viewInverse.transform(nearCorners.bottomLeft) };
|
||||
vec3 max{ min };
|
||||
|
@ -74,7 +89,10 @@ void LightStage::Shadow::setKeylightFrustum(const ViewFrustum& viewFrustum, floa
|
|||
fitFrustum(farCorners.topLeft);
|
||||
fitFrustum(farCorners.topRight);
|
||||
|
||||
glm::mat4 ortho = glm::ortho<float>(min.x, max.x, min.y, max.y, -max.z, -min.z);
|
||||
// Re-adjust near shadow distance
|
||||
auto near = glm::max(max.z, -nearDepth);
|
||||
auto far = -min.z;
|
||||
glm::mat4 ortho = glm::ortho<float>(min.x, max.x, min.y, max.y, near, far);
|
||||
_frustum->setProjection(ortho);
|
||||
|
||||
// Calculate the frustum's internal state
|
||||
|
@ -85,6 +103,16 @@ void LightStage::Shadow::setKeylightFrustum(const ViewFrustum& viewFrustum, floa
|
|||
_schemaBuffer.edit<Schema>().viewInverse = viewInverse.getMatrix();
|
||||
}
|
||||
|
||||
void LightStage::Shadow::setFrustum(const ViewFrustum& shadowFrustum) {
|
||||
const Transform view{ shadowFrustum.getView() };
|
||||
const Transform viewInverse{ view.getInverseMatrix() };
|
||||
|
||||
*_frustum = shadowFrustum;
|
||||
// Update the buffer
|
||||
_schemaBuffer.edit<Schema>().projection = shadowFrustum.getProjection();
|
||||
_schemaBuffer.edit<Schema>().viewInverse = viewInverse.getMatrix();
|
||||
}
|
||||
|
||||
const glm::mat4& LightStage::Shadow::getView() const {
|
||||
return _frustum->getView();
|
||||
}
|
||||
|
@ -100,11 +128,9 @@ LightStage::Index LightStage::findLight(const LightPointer& light) const {
|
|||
} else {
|
||||
return (*found).second;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
LightStage::Index LightStage::addLight(const LightPointer& light) {
|
||||
|
||||
auto found = _lightMap.find(light);
|
||||
if (found == _lightMap.end()) {
|
||||
auto lightId = _lights.newElement(light);
|
||||
|
@ -115,6 +141,7 @@ LightStage::Index LightStage::addLight(const LightPointer& light) {
|
|||
if (lightId >= (Index) _descs.size()) {
|
||||
_descs.emplace_back(Desc());
|
||||
} else {
|
||||
assert(_descs[lightId].shadowId == INVALID_INDEX);
|
||||
_descs.emplace(_descs.begin() + lightId, Desc());
|
||||
}
|
||||
|
||||
|
@ -133,6 +160,7 @@ LightStage::Index LightStage::addShadow(Index lightIndex) {
|
|||
auto light = getLight(lightIndex);
|
||||
Index shadowId = INVALID_INDEX;
|
||||
if (light) {
|
||||
assert(_descs[lightIndex].shadowId == INVALID_INDEX);
|
||||
shadowId = _shadows.newElement(std::make_shared<Shadow>(light));
|
||||
_descs[lightIndex].shadowId = shadowId;
|
||||
}
|
||||
|
@ -140,18 +168,65 @@ LightStage::Index LightStage::addShadow(Index lightIndex) {
|
|||
}
|
||||
|
||||
LightStage::LightPointer LightStage::removeLight(Index index) {
|
||||
LightPointer removed = _lights.freeElement(index);
|
||||
|
||||
if (removed) {
|
||||
LightPointer removedLight = _lights.freeElement(index);
|
||||
if (removedLight) {
|
||||
auto shadowId = _descs[index].shadowId;
|
||||
// Remove shadow if one exists for this light
|
||||
if (shadowId != INVALID_INDEX) {
|
||||
_shadows.freeElement(shadowId);
|
||||
auto removedShadow = _shadows.freeElement(shadowId);
|
||||
assert(removedShadow);
|
||||
assert(removedShadow->getLight() == removedLight);
|
||||
}
|
||||
_lightMap.erase(removed);
|
||||
_lightMap.erase(removedLight);
|
||||
_descs[index] = Desc();
|
||||
}
|
||||
return removed;
|
||||
assert(_descs.size() <= index || _descs[index].shadowId == INVALID_INDEX);
|
||||
return removedLight;
|
||||
}
|
||||
|
||||
LightStage::LightPointer LightStage::getCurrentKeyLight() const {
|
||||
Index keyLightId{ 0 };
|
||||
if (!_currentFrame._sunLights.empty()) {
|
||||
keyLightId = _currentFrame._sunLights.front();
|
||||
}
|
||||
return _lights.get(keyLightId);
|
||||
}
|
||||
|
||||
LightStage::LightPointer LightStage::getCurrentAmbientLight() const {
|
||||
Index keyLightId{ 0 };
|
||||
if (!_currentFrame._ambientLights.empty()) {
|
||||
keyLightId = _currentFrame._ambientLights.front();
|
||||
}
|
||||
return _lights.get(keyLightId);
|
||||
}
|
||||
|
||||
LightStage::ShadowPointer LightStage::getCurrentKeyShadow() const {
|
||||
Index keyLightId{ 0 };
|
||||
if (!_currentFrame._sunLights.empty()) {
|
||||
keyLightId = _currentFrame._sunLights.front();
|
||||
}
|
||||
auto shadow = getShadow(keyLightId);
|
||||
assert(shadow == nullptr || shadow->getLight() == getLight(keyLightId));
|
||||
return shadow;
|
||||
}
|
||||
|
||||
LightStage::LightAndShadow LightStage::getCurrentKeyLightAndShadow() const {
|
||||
Index keyLightId{ 0 };
|
||||
if (!_currentFrame._sunLights.empty()) {
|
||||
keyLightId = _currentFrame._sunLights.front();
|
||||
}
|
||||
auto shadow = getShadow(keyLightId);
|
||||
auto light = getLight(keyLightId);
|
||||
assert(shadow == nullptr || shadow->getLight() == light);
|
||||
return LightAndShadow(light, shadow);
|
||||
}
|
||||
|
||||
LightStage::Index LightStage::getShadowId(Index lightId) const {
|
||||
if (checkLightId(lightId)) {
|
||||
return _descs[lightId].shadowId;
|
||||
} else {
|
||||
return INVALID_INDEX;
|
||||
}
|
||||
}
|
||||
|
||||
void LightStage::updateLightArrayBuffer(Index lightId) {
|
||||
|
|
|
@ -48,8 +48,9 @@ public:
|
|||
|
||||
Shadow(model::LightPointer light);
|
||||
|
||||
void setKeylightFrustum(const ViewFrustum& viewFrustum, float nearDepth, float farDepth);
|
||||
void setKeylightFrustum(const ViewFrustum& viewFrustum, float viewMinShadowDistance, float viewMaxShadowDistance, float nearDepth = 1.0f, float farDepth = 1000.0f);
|
||||
|
||||
void setFrustum(const ViewFrustum& shadowFrustum);
|
||||
const std::shared_ptr<ViewFrustum> getFrustum() const { return _frustum; }
|
||||
|
||||
const glm::mat4& getView() const;
|
||||
|
@ -57,32 +58,36 @@ public:
|
|||
|
||||
const UniformBufferView& getBuffer() const { return _schemaBuffer; }
|
||||
|
||||
gpu::FramebufferPointer framebuffer;
|
||||
gpu::TexturePointer map;
|
||||
// Shadow maps are shared among all lights for the moment as only one key light
|
||||
// is used.
|
||||
static gpu::FramebufferPointer framebuffer;
|
||||
static gpu::TexturePointer map;
|
||||
|
||||
const model::LightPointer& getLight() const { return _light; }
|
||||
|
||||
protected:
|
||||
|
||||
model::LightPointer _light;
|
||||
std::shared_ptr<ViewFrustum> _frustum;
|
||||
|
||||
class Schema {
|
||||
public:
|
||||
|
||||
Schema();
|
||||
|
||||
glm::mat4 projection;
|
||||
glm::mat4 viewInverse;
|
||||
|
||||
glm::float32 bias = 0.005f;
|
||||
glm::float32 scale = 1 / MAP_SIZE;
|
||||
glm::float32 bias;
|
||||
glm::float32 scale;
|
||||
};
|
||||
UniformBufferView _schemaBuffer = nullptr;
|
||||
|
||||
friend class Light;
|
||||
};
|
||||
|
||||
using ShadowPointer = std::shared_ptr<Shadow>;
|
||||
using Shadows = render::indexed_container::IndexedPointerVector<Shadow>;
|
||||
|
||||
struct Desc {
|
||||
Index shadowId { INVALID_INDEX };
|
||||
};
|
||||
using Descs = std::vector<Desc>;
|
||||
|
||||
Index findLight(const LightPointer& light) const;
|
||||
Index addLight(const LightPointer& light);
|
||||
|
||||
|
@ -100,50 +105,29 @@ public:
|
|||
return _lights.get(lightId);
|
||||
}
|
||||
|
||||
Index getShadowId(Index lightId) const {
|
||||
if (checkLightId(lightId)) {
|
||||
return _descs[lightId].shadowId;
|
||||
} else {
|
||||
return INVALID_INDEX;
|
||||
}
|
||||
}
|
||||
Index getShadowId(Index lightId) const;
|
||||
|
||||
ShadowPointer getShadow(Index lightId) const {
|
||||
return _shadows.get(getShadowId(lightId));
|
||||
}
|
||||
|
||||
using LightAndShadow = std::pair<LightPointer, ShadowPointer>;
|
||||
LightAndShadow getLightAndShadow(Index lightId) const {
|
||||
return LightAndShadow(getLight(lightId), getShadow(lightId));
|
||||
auto light = getLight(lightId);
|
||||
auto shadow = getShadow(lightId);
|
||||
assert(shadow == nullptr || shadow->getLight() == light);
|
||||
return LightAndShadow(light, shadow);
|
||||
}
|
||||
|
||||
LightPointer getCurrentKeyLight() const {
|
||||
Index keyLightId{ 0 };
|
||||
if (!_currentFrame._sunLights.empty()) {
|
||||
keyLightId = _currentFrame._sunLights.front();
|
||||
}
|
||||
return _lights.get(keyLightId);
|
||||
}
|
||||
|
||||
ShadowPointer getCurrentKeyShadow() const {
|
||||
Index keyLightId{ 0 };
|
||||
if (!_currentFrame._sunLights.empty()) {
|
||||
keyLightId = _currentFrame._sunLights.front();
|
||||
}
|
||||
return getShadow(keyLightId);
|
||||
}
|
||||
|
||||
LightAndShadow getCurrentKeyLightAndShadow() const {
|
||||
Index keyLightId{ 0 };
|
||||
if (!_currentFrame._sunLights.empty()) {
|
||||
keyLightId = _currentFrame._sunLights.front();
|
||||
}
|
||||
return LightAndShadow(getLight(keyLightId), getShadow(keyLightId));
|
||||
}
|
||||
LightPointer getCurrentKeyLight() const;
|
||||
LightPointer getCurrentAmbientLight() const;
|
||||
ShadowPointer getCurrentKeyShadow() const;
|
||||
LightAndShadow getCurrentKeyLightAndShadow() const;
|
||||
|
||||
LightStage();
|
||||
Lights _lights;
|
||||
LightMap _lightMap;
|
||||
Descs _descs;
|
||||
|
||||
gpu::BufferPointer getLightArrayBuffer() const { return _lightArrayBuffer; }
|
||||
void updateLightArrayBuffer(Index lightId);
|
||||
|
||||
class Frame {
|
||||
public:
|
||||
|
@ -172,15 +156,24 @@ public:
|
|||
|
||||
Frame _currentFrame;
|
||||
|
||||
gpu::BufferPointer _lightArrayBuffer;
|
||||
void updateLightArrayBuffer(Index lightId);
|
||||
protected:
|
||||
|
||||
struct Desc {
|
||||
Index shadowId{ INVALID_INDEX };
|
||||
};
|
||||
using Descs = std::vector<Desc>;
|
||||
|
||||
gpu::BufferPointer _lightArrayBuffer;
|
||||
|
||||
Lights _lights;
|
||||
Shadows _shadows;
|
||||
Descs _descs;
|
||||
LightMap _lightMap;
|
||||
|
||||
};
|
||||
using LightStagePointer = std::shared_ptr<LightStage>;
|
||||
|
||||
|
||||
|
||||
class LightStageSetup {
|
||||
public:
|
||||
using JobModel = render::Job::Model<LightStageSetup>;
|
||||
|
|
|
@ -42,10 +42,9 @@
|
|||
#include "ToneMappingEffect.h"
|
||||
#include "SubsurfaceScattering.h"
|
||||
#include "DrawHaze.h"
|
||||
#include "BloomEffect.h"
|
||||
#include "HighlightEffect.h"
|
||||
|
||||
#include <gpu/StandardShaderLib.h>
|
||||
|
||||
#include <sstream>
|
||||
|
||||
using namespace render;
|
||||
|
@ -168,7 +167,7 @@ void RenderDeferredTask::build(JobModel& task, const render::Varying& input, ren
|
|||
const auto transparentsInputs = DrawDeferred::Inputs(transparents, lightingModel).asVarying();
|
||||
task.addJob<DrawDeferred>("DrawTransparentDeferred", transparentsInputs, shapePlumber);
|
||||
|
||||
// LIght Cluster Grid Debuging job
|
||||
// Light Cluster Grid Debuging job
|
||||
{
|
||||
const auto debugLightClustersInputs = DebugLightClusters::Inputs(deferredFrameTransform, deferredFramebuffer, lightingModel, linearDepthTarget, lightClusters).asVarying();
|
||||
task.addJob<DebugLightClusters>("DebugLightClusters", debugLightClustersInputs);
|
||||
|
@ -179,6 +178,10 @@ void RenderDeferredTask::build(JobModel& task, const render::Varying& input, ren
|
|||
|
||||
const auto toneAndPostRangeTimer = task.addJob<BeginGPURangeTimer>("BeginToneAndPostRangeTimer", "PostToneOverlaysAntialiasing");
|
||||
|
||||
// Add bloom
|
||||
const auto bloomInputs = Bloom::Inputs(deferredFrameTransform, lightingFramebuffer).asVarying();
|
||||
task.addJob<Bloom>("Bloom", bloomInputs);
|
||||
|
||||
// Lighting Buffer ready for tone mapping
|
||||
const auto toneMappingInputs = ToneMappingDeferred::Inputs(lightingFramebuffer, primaryFramebuffer).asVarying();
|
||||
task.addJob<ToneMappingDeferred>("ToneMapping", toneMappingInputs);
|
||||
|
@ -193,13 +196,18 @@ void RenderDeferredTask::build(JobModel& task, const render::Varying& input, ren
|
|||
|
||||
task.addJob<EndGPURangeTimer>("HighlightRangeTimer", outlineRangeTimer);
|
||||
|
||||
{ // DEbug the bounds of the rendered items, still look at the zbuffer
|
||||
{ // Debug the bounds of the rendered items, still look at the zbuffer
|
||||
task.addJob<DrawBounds>("DrawMetaBounds", metas);
|
||||
task.addJob<DrawBounds>("DrawOpaqueBounds", opaques);
|
||||
task.addJob<DrawBounds>("DrawTransparentBounds", transparents);
|
||||
|
||||
task.addJob<DrawBounds>("DrawLightBounds", lights);
|
||||
task.addJob<DrawBounds>("DrawZones", zones);
|
||||
const auto frustums = task.addJob<ExtractFrustums>("ExtractFrustums");
|
||||
const auto viewFrustum = frustums.getN<ExtractFrustums::Output>(ExtractFrustums::VIEW_FRUSTUM);
|
||||
const auto shadowFrustum = frustums.getN<ExtractFrustums::Output>(ExtractFrustums::SHADOW_FRUSTUM);
|
||||
task.addJob<DrawFrustum>("DrawViewFrustum", viewFrustum, glm::vec3(1.0f, 1.0f, 0.0f));
|
||||
task.addJob<DrawFrustum>("DrawShadowFrustum", shadowFrustum, glm::vec3(0.0f, 0.0f, 1.0f));
|
||||
|
||||
// Render.getConfig("RenderMainView.DrawSelectionBounds").enabled = true
|
||||
task.addJob<DrawBounds>("DrawSelectionBounds", selectedItems);
|
||||
|
@ -448,6 +456,11 @@ void CompositeHUD::run(const RenderContextPointer& renderContext) {
|
|||
assert(renderContext->args);
|
||||
assert(renderContext->args->_context);
|
||||
|
||||
// We do not want to render HUD elements in secondary camera
|
||||
if (renderContext->args->_renderMode == RenderArgs::RenderMode::SECONDARY_CAMERA_RENDER_MODE) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Grab the HUD texture
|
||||
gpu::doInBatch(renderContext->args->_context, [&](gpu::Batch& batch) {
|
||||
if (renderContext->args->_hudOperator) {
|
||||
|
@ -527,3 +540,32 @@ void Blit::run(const RenderContextPointer& renderContext, const gpu::Framebuffer
|
|||
});
|
||||
}
|
||||
|
||||
void ExtractFrustums::run(const render::RenderContextPointer& renderContext, Output& output) {
|
||||
assert(renderContext->args);
|
||||
assert(renderContext->args->_context);
|
||||
|
||||
RenderArgs* args = renderContext->args;
|
||||
|
||||
// Return view frustum
|
||||
auto& viewFrustum = output[VIEW_FRUSTUM].edit<ViewFrustumPointer>();
|
||||
if (!viewFrustum) {
|
||||
viewFrustum = std::make_shared<ViewFrustum>(args->getViewFrustum());
|
||||
} else {
|
||||
*viewFrustum = args->getViewFrustum();
|
||||
}
|
||||
|
||||
// Return shadow frustum
|
||||
auto& shadowFrustum = output[SHADOW_FRUSTUM].edit<ViewFrustumPointer>();
|
||||
auto lightStage = args->_scene->getStage<LightStage>(LightStage::getName());
|
||||
if (lightStage) {
|
||||
auto globalShadow = lightStage->getCurrentKeyShadow();
|
||||
|
||||
if (globalShadow) {
|
||||
shadowFrustum = globalShadow->getFrustum();
|
||||
} else {
|
||||
shadowFrustum.reset();
|
||||
}
|
||||
} else {
|
||||
shadowFrustum.reset();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -170,6 +170,22 @@ public:
|
|||
void run(const render::RenderContextPointer& renderContext, const gpu::FramebufferPointer& srcFramebuffer);
|
||||
};
|
||||
|
||||
class ExtractFrustums {
|
||||
public:
|
||||
|
||||
enum Frustum {
|
||||
VIEW_FRUSTUM,
|
||||
SHADOW_FRUSTUM,
|
||||
|
||||
FRUSTUM_COUNT
|
||||
};
|
||||
|
||||
using Output = render::VaryingArray<ViewFrustumPointer, FRUSTUM_COUNT>;
|
||||
using JobModel = render::Job::ModelO<ExtractFrustums, Output>;
|
||||
|
||||
void run(const render::RenderContextPointer& renderContext, Output& output);
|
||||
};
|
||||
|
||||
class RenderDeferredTaskConfig : public render::Task::Config {
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(float fadeScale MEMBER fadeScale NOTIFY dirty)
|
||||
|
|
|
@ -22,25 +22,136 @@
|
|||
#include "DeferredLightingEffect.h"
|
||||
#include "FramebufferCache.h"
|
||||
|
||||
// These values are used for culling the objects rendered in the shadow map
|
||||
// but are readjusted afterwards
|
||||
#define SHADOW_FRUSTUM_NEAR 1.0f
|
||||
#define SHADOW_FRUSTUM_FAR 500.0f
|
||||
|
||||
using namespace render;
|
||||
|
||||
extern void initZPassPipelines(ShapePlumber& plumber, gpu::StatePointer state);
|
||||
|
||||
void RenderShadowMap::run(const render::RenderContextPointer& renderContext,
|
||||
const render::ShapeBounds& inShapes) {
|
||||
static void computeNearFar(const Triangle& triangle, const Plane shadowClipPlanes[4], float& near, float& far) {
|
||||
static const int MAX_TRIANGLE_COUNT = 16;
|
||||
Triangle clippedTriangles[MAX_TRIANGLE_COUNT];
|
||||
auto clippedTriangleCount = clipTriangleWithPlanes(triangle, shadowClipPlanes, 4, clippedTriangles, MAX_TRIANGLE_COUNT);
|
||||
|
||||
for (auto i = 0; i < clippedTriangleCount; i++) {
|
||||
const auto& clippedTriangle = clippedTriangles[i];
|
||||
|
||||
near = glm::min(near, -clippedTriangle.v0.z);
|
||||
near = glm::min(near, -clippedTriangle.v1.z);
|
||||
near = glm::min(near, -clippedTriangle.v2.z);
|
||||
|
||||
far = glm::max(far, -clippedTriangle.v0.z);
|
||||
far = glm::max(far, -clippedTriangle.v1.z);
|
||||
far = glm::max(far, -clippedTriangle.v2.z);
|
||||
}
|
||||
}
|
||||
|
||||
static void computeNearFar(const glm::vec3 sceneBoundVertices[8], const Plane shadowClipPlanes[4], float& near, float& far) {
|
||||
// This code is inspired from Microsoft's CascadedShadowMaps11 sample which is under MIT licence.
|
||||
// See https://code.msdn.microsoft.com/windowsdesktop/Direct3D-Shadow-Win32-2d72a4f2/sourcecode?fileId=121915&pathId=1645833187
|
||||
// Basically it decomposes the object bounding box in triangles and clips each triangle with the shadow
|
||||
// frustum planes. Finally it computes the minimum and maximum depth of the clipped triangle vertices
|
||||
// in shadow space to extract the near and far distances of the shadow frustum.
|
||||
static const std::array<int[4], 6> boxQuadVertexIndices = { {
|
||||
{ TOP_LEFT_FAR, BOTTOM_LEFT_FAR, BOTTOM_RIGHT_FAR, TOP_RIGHT_FAR },
|
||||
{ TOP_LEFT_NEAR, BOTTOM_LEFT_NEAR, BOTTOM_RIGHT_NEAR, TOP_RIGHT_NEAR },
|
||||
{ TOP_RIGHT_FAR, BOTTOM_RIGHT_FAR, BOTTOM_RIGHT_NEAR, TOP_RIGHT_NEAR },
|
||||
{ TOP_LEFT_FAR, BOTTOM_LEFT_FAR, BOTTOM_LEFT_NEAR, TOP_LEFT_NEAR },
|
||||
{ BOTTOM_LEFT_FAR, BOTTOM_RIGHT_FAR, BOTTOM_RIGHT_NEAR, BOTTOM_LEFT_NEAR },
|
||||
{ TOP_LEFT_FAR, TOP_RIGHT_FAR, TOP_RIGHT_NEAR, TOP_LEFT_NEAR }
|
||||
} };
|
||||
Triangle triangle;
|
||||
|
||||
for (auto quadVertexIndices : boxQuadVertexIndices) {
|
||||
triangle.v0 = sceneBoundVertices[quadVertexIndices[0]];
|
||||
triangle.v1 = sceneBoundVertices[quadVertexIndices[1]];
|
||||
triangle.v2 = sceneBoundVertices[quadVertexIndices[2]];
|
||||
computeNearFar(triangle, shadowClipPlanes, near, far);
|
||||
triangle.v1 = sceneBoundVertices[quadVertexIndices[3]];
|
||||
computeNearFar(triangle, shadowClipPlanes, near, far);
|
||||
}
|
||||
}
|
||||
|
||||
static void adjustNearFar(const AABox& inShapeBounds, ViewFrustum& shadowFrustum) {
|
||||
const Transform shadowView{ shadowFrustum.getView() };
|
||||
const Transform shadowViewInverse{ shadowView.getInverseMatrix() };
|
||||
|
||||
glm::vec3 sceneBoundVertices[8];
|
||||
// Keep only the left, right, top and bottom shadow frustum planes as we wish to determine
|
||||
// the near and far
|
||||
Plane shadowClipPlanes[4];
|
||||
int i;
|
||||
|
||||
// The vertices of the scene bounding box are expressed in the shadow frustum's local space
|
||||
for (i = 0; i < 8; i++) {
|
||||
sceneBoundVertices[i] = shadowViewInverse.transform(inShapeBounds.getVertex(static_cast<BoxVertex>(i)));
|
||||
}
|
||||
// This indirection array is just a protection in case the ViewFrustum::PlaneIndex enum
|
||||
// changes order especially as we don't need to test the NEAR and FAR planes.
|
||||
static const ViewFrustum::PlaneIndex planeIndices[4] = {
|
||||
ViewFrustum::TOP_PLANE,
|
||||
ViewFrustum::BOTTOM_PLANE,
|
||||
ViewFrustum::LEFT_PLANE,
|
||||
ViewFrustum::RIGHT_PLANE
|
||||
};
|
||||
// Same goes for the shadow frustum planes.
|
||||
for (i = 0; i < 4; i++) {
|
||||
const auto& worldPlane = shadowFrustum.getPlanes()[planeIndices[i]];
|
||||
// We assume the transform doesn't have a non uniform scale component to apply the
|
||||
// transform to the normal without using the correct transpose of inverse, which should be the
|
||||
// case for a view matrix.
|
||||
auto planeNormal = shadowViewInverse.transformDirection(worldPlane.getNormal());
|
||||
auto planePoint = shadowViewInverse.transform(worldPlane.getPoint());
|
||||
shadowClipPlanes[i].setNormalAndPoint(planeNormal, planePoint);
|
||||
}
|
||||
|
||||
float near = std::numeric_limits<float>::max();
|
||||
float far = 0.0f;
|
||||
|
||||
computeNearFar(sceneBoundVertices, shadowClipPlanes, near, far);
|
||||
// Limit the far range to the one used originally. There's no point in rendering objects
|
||||
// that are not in the view frustum.
|
||||
far = glm::min(far, shadowFrustum.getFarClip());
|
||||
|
||||
const auto depthEpsilon = 0.1f;
|
||||
auto projMatrix = glm::ortho(-1.0f, 1.0f, -1.0f, 1.0f, near - depthEpsilon, far + depthEpsilon);
|
||||
auto shadowProjection = shadowFrustum.getProjection();
|
||||
|
||||
shadowProjection[2][2] = projMatrix[2][2];
|
||||
shadowProjection[3][2] = projMatrix[3][2];
|
||||
shadowFrustum.setProjection(shadowProjection);
|
||||
shadowFrustum.calculate();
|
||||
}
|
||||
|
||||
void RenderShadowMap::run(const render::RenderContextPointer& renderContext, const Inputs& inputs) {
|
||||
assert(renderContext->args);
|
||||
assert(renderContext->args->hasViewFrustum());
|
||||
|
||||
const auto& inShapes = inputs.get0();
|
||||
const auto& inShapeBounds = inputs.get1();
|
||||
|
||||
auto lightStage = renderContext->_scene->getStage<LightStage>();
|
||||
assert(lightStage);
|
||||
|
||||
const auto shadow = lightStage->getCurrentKeyShadow();
|
||||
auto shadow = lightStage->getCurrentKeyShadow();
|
||||
if (!shadow) return;
|
||||
|
||||
const auto& fbo = shadow->framebuffer;
|
||||
|
||||
RenderArgs* args = renderContext->args;
|
||||
ShapeKey::Builder defaultKeyBuilder;
|
||||
auto adjustedShadowFrustum = args->getViewFrustum();
|
||||
|
||||
// Adjust the frustum near and far depths based on the rendered items bounding box to have
|
||||
// the minimal Z range.
|
||||
adjustNearFar(inShapeBounds, adjustedShadowFrustum);
|
||||
// Reapply the frustum as it has been adjusted
|
||||
shadow->setFrustum(adjustedShadowFrustum);
|
||||
args->popViewFrustum();
|
||||
args->pushViewFrustum(adjustedShadowFrustum);
|
||||
|
||||
gpu::doInBatch(args->_context, [&](gpu::Batch& batch) {
|
||||
args->_batch = &batch;
|
||||
|
@ -55,8 +166,13 @@ void RenderShadowMap::run(const render::RenderContextPointer& renderContext,
|
|||
gpu::Framebuffer::BUFFER_COLOR0 | gpu::Framebuffer::BUFFER_DEPTH,
|
||||
vec4(vec3(1.0, 1.0, 1.0), 0.0), 1.0, 0, true);
|
||||
|
||||
batch.setProjectionTransform(shadow->getProjection());
|
||||
batch.setViewTransform(shadow->getView(), false);
|
||||
glm::mat4 projMat;
|
||||
Transform viewMat;
|
||||
args->getViewFrustum().evalProjectionMatrix(projMat);
|
||||
args->getViewFrustum().evalViewTransform(viewMat);
|
||||
|
||||
batch.setProjectionTransform(projMat);
|
||||
batch.setViewTransform(viewMat, false);
|
||||
|
||||
auto shadowPipeline = _shapePlumber->pickPipeline(args, defaultKeyBuilder);
|
||||
auto shadowSkinnedPipeline = _shapePlumber->pickPipeline(args, defaultKeyBuilder.withSkinned());
|
||||
|
@ -87,7 +203,7 @@ void RenderShadowMap::run(const render::RenderContextPointer& renderContext,
|
|||
}
|
||||
|
||||
void RenderShadowTask::build(JobModel& task, const render::Varying& input, render::Varying& output, CullFunctor cullFunctor) {
|
||||
cullFunctor = cullFunctor ? cullFunctor : [](const RenderArgs*, const AABox&){ return true; };
|
||||
cullFunctor = cullFunctor ? cullFunctor : [](const RenderArgs*, const AABox&) { return true; };
|
||||
|
||||
// Prepare the ShapePipeline
|
||||
ShapePlumberPointer shapePlumber = std::make_shared<ShapePlumber>();
|
||||
|
@ -109,10 +225,10 @@ void RenderShadowTask::build(JobModel& task, const render::Varying& input, rende
|
|||
|
||||
// Sort
|
||||
const auto sortedPipelines = task.addJob<PipelineSortShapes>("PipelineSortShadowSort", culledShadowSelection);
|
||||
const auto sortedShapes = task.addJob<DepthSortShapes>("DepthSortShadowMap", sortedPipelines);
|
||||
const auto sortedShapesAndBounds = task.addJob<DepthSortShapesAndComputeBounds>("DepthSortShadowMap", sortedPipelines, true);
|
||||
|
||||
// GPU jobs: Render to shadow map
|
||||
task.addJob<RenderShadowMap>("RenderShadowMap", sortedShapes, shapePlumber);
|
||||
task.addJob<RenderShadowMap>("RenderShadowMap", sortedShapesAndBounds, shapePlumber);
|
||||
|
||||
task.addJob<RenderShadowTeardown>("ShadowTeardown", cachedMode);
|
||||
}
|
||||
|
@ -135,8 +251,8 @@ void RenderShadowSetup::run(const render::RenderContextPointer& renderContext, O
|
|||
|
||||
auto nearClip = args->getViewFrustum().getNearClip();
|
||||
float nearDepth = -args->_boomOffset.z;
|
||||
const int SHADOW_FAR_DEPTH = 20;
|
||||
globalShadow->setKeylightFrustum(args->getViewFrustum(), nearDepth, nearClip + SHADOW_FAR_DEPTH);
|
||||
const float SHADOW_MAX_DISTANCE = 20.0f;
|
||||
globalShadow->setKeylightFrustum(args->getViewFrustum(), nearDepth, nearClip + SHADOW_MAX_DISTANCE, SHADOW_FRUSTUM_NEAR, SHADOW_FRUSTUM_FAR);
|
||||
|
||||
// Set the keylight render args
|
||||
args->pushViewFrustum(*(globalShadow->getFrustum()));
|
||||
|
|
|
@ -21,11 +21,11 @@ class ViewFrustum;
|
|||
|
||||
class RenderShadowMap {
|
||||
public:
|
||||
using JobModel = render::Job::ModelI<RenderShadowMap, render::ShapeBounds>;
|
||||
using Inputs = render::VaryingSet2<render::ShapeBounds, AABox>;
|
||||
using JobModel = render::Job::ModelI<RenderShadowMap, Inputs>;
|
||||
|
||||
RenderShadowMap(render::ShapePlumberPointer shapePlumber) : _shapePlumber{ shapePlumber } {}
|
||||
void run(const render::RenderContextPointer& renderContext,
|
||||
const render::ShapeBounds& inShapes);
|
||||
void run(const render::RenderContextPointer& renderContext, const Inputs& inputs);
|
||||
|
||||
protected:
|
||||
render::ShapePlumberPointer _shapePlumber;
|
||||
|
|
|
@ -19,7 +19,10 @@
|
|||
void RenderViewTask::build(JobModel& task, const render::Varying& input, render::Varying& output, render::CullFunctor cullFunctor, bool isDeferred) {
|
||||
// auto items = input.get<Input>();
|
||||
|
||||
task.addJob<RenderShadowTask>("RenderShadowTask", cullFunctor);
|
||||
// Shadows use an orthographic projection because they are linked to sunlights
|
||||
// but the cullFunctor passed is probably tailored for perspective projection and culls too much.
|
||||
// TODO : create a special cull functor for this.
|
||||
task.addJob<RenderShadowTask>("RenderShadowTask", nullptr);
|
||||
|
||||
const auto items = task.addJob<RenderFetchCullSortTask>("FetchCullSort", cullFunctor);
|
||||
assert(items.canCast<RenderFetchCullSortTask::Output>());
|
||||
|
|
|
@ -68,6 +68,8 @@ vec2 PCFkernel[4] = vec2[4](
|
|||
);
|
||||
|
||||
float evalShadowAttenuationPCF(vec4 position, vec4 shadowTexcoord) {
|
||||
// PCF is buggy so disable it for the time being
|
||||
#if 0
|
||||
float pcfRadius = 3.0;
|
||||
float shadowScale = getShadowScale();
|
||||
|
||||
|
@ -80,6 +82,9 @@ float evalShadowAttenuationPCF(vec4 position, vec4 shadowTexcoord) {
|
|||
fetchShadow(shadowTexcoord.xyz + shadowScale * vec3(offset + PCFkernel[2], 0.0)) +
|
||||
fetchShadow(shadowTexcoord.xyz + shadowScale * vec3(offset + PCFkernel[3], 0.0))
|
||||
));
|
||||
#else
|
||||
float shadowAttenuation = fetchShadow(shadowTexcoord.xyz);
|
||||
#endif
|
||||
|
||||
return shadowAttenuation;
|
||||
}
|
||||
|
|
|
@ -10,8 +10,8 @@
|
|||
//
|
||||
|
||||
in vec4 _color;
|
||||
in float distanceFromCenter;
|
||||
|
||||
in float distanceFromCenter;
|
||||
out vec4 _fragColor;
|
||||
|
||||
void main(void) {
|
||||
|
|
|
@ -16,6 +16,7 @@ layout(std140) uniform lineData {
|
|||
vec4 p1;
|
||||
vec4 p2;
|
||||
vec4 color;
|
||||
float width;
|
||||
};
|
||||
|
||||
out vec4 _color;
|
||||
|
@ -39,7 +40,7 @@ void main(void) {
|
|||
// Find the vector from the eye to one of the points
|
||||
vec3 v2 = normalize(p1eye.xyz);
|
||||
// The orthogonal vector is the cross product of these two
|
||||
vec3 orthogonal = cross(v1, v2) * 0.02;
|
||||
vec3 orthogonal = cross(v1, v2) * width;
|
||||
|
||||
// Deteremine which end to emit based on the vertex id (even / odd)
|
||||
vec4 eye = (0 == gl_VertexID % 2) ? p1eye : p2eye;
|
||||
|
|
|
@ -29,11 +29,10 @@ enum BlurShaderMapSlots {
|
|||
BlurTask_DepthSlot,
|
||||
};
|
||||
|
||||
const float BLUR_NUM_SAMPLES = 7.0f;
|
||||
|
||||
BlurParams::BlurParams() {
|
||||
Params params;
|
||||
_parametersBuffer = gpu::BufferView(std::make_shared<gpu::Buffer>(sizeof(Params), (const gpu::Byte*) ¶ms));
|
||||
setFilterGaussianTaps(3);
|
||||
}
|
||||
|
||||
void BlurParams::setWidthHeight(int width, int height, bool isStereo) {
|
||||
|
@ -49,10 +48,10 @@ void BlurParams::setWidthHeight(int width, int height, bool isStereo) {
|
|||
}
|
||||
}
|
||||
|
||||
void BlurParams::setTexcoordTransform(const glm::vec4 texcoordTransformViewport) {
|
||||
auto texcoordTransform = _parametersBuffer.get<Params>().texcoordTransform;
|
||||
if (texcoordTransformViewport != texcoordTransform) {
|
||||
_parametersBuffer.edit<Params>().texcoordTransform = texcoordTransform;
|
||||
void BlurParams::setTexcoordTransform(glm::vec4 texcoordTransformViewport) {
|
||||
auto& params = _parametersBuffer.get<Params>();
|
||||
if (texcoordTransformViewport != params.texcoordTransform) {
|
||||
_parametersBuffer.edit<Params>().texcoordTransform = texcoordTransformViewport;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -60,7 +59,58 @@ void BlurParams::setFilterRadiusScale(float scale) {
|
|||
auto filterInfo = _parametersBuffer.get<Params>().filterInfo;
|
||||
if (scale != filterInfo.x) {
|
||||
_parametersBuffer.edit<Params>().filterInfo.x = scale;
|
||||
_parametersBuffer.edit<Params>().filterInfo.y = scale / BLUR_NUM_SAMPLES;
|
||||
}
|
||||
}
|
||||
|
||||
void BlurParams::setFilterNumTaps(int count) {
|
||||
assert(count <= BLUR_MAX_NUM_TAPS);
|
||||
auto filterInfo = _parametersBuffer.get<Params>().filterInfo;
|
||||
if (count != (int)filterInfo.y) {
|
||||
_parametersBuffer.edit<Params>().filterInfo.y = count;
|
||||
}
|
||||
}
|
||||
|
||||
void BlurParams::setFilterTap(int index, float offset, float value) {
|
||||
auto filterTaps = _parametersBuffer.edit<Params>().filterTaps;
|
||||
assert(index < BLUR_MAX_NUM_TAPS);
|
||||
filterTaps[index].x = offset;
|
||||
filterTaps[index].y = value;
|
||||
}
|
||||
|
||||
void BlurParams::setFilterGaussianTaps(int numHalfTaps, float sigma) {
|
||||
auto& params = _parametersBuffer.edit<Params>();
|
||||
const int numTaps = 2 * numHalfTaps + 1;
|
||||
assert(numTaps <= BLUR_MAX_NUM_TAPS);
|
||||
assert(sigma > 0.0f);
|
||||
const float inverseTwoSigmaSquared = float(0.5 / double(sigma*sigma));
|
||||
float totalWeight = 1.0f;
|
||||
float weight;
|
||||
float offset;
|
||||
int i;
|
||||
|
||||
params.filterInfo.y = numTaps;
|
||||
params.filterTaps[0].x = 0.0f;
|
||||
params.filterTaps[0].y = 1.0f;
|
||||
|
||||
for (i = 0; i < numHalfTaps; i++) {
|
||||
offset = i + 1;
|
||||
weight = (float)exp(-offset*offset * inverseTwoSigmaSquared);
|
||||
params.filterTaps[i + 1].x = offset;
|
||||
params.filterTaps[i + 1].y = weight;
|
||||
params.filterTaps[i + 1 + numHalfTaps].x = -offset;
|
||||
params.filterTaps[i + 1 + numHalfTaps].y = weight;
|
||||
totalWeight += 2 * weight;
|
||||
}
|
||||
|
||||
// Tap weights will be normalized in shader because side cases on edges of screen
|
||||
// won't have the same number of taps as in the center.
|
||||
}
|
||||
|
||||
void BlurParams::setOutputAlpha(float value) {
|
||||
value = glm::clamp(value, 0.0f, 1.0f);
|
||||
auto filterInfo = _parametersBuffer.get<Params>().filterInfo;
|
||||
if (value != filterInfo.z) {
|
||||
_parametersBuffer.edit<Params>().filterInfo.z = value;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -86,17 +136,23 @@ void BlurParams::setLinearDepthPosFar(float farPosDepth) {
|
|||
}
|
||||
|
||||
|
||||
BlurInOutResource::BlurInOutResource(bool generateOutputFramebuffer) :
|
||||
_generateOutputFramebuffer(generateOutputFramebuffer)
|
||||
{
|
||||
|
||||
BlurInOutResource::BlurInOutResource(bool generateOutputFramebuffer, unsigned int downsampleFactor) :
|
||||
_downsampleFactor(downsampleFactor),
|
||||
_generateOutputFramebuffer(generateOutputFramebuffer) {
|
||||
assert(downsampleFactor > 0);
|
||||
}
|
||||
|
||||
bool BlurInOutResource::updateResources(const gpu::FramebufferPointer& sourceFramebuffer, Resources& blurringResources) {
|
||||
if (!sourceFramebuffer) {
|
||||
return false;
|
||||
}
|
||||
if (_blurredFramebuffer && _blurredFramebuffer->getSize() != sourceFramebuffer->getSize()) {
|
||||
|
||||
auto blurBufferSize = sourceFramebuffer->getSize();
|
||||
|
||||
blurBufferSize.x /= _downsampleFactor;
|
||||
blurBufferSize.y /= _downsampleFactor;
|
||||
|
||||
if (_blurredFramebuffer && _blurredFramebuffer->getSize() != blurBufferSize) {
|
||||
_blurredFramebuffer.reset();
|
||||
}
|
||||
|
||||
|
@ -108,7 +164,7 @@ bool BlurInOutResource::updateResources(const gpu::FramebufferPointer& sourceFra
|
|||
// _blurredFramebuffer->setDepthStencilBuffer(sourceFramebuffer->getDepthStencilBuffer(), sourceFramebuffer->getDepthStencilBufferFormat());
|
||||
//}
|
||||
auto blurringSampler = gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_LINEAR_MIP_POINT);
|
||||
auto blurringTarget = gpu::Texture::create2D(sourceFramebuffer->getRenderBuffer(0)->getTexelFormat(), sourceFramebuffer->getWidth(), sourceFramebuffer->getHeight(), gpu::Texture::SINGLE_MIP, blurringSampler);
|
||||
auto blurringTarget = gpu::Texture::createRenderBuffer(sourceFramebuffer->getRenderBuffer(0)->getTexelFormat(), blurBufferSize.x, blurBufferSize.y, gpu::Texture::SINGLE_MIP, blurringSampler);
|
||||
_blurredFramebuffer->setRenderBuffer(0, blurringTarget);
|
||||
}
|
||||
|
||||
|
@ -117,7 +173,7 @@ bool BlurInOutResource::updateResources(const gpu::FramebufferPointer& sourceFra
|
|||
blurringResources.blurringTexture = _blurredFramebuffer->getRenderBuffer(0);
|
||||
|
||||
if (_generateOutputFramebuffer) {
|
||||
if (_outputFramebuffer && _outputFramebuffer->getSize() != sourceFramebuffer->getSize()) {
|
||||
if (_outputFramebuffer && _outputFramebuffer->getSize() != blurBufferSize) {
|
||||
_outputFramebuffer.reset();
|
||||
}
|
||||
|
||||
|
@ -131,7 +187,7 @@ bool BlurInOutResource::updateResources(const gpu::FramebufferPointer& sourceFra
|
|||
_outputFramebuffer->setDepthStencilBuffer(sourceFramebuffer->getDepthStencilBuffer(), sourceFramebuffer->getDepthStencilBufferFormat());
|
||||
}*/
|
||||
auto blurringSampler = gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_LINEAR_MIP_POINT);
|
||||
auto blurringTarget = gpu::Texture::create2D(sourceFramebuffer->getRenderBuffer(0)->getTexelFormat(), sourceFramebuffer->getWidth(), sourceFramebuffer->getHeight(), gpu::Texture::SINGLE_MIP, blurringSampler);
|
||||
auto blurringTarget = gpu::Texture::createRenderBuffer(sourceFramebuffer->getRenderBuffer(0)->getTexelFormat(), blurBufferSize.x, blurBufferSize.y, gpu::Texture::SINGLE_MIP, blurringSampler);
|
||||
_outputFramebuffer->setRenderBuffer(0, blurringTarget);
|
||||
}
|
||||
|
||||
|
@ -145,8 +201,8 @@ bool BlurInOutResource::updateResources(const gpu::FramebufferPointer& sourceFra
|
|||
return true;
|
||||
}
|
||||
|
||||
BlurGaussian::BlurGaussian(bool generateOutputFramebuffer) :
|
||||
_inOutResources(generateOutputFramebuffer)
|
||||
BlurGaussian::BlurGaussian(bool generateOutputFramebuffer, unsigned int downsampleFactor) :
|
||||
_inOutResources(generateOutputFramebuffer, downsampleFactor)
|
||||
{
|
||||
_parameters = std::make_shared<BlurParams>();
|
||||
}
|
||||
|
@ -196,7 +252,16 @@ gpu::PipelinePointer BlurGaussian::getBlurHPipeline() {
|
|||
}
|
||||
|
||||
void BlurGaussian::configure(const Config& config) {
|
||||
auto state = getBlurHPipeline()->getState();
|
||||
|
||||
_parameters->setFilterRadiusScale(config.filterScale);
|
||||
_parameters->setOutputAlpha(config.mix);
|
||||
if (config.mix < 1.0f) {
|
||||
state->setBlendFunction(config.mix < 1.0f, gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA,
|
||||
gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA);
|
||||
} else {
|
||||
state->setBlendFunction(false);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -206,7 +271,6 @@ void BlurGaussian::run(const RenderContextPointer& renderContext, const gpu::Fra
|
|||
|
||||
RenderArgs* args = renderContext->args;
|
||||
|
||||
|
||||
BlurInOutResource::Resources blurringResources;
|
||||
if (!_inOutResources.updateResources(sourceFramebuffer, blurringResources)) {
|
||||
// early exit if no valid blurring resources
|
||||
|
@ -216,14 +280,15 @@ void BlurGaussian::run(const RenderContextPointer& renderContext, const gpu::Fra
|
|||
|
||||
auto blurVPipeline = getBlurVPipeline();
|
||||
auto blurHPipeline = getBlurHPipeline();
|
||||
glm::ivec4 viewport { 0, 0, blurredFramebuffer->getWidth(), blurredFramebuffer->getHeight() };
|
||||
|
||||
_parameters->setWidthHeight(args->_viewport.z, args->_viewport.w, args->isStereo());
|
||||
glm::ivec2 textureSize(blurringResources.sourceTexture->getDimensions());
|
||||
_parameters->setTexcoordTransform(gpu::Framebuffer::evalSubregionTexcoordTransformCoefficients(textureSize, args->_viewport));
|
||||
glm::ivec2 textureSize = blurredFramebuffer->getSize();
|
||||
_parameters->setWidthHeight(blurredFramebuffer->getWidth(), blurredFramebuffer->getHeight(), args->isStereo());
|
||||
_parameters->setTexcoordTransform(gpu::Framebuffer::evalSubregionTexcoordTransformCoefficients(textureSize, viewport));
|
||||
|
||||
gpu::doInBatch(args->_context, [=](gpu::Batch& batch) {
|
||||
batch.enableStereo(false);
|
||||
batch.setViewportTransform(args->_viewport);
|
||||
batch.setViewportTransform(viewport);
|
||||
|
||||
batch.setUniformBuffer(BlurTask_ParamsSlot, _parameters->_parametersBuffer);
|
||||
|
||||
|
@ -251,7 +316,7 @@ void BlurGaussian::run(const RenderContextPointer& renderContext, const gpu::Fra
|
|||
|
||||
|
||||
BlurGaussianDepthAware::BlurGaussianDepthAware(bool generateOutputFramebuffer, const BlurParamsPointer& params) :
|
||||
_inOutResources(generateOutputFramebuffer),
|
||||
_inOutResources(generateOutputFramebuffer, 1U),
|
||||
_parameters((params ? params : std::make_shared<BlurParams>()))
|
||||
{
|
||||
}
|
||||
|
|
|
@ -14,6 +14,8 @@
|
|||
|
||||
#include "Engine.h"
|
||||
|
||||
#include "BlurTask_shared.slh"
|
||||
|
||||
namespace render {
|
||||
|
||||
|
||||
|
@ -25,6 +27,11 @@ public:
|
|||
void setTexcoordTransform(const glm::vec4 texcoordTransformViewport);
|
||||
|
||||
void setFilterRadiusScale(float scale);
|
||||
void setFilterNumTaps(int count);
|
||||
// Tap 0 is considered the center of the kernel
|
||||
void setFilterTap(int index, float offset, float value);
|
||||
void setFilterGaussianTaps(int numHalfTaps, float sigma = 1.47f);
|
||||
void setOutputAlpha(float value);
|
||||
|
||||
void setDepthPerspective(float oneOverTan2FOV);
|
||||
void setDepthThreshold(float threshold);
|
||||
|
@ -40,7 +47,7 @@ public:
|
|||
// Viewport to Texcoord info, if the region of the blur (viewport) is smaller than the full frame
|
||||
glm::vec4 texcoordTransform{ 0.0f, 0.0f, 1.0f, 1.0f };
|
||||
|
||||
// Filter info (radius scale
|
||||
// Filter info (radius scale, number of taps, output alpha)
|
||||
glm::vec4 filterInfo{ 1.0f, 0.0f, 0.0f, 0.0f };
|
||||
|
||||
// Depth info (radius scale
|
||||
|
@ -52,6 +59,9 @@ public:
|
|||
// LinearDepth info is { f }
|
||||
glm::vec4 linearDepthInfo{ 0.0f };
|
||||
|
||||
// Taps (offset, weight)
|
||||
glm::vec2 filterTaps[BLUR_MAX_NUM_TAPS];
|
||||
|
||||
Params() {}
|
||||
};
|
||||
gpu::BufferView _parametersBuffer;
|
||||
|
@ -62,7 +72,7 @@ using BlurParamsPointer = std::shared_ptr<BlurParams>;
|
|||
|
||||
class BlurInOutResource {
|
||||
public:
|
||||
BlurInOutResource(bool generateOutputFramebuffer = false);
|
||||
BlurInOutResource(bool generateOutputFramebuffer, unsigned int downsampleFactor);
|
||||
|
||||
struct Resources {
|
||||
gpu::TexturePointer sourceTexture;
|
||||
|
@ -75,8 +85,9 @@ public:
|
|||
|
||||
gpu::FramebufferPointer _blurredFramebuffer;
|
||||
|
||||
// the output framebuffer defined if the job needs to output the result in a new framebuffer and not in place in th einput buffer
|
||||
// the output framebuffer defined if the job needs to output the result in a new framebuffer and not in place in the input buffer
|
||||
gpu::FramebufferPointer _outputFramebuffer;
|
||||
unsigned int _downsampleFactor{ 1U };
|
||||
bool _generateOutputFramebuffer{ false };
|
||||
};
|
||||
|
||||
|
@ -84,12 +95,15 @@ public:
|
|||
class BlurGaussianConfig : public Job::Config {
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(bool enabled WRITE setEnabled READ isEnabled NOTIFY dirty) // expose enabled flag
|
||||
Q_PROPERTY(float filterScale MEMBER filterScale NOTIFY dirty) // expose enabled flag
|
||||
Q_PROPERTY(float filterScale MEMBER filterScale NOTIFY dirty)
|
||||
Q_PROPERTY(float mix MEMBER mix NOTIFY dirty)
|
||||
public:
|
||||
|
||||
BlurGaussianConfig() : Job::Config(true) {}
|
||||
|
||||
float filterScale{ 0.2f };
|
||||
float mix{ 1.0f };
|
||||
|
||||
signals :
|
||||
void dirty();
|
||||
|
||||
|
@ -102,11 +116,13 @@ public:
|
|||
using Config = BlurGaussianConfig;
|
||||
using JobModel = Job::ModelIO<BlurGaussian, gpu::FramebufferPointer, gpu::FramebufferPointer, Config>;
|
||||
|
||||
BlurGaussian(bool generateOutputFramebuffer = false);
|
||||
BlurGaussian(bool generateOutputFramebuffer = false, unsigned int downsampleFactor = 1U);
|
||||
|
||||
void configure(const Config& config);
|
||||
void run(const RenderContextPointer& renderContext, const gpu::FramebufferPointer& sourceFramebuffer, gpu::FramebufferPointer& blurredFramebuffer);
|
||||
|
||||
BlurParamsPointer getParameters() const { return _parameters; }
|
||||
|
||||
protected:
|
||||
|
||||
BlurParamsPointer _parameters;
|
||||
|
|
|
@ -9,17 +9,7 @@
|
|||
|
||||
<@func declareBlurUniforms()@>
|
||||
|
||||
#define NUM_TAPS 7
|
||||
#define NUM_TAPS_OFFSET 3.0f
|
||||
|
||||
float uniformFilterWidth = 0.05f;
|
||||
|
||||
const float gaussianDistributionCurve[NUM_TAPS] = float[](
|
||||
0.383f, 0.006f, 0.061f, 0.242f, 0.242f, 0.061f, 0.006f
|
||||
);
|
||||
const float gaussianDistributionOffset[NUM_TAPS] = float[](
|
||||
0.0f, -3.0f, -2.0f, -1.0f, 1.0f, 2.0f, 3.0f
|
||||
);
|
||||
<@include BlurTask_shared.slh@>
|
||||
|
||||
struct BlurParameters {
|
||||
vec4 resolutionInfo;
|
||||
|
@ -28,6 +18,7 @@ struct BlurParameters {
|
|||
vec4 depthInfo;
|
||||
vec4 stereoInfo;
|
||||
vec4 linearDepthInfo;
|
||||
vec2 taps[BLUR_MAX_NUM_TAPS];
|
||||
};
|
||||
|
||||
uniform blurParamsBuffer {
|
||||
|
@ -46,6 +37,25 @@ float getFilterScale() {
|
|||
return parameters.filterInfo.x;
|
||||
}
|
||||
|
||||
int getFilterNumTaps() {
|
||||
return int(parameters.filterInfo.y);
|
||||
}
|
||||
|
||||
float getOutputAlpha() {
|
||||
return parameters.filterInfo.z;
|
||||
}
|
||||
|
||||
vec2 getFilterTap(int index) {
|
||||
return parameters.taps[index];
|
||||
}
|
||||
|
||||
float getFilterTapOffset(vec2 tap) {
|
||||
return tap.x;
|
||||
}
|
||||
|
||||
float getFilterTapWeight(vec2 tap) {
|
||||
return tap.y;
|
||||
}
|
||||
|
||||
float getDepthThreshold() {
|
||||
return parameters.depthInfo.x;
|
||||
|
@ -70,19 +80,29 @@ uniform sampler2D sourceMap;
|
|||
|
||||
vec4 pixelShaderGaussian(vec2 texcoord, vec2 direction, vec2 pixelStep) {
|
||||
texcoord = evalTexcoordTransformed(texcoord);
|
||||
vec4 sampleCenter = texture(sourceMap, texcoord);
|
||||
|
||||
vec2 finalStep = getFilterScale() * direction * pixelStep;
|
||||
vec4 srcBlurred = vec4(0.0);
|
||||
float totalWeight = 0.f;
|
||||
int numTaps = getFilterNumTaps();
|
||||
|
||||
for(int i = 0; i < NUM_TAPS; i++) {
|
||||
// Fetch color and depth for current sample.
|
||||
vec2 sampleCoord = texcoord + (gaussianDistributionOffset[i] * finalStep);
|
||||
vec4 srcSample = texture(sourceMap, sampleCoord);
|
||||
// Accumulate.
|
||||
srcBlurred += gaussianDistributionCurve[i] * srcSample;
|
||||
for(int i = 0; i < numTaps; i++) {
|
||||
vec2 tapInfo = getFilterTap(i);
|
||||
// Fetch color for current sample.
|
||||
vec2 sampleCoord = texcoord + (getFilterTapOffset(tapInfo) * finalStep);
|
||||
if (all(greaterThanEqual(sampleCoord, vec2(0,0))) && all(lessThanEqual(sampleCoord, vec2(1.0,1.0)))) {
|
||||
vec4 srcSample = texture(sourceMap, sampleCoord);
|
||||
float weight = getFilterTapWeight(tapInfo);
|
||||
// Accumulate.
|
||||
srcBlurred += srcSample * weight;
|
||||
totalWeight += weight;
|
||||
}
|
||||
}
|
||||
|
||||
if (totalWeight>0.0) {
|
||||
srcBlurred /= totalWeight;
|
||||
}
|
||||
srcBlurred.a = getOutputAlpha();
|
||||
return srcBlurred;
|
||||
}
|
||||
|
||||
|
@ -95,15 +115,6 @@ vec4 pixelShaderGaussian(vec2 texcoord, vec2 direction, vec2 pixelStep) {
|
|||
uniform sampler2D sourceMap;
|
||||
uniform sampler2D depthMap;
|
||||
|
||||
#define NUM_HALF_TAPS 4
|
||||
|
||||
const float gaussianDistributionCurveHalf[NUM_HALF_TAPS] = float[](
|
||||
0.383f, 0.242f, 0.061f, 0.006f
|
||||
);
|
||||
const float gaussianDistributionOffsetHalf[NUM_HALF_TAPS] = float[](
|
||||
0.0f, 1.0f, 2.0f, 3.0f
|
||||
);
|
||||
|
||||
vec4 pixelShaderGaussianDepthAware(vec2 texcoord, vec2 direction, vec2 pixelStep) {
|
||||
texcoord = evalTexcoordTransformed(texcoord);
|
||||
float sampleDepth = texture(depthMap, texcoord).x;
|
||||
|
@ -122,45 +133,36 @@ vec4 pixelShaderGaussianDepthAware(vec2 texcoord, vec2 direction, vec2 pixelStep
|
|||
float scale = distanceToProjectionWindow / sampleDepth;
|
||||
|
||||
vec2 finalStep = filterScale * scale * direction * pixelStep;
|
||||
int numTaps = getFilterNumTaps();
|
||||
|
||||
// Accumulate the center sample
|
||||
vec4 srcBlurred = gaussianDistributionCurve[0] * sampleCenter;
|
||||
vec2 tapInfo = getFilterTap(0);
|
||||
float totalWeight = getFilterTapWeight(tapInfo);
|
||||
vec4 srcBlurred = sampleCenter * totalWeight;
|
||||
|
||||
for(int i = 1; i < numTaps; i++) {
|
||||
tapInfo = getFilterTap(i);
|
||||
|
||||
for(int i = 1; i < NUM_TAPS; i++) {
|
||||
// Fetch color and depth for current sample.
|
||||
vec2 sampleCoord = texcoord + (gaussianDistributionOffset[i] * finalStep);
|
||||
float srcDepth = texture(depthMap, sampleCoord).x;
|
||||
vec4 srcSample = texture(sourceMap, sampleCoord);
|
||||
vec2 sampleCoord = texcoord + (getFilterTapOffset(tapInfo) * finalStep);
|
||||
if (all(greaterThanEqual(sampleCoord, vec2(0,0))) && all(lessThanEqual(sampleCoord, vec2(1.0,1.0)))) {
|
||||
float srcDepth = texture(depthMap, sampleCoord).x;
|
||||
vec4 srcSample = texture(sourceMap, sampleCoord);
|
||||
float weight = getFilterTapWeight(tapInfo);
|
||||
|
||||
// If the difference in depth is huge, we lerp color back.
|
||||
float s = clamp(depthThreshold * distanceToProjectionWindow * filterScale * abs(srcDepth - sampleDepth), 0.0, 1.0);
|
||||
srcSample = mix(srcSample, sampleCenter, s);
|
||||
// If the difference in depth is huge, we lerp color back.
|
||||
float s = clamp(depthThreshold * distanceToProjectionWindow * filterScale * abs(srcDepth - sampleDepth), 0.0, 1.0);
|
||||
srcSample = mix(srcSample, sampleCenter, s);
|
||||
|
||||
// Accumulate.
|
||||
srcBlurred += gaussianDistributionCurve[i] * srcSample;
|
||||
// Accumulate.
|
||||
srcBlurred += srcSample * weight;
|
||||
totalWeight += weight;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
for(int i = 1; i < NUM_HALF_TAPS; i++) {
|
||||
// Fetch color and depth for current sample.
|
||||
vec2 texcoordOffset = (gaussianDistributionOffsetHalf[i] * finalStep);
|
||||
|
||||
float srcDepthN = texture(depthMap, texcoord - texcoordOffset).x;
|
||||
float srcDepthP = texture(depthMap, texcoord + texcoordOffset).x;
|
||||
vec4 srcSampleN = texture(sourceMap, texcoord - texcoordOffset);
|
||||
vec4 srcSampleP = texture(sourceMap, texcoord + texcoordOffset);
|
||||
|
||||
// If the difference in depth is huge, we lerp color back.
|
||||
float sN = clamp(depthThreshold * distanceToProjectionWindow * filterScale * abs(srcDepthN - sampleDepth), 0.0, 1.0);
|
||||
float sP = clamp(depthThreshold * distanceToProjectionWindow * filterScale * abs(srcDepthP - sampleDepth), 0.0, 1.0);
|
||||
|
||||
srcSampleN = mix(srcSampleN, sampleCenter, sN);
|
||||
srcSampleP = mix(srcSampleP, sampleCenter, sP);
|
||||
|
||||
// Accumulate.
|
||||
srcBlurred += gaussianDistributionCurveHalf[i] * (srcSampleP + srcSampleN);
|
||||
}*/
|
||||
|
||||
if (totalWeight>0.0) {
|
||||
srcBlurred /= totalWeight;
|
||||
}
|
||||
return srcBlurred;
|
||||
}
|
||||
|
||||
|
|
10
libraries/render/src/render/BlurTask_shared.slh
Normal file
10
libraries/render/src/render/BlurTask_shared.slh
Normal file
|
@ -0,0 +1,10 @@
|
|||
// Generated on <$_SCRIBE_DATE$>
|
||||
//
|
||||
// Created by Olivier Prat on 09/25/17.
|
||||
// Copyright 2017 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#define BLUR_MAX_NUM_TAPS 33
|
|
@ -20,7 +20,7 @@
|
|||
#include <PerfStat.h>
|
||||
#include <ViewFrustum.h>
|
||||
#include <gpu/Context.h>
|
||||
|
||||
#include <gpu/StandardShaderLib.h>
|
||||
|
||||
#include <drawItemBounds_vert.h>
|
||||
#include <drawItemBounds_frag.h>
|
||||
|
@ -215,3 +215,85 @@ void DrawBounds::run(const RenderContextPointer& renderContext,
|
|||
});
|
||||
}
|
||||
|
||||
gpu::PipelinePointer DrawFrustum::_pipeline;
|
||||
gpu::BufferView DrawFrustum::_frustumMeshIndices;
|
||||
|
||||
DrawFrustum::DrawFrustum(const glm::vec3& color) :
|
||||
_color{ color } {
|
||||
_frustumMeshVertices = gpu::BufferView(std::make_shared<gpu::Buffer>(sizeof(glm::vec3) * 8, nullptr), gpu::Element::VEC3F_XYZ);
|
||||
_frustumMeshStream.addBuffer(_frustumMeshVertices._buffer, _frustumMeshVertices._offset, _frustumMeshVertices._stride);
|
||||
}
|
||||
|
||||
void DrawFrustum::configure(const Config& configuration) {
|
||||
_updateFrustum = !configuration.isFrozen;
|
||||
}
|
||||
|
||||
void DrawFrustum::run(const render::RenderContextPointer& renderContext, const Input& input) {
|
||||
assert(renderContext->args);
|
||||
assert(renderContext->args->_context);
|
||||
|
||||
RenderArgs* args = renderContext->args;
|
||||
if (input) {
|
||||
const auto& frustum = *input;
|
||||
|
||||
static uint8_t indexData[] = { 0, 1, 2, 3, 0, 4, 5, 6, 7, 4, 5, 1, 2, 6, 7, 3 };
|
||||
|
||||
if (!_frustumMeshIndices._buffer) {
|
||||
auto indices = std::make_shared<gpu::Buffer>(sizeof(indexData), indexData);
|
||||
_frustumMeshIndices = gpu::BufferView(indices, gpu::Element(gpu::SCALAR, gpu::UINT8, gpu::INDEX));
|
||||
}
|
||||
|
||||
if (!_pipeline) {
|
||||
auto vs = gpu::StandardShaderLib::getDrawTransformVertexPositionVS();
|
||||
auto ps = gpu::StandardShaderLib::getDrawColorPS();
|
||||
gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps);
|
||||
|
||||
gpu::Shader::BindingSet slotBindings;
|
||||
slotBindings.insert(gpu::Shader::Binding("color", 0));
|
||||
gpu::Shader::makeProgram(*program, slotBindings);
|
||||
|
||||
gpu::StatePointer state = gpu::StatePointer(new gpu::State());
|
||||
state->setDepthTest(gpu::State::DepthTest(true, false));
|
||||
_pipeline = gpu::Pipeline::create(program, state);
|
||||
}
|
||||
|
||||
if (_updateFrustum) {
|
||||
updateFrustum(frustum);
|
||||
}
|
||||
|
||||
// Render the frustums in wireframe
|
||||
gpu::doInBatch(args->_context, [&](gpu::Batch& batch) {
|
||||
args->_batch = &batch;
|
||||
batch.setViewportTransform(args->_viewport);
|
||||
batch.setStateScissorRect(args->_viewport);
|
||||
|
||||
glm::mat4 projMat;
|
||||
Transform viewMat;
|
||||
args->getViewFrustum().evalProjectionMatrix(projMat);
|
||||
args->getViewFrustum().evalViewTransform(viewMat);
|
||||
|
||||
batch.setProjectionTransform(projMat);
|
||||
batch.setViewTransform(viewMat);
|
||||
batch.setPipeline(_pipeline);
|
||||
batch.setIndexBuffer(_frustumMeshIndices);
|
||||
|
||||
batch._glUniform4f(0, _color.x, _color.y, _color.z, 1.0f);
|
||||
batch.setInputStream(0, _frustumMeshStream);
|
||||
batch.drawIndexed(gpu::LINE_STRIP, sizeof(indexData) / sizeof(indexData[0]), 0U);
|
||||
|
||||
args->_batch = nullptr;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void DrawFrustum::updateFrustum(const ViewFrustum& frustum) {
|
||||
auto& vertices = _frustumMeshVertices.edit<std::array<glm::vec3, 8U> >();
|
||||
vertices[0] = frustum.getNearTopLeft();
|
||||
vertices[1] = frustum.getNearTopRight();
|
||||
vertices[2] = frustum.getNearBottomRight();
|
||||
vertices[3] = frustum.getNearBottomLeft();
|
||||
vertices[4] = frustum.getFarTopLeft();
|
||||
vertices[5] = frustum.getFarTopRight();
|
||||
vertices[6] = frustum.getFarBottomRight();
|
||||
vertices[7] = frustum.getFarBottomLeft();
|
||||
}
|
||||
|
|
|
@ -70,6 +70,43 @@ private:
|
|||
int _colorLocation { -1 };
|
||||
};
|
||||
|
||||
class DrawFrustumConfig : public render::JobConfig {
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(bool isFrozen MEMBER isFrozen NOTIFY dirty)
|
||||
public:
|
||||
|
||||
DrawFrustumConfig(bool enabled = false) : JobConfig(enabled) {}
|
||||
|
||||
bool isFrozen{ false };
|
||||
signals:
|
||||
void dirty();
|
||||
|
||||
};
|
||||
|
||||
class DrawFrustum {
|
||||
public:
|
||||
using Config = DrawFrustumConfig;
|
||||
using Input = ViewFrustumPointer;
|
||||
using JobModel = render::Job::ModelI<DrawFrustum, Input, Config>;
|
||||
|
||||
DrawFrustum(const glm::vec3& color = glm::vec3(1.0f, 1.0f, 1.0f));
|
||||
|
||||
void configure(const Config& configuration);
|
||||
void run(const render::RenderContextPointer& renderContext, const Input& input);
|
||||
|
||||
private:
|
||||
|
||||
static gpu::PipelinePointer _pipeline;
|
||||
static gpu::BufferView _frustumMeshIndices;
|
||||
|
||||
bool _updateFrustum{ true };
|
||||
gpu::BufferView _frustumMeshVertices;
|
||||
gpu::BufferStream _frustumMeshStream;
|
||||
glm::vec3 _color;
|
||||
|
||||
void updateFrustum(const ViewFrustum& frustum);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // hifi_render_DrawTask_h
|
||||
|
|
83
libraries/render/src/render/ResampleTask.cpp
Normal file
83
libraries/render/src/render/ResampleTask.cpp
Normal file
|
@ -0,0 +1,83 @@
|
|||
//
|
||||
// ResampleTask.cpp
|
||||
// render/src/render
|
||||
//
|
||||
// Various to upsample or downsample textures into framebuffers.
|
||||
//
|
||||
// Created by Olivier Prat on 10/09/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 "ResampleTask.h"
|
||||
|
||||
#include "gpu/Context.h"
|
||||
#include "gpu/StandardShaderLib.h"
|
||||
|
||||
using namespace render;
|
||||
|
||||
gpu::PipelinePointer HalfDownsample::_pipeline;
|
||||
|
||||
HalfDownsample::HalfDownsample() {
|
||||
|
||||
}
|
||||
|
||||
void HalfDownsample::configure(const Config& config) {
|
||||
|
||||
}
|
||||
|
||||
gpu::FramebufferPointer HalfDownsample::getResampledFrameBuffer(const gpu::FramebufferPointer& sourceFramebuffer) {
|
||||
auto resampledFramebufferSize = sourceFramebuffer->getSize();
|
||||
|
||||
resampledFramebufferSize.x /= 2U;
|
||||
resampledFramebufferSize.y /= 2U;
|
||||
|
||||
if (!_destinationFrameBuffer || resampledFramebufferSize != _destinationFrameBuffer->getSize()) {
|
||||
_destinationFrameBuffer = gpu::FramebufferPointer(gpu::Framebuffer::create("HalfOutput"));
|
||||
|
||||
auto sampler = gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_LINEAR_MIP_POINT);
|
||||
auto target = gpu::Texture::createRenderBuffer(sourceFramebuffer->getRenderBuffer(0)->getTexelFormat(), resampledFramebufferSize.x, resampledFramebufferSize.y, gpu::Texture::SINGLE_MIP, sampler);
|
||||
_destinationFrameBuffer->setRenderBuffer(0, target);
|
||||
}
|
||||
return _destinationFrameBuffer;
|
||||
}
|
||||
|
||||
void HalfDownsample::run(const RenderContextPointer& renderContext, const gpu::FramebufferPointer& sourceFramebuffer, gpu::FramebufferPointer& resampledFrameBuffer) {
|
||||
assert(renderContext->args);
|
||||
assert(renderContext->args->hasViewFrustum());
|
||||
RenderArgs* args = renderContext->args;
|
||||
|
||||
resampledFrameBuffer = getResampledFrameBuffer(sourceFramebuffer);
|
||||
|
||||
if (!_pipeline) {
|
||||
auto vs = gpu::StandardShaderLib::getDrawTransformUnitQuadVS();
|
||||
auto ps = gpu::StandardShaderLib::getDrawTextureOpaquePS();
|
||||
gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps);
|
||||
|
||||
gpu::Shader::BindingSet slotBindings;
|
||||
gpu::Shader::makeProgram(*program, slotBindings);
|
||||
|
||||
gpu::StatePointer state = gpu::StatePointer(new gpu::State());
|
||||
state->setDepthTest(gpu::State::DepthTest(false, false));
|
||||
_pipeline = gpu::Pipeline::create(program, state);
|
||||
}
|
||||
|
||||
const auto bufferSize = resampledFrameBuffer->getSize();
|
||||
glm::ivec4 viewport{ 0, 0, bufferSize.x, bufferSize.y };
|
||||
|
||||
gpu::doInBatch(args->_context, [&](gpu::Batch& batch) {
|
||||
batch.enableStereo(false);
|
||||
|
||||
batch.setFramebuffer(resampledFrameBuffer);
|
||||
|
||||
batch.setViewportTransform(viewport);
|
||||
batch.setProjectionTransform(glm::mat4());
|
||||
batch.resetViewTransform();
|
||||
batch.setPipeline(_pipeline);
|
||||
|
||||
batch.setModelTransform(gpu::Framebuffer::evalSubregionTexcoordTransform(bufferSize, viewport));
|
||||
batch.setResourceTexture(0, sourceFramebuffer->getRenderBuffer(0));
|
||||
batch.draw(gpu::TRIANGLE_STRIP, 4);
|
||||
});
|
||||
}
|
41
libraries/render/src/render/ResampleTask.h
Normal file
41
libraries/render/src/render/ResampleTask.h
Normal file
|
@ -0,0 +1,41 @@
|
|||
//
|
||||
// ResampleTask.h
|
||||
// render/src/render
|
||||
//
|
||||
// Various to upsample or downsample textures into framebuffers.
|
||||
//
|
||||
// Created by Olivier Prat on 10/09/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_ResampleTask_h
|
||||
#define hifi_render_ResampleTask_h
|
||||
|
||||
#include "Engine.h"
|
||||
|
||||
namespace render {
|
||||
|
||||
class HalfDownsample {
|
||||
public:
|
||||
using Config = JobConfig;
|
||||
using JobModel = Job::ModelIO<HalfDownsample, gpu::FramebufferPointer, gpu::FramebufferPointer, Config>;
|
||||
|
||||
HalfDownsample();
|
||||
|
||||
void configure(const Config& config);
|
||||
void run(const RenderContextPointer& renderContext, const gpu::FramebufferPointer& sourceFramebuffer, gpu::FramebufferPointer& resampledFrameBuffer);
|
||||
|
||||
protected:
|
||||
|
||||
static gpu::PipelinePointer _pipeline;
|
||||
|
||||
gpu::FramebufferPointer _destinationFrameBuffer;
|
||||
|
||||
gpu::FramebufferPointer getResampledFrameBuffer(const gpu::FramebufferPointer& sourceFramebuffer);
|
||||
};
|
||||
}
|
||||
|
||||
#endif // hifi_render_ResampleTask_h
|
|
@ -40,7 +40,8 @@ struct BackToFrontSort {
|
|||
}
|
||||
};
|
||||
|
||||
void render::depthSortItems(const RenderContextPointer& renderContext, bool frontToBack, const ItemBounds& inItems, ItemBounds& outItems) {
|
||||
void render::depthSortItems(const RenderContextPointer& renderContext, bool frontToBack,
|
||||
const ItemBounds& inItems, ItemBounds& outItems, AABox* bounds) {
|
||||
assert(renderContext->args);
|
||||
assert(renderContext->args->hasViewFrustum());
|
||||
|
||||
|
@ -74,12 +75,26 @@ void render::depthSortItems(const RenderContextPointer& renderContext, bool fron
|
|||
std::sort(itemBoundSorts.begin(), itemBoundSorts.end(), backToFrontSort);
|
||||
}
|
||||
|
||||
// Finally once sorted result to a list of itemID
|
||||
// Finally once sorted result to a list of itemID and keep uniques
|
||||
render::ItemID previousID = Item::INVALID_ITEM_ID;
|
||||
for (auto& item : itemBoundSorts) {
|
||||
if (item._id != previousID) {
|
||||
outItems.emplace_back(ItemBound(item._id, item._bounds));
|
||||
previousID = item._id;
|
||||
if (!bounds) {
|
||||
for (auto& item : itemBoundSorts) {
|
||||
if (item._id != previousID) {
|
||||
outItems.emplace_back(ItemBound(item._id, item._bounds));
|
||||
previousID = item._id;
|
||||
}
|
||||
}
|
||||
} else if (!itemBoundSorts.empty()) {
|
||||
if (bounds->isNull()) {
|
||||
*bounds = itemBoundSorts.front()._bounds;
|
||||
}
|
||||
for (auto& item : itemBoundSorts) {
|
||||
if (item._id != previousID) {
|
||||
outItems.emplace_back(ItemBound(item._id, item._bounds));
|
||||
previousID = item._id;
|
||||
*bounds += item._bounds;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -119,6 +134,27 @@ void DepthSortShapes::run(const RenderContextPointer& renderContext, const Shape
|
|||
}
|
||||
}
|
||||
|
||||
void DepthSortShapesAndComputeBounds::run(const RenderContextPointer& renderContext, const ShapeBounds& inShapes, Outputs& outputs) {
|
||||
auto& outShapes = outputs.edit0();
|
||||
auto& outBounds = outputs.edit1();
|
||||
|
||||
outShapes.clear();
|
||||
outShapes.reserve(inShapes.size());
|
||||
outBounds = AABox();
|
||||
|
||||
for (auto& pipeline : inShapes) {
|
||||
auto& inItems = pipeline.second;
|
||||
auto outItems = outShapes.find(pipeline.first);
|
||||
if (outItems == outShapes.end()) {
|
||||
outItems = outShapes.insert(std::make_pair(pipeline.first, ItemBounds{})).first;
|
||||
}
|
||||
AABox bounds;
|
||||
|
||||
depthSortItems(renderContext, _frontToBack, inItems, outItems->second, &bounds);
|
||||
outBounds += bounds;
|
||||
}
|
||||
}
|
||||
|
||||
void DepthSortItems::run(const RenderContextPointer& renderContext, const ItemBounds& inItems, ItemBounds& outItems) {
|
||||
depthSortItems(renderContext, _frontToBack, inItems, outItems);
|
||||
}
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
#include "Engine.h"
|
||||
|
||||
namespace render {
|
||||
void depthSortItems(const RenderContextPointer& renderContext, bool frontToBack, const ItemBounds& inItems, ItemBounds& outItems);
|
||||
void depthSortItems(const RenderContextPointer& renderContext, bool frontToBack, const ItemBounds& inItems, ItemBounds& outItems, AABox* bounds = nullptr);
|
||||
|
||||
class PipelineSortShapes {
|
||||
public:
|
||||
|
@ -33,6 +33,17 @@ namespace render {
|
|||
void run(const RenderContextPointer& renderContext, const ShapeBounds& inShapes, ShapeBounds& outShapes);
|
||||
};
|
||||
|
||||
class DepthSortShapesAndComputeBounds {
|
||||
public:
|
||||
using Outputs = VaryingSet2<ShapeBounds, AABox>;
|
||||
using JobModel = Job::ModelIO<DepthSortShapesAndComputeBounds, ShapeBounds, Outputs>;
|
||||
|
||||
bool _frontToBack;
|
||||
DepthSortShapesAndComputeBounds(bool frontToBack = true) : _frontToBack(frontToBack) {}
|
||||
|
||||
void run(const RenderContextPointer& renderContext, const ShapeBounds& inShapes, Outputs& outputs);
|
||||
};
|
||||
|
||||
class DepthSortItems {
|
||||
public:
|
||||
using JobModel = Job::ModelIO<DepthSortItems, ItemBounds, ItemBounds>;
|
||||
|
|
|
@ -171,6 +171,8 @@ public:
|
|||
_concept->setCPURunTime((double)(usecTimestampNow() - start) / 1000.0);
|
||||
}
|
||||
|
||||
const std::string& getName() const { return _name; }
|
||||
|
||||
protected:
|
||||
ConceptPointer _concept;
|
||||
std::string _name = "";
|
||||
|
@ -206,6 +208,24 @@ public:
|
|||
|
||||
const Varying getInput() const override { return _input; }
|
||||
const Varying getOutput() const override { return _output; }
|
||||
typename Jobs::iterator editJob(std::string name) {
|
||||
typename Jobs::iterator jobIt;
|
||||
for (jobIt = _jobs.begin(); jobIt != _jobs.end(); ++jobIt) {
|
||||
if (jobIt->getName() == name) {
|
||||
return jobIt;
|
||||
}
|
||||
}
|
||||
return jobIt;
|
||||
}
|
||||
typename Jobs::const_iterator getJob(std::string name) const {
|
||||
typename Jobs::const_iterator jobIt;
|
||||
for (jobIt = _jobs.begin(); jobIt != _jobs.end(); ++jobIt) {
|
||||
if (jobIt->getName() == name) {
|
||||
return jobIt;
|
||||
}
|
||||
}
|
||||
return jobIt;
|
||||
}
|
||||
|
||||
TaskConcept(const Varying& input, QConfigPointer config) : Concept(config), _input(input) {}
|
||||
|
||||
|
|
|
@ -127,10 +127,11 @@ public:
|
|||
|
||||
AABox getOctreeChild(OctreeChild child) const; // returns the AABox of the would be octree child of this AABox
|
||||
|
||||
glm::vec4 getPlane(BoxFace face) const;
|
||||
|
||||
private:
|
||||
glm::vec3 getClosestPointOnFace(const glm::vec3& point, BoxFace face) const;
|
||||
glm::vec3 getClosestPointOnFace(const glm::vec4& origin, const glm::vec4& direction, BoxFace face) const;
|
||||
glm::vec4 getPlane(BoxFace face) const;
|
||||
|
||||
static BoxFace getOppositeFace(BoxFace face);
|
||||
|
||||
|
|
|
@ -14,10 +14,12 @@
|
|||
#include <assert.h>
|
||||
#include <cstring>
|
||||
#include <cmath>
|
||||
#include <bitset>
|
||||
#include <glm/gtx/quaternion.hpp>
|
||||
|
||||
#include "NumericalConstants.h"
|
||||
#include "GLMHelpers.h"
|
||||
#include "Plane.h"
|
||||
|
||||
glm::vec3 computeVectorFromPointToSegment(const glm::vec3& point, const glm::vec3& start, const glm::vec3& end) {
|
||||
// compute the projection of the point vector onto the segment vector
|
||||
|
@ -314,6 +316,134 @@ bool findRayTriangleIntersection(const glm::vec3& origin, const glm::vec3& direc
|
|||
return false;
|
||||
}
|
||||
|
||||
static void getTrianglePlaneIntersectionPoints(const glm::vec3 trianglePoints[3], const float pointPlaneDistances[3],
|
||||
const int clippedPointIndex, const int keptPointIndices[2],
|
||||
glm::vec3 points[2]) {
|
||||
assert(clippedPointIndex >= 0 && clippedPointIndex < 3);
|
||||
const auto& clippedPoint = trianglePoints[clippedPointIndex];
|
||||
const float clippedPointPlaneDistance = pointPlaneDistances[clippedPointIndex];
|
||||
for (auto i = 0; i < 2; i++) {
|
||||
assert(keptPointIndices[i] >= 0 && keptPointIndices[i] < 3);
|
||||
const auto& keptPoint = trianglePoints[keptPointIndices[i]];
|
||||
const float keptPointPlaneDistance = pointPlaneDistances[keptPointIndices[i]];
|
||||
auto intersectionEdgeRatio = clippedPointPlaneDistance / (clippedPointPlaneDistance - keptPointPlaneDistance);
|
||||
points[i] = clippedPoint + (keptPoint - clippedPoint) * intersectionEdgeRatio;
|
||||
}
|
||||
}
|
||||
|
||||
int clipTriangleWithPlane(const Triangle& triangle, const Plane& plane, Triangle* clippedTriangles, int maxClippedTriangleCount) {
|
||||
float pointDistanceToPlane[3];
|
||||
std::bitset<3> arePointsClipped;
|
||||
glm::vec3 triangleVertices[3] = { triangle.v0, triangle.v1, triangle.v2 };
|
||||
int clippedTriangleCount = 0;
|
||||
int i;
|
||||
|
||||
assert(clippedTriangleCount > 0);
|
||||
|
||||
for (i = 0; i < 3; i++) {
|
||||
pointDistanceToPlane[i] = plane.distance(triangleVertices[i]);
|
||||
arePointsClipped.set(i, pointDistanceToPlane[i] < 0.0f);
|
||||
}
|
||||
|
||||
switch (arePointsClipped.count()) {
|
||||
case 0:
|
||||
// Easy, the entire triangle is kept as is.
|
||||
*clippedTriangles = triangle;
|
||||
clippedTriangleCount = 1;
|
||||
break;
|
||||
|
||||
case 1:
|
||||
{
|
||||
int clippedPointIndex = 2;
|
||||
int keptPointIndices[2] = { 0, 1 };
|
||||
glm::vec3 newVertices[2];
|
||||
|
||||
// Determine which point was clipped.
|
||||
if (arePointsClipped.test(0)) {
|
||||
clippedPointIndex = 0;
|
||||
keptPointIndices[0] = 2;
|
||||
} else if (arePointsClipped.test(1)) {
|
||||
clippedPointIndex = 1;
|
||||
keptPointIndices[1] = 2;
|
||||
}
|
||||
// We have a quad now, so we need to create two triangles.
|
||||
getTrianglePlaneIntersectionPoints(triangleVertices, pointDistanceToPlane, clippedPointIndex, keptPointIndices, newVertices);
|
||||
clippedTriangles->v0 = triangleVertices[keptPointIndices[0]];
|
||||
clippedTriangles->v1 = triangleVertices[keptPointIndices[1]];
|
||||
clippedTriangles->v2 = newVertices[1];
|
||||
clippedTriangles++;
|
||||
clippedTriangleCount++;
|
||||
|
||||
if (clippedTriangleCount < maxClippedTriangleCount) {
|
||||
clippedTriangles->v0 = triangleVertices[keptPointIndices[0]];
|
||||
clippedTriangles->v1 = newVertices[0];
|
||||
clippedTriangles->v2 = newVertices[1];
|
||||
clippedTriangles++;
|
||||
clippedTriangleCount++;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 2:
|
||||
{
|
||||
int keptPointIndex = 2;
|
||||
int clippedPointIndices[2] = { 0, 1 };
|
||||
glm::vec3 newVertices[2];
|
||||
|
||||
// Determine which point was NOT clipped.
|
||||
if (!arePointsClipped.test(0)) {
|
||||
keptPointIndex = 0;
|
||||
clippedPointIndices[0] = 2;
|
||||
} else if (!arePointsClipped.test(1)) {
|
||||
keptPointIndex = 1;
|
||||
clippedPointIndices[1] = 2;
|
||||
}
|
||||
// We have a single triangle
|
||||
getTrianglePlaneIntersectionPoints(triangleVertices, pointDistanceToPlane, keptPointIndex, clippedPointIndices, newVertices);
|
||||
clippedTriangles->v0 = triangleVertices[keptPointIndex];
|
||||
clippedTriangles->v1 = newVertices[0];
|
||||
clippedTriangles->v2 = newVertices[1];
|
||||
clippedTriangleCount = 1;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
// Entire triangle is clipped.
|
||||
break;
|
||||
}
|
||||
|
||||
return clippedTriangleCount;
|
||||
}
|
||||
|
||||
int clipTriangleWithPlanes(const Triangle& triangle, const Plane* planes, int planeCount, Triangle* clippedTriangles, int maxClippedTriangleCount) {
|
||||
auto planesEnd = planes + planeCount;
|
||||
int triangleCount = 1;
|
||||
std::vector<Triangle> trianglesToTest;
|
||||
|
||||
assert(maxClippedTriangleCount > 0);
|
||||
|
||||
*clippedTriangles = triangle;
|
||||
|
||||
while (planes < planesEnd) {
|
||||
int clippedSubTriangleCount;
|
||||
|
||||
trianglesToTest.clear();
|
||||
trianglesToTest.insert(trianglesToTest.begin(), clippedTriangles, clippedTriangles + triangleCount);
|
||||
triangleCount = 0;
|
||||
|
||||
for (const auto& triangleToTest : trianglesToTest) {
|
||||
clippedSubTriangleCount = clipTriangleWithPlane(triangleToTest, *planes,
|
||||
clippedTriangles + triangleCount, maxClippedTriangleCount - triangleCount);
|
||||
triangleCount += clippedSubTriangleCount;
|
||||
if (triangleCount >= maxClippedTriangleCount) {
|
||||
return triangleCount;
|
||||
}
|
||||
}
|
||||
++planes;
|
||||
}
|
||||
return triangleCount;
|
||||
}
|
||||
|
||||
// Do line segments (r1p1.x, r1p1.y)--(r1p2.x, r1p2.y) and (r2p1.x, r2p1.y)--(r2p2.x, r2p2.y) intersect?
|
||||
// from: http://ptspts.blogspot.com/2010/06/how-to-determine-if-two-line-segments.html
|
||||
bool doLineSegmentsIntersect(glm::vec2 r1p1, glm::vec2 r1p2, glm::vec2 r2p1, glm::vec2 r2p2) {
|
||||
|
|
|
@ -15,6 +15,8 @@
|
|||
#include <glm/glm.hpp>
|
||||
#include <vector>
|
||||
|
||||
class Plane;
|
||||
|
||||
glm::vec3 computeVectorFromPointToSegment(const glm::vec3& point, const glm::vec3& start, const glm::vec3& end);
|
||||
|
||||
/// Computes the penetration between a point and a sphere (centered at the origin)
|
||||
|
@ -109,6 +111,8 @@ inline bool findRayTriangleIntersection(const glm::vec3& origin, const glm::vec3
|
|||
return findRayTriangleIntersection(origin, direction, triangle.v0, triangle.v1, triangle.v2, distance, allowBackface);
|
||||
}
|
||||
|
||||
int clipTriangleWithPlane(const Triangle& triangle, const Plane& plane, Triangle* clippedTriangles, int maxClippedTriangleCount);
|
||||
int clipTriangleWithPlanes(const Triangle& triangle, const Plane* planes, int planeCount, Triangle* clippedTriangles, int maxClippedTriangleCount);
|
||||
|
||||
bool doLineSegmentsIntersect(glm::vec2 r1p1, glm::vec2 r1p2, glm::vec2 r2p1, glm::vec2 r2p2);
|
||||
bool isOnSegment(float xi, float yi, float xj, float yj, float xk, float yk);
|
||||
|
|
|
@ -19,7 +19,9 @@
|
|||
|
||||
class Plane {
|
||||
public:
|
||||
Plane(const glm::vec3 &v1, const glm::vec3 &v2, const glm::vec3 &v3) { set3Points(v1,v2,v3); }
|
||||
Plane(const glm::vec3 &v1, const glm::vec3 &v2, const glm::vec3 &v3) { set3Points(v1, v2, v3); }
|
||||
Plane(const glm::vec3 &normal, const glm::vec3 &point) { setNormalAndPoint(normal, point); }
|
||||
Plane(float a, float b, float c, float d) { setCoefficients(a, b, c, d); }
|
||||
Plane() : _normal(0.0f), _point(0.0f), _dCoefficient(0.0f) {};
|
||||
~Plane() {} ;
|
||||
|
||||
|
|
|
@ -128,12 +128,21 @@ void aaCubeFromScriptValue(const QScriptValue &object, AACube& aaCube);
|
|||
|
||||
// MathPicks also have to overide operator== for their type
|
||||
class MathPick {
|
||||
public:
|
||||
virtual ~MathPick() {}
|
||||
virtual operator bool() const = 0;
|
||||
virtual QVariantMap toVariantMap() const = 0;
|
||||
};
|
||||
|
||||
class PickRay : public MathPick {
|
||||
public:
|
||||
/**jsdoc
|
||||
* The mathematical definition of a ray.
|
||||
*
|
||||
* @typedef {Object} PickRay
|
||||
* @property {Vec3} origin The origin of the ray.
|
||||
* @property {Vec3} direction The direction of the ray.
|
||||
*/
|
||||
PickRay() : origin(NAN), direction(NAN) { }
|
||||
PickRay(const QVariantMap& pickVariant) : origin(vec3FromVariant(pickVariant["origin"])), direction(vec3FromVariant(pickVariant["direction"])) {}
|
||||
PickRay(const glm::vec3& origin, const glm::vec3 direction) : origin(origin), direction(direction) {}
|
||||
|
@ -156,6 +165,15 @@ public:
|
|||
|
||||
class StylusTip : public MathPick {
|
||||
public:
|
||||
/**jsdoc
|
||||
* The mathematical definition of a stylus tip.
|
||||
*
|
||||
* @typedef {Object} StylusTip
|
||||
* @property {number} side The hand the tip is attached to. 0 == left, 1 == right.
|
||||
* @property {Vec3} position The position of the tip.
|
||||
* @property {Quat} orientation The orientation of the tip.
|
||||
* @property {Vec3} velocity The velocity of the tip.
|
||||
*/
|
||||
StylusTip() : position(NAN), velocity(NAN) {}
|
||||
StylusTip(const QVariantMap& pickVariant) : side(bilateral::Side(pickVariant["side"].toInt())), position(vec3FromVariant(pickVariant["position"])),
|
||||
orientation(quatFromVariant(pickVariant["orientation"])), velocity(vec3FromVariant(pickVariant["velocity"])) {}
|
||||
|
|
|
@ -1081,7 +1081,7 @@ void setMaxCores(uint8_t maxCores) {
|
|||
void quitWithParentProcess() {
|
||||
if (qApp) {
|
||||
qDebug() << "Parent process died, quitting";
|
||||
qApp->quit();
|
||||
exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1113,3 +1113,57 @@ void watchParentProcess(int parentPID) {
|
|||
timer->start();
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
QString getLastErrorAsString() {
|
||||
DWORD errorMessageID = ::GetLastError();
|
||||
if (errorMessageID == 0) {
|
||||
return QString();
|
||||
}
|
||||
|
||||
LPSTR messageBuffer = nullptr;
|
||||
size_t size = FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
|
||||
nullptr, errorMessageID, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)&messageBuffer, 0, nullptr);
|
||||
|
||||
auto message = QString::fromLocal8Bit(messageBuffer, (int)size);
|
||||
|
||||
//Free the buffer.
|
||||
LocalFree(messageBuffer);
|
||||
|
||||
return message;
|
||||
}
|
||||
|
||||
// All processes in the group will shut down with the process creating the group
|
||||
void* createProcessGroup() {
|
||||
HANDLE jobObject = CreateJobObject(nullptr, nullptr);
|
||||
if (jobObject == nullptr) {
|
||||
qWarning() << "Could NOT create job object:" << getLastErrorAsString();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
JOBOBJECT_EXTENDED_LIMIT_INFORMATION JELI;
|
||||
if (!QueryInformationJobObject(jobObject, JobObjectExtendedLimitInformation, &JELI, sizeof(JELI), nullptr)) {
|
||||
qWarning() << "Could NOT query job object information" << getLastErrorAsString();
|
||||
return nullptr;
|
||||
}
|
||||
JELI.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE;
|
||||
if (!SetInformationJobObject(jobObject, JobObjectExtendedLimitInformation, &JELI, sizeof(JELI))) {
|
||||
qWarning() << "Could NOT set job object information" << getLastErrorAsString();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return jobObject;
|
||||
}
|
||||
|
||||
void addProcessToGroup(void* processGroup, qint64 processId) {
|
||||
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, processId);
|
||||
if (hProcess == nullptr) {
|
||||
qCritical() << "Could NOT open process" << getLastErrorAsString();
|
||||
}
|
||||
if (!AssignProcessToJobObject(processGroup, hProcess)) {
|
||||
qCritical() << "Could NOT assign process to job object" << getLastErrorAsString();
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -238,4 +238,10 @@ void setMaxCores(uint8_t maxCores);
|
|||
const QString PARENT_PID_OPTION = "parent-pid";
|
||||
void watchParentProcess(int parentPID);
|
||||
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
void* createProcessGroup();
|
||||
void addProcessToGroup(void* processGroup, qint64 processId);
|
||||
#endif
|
||||
|
||||
#endif // hifi_SharedUtil_h
|
||||
|
|
|
@ -149,6 +149,7 @@ public:
|
|||
|
||||
Vec4 transform(const Vec4& pos) const;
|
||||
Vec3 transform(const Vec3& pos) const;
|
||||
Vec3 transformDirection(const Vec3& dir) const;
|
||||
|
||||
bool containsNaN() const { return isNaN(_rotation) || isNaN(glm::dot(_scale, _translation)); }
|
||||
|
||||
|
@ -541,6 +542,13 @@ inline Transform::Vec3 Transform::transform(const Vec3& pos) const {
|
|||
return Vec3(result.x / result.w, result.y / result.w, result.z / result.w);
|
||||
}
|
||||
|
||||
inline Transform::Vec3 Transform::transformDirection(const Vec3& dir) const {
|
||||
Mat4 m;
|
||||
getMatrix(m);
|
||||
Vec4 result = m * Vec4(dir, 0.0f);
|
||||
return Vec3(result.x, result.y, result.z);
|
||||
}
|
||||
|
||||
inline Transform::Mat4& Transform::getCachedMatrix(Transform::Mat4& result) const {
|
||||
updateCache();
|
||||
result = (*_matrix);
|
||||
|
|
|
@ -28,7 +28,7 @@
|
|||
|
||||
#include "ui/Logging.h"
|
||||
|
||||
#include <pointers/PointerManager.h>
|
||||
#include <PointerManager.h>
|
||||
|
||||
// Needs to match the constants in resources/qml/Global.js
|
||||
class OffscreenFlags : public QObject {
|
||||
|
|
|
@ -1169,6 +1169,7 @@ static const uint8_t BACKSPACE_SYMBOL[] = { 0xE2, 0x86, 0x90, 0x00 };
|
|||
static const uint8_t LEFT_ARROW[] = { 0xE2, 0x9D, 0xAC, 0x00 };
|
||||
static const uint8_t RIGHT_ARROW[] = { 0xE2, 0x9D, 0xAD, 0x00 };
|
||||
static const uint8_t RETURN_SYMBOL[] = { 0xE2, 0x8F, 0x8E, 0x00 };
|
||||
static const uint8_t COLLAPSE_KEYBOARD[] = { 0xEE, 0x80, 0xAB, 0x00 };
|
||||
static const char PUNCTUATION_STRING[] = "123";
|
||||
static const char ALPHABET_STRING[] = "abc";
|
||||
|
||||
|
@ -1192,6 +1193,9 @@ void OffscreenQmlSurface::synthesizeKeyPress(QString key, QObject* targetOverrid
|
|||
if (equals(utf8Key, SHIFT_ARROW) || equals(utf8Key, NUMERIC_SHIFT_ARROW) ||
|
||||
equals(utf8Key, (uint8_t*)PUNCTUATION_STRING) || equals(utf8Key, (uint8_t*)ALPHABET_STRING)) {
|
||||
return; // ignore
|
||||
} else if (equals(utf8Key, COLLAPSE_KEYBOARD)) {
|
||||
lowerKeyboard();
|
||||
return;
|
||||
} else if (equals(utf8Key, BACKSPACE_SYMBOL)) {
|
||||
scanCode = Qt::Key_Backspace;
|
||||
keyString = "\x08";
|
||||
|
@ -1213,7 +1217,19 @@ void OffscreenQmlSurface::synthesizeKeyPress(QString key, QObject* targetOverrid
|
|||
}
|
||||
}
|
||||
|
||||
void OffscreenQmlSurface::lowerKeyboard() {
|
||||
|
||||
QSignalBlocker blocker(_quickWindow);
|
||||
|
||||
if (_currentFocusItem) {
|
||||
_currentFocusItem->setFocus(false);
|
||||
setKeyboardRaised(_currentFocusItem, false);
|
||||
}
|
||||
}
|
||||
|
||||
void OffscreenQmlSurface::setKeyboardRaised(QObject* object, bool raised, bool numeric, bool passwordField) {
|
||||
qCDebug(uiLogging) << "setKeyboardRaised: " << object << ", raised: " << raised << ", numeric: " << numeric << ", password: " << passwordField;
|
||||
|
||||
#if Q_OS_ANDROID
|
||||
return;
|
||||
#endif
|
||||
|
@ -1248,6 +1264,10 @@ void OffscreenQmlSurface::setKeyboardRaised(QObject* object, bool raised, bool n
|
|||
item->setProperty("passwordField", QVariant(passwordField));
|
||||
}
|
||||
|
||||
if (raised) {
|
||||
item->setProperty("keyboardRaised", QVariant(!raised));
|
||||
}
|
||||
|
||||
item->setProperty("keyboardRaised", QVariant(raised));
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -87,6 +87,7 @@ public:
|
|||
|
||||
void setKeyboardRaised(QObject* object, bool raised, bool numeric = false, bool passwordField = false);
|
||||
Q_INVOKABLE void synthesizeKeyPress(QString key, QObject* targetOverride = nullptr);
|
||||
Q_INVOKABLE void lowerKeyboard();
|
||||
|
||||
using TextureAndFence = std::pair<uint32_t, void*>;
|
||||
// Checks to see if a new texture is available. If one is, the function returns true and
|
||||
|
|
|
@ -96,7 +96,6 @@ function rayCastTest() {
|
|||
color: color,
|
||||
alpha: 1,
|
||||
visible: visible,
|
||||
lineWidth: 2,
|
||||
start: origin,
|
||||
end: Vec3.sum(origin,Vec3.multiply(5,direction))
|
||||
});
|
||||
|
|
119
scripts/developer/utilities/render/bloom.qml
Normal file
119
scripts/developer/utilities/render/bloom.qml
Normal file
|
@ -0,0 +1,119 @@
|
|||
//
|
||||
// bloom.qml
|
||||
// developer/utilities/render
|
||||
//
|
||||
// Olivier Prat, created on 09/25/2017.
|
||||
// Copyright 2017 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or https://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
import QtQuick 2.5
|
||||
import QtQuick.Controls 1.4
|
||||
import "configSlider"
|
||||
|
||||
Item {
|
||||
id: root
|
||||
property var config: Render.getConfig("RenderMainView.Bloom")
|
||||
property var configThreshold: Render.getConfig("RenderMainView.BloomThreshold")
|
||||
property var configDebug: Render.getConfig("RenderMainView.DebugBloom")
|
||||
|
||||
Column {
|
||||
spacing: 8
|
||||
|
||||
CheckBox {
|
||||
text: "Enable"
|
||||
checked: root.config["enabled"]
|
||||
onCheckedChanged: {
|
||||
root.config["enabled"] = checked;
|
||||
}
|
||||
}
|
||||
GroupBox {
|
||||
title: "Debug"
|
||||
Row {
|
||||
ExclusiveGroup { id: debugGroup }
|
||||
RadioButton {
|
||||
text : "Off"
|
||||
checked : !root.configDebug["enabled"]
|
||||
onCheckedChanged: {
|
||||
if (checked) {
|
||||
root.configDebug["enabled"] = false
|
||||
}
|
||||
}
|
||||
exclusiveGroup : debugGroup
|
||||
}
|
||||
RadioButton {
|
||||
text : "Lvl 0"
|
||||
checked :root.configDebug["enabled"] && root.configDebug["mode"]==0
|
||||
onCheckedChanged: {
|
||||
if (checked) {
|
||||
root.configDebug["enabled"] = true
|
||||
root.configDebug["mode"] = 0
|
||||
}
|
||||
}
|
||||
exclusiveGroup : debugGroup
|
||||
}
|
||||
RadioButton {
|
||||
text : "Lvl 1"
|
||||
checked : root.configDebug["enabled"] && root.configDebug["mode"]==1
|
||||
onCheckedChanged: {
|
||||
if (checked) {
|
||||
root.configDebug["enabled"] = true
|
||||
root.configDebug["mode"] = 1
|
||||
}
|
||||
}
|
||||
exclusiveGroup : debugGroup
|
||||
}
|
||||
RadioButton {
|
||||
text : "Lvl 2"
|
||||
checked : root.configDebug["enabled"] && root.configDebug["mode"]==2
|
||||
onCheckedChanged: {
|
||||
if (checked) {
|
||||
root.configDebug["enabled"] = true
|
||||
root.configDebug["mode"] = 2
|
||||
}
|
||||
}
|
||||
exclusiveGroup : debugGroup
|
||||
}
|
||||
RadioButton {
|
||||
text : "All"
|
||||
checked : root.configDebug["enabled"] && root.configDebug["mode"]==3
|
||||
onCheckedChanged: {
|
||||
if (checked) {
|
||||
root.configDebug["enabled"] = true
|
||||
root.configDebug["mode"] = 3
|
||||
}
|
||||
}
|
||||
exclusiveGroup : debugGroup
|
||||
}
|
||||
}
|
||||
}
|
||||
ConfigSlider {
|
||||
label: "Intensity"
|
||||
integral: false
|
||||
config: root.config
|
||||
property: "intensity"
|
||||
max: 5.0
|
||||
min: 0.0
|
||||
width: 280
|
||||
}
|
||||
ConfigSlider {
|
||||
label: "Size"
|
||||
integral: false
|
||||
config: root.config
|
||||
property: "size"
|
||||
max: 1.0
|
||||
min: 0.0
|
||||
width: 280
|
||||
}
|
||||
ConfigSlider {
|
||||
label: "Threshold"
|
||||
integral: false
|
||||
config: root.configThreshold
|
||||
property: "threshold"
|
||||
max: 2.0
|
||||
min: 0.0
|
||||
width: 280
|
||||
}
|
||||
}
|
||||
}
|
20
scripts/developer/utilities/render/debugBloom.js
Normal file
20
scripts/developer/utilities/render/debugBloom.js
Normal file
|
@ -0,0 +1,20 @@
|
|||
//
|
||||
// debugBloom.js
|
||||
// developer/utilities/render
|
||||
//
|
||||
// Olivier Prat, created on 09/25/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
|
||||
//
|
||||
|
||||
// Set up the qml ui
|
||||
var qml = Script.resolvePath('bloom.qml');
|
||||
var window = new OverlayWindow({
|
||||
title: 'Bloom',
|
||||
source: qml,
|
||||
width: 285,
|
||||
height: 170,
|
||||
});
|
||||
window.closed.connect(function() { Script.stop(); });
|
20
scripts/developer/utilities/render/debugShadow.js
Normal file
20
scripts/developer/utilities/render/debugShadow.js
Normal file
|
@ -0,0 +1,20 @@
|
|||
//
|
||||
// debugShadow.js
|
||||
// developer/utilities/render
|
||||
//
|
||||
// Olivier Prat, created on 10/25/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
|
||||
//
|
||||
|
||||
// Set up the qml ui
|
||||
var qml = Script.resolvePath('shadow.qml');
|
||||
var window = new OverlayWindow({
|
||||
title: 'Shadow Debug',
|
||||
source: qml,
|
||||
width: 200,
|
||||
height: 90
|
||||
});
|
||||
window.closed.connect(function() { Script.stop(); });
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue