fixing merge conflicts

This commit is contained in:
Dante Ruiz 2017-11-16 10:04:36 -08:00
commit f6a74cc80e
118 changed files with 2539 additions and 335 deletions

View file

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

@ -66,6 +66,9 @@ TAGS
*.sw[po]
*.qmlc
# ignore QML compilation output
*.qmlc
# ignore node files for the console
node_modules
npm-debug.log

View file

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

After

Width:  |  Height:  |  Size: 1 KiB

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -366,7 +366,7 @@ StackView {
HifiControls.Keyboard {
id: keyboard
raised: parent.keyboardEnabled
raised: parent.keyboardEnabled && parent.keyboardRaised
numeric: parent.punctuationMode
anchors {
bottom: parent.bottom

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -9,7 +9,7 @@
#define hifi_RayPick_h
#include <RegisteredMetaTypes.h>
#include <pointers/Pick.h>
#include <Pick.h>
class EntityItemID;
class OverlayID;

View file

@ -14,7 +14,7 @@
#include <QVariant>
#include "GLMHelpers.h"
#include <pointers/PickManager.h>
#include <PickManager.h>
#include "StaticRayPick.h"
#include "JointRayPick.h"

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

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

View file

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

View file

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

View file

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

View file

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

View file

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

View 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);
}

View 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);
}

View 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

View 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);
}

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -10,8 +10,8 @@
//
in vec4 _color;
in float distanceFromCenter;
in float distanceFromCenter;
out vec4 _fragColor;
void main(void) {

View file

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

View file

@ -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*) &params));
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>()))
{
}

View file

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

View file

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

View 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

View file

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

View file

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

View 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);
});
}

View 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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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"])) {}

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -96,7 +96,6 @@ function rayCastTest() {
color: color,
alpha: 1,
visible: visible,
lineWidth: 2,
start: origin,
end: Vec3.sum(origin,Vec3.multiply(5,direction))
});

View 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
}
}
}

View 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(); });

View 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